Decompiled source of MLVScan v2.0.2

Plugins/MLVScan.MelonLoader.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using MLVScan.Abstractions;
using MLVScan.Adapters;
using MLVScan.MelonLoader;
using MLVScan.Models;
using MLVScan.Models.CrossAssembly;
using MLVScan.Models.DataFlow;
using MLVScan.Models.Dto;
using MLVScan.Models.Rules;
using MLVScan.Models.Rules.Helpers;
using MLVScan.Models.ThreatIntel;
using MLVScan.Services;
using MLVScan.Services.Caching;
using MLVScan.Services.Configuration;
using MLVScan.Services.DataFlow;
using MLVScan.Services.Diagnostics;
using MLVScan.Services.Helpers;
using MLVScan.Services.Resolution;
using MLVScan.Services.Scope;
using MLVScan.Services.ThreatIntel;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Microsoft.Win32.SafeHandles;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(MelonLoaderPlugin), "MLVScan", "2.0.2", "Bars", null)]
[assembly: MelonPriority(int.MinValue)]
[assembly: MelonColor(255, 139, 0, 0)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MLVScan.MelonLoader")]
[assembly: AssemblyConfiguration("MelonLoader")]
[assembly: AssemblyFileVersion("2.0.2")]
[assembly: AssemblyInformationalVersion("2.0.2+8f6dd31d0a1cb0fc97580de7b8295c61eb93fa4e")]
[assembly: AssemblyProduct("MLVScan.MelonLoader")]
[assembly: AssemblyTitle("MLVScan.MelonLoader")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MLVScan
{
	internal static class MLVScanBuildInfo
	{
		public const string PlatformVersion = "2.0.2";
	}
	public static class PlatformConstants
	{
		public const string PlatformVersion = "2.0.2";

		public const string PlatformName = "MLVScan.MelonLoader";

		public static string GetVersionString()
		{
			return "MLVScan.MelonLoader v2.0.2";
		}

		public static string GetFullVersionInfo()
		{
			return "Engine: MLVScan.Core v" + MLVScanVersions.CoreVersion + " Platform: " + GetVersionString();
		}
	}
}
namespace MLVScan.Adapters
{
	public class MelonScanLogger : IScanLogger
	{
		private readonly Instance _logger;

		public MelonScanLogger(Instance logger)
		{
			_logger = logger ?? throw new ArgumentNullException("logger");
		}

		public void Debug(string message)
		{
			_logger.Msg("[DEBUG] " + message);
		}

		public void Info(string message)
		{
			_logger.Msg(message);
		}

		public void Warning(string message)
		{
			_logger.Warning(message);
		}

		public void Error(string message)
		{
			_logger.Error(message);
		}

		public void Error(string message, Exception exception)
		{
			_logger.Error($"{message}: {exception}");
		}
	}
	public class GameAssemblyResolverProvider : CatalogingAssemblyResolverProviderBase
	{
		public GameAssemblyResolverProvider(LoaderScanTelemetryHub telemetry)
			: base(telemetry)
		{
		}

		protected override IEnumerable<ResolverRoot> GetStableRoots()
		{
			List<ResolverRoot> list = new List<ResolverRoot>();
			try
			{
				string path = Path.Combine(MelonEnvironment.GameRootDirectory, Path.GetFileNameWithoutExtension(MelonEnvironment.GameExecutablePath) + "_Data", "Managed");
				if (Directory.Exists(path))
				{
					list.Add(new ResolverRoot(path, 0));
				}
				string path2 = Path.Combine(MelonEnvironment.GameRootDirectory, "MelonLoader", "net35");
				if (Directory.Exists(path2))
				{
					list.Add(new ResolverRoot(path2, 5));
				}
				string path3 = Path.Combine(MelonEnvironment.GameRootDirectory, "MelonLoader", "net6");
				if (Directory.Exists(path3))
				{
					list.Add(new ResolverRoot(path3, 6));
				}
			}
			catch (Exception)
			{
				return list;
			}
			return list;
		}
	}
}
namespace MLVScan.MelonLoader
{
	public class MelonLoaderPlugin : MelonPlugin
	{
		private MelonLoaderServiceFactory _serviceFactory;

		private MelonConfigManager _configManager;

		private MelonPlatformEnvironment _environment;

		private MelonPluginScanner _pluginScanner;

		private MelonPluginDisabler _pluginDisabler;

		private IlDumpService _ilDumpService;

		private DeveloperReportGenerator _developerReportGenerator;

		private ReportUploadService _reportUploadService;

		private bool _initialized = false;

		private bool _showUploadConsentPopup;

		private string _pendingUploadPath = string.Empty;

		private string _pendingUploadModName = string.Empty;

		private string _pendingUploadVerdictKind = string.Empty;

		private bool _pendingUploadWasBlocked = true;

		private List<ScanFinding> _pendingUploadFindings;

		public override void OnEarlyInitializeMelon()
		{
			try
			{
				((MelonBase)this).LoggerInstance.Msg("Pre-scanning for malicious mods...");
				_serviceFactory = new MelonLoaderServiceFactory(((MelonBase)this).LoggerInstance);
				_configManager = _serviceFactory.CreateConfigManager();
				_environment = _serviceFactory.CreateEnvironment();
				_pluginScanner = _serviceFactory.CreatePluginScanner();
				_pluginDisabler = _serviceFactory.CreatePluginDisabler();
				_ilDumpService = _serviceFactory.CreateIlDumpService();
				_developerReportGenerator = _serviceFactory.CreateDeveloperReportGenerator();
				_reportUploadService = _serviceFactory.CreateReportUploadService();
				_initialized = true;
				ScanAndDisableMods(force: true);
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Error in pre-mod scan: " + ex.Message);
				((MelonBase)this).LoggerInstance.Error(ex.StackTrace);
			}
		}

		public override void OnInitializeMelon()
		{
			try
			{
				((MelonBase)this).LoggerInstance.Msg("MLVScan initialization complete");
				if (_configManager.Config.WhitelistedHashes.Length != 0)
				{
					((MelonBase)this).LoggerInstance.Msg($"{_configManager.Config.WhitelistedHashes.Length} mod(s) are whitelisted and won't be scanned");
					((MelonBase)this).LoggerInstance.Msg("To manage whitelisted mods, edit MelonPreferences.cfg");
				}
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Error initializing MLVScan: " + ex.Message);
				((MelonBase)this).LoggerInstance.Error(ex.StackTrace);
			}
		}

		public override void OnGUI()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			if (_showUploadConsentPopup)
			{
				float num = Math.Min(620f, (float)Screen.width - 40f);
				float num2 = 280f;
				float num3 = ((float)Screen.width - num) / 2f;
				float num4 = ((float)Screen.height - num2) / 2f;
				GUI.Box(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), string.Empty);
				GUI.Box(new Rect(num3, num4, num, num2), "MLVScan Upload Consent");
				GUI.Label(new Rect(num3 + 20f, num4 + 40f, num - 40f, 140f), ConsentMessageHelper.GetUploadConsentMessage(_pendingUploadModName, _pendingUploadVerdictKind, _pendingUploadWasBlocked) + "\n\nWould you like to upload this file to the MLVScan API for human review?\n\nYes: upload this mod now and enable automatic uploads for future detections.\nNo: do not upload and do not show this prompt again.");
				if (GUI.Button(new Rect(num3 + 20f, num4 + num2 - 60f, (num - 60f) / 2f, 36f), "Yes, upload"))
				{
					HandleUploadConsentDecision(approved: true);
				}
				if (GUI.Button(new Rect(num3 + 40f + (num - 60f) / 2f, num4 + num2 - 60f, (num - 60f) / 2f, 36f), "No thanks"))
				{
					HandleUploadConsentDecision(approved: false);
				}
			}
		}

		public Dictionary<string, ScannedPluginResult> ScanAndDisableMods(bool force = false)
		{
			try
			{
				if (!_initialized)
				{
					((MelonBase)this).LoggerInstance.Error("Cannot scan mods - MLVScan not properly initialized");
					return new Dictionary<string, ScannedPluginResult>();
				}
				((MelonBase)this).LoggerInstance.Msg("Scanning mods for threats...");
				Dictionary<string, ScannedPluginResult> source = _pluginScanner.ScanAllPlugins(force);
				Dictionary<string, ScannedPluginResult> dictionary = source.Where((KeyValuePair<string, ScannedPluginResult> kv) => kv.Value != null && ScanResultFacts.RequiresAttention(kv.Value)).ToDictionary((KeyValuePair<string, ScannedPluginResult> kv) => kv.Key, (KeyValuePair<string, ScannedPluginResult> kv) => kv.Value);
				if (dictionary.Count > 0)
				{
					List<DisabledPluginInfo> list = _pluginDisabler.DisableSuspiciousPlugins(dictionary, force);
					int count = list.Count;
					int num = dictionary.Count - count;
					if (count > 0)
					{
						((MelonBase)this).LoggerInstance.Msg($"Disabled {count} mod(s) that matched the active blocking policy");
					}
					if (num > 0)
					{
						((MelonBase)this).LoggerInstance.Warning($"{num} mod(s) require manual review but were not blocked by the current configuration");
					}
					GenerateDetailedReports(list, dictionary);
					((MelonBase)this).LoggerInstance.Msg("To whitelist any false positives, add their SHA256 hash to the MLVScan → WhitelistedHashes setting in MelonPreferences.cfg");
				}
				else
				{
					((MelonBase)this).LoggerInstance.Msg("No mods requiring action were found");
				}
				return dictionary;
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("Error scanning mods: " + ex.Message);
				return new Dictionary<string, ScannedPluginResult>();
			}
		}

		private void GenerateDetailedReports(List<DisabledPluginInfo> disabledMods, Dictionary<string, ScannedPluginResult> scanResults)
		{
			bool valueOrDefault = (_configManager?.Config?.Scan?.DeveloperMode).GetValueOrDefault();
			Dictionary<string, DisabledPluginInfo> dictionary = (disabledMods ?? new List<DisabledPluginInfo>()).ToDictionary<DisabledPluginInfo, string>((DisabledPluginInfo info) => info.OriginalPath, StringComparer.OrdinalIgnoreCase);
			if (valueOrDefault)
			{
				((MelonBase)this).LoggerInstance.Msg("Developer Mode: Enabled");
			}
			((MelonBase)this).LoggerInstance.Warning("======= DETAILED SCAN REPORT =======");
			((MelonBase)this).LoggerInstance.Msg(PlatformConstants.GetFullVersionInfo());
			foreach (KeyValuePair<string, ScannedPluginResult> item in scanResults.OrderBy<KeyValuePair<string, ScannedPluginResult>, string>((KeyValuePair<string, ScannedPluginResult> kv) => Path.GetFileName(kv.Key), StringComparer.OrdinalIgnoreCase))
			{
				item.Deconstruct(out var key, out var value);
				string text = key;
				ScannedPluginResult scannedPluginResult = value;
				dictionary.TryGetValue(text, out var value2);
				bool flag = value2 != null;
				string fileName = Path.GetFileName(text);
				string text2 = value2?.FileHash ?? scannedPluginResult?.FileHash ?? string.Empty;
				string text3 = value2?.OriginalPath ?? scannedPluginResult?.FilePath ?? string.Empty;
				string text4 = ((flag && File.Exists(value2.DisabledPath)) ? value2.DisabledPath : (scannedPluginResult?.FilePath ?? text));
				List<ScanFinding> list = scannedPluginResult?.Findings ?? new List<ScanFinding>();
				ThreatVerdictInfo threatVerdictInfo = value2?.ThreatVerdict ?? scannedPluginResult?.ThreatVerdict ?? new ThreatVerdictInfo();
				ScanStatusInfo scanStatusInfo = value2?.ScanStatus ?? scannedPluginResult?.ScanStatus ?? new ScanStatusInfo();
				string outcomeLabel = ThreatVerdictTextFormatter.GetOutcomeLabel(scannedPluginResult);
				string outcomeSummary = ThreatVerdictTextFormatter.GetOutcomeSummary(scannedPluginResult);
				Dictionary<string, List<ScanFinding>> dictionary2 = (from f in list
					group f by f.Description).ToDictionary((IGrouping<string, ScanFinding> g) => g.Key, (IGrouping<string, ScanFinding> g) => g.ToList());
				((MelonBase)this).LoggerInstance.Warning((flag ? "BLOCKED MOD" : "REVIEW REQUIRED") + ": " + fileName);
				((MelonBase)this).LoggerInstance.Msg("SHA256 Hash: " + text2);
				((MelonBase)this).LoggerInstance.Msg("-------------------------------");
				if (list.Count == 0 && threatVerdictInfo.Kind == ThreatVerdictKind.None && scanStatusInfo.Kind == ScanStatusKind.Complete)
				{
					((MelonBase)this).LoggerInstance.Msg("No specific findings were retained.");
					continue;
				}
				if (threatVerdictInfo.Kind != 0)
				{
					QueueConsentPromptIfNeeded(text4, fileName, list, threatVerdictInfo, flag);
				}
				((MelonBase)this).LoggerInstance.Warning($"Total retained findings: {list.Count}");
				if (!string.IsNullOrWhiteSpace(outcomeLabel))
				{
					((MelonBase)this).LoggerInstance.Warning("Outcome: " + outcomeLabel);
				}
				if (!string.IsNullOrWhiteSpace(outcomeSummary))
				{
					((MelonBase)this).LoggerInstance.Msg(outcomeSummary);
				}
				if (scanStatusInfo.Kind != 0)
				{
					((MelonBase)this).LoggerInstance.Msg(flag ? "Action: blocked by current incomplete-scan policy." : "Action: manual review required; not blocked by current config.");
				}
				string primaryFamilyLabel = ThreatVerdictTextFormatter.GetPrimaryFamilyLabel(threatVerdictInfo);
				if (!string.IsNullOrWhiteSpace(primaryFamilyLabel))
				{
					((MelonBase)this).LoggerInstance.Msg("Family: " + primaryFamilyLabel);
				}
				string confidenceLabel = ThreatVerdictTextFormatter.GetConfidenceLabel(threatVerdictInfo);
				if (!string.IsNullOrWhiteSpace(confidenceLabel))
				{
					((MelonBase)this).LoggerInstance.Msg("Confidence: " + confidenceLabel);
				}
				Dictionary<Severity, int> dictionary3 = (from f in list
					group f by f.Severity into g
					orderby (int)g.Key descending
					select g).ToDictionary((IGrouping<Severity, ScanFinding> g) => g.Key, (IGrouping<Severity, ScanFinding> g) => g.Count());
				((MelonBase)this).LoggerInstance.Warning("Severity breakdown:");
				foreach (KeyValuePair<Severity, int> item2 in dictionary3)
				{
					string arg = FormatSeverityLabel(item2.Key);
					((MelonBase)this).LoggerInstance.Msg($"  {arg}: {item2.Value} issue(s)");
				}
				((MelonBase)this).LoggerInstance.Msg("-------------------------------");
				List<string> topFindingSummaries = ThreatVerdictTextFormatter.GetTopFindingSummaries(list);
				if (topFindingSummaries.Count > 0)
				{
					((MelonBase)this).LoggerInstance.Warning("Top signals:");
					foreach (string item3 in topFindingSummaries)
					{
						((MelonBase)this).LoggerInstance.Msg("  - " + item3);
					}
				}
				if (valueOrDefault)
				{
					((MelonBase)this).LoggerInstance.Msg("Developer mode is enabled. Full remediation guidance is included in the report file.");
				}
				((MelonBase)this).LoggerInstance.Msg("Full technical details were written to the saved report file for human review.");
				((MelonBase)this).LoggerInstance.Msg("-------------------------------");
				DisplaySecurityNotice(fileName, threatVerdictInfo, scanStatusInfo, flag);
				try
				{
					string text5 = _environment?.ReportsDirectory ?? Path.Combine(MelonEnvironment.UserDataDirectory, "MLVScan", "Reports");
					if (!Directory.Exists(text5))
					{
						Directory.CreateDirectory(text5);
					}
					string text6 = DateTime.Now.ToString("yyyyMMdd_HHmmss");
					string text7 = Path.Combine(text5, fileName + "_" + text6 + ".report.txt");
					string text8 = Path.Combine(text5, "Prompts");
					if (!Directory.Exists(text8))
					{
						Directory.CreateDirectory(text8);
					}
					MelonConfigManager configManager = _configManager;
					if (configManager != null && (configManager.Config?.DumpFullIlReports).GetValueOrDefault() && _ilDumpService != null && scanStatusInfo.Kind == ScanStatusKind.Complete)
					{
						string path = Path.Combine(text5, "IL");
						string text9 = Path.Combine(path, fileName + "_" + text6 + ".il.txt");
						if (_ilDumpService.TryDumpAssembly(text4, text9))
						{
							((MelonBase)this).LoggerInstance.Msg("Full IL dump saved to: " + text9);
						}
						else
						{
							((MelonBase)this).LoggerInstance.Warning("Failed to dump IL for this mod (see logs for details).");
						}
					}
					else
					{
						MelonConfigManager configManager2 = _configManager;
						if (configManager2 != null && (configManager2.Config?.DumpFullIlReports).GetValueOrDefault() && scanStatusInfo.Kind == ScanStatusKind.RequiresReview)
						{
							((MelonBase)this).LoggerInstance.Warning("Skipped full IL dump because this file was not fully analyzed by the loader.");
						}
					}
					PromptGeneratorService promptGeneratorService = ((scanStatusInfo.Kind == ScanStatusKind.Complete) ? _serviceFactory.CreatePromptGenerator() : null);
					using (StreamWriter streamWriter = new StreamWriter(text7))
					{
						if (valueOrDefault && _developerReportGenerator != null)
						{
							string value3 = _developerReportGenerator.GenerateFileReport(fileName, text2, list, threatVerdictInfo, scanStatusInfo);
							streamWriter.Write(value3);
						}
						else
						{
							streamWriter.WriteLine("MLVScan Security Report");
							streamWriter.WriteLine(PlatformConstants.GetFullVersionInfo());
							streamWriter.WriteLine($"Generated: {DateTime.Now}");
							streamWriter.WriteLine("Mod File: " + fileName);
							streamWriter.WriteLine("Outcome: " + outcomeLabel);
							if (!string.IsNullOrWhiteSpace(outcomeSummary))
							{
								streamWriter.WriteLine("Outcome Summary: " + outcomeSummary);
							}
							streamWriter.WriteLine("Action Taken: " + (flag ? "Blocked" : "Manual review required (not blocked by current config)"));
							streamWriter.WriteLine("SHA256 Hash: " + text2);
							streamWriter.WriteLine("Original Path: " + text3);
							streamWriter.WriteLine((flag ? "Disabled Path" : "Current Path") + ": " + text4);
							streamWriter.WriteLine("Path Used For Analysis: " + text4);
							streamWriter.WriteLine($"Total Retained Findings: {list.Count}");
							streamWriter.WriteLine();
							ThreatVerdictTextFormatter.WriteThreatVerdictSection(streamWriter, threatVerdictInfo);
							ThreatVerdictTextFormatter.WriteScanStatusSection(streamWriter, scanStatusInfo);
							streamWriter.WriteLine("\nSeverity Breakdown:");
							foreach (KeyValuePair<Severity, int> item4 in dictionary3)
							{
								streamWriter.WriteLine($"- {item4.Key}: {item4.Value} issue(s)");
							}
							streamWriter.WriteLine("==============================================");
							foreach (KeyValuePair<string, List<ScanFinding>> item5 in dictionary2)
							{
								streamWriter.WriteLine("\n== " + item5.Key + " ==");
								streamWriter.WriteLine($"Severity: {item5.Value[0].Severity}");
								streamWriter.WriteLine($"Instances: {item5.Value.Count}");
								streamWriter.WriteLine("\nLocations & Analysis:");
								foreach (ScanFinding item6 in item5.Value)
								{
									streamWriter.WriteLine("- " + item6.Location);
									if (item6.HasCallChain && item6.CallChain != null)
									{
										streamWriter.WriteLine("  Call Chain Analysis:");
										streamWriter.WriteLine("  " + item6.CallChain.Summary);
										streamWriter.WriteLine("  Attack Path:");
										foreach (CallChainNode node in item6.CallChain.Nodes)
										{
											CallChainNodeType nodeType = node.NodeType;
											if (1 == 0)
											{
											}
											key = nodeType switch
											{
												CallChainNodeType.EntryPoint => "[ENTRY]", 
												CallChainNodeType.IntermediateCall => "[CALL]", 
												CallChainNodeType.SuspiciousDeclaration => "[DECL]", 
												_ => "[???]", 
											};
											if (1 == 0)
											{
											}
											string text10 = key;
											streamWriter.WriteLine("    " + text10 + " " + node.Location);
											if (!string.IsNullOrEmpty(node.Description))
											{
												streamWriter.WriteLine("         " + node.Description);
											}
										}
									}
									if (item6.HasDataFlow && item6.DataFlowChain != null)
									{
										streamWriter.WriteLine("  Data Flow Analysis:");
										streamWriter.WriteLine($"  Pattern: {item6.DataFlowChain.Pattern}");
										streamWriter.WriteLine("  " + item6.DataFlowChain.Summary);
										if (item6.DataFlowChain.IsCrossMethod)
										{
											streamWriter.WriteLine($"  Cross-method flow through {item6.DataFlowChain.InvolvedMethods.Count} methods");
										}
										streamWriter.WriteLine("  Data Flow Chain:");
										foreach (DataFlowNode node2 in item6.DataFlowChain.Nodes)
										{
											DataFlowNodeType nodeType2 = node2.NodeType;
											if (1 == 0)
											{
											}
											key = nodeType2 switch
											{
												DataFlowNodeType.Source => "[SOURCE]", 
												DataFlowNodeType.Transform => "[TRANSFORM]", 
												DataFlowNodeType.Sink => "[SINK]", 
												DataFlowNodeType.Intermediate => "[PASS]", 
												_ => "[???]", 
											};
											if (1 == 0)
											{
											}
											string text11 = key;
											streamWriter.WriteLine("    " + text11 + " " + node2.Operation + " (" + node2.DataDescription + ") @ " + node2.Location);
										}
									}
									if (!string.IsNullOrEmpty(item6.CodeSnippet))
									{
										streamWriter.WriteLine("  Code Snippet (IL):");
										string[] array = item6.CodeSnippet.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
										foreach (string text12 in array)
										{
											streamWriter.WriteLine("    " + text12);
										}
										streamWriter.WriteLine();
									}
								}
							}
							WriteSecurityNoticeToReport(streamWriter, threatVerdictInfo, scanStatusInfo, flag);
						}
					}
					bool flag2 = false;
					if (promptGeneratorService != null)
					{
						flag2 = promptGeneratorService.SavePromptToFile(text4, list, text8);
					}
					else if (scanStatusInfo.Kind == ScanStatusKind.RequiresReview)
					{
						((MelonBase)this).LoggerInstance.Warning("Skipped LLM prompt generation because this file was not fully analyzed by the loader.");
					}
					if (flag2)
					{
						((MelonBase)this).LoggerInstance.Msg("Detailed report saved to: " + text7);
						((MelonBase)this).LoggerInstance.Msg("LLM analysis prompt saved to: " + Path.Combine(text8, fileName + ".prompt.md"));
						((MelonBase)this).LoggerInstance.Msg("You can copy the contents of the prompt file into ChatGPT to help determine if this is malware or a false positive, although don't trust ChatGPT to be 100% accurate.");
					}
					else
					{
						((MelonBase)this).LoggerInstance.Msg("Detailed report saved to: " + text7);
					}
					MelonConfigManager configManager3 = _configManager;
					if (configManager3 == null || !(configManager3.Config?.EnableReportUpload).GetValueOrDefault() || _reportUploadService == null || threatVerdictInfo.Kind == ThreatVerdictKind.None)
					{
						continue;
					}
					try
					{
						string reportUploadApiBaseUrl = _configManager.GetReportUploadApiBaseUrl();
						if (!string.IsNullOrWhiteSpace(reportUploadApiBaseUrl) && File.Exists(text4))
						{
							byte[] assemblyBytes = File.ReadAllBytes(text4);
							SubmissionMetadata metadata = BuildSubmissionMetadata(fileName, list);
							_reportUploadService.UploadReportNonBlocking(assemblyBytes, fileName, metadata, reportUploadApiBaseUrl);
						}
					}
					catch (Exception ex)
					{
						((MelonBase)this).LoggerInstance.Warning("Report upload skipped for " + fileName + ": " + ex.Message);
					}
				}
				catch (Exception ex2)
				{
					((MelonBase)this).LoggerInstance.Error("Failed to save detailed report: " + ex2.Message);
				}
			}
			((MelonBase)this).LoggerInstance.Warning("====== END OF SCAN REPORT ======");
		}

		private void QueueConsentPromptIfNeeded(string accessiblePath, string modName, List<ScanFinding> findings, ThreatVerdictInfo threatVerdict, bool wasBlocked)
		{
			if (_configManager != null && !_showUploadConsentPopup)
			{
				MLVScanConfig config = _configManager.Config;
				if (!config.ReportUploadConsentAsked)
				{
					_showUploadConsentPopup = true;
					_pendingUploadPath = accessiblePath;
					_pendingUploadModName = modName;
					_pendingUploadVerdictKind = threatVerdict?.Kind.ToString() ?? string.Empty;
					_pendingUploadWasBlocked = wasBlocked;
					_pendingUploadFindings = findings;
					config.ReportUploadConsentPending = true;
					config.PendingReportUploadPath = accessiblePath ?? string.Empty;
					config.PendingReportUploadVerdictKind = _pendingUploadVerdictKind;
					_configManager.SaveConfig(config);
					((MelonBase)this).LoggerInstance.Warning("MLVScan is waiting for your upload consent decision in the in-game popup.");
				}
			}
		}

		private void HandleUploadConsentDecision(bool approved)
		{
			_showUploadConsentPopup = false;
			if (_configManager == null)
			{
				return;
			}
			MLVScanConfig config = _configManager.Config;
			config.ReportUploadConsentAsked = true;
			config.ReportUploadConsentPending = false;
			config.PendingReportUploadPath = string.Empty;
			config.PendingReportUploadVerdictKind = string.Empty;
			config.EnableReportUpload = approved;
			_configManager.SaveConfig(config);
			if (!approved)
			{
				((MelonBase)this).LoggerInstance.Msg("MLVScan report upload declined. You will not be prompted again.");
				_pendingUploadPath = string.Empty;
				_pendingUploadModName = string.Empty;
				_pendingUploadVerdictKind = string.Empty;
				_pendingUploadWasBlocked = true;
				_pendingUploadFindings = null;
				return;
			}
			((MelonBase)this).LoggerInstance.Msg("MLVScan report upload enabled. Uploading the flagged mod now.");
			try
			{
				if (_reportUploadService != null && !string.IsNullOrWhiteSpace(_pendingUploadPath) && File.Exists(_pendingUploadPath))
				{
					string reportUploadApiBaseUrl = _configManager.GetReportUploadApiBaseUrl();
					if (!string.IsNullOrWhiteSpace(reportUploadApiBaseUrl))
					{
						byte[] assemblyBytes = File.ReadAllBytes(_pendingUploadPath);
						SubmissionMetadata metadata = BuildSubmissionMetadata(_pendingUploadModName, _pendingUploadFindings ?? new List<ScanFinding>());
						_reportUploadService.UploadReportNonBlocking(assemblyBytes, _pendingUploadModName, metadata, reportUploadApiBaseUrl);
					}
				}
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("Report upload skipped for " + _pendingUploadModName + ": " + ex.Message);
			}
			finally
			{
				_pendingUploadPath = string.Empty;
				_pendingUploadModName = string.Empty;
				_pendingUploadVerdictKind = string.Empty;
				_pendingUploadWasBlocked = true;
				_pendingUploadFindings = null;
			}
		}

		private static SubmissionMetadata BuildSubmissionMetadata(string modName, List<ScanFinding> findings)
		{
			List<FindingSummaryItem> findingSummary = (from f in findings.Take(20)
				select new FindingSummaryItem
				{
					RuleId = f.RuleId,
					Description = f.Description,
					Severity = f.Severity.ToString(),
					Location = RedactionHelper.RedactLocation(f.Location)
				}).ToList();
			return new SubmissionMetadata
			{
				LoaderType = "MelonLoader",
				LoaderVersion = null,
				PluginVersion = "2.0.2",
				ModName = RedactionHelper.RedactFilename(modName),
				FindingSummary = findingSummary,
				ConsentVersion = "1",
				ConsentTimestamp = DateTime.UtcNow.ToString("o")
			};
		}

		private static string FormatSeverityLabel(Severity severity)
		{
			if (1 == 0)
			{
			}
			string result = severity switch
			{
				Severity.Critical => "CRITICAL", 
				Severity.High => "HIGH", 
				Severity.Medium => "MEDIUM", 
				Severity.Low => "LOW", 
				_ => severity.ToString().ToUpper(), 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private void DisplaySecurityNotice(string modName, ThreatVerdictInfo threatVerdict, ScanStatusInfo scanStatus, bool wasBlocked)
		{
			((MelonBase)this).LoggerInstance.Warning("IMPORTANT SECURITY NOTICE");
			((MelonBase)this).LoggerInstance.Msg(wasBlocked ? ("MLVScan detected and disabled " + modName + " before it was loaded.") : ("MLVScan flagged " + modName + " for review but did not block it under the current configuration."));
			if (IsKnownThreatVerdict(threatVerdict))
			{
				((MelonBase)this).LoggerInstance.Msg("This mod is likely malware because it matched previously analyzed malware intelligence.");
				((MelonBase)this).LoggerInstance.Msg("If this is your first time running the game with this mod, your PC is likely safe.");
				((MelonBase)this).LoggerInstance.Msg("However, if you've previously run the game with this mod, your system MAY be infected.");
				((MelonBase)this).LoggerInstance.Warning("Recommended security steps:");
				((MelonBase)this).LoggerInstance.Msg("1. Check with the modding community first - no detection is perfect");
				((MelonBase)this).LoggerInstance.Msg("   Join the modding Discord at: https://discord.gg/UD4K4chKak");
				((MelonBase)this).LoggerInstance.Msg("   Ask about this mod in the MLVScan thread of #mod-releases to confirm if it's actually malicious");
				((MelonBase)this).LoggerInstance.Msg("2. Run a full system scan with a trusted antivirus like Malwarebytes");
				((MelonBase)this).LoggerInstance.Msg("   Malwarebytes is recommended as a free and effective antivirus solution");
				((MelonBase)this).LoggerInstance.Msg("3. Use Microsoft Safety Scanner for a secondary scan");
				((MelonBase)this).LoggerInstance.Msg("4. Change important passwords if antivirus shows a threat");
				((MelonBase)this).LoggerInstance.Warning("Resources for malware removal:");
				((MelonBase)this).LoggerInstance.Msg("- Malwarebytes: https://www.malwarebytes.com/cybersecurity/basics/how-to-remove-virus-from-computer");
				((MelonBase)this).LoggerInstance.Msg("- Microsoft Safety Scanner: https://learn.microsoft.com/en-us/defender-endpoint/safety-scanner-download");
			}
			else if (threatVerdict != null && threatVerdict.Kind == ThreatVerdictKind.Suspicious)
			{
				((MelonBase)this).LoggerInstance.Msg("This mod was flagged because it triggered suspicious correlated behavior.");
				((MelonBase)this).LoggerInstance.Msg("It may still be a false positive, so review the saved report before assuming infection.");
				((MelonBase)this).LoggerInstance.Warning("Recommended review steps:");
				((MelonBase)this).LoggerInstance.Msg("1. Check with the modding community first - no detection is perfect");
				((MelonBase)this).LoggerInstance.Msg("   Join the modding Discord at: https://discord.gg/UD4K4chKak");
				((MelonBase)this).LoggerInstance.Msg("   Ask about this mod in the MLVScan thread of #mod-releases to confirm if it is actually malicious");
				((MelonBase)this).LoggerInstance.Msg("2. Review the saved report for the exact behavior that triggered the block");
				((MelonBase)this).LoggerInstance.Msg("3. Only run a full antivirus scan if you have already executed this mod or still do not trust it");
				((MelonBase)this).LoggerInstance.Msg("4. Whitelist the SHA256 only if you have independently verified the mod is safe");
			}
			else if (scanStatus != null && scanStatus.Kind == ScanStatusKind.RequiresReview)
			{
				((MelonBase)this).LoggerInstance.Msg("This mod could not be fully analyzed by the loader because it exceeded the current in-memory scan limit.");
				((MelonBase)this).LoggerInstance.Msg("MLVScan still calculated its SHA-256 hash and checked exact known-malicious sample matches.");
				((MelonBase)this).LoggerInstance.Warning("Recommended review steps:");
				((MelonBase)this).LoggerInstance.Msg("1. Review the saved report before assuming the mod is safe");
				((MelonBase)this).LoggerInstance.Msg("2. Validate the mod with trusted community sources or the original author");
				((MelonBase)this).LoggerInstance.Msg("3. Enable BlockIncompleteScans if you want oversized or incomplete scans blocked automatically");
				((MelonBase)this).LoggerInstance.Msg("4. Whitelist the SHA256 only after independent verification");
			}
			else
			{
				((MelonBase)this).LoggerInstance.Msg("Review the saved report before deciding whether to whitelist this mod.");
			}
		}

		private static void WriteSecurityNoticeToReport(StreamWriter writer, ThreatVerdictInfo threatVerdict, ScanStatusInfo scanStatus, bool wasBlocked)
		{
			writer.WriteLine("\n\n============== SECURITY NOTICE ==============\n");
			writer.WriteLine("IMPORTANT: READ THIS SECURITY INFORMATION\n");
			writer.WriteLine(wasBlocked ? "MLVScan detected and disabled this mod before it was loaded." : "MLVScan flagged this mod for review but did not block it under the current configuration.");
			if (IsKnownThreatVerdict(threatVerdict))
			{
				writer.WriteLine("This mod is likely malware because it matched previously analyzed malware intelligence.\n");
				writer.WriteLine("YOUR SYSTEM SECURITY STATUS:");
				writer.WriteLine("- If this is your FIRST TIME starting the game with this mod installed:");
				writer.WriteLine("  Your PC is likely SAFE as MLVScan prevented the mod from loading.");
				writer.WriteLine("\n- If you have PREVIOUSLY PLAYED the game with this mod loaded:");
				writer.WriteLine("  Your system MAY BE INFECTED with malware. Take action immediately.\n");
				writer.WriteLine("RECOMMENDED SECURITY STEPS:");
				writer.WriteLine("1. Check with the modding community first - no detection system is perfect");
				writer.WriteLine("   Join the modding Discord at: https://discord.gg/UD4K4chKak");
				writer.WriteLine("   Ask about this mod in the #MLVScan or #report-mods channels to confirm if it is actually malicious");
				writer.WriteLine("\n2. Run a full system scan with a reputable antivirus program");
				writer.WriteLine("   Free option: Malwarebytes (https://www.malwarebytes.com/)");
				writer.WriteLine("   Malwarebytes is recommended as a free and effective antivirus solution");
				writer.WriteLine("\n3. Run Microsoft Safety Scanner as a secondary check");
				writer.WriteLine("   Download: https://learn.microsoft.com/en-us/defender-endpoint/safety-scanner-download");
				writer.WriteLine("\n4. Update all your software from official sources");
				writer.WriteLine("\n5. Change passwords for important accounts (from a clean device if possible)");
				writer.WriteLine("\n6. Monitor your accounts for any suspicious activity");
				writer.WriteLine("\nDETAILED MALWARE REMOVAL GUIDES:");
				writer.WriteLine("- Malwarebytes Guide: https://www.malwarebytes.com/cybersecurity/basics/how-to-remove-virus-from-computer");
				writer.WriteLine("- Microsoft Safety Scanner: https://learn.microsoft.com/en-us/defender-endpoint/safety-scanner-download");
				writer.WriteLine("- XWorm (Common Modding Malware) Removal Guide: https://www.pcrisk.com/removal-guides/27436-xworm-rat");
			}
			else if (threatVerdict != null && threatVerdict.Kind == ThreatVerdictKind.Suspicious)
			{
				writer.WriteLine("This mod was flagged because it triggered suspicious correlated behavior.\n");
				writer.WriteLine("IMPORTANT:");
				writer.WriteLine("- This may still be a false positive.");
				writer.WriteLine("- Review the report details and verify the mod with trusted community sources before assuming infection.\n");
				writer.WriteLine("RECOMMENDED REVIEW STEPS:");
				writer.WriteLine("1. Check with the modding community first - no detection system is perfect");
				writer.WriteLine("   Join the modding Discord at: https://discord.gg/UD4K4chKak");
				writer.WriteLine("   Ask about this mod in the #MLVScan or #report-mods channels to confirm if it is actually malicious");
				writer.WriteLine("\n2. Review the detailed findings and call/data-flow evidence in this report");
				writer.WriteLine("\n3. Only run a full antivirus scan if you already executed the mod or still do not trust it");
				writer.WriteLine("\n4. Whitelist the SHA256 only after independent verification");
			}
			else if (scanStatus != null && scanStatus.Kind == ScanStatusKind.RequiresReview)
			{
				writer.WriteLine("This mod could not be fully analyzed by the loader because it exceeded the current in-memory scan limit.\n");
				writer.WriteLine("IMPORTANT:");
				writer.WriteLine("- MLVScan still calculated the SHA-256 hash and checked exact known-malicious sample matches.");
				writer.WriteLine("- No retained malicious verdict was produced, but the file was not fully analyzed.");
				writer.WriteLine("- Review the report details and verify the mod with trusted community sources before assuming it is safe.\n");
				writer.WriteLine("RECOMMENDED REVIEW STEPS:");
				writer.WriteLine("1. Review the report details and confirm whether this mod is expected to be unusually large");
				writer.WriteLine("\n2. Validate the mod with the original author or trusted community sources");
				writer.WriteLine("\n3. Enable BlockIncompleteScans if you want oversized or incomplete scans blocked automatically");
				writer.WriteLine("\n4. Whitelist the SHA256 only after independent verification");
			}
			writer.WriteLine("\n=============================================");
		}

		private static bool IsKnownThreatVerdict(ThreatVerdictInfo threatVerdict)
		{
			return (threatVerdict != null && threatVerdict.Kind == ThreatVerdictKind.KnownMaliciousSample) || (threatVerdict != null && threatVerdict.Kind == ThreatVerdictKind.KnownMalwareFamily);
		}
	}
	public class MelonLoaderServiceFactory
	{
		private readonly Instance _melonLogger;

		private readonly IScanLogger _scanLogger;

		private readonly IAssemblyResolverProvider _resolverProvider;

		private readonly MelonConfigManager _configManager;

		private readonly MelonPlatformEnvironment _environment;

		private readonly MLVScanConfig _fallbackConfig;

		private readonly LoaderScanTelemetryHub _telemetry;

		public MelonLoaderServiceFactory(Instance logger)
		{
			_melonLogger = logger ?? throw new ArgumentNullException("logger");
			_scanLogger = new MelonScanLogger(logger);
			_telemetry = new LoaderScanTelemetryHub();
			_resolverProvider = new GameAssemblyResolverProvider(_telemetry);
			_environment = new MelonPlatformEnvironment();
			_fallbackConfig = new MLVScanConfig();
			try
			{
				_configManager = new MelonConfigManager(logger);
			}
			catch (Exception ex)
			{
				_melonLogger.Error("Failed to create ConfigManager: " + ex.Message);
				_melonLogger.Msg("Using default configuration values");
			}
		}

		public MelonConfigManager CreateConfigManager()
		{
			if (_configManager == null)
			{
				throw new InvalidOperationException("Configuration manager unavailable: failed to initialize during factory construction.");
			}
			return _configManager;
		}

		public MelonPlatformEnvironment CreateEnvironment()
		{
			return _environment;
		}

		public AssemblyScanner CreateAssemblyScanner()
		{
			MLVScanConfig mLVScanConfig = _configManager?.Config ?? _fallbackConfig;
			IReadOnlyList<IScanRule> rules = RuleFactory.CreateDefaultRules();
			return new AssemblyScanner(rules, mLVScanConfig.Scan, _resolverProvider);
		}

		public MelonPluginScanner CreatePluginScanner()
		{
			MLVScanConfig config = _configManager?.Config ?? _fallbackConfig;
			return new MelonPluginScanner(_scanLogger, _resolverProvider, config, _configManager, _environment, _telemetry);
		}

		public MelonPluginDisabler CreatePluginDisabler()
		{
			MLVScanConfig config = _configManager?.Config ?? _fallbackConfig;
			return new MelonPluginDisabler(_scanLogger, config);
		}

		public PromptGeneratorService CreatePromptGenerator()
		{
			MLVScanConfig mLVScanConfig = _configManager?.Config ?? _fallbackConfig;
			return new PromptGeneratorService(mLVScanConfig.Scan, _scanLogger);
		}

		public IlDumpService CreateIlDumpService()
		{
			return new IlDumpService(_scanLogger, _environment);
		}

		public DeveloperReportGenerator CreateDeveloperReportGenerator()
		{
			return new DeveloperReportGenerator(_scanLogger);
		}

		public ReportUploadService CreateReportUploadService()
		{
			return new ReportUploadService(_configManager, delegate(string msg)
			{
				_melonLogger.Msg(msg);
			}, delegate(string msg)
			{
				_melonLogger.Warning(msg);
			}, delegate(string msg)
			{
				_melonLogger.Error(msg);
			});
		}
	}
	public class MelonConfigManager : IConfigManager
	{
		private readonly Instance _logger;

		private readonly MelonPreferences_Category _category;

		private readonly MelonPreferences_Entry<bool> _enableAutoScan;

		private readonly MelonPreferences_Entry<bool> _enableAutoDisable;

		private readonly MelonPreferences_Entry<bool> _enableScanCache;

		private readonly MelonPreferences_Entry<bool> _blockKnownThreats;

		private readonly MelonPreferences_Entry<bool> _blockSuspicious;

		private readonly MelonPreferences_Entry<bool> _blockIncompleteScans;

		private readonly MelonPreferences_Entry<string[]> _scanDirectories;

		private readonly MelonPreferences_Entry<string[]> _whitelistedHashes;

		private readonly MelonPreferences_Entry<bool> _dumpFullIlReports;

		private readonly MelonPreferences_Entry<bool> _developerMode;

		private readonly MelonPreferences_Entry<bool> _enableReportUpload;

		private readonly MelonPreferences_Entry<bool> _reportUploadConsentAsked;

		private readonly MelonPreferences_Entry<bool> _reportUploadConsentPending;

		private readonly MelonPreferences_Entry<string> _pendingReportUploadPath;

		private readonly MelonPreferences_Entry<string> _pendingReportUploadVerdictKind;

		private readonly MelonPreferences_Entry<string> _reportUploadApiBaseUrl;

		private readonly MelonPreferences_Entry<string[]> _uploadedReportHashes;

		private readonly MelonPreferences_Entry<bool> _includeMods;

		private readonly MelonPreferences_Entry<bool> _includePlugins;

		private readonly MelonPreferences_Entry<bool> _includeUserLibs;

		private readonly MelonPreferences_Entry<bool> _includeThunderstoreProfiles;

		private readonly MelonPreferences_Entry<string[]> _additionalTargetRoots;

		private readonly MelonPreferences_Entry<string[]> _excludedTargetRoots;

		public MLVScanConfig Config { get; private set; }

		public MelonConfigManager(Instance logger)
		{
			_logger = logger ?? throw new ArgumentNullException("logger");
			try
			{
				_category = MelonPreferences.CreateCategory("MLVScan");
				_enableAutoScan = _category.CreateEntry<bool>("EnableAutoScan", true, (string)null, "Whether to scan mods at startup", false, false, (ValueValidator)null, (string)null);
				_enableAutoDisable = _category.CreateEntry<bool>("EnableAutoDisable", true, (string)null, "Whether to automatically disable mods that meet the active blocking policy", false, false, (ValueValidator)null, (string)null);
				_enableScanCache = _category.CreateEntry<bool>("EnableScanCache", true, (string)null, "Whether to reuse scan results for unchanged files using a local authenticated cache", false, false, (ValueValidator)null, (string)null);
				_blockKnownThreats = _category.CreateEntry<bool>("BlockKnownThreats", true, (string)null, "Whether to block mods that match a known threat family or exact malicious sample", false, false, (ValueValidator)null, (string)null);
				_blockSuspicious = _category.CreateEntry<bool>("BlockSuspicious", true, (string)null, "Whether to block suspicious unknown behavior that may still be a false positive", false, false, (ValueValidator)null, (string)null);
				_blockIncompleteScans = _category.CreateEntry<bool>("BlockIncompleteScans", false, (string)null, "Whether to block mods that could not be fully analyzed and require manual review", false, false, (ValueValidator)null, (string)null);
				_scanDirectories = _category.CreateEntry<string[]>("ScanDirectories", new string[2] { "Mods", "Plugins" }, (string)null, "Directories to scan for mods", false, false, (ValueValidator)null, (string)null);
				_whitelistedHashes = _category.CreateEntry<string[]>("WhitelistedHashes", Array.Empty<string>(), (string)null, "List of mod SHA256 hashes to skip when scanning", false, false, (ValueValidator)null, (string)null);
				_dumpFullIlReports = _category.CreateEntry<bool>("DumpFullIlReports", false, (string)null, "When enabled, saves full IL dumps for scanned mods next to reports", false, false, (ValueValidator)null, (string)null);
				_developerMode = _category.CreateEntry<bool>("DeveloperMode", false, (string)null, "Developer mode: Shows remediation guidance to help mod developers fix false positives", false, false, (ValueValidator)null, (string)null);
				_enableReportUpload = _category.CreateEntry<bool>("EnableReportUpload", false, (string)null, "When enabled (and consent given), send reports to MLVScan API for false positive analysis", false, false, (ValueValidator)null, (string)null);
				_reportUploadConsentAsked = _category.CreateEntry<bool>("ReportUploadConsentAsked", false, (string)null, "Whether the first-run consent prompt has been shown (internal)", false, false, (ValueValidator)null, (string)null);
				_reportUploadConsentPending = _category.CreateEntry<bool>("ReportUploadConsentPending", false, (string)null, "Whether an upload consent popup is pending (internal)", false, false, (ValueValidator)null, (string)null);
				_pendingReportUploadPath = _category.CreateEntry<string>("PendingReportUploadPath", string.Empty, (string)null, "Suspicious mod path waiting for upload consent (internal)", false, false, (ValueValidator)null, (string)null);
				_pendingReportUploadVerdictKind = _category.CreateEntry<string>("PendingReportUploadVerdictKind", string.Empty, (string)null, "Threat verdict kind for the pending upload consent item (internal)", false, false, (ValueValidator)null, (string)null);
				_reportUploadApiBaseUrl = _category.CreateEntry<string>("ReportUploadApiBaseUrl", "https://api.mlvscan.com", (string)null, "API base URL for report uploads", false, false, (ValueValidator)null, (string)null);
				_uploadedReportHashes = _category.CreateEntry<string[]>("UploadedReportHashes", Array.Empty<string>(), (string)null, "List of assembly SHA256 hashes already uploaded to the MLVScan API (internal)", false, false, (ValueValidator)null, (string)null);
				_includeMods = _category.CreateEntry<bool>("IncludeMods", true, (string)null, "Whether to include Mods folders in the target scan scope", false, false, (ValueValidator)null, (string)null);
				_includePlugins = _category.CreateEntry<bool>("IncludePlugins", true, (string)null, "Whether to include Plugins folders in the target scan scope", false, false, (ValueValidator)null, (string)null);
				_includeUserLibs = _category.CreateEntry<bool>("IncludeUserLibs", true, (string)null, "Whether to include UserLibs folders in the target scan scope", false, false, (ValueValidator)null, (string)null);
				_includeThunderstoreProfiles = _category.CreateEntry<bool>("IncludeThunderstoreProfiles", true, (string)null, "Whether to include Thunderstore profile folders in the target scan scope", false, false, (ValueValidator)null, (string)null);
				_additionalTargetRoots = _category.CreateEntry<string[]>("AdditionalTargetRoots", Array.Empty<string>(), (string)null, "Additional absolute paths to include in the target scan scope", false, false, (ValueValidator)null, (string)null);
				_excludedTargetRoots = _category.CreateEntry<string[]>("ExcludedTargetRoots", Array.Empty<string>(), (string)null, "Absolute paths to exclude from the target scan scope", false, false, (ValueValidator)null, (string)null);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_enableAutoScan.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_enableAutoDisable.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_enableScanCache.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_blockKnownThreats.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_blockSuspicious.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_blockIncompleteScans.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string[], string[]>>)(object)_scanDirectories.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string[], string[]>>)(object)_whitelistedHashes.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_dumpFullIlReports.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_developerMode.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_enableReportUpload.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_reportUploadConsentAsked.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_reportUploadConsentPending.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string, string>>)(object)_pendingReportUploadPath.OnEntryValueChanged).Subscribe((LemonAction<string, string>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string, string>>)(object)_pendingReportUploadVerdictKind.OnEntryValueChanged).Subscribe((LemonAction<string, string>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string, string>>)(object)_reportUploadApiBaseUrl.OnEntryValueChanged).Subscribe((LemonAction<string, string>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string[], string[]>>)(object)_uploadedReportHashes.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_includeMods.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_includePlugins.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_includeUserLibs.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<bool, bool>>)(object)_includeThunderstoreProfiles.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string[], string[]>>)(object)_additionalTargetRoots.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false);
				((MelonEventBase<LemonAction<string[], string[]>>)(object)_excludedTargetRoots.OnEntryValueChanged).Subscribe((LemonAction<string[], string[]>)OnConfigChanged, 0, false);
				UpdateConfigFromPreferences();
				CleanupLegacyPreferenceEntries();
				_logger.Msg("Configuration loaded successfully");
			}
			catch (Exception ex)
			{
				_logger.Error("Failed to initialize config system: " + ex.Message);
				_logger.Msg("Using fallback in-memory configuration");
				Config = new MLVScanConfig();
			}
		}

		public MLVScanConfig LoadConfig()
		{
			UpdateConfigFromPreferences();
			return Config;
		}

		private void OnConfigChanged<T>(T oldValue, T newValue)
		{
			UpdateConfigFromPreferences();
			_logger.Msg("Configuration updated");
		}

		private void UpdateConfigFromPreferences()
		{
			Config = new MLVScanConfig
			{
				EnableAutoScan = _enableAutoScan.Value,
				EnableAutoDisable = _enableAutoDisable.Value,
				EnableScanCache = _enableScanCache.Value,
				BlockKnownThreats = _blockKnownThreats.Value,
				BlockSuspicious = _blockSuspicious.Value,
				BlockIncompleteScans = _blockIncompleteScans.Value,
				ScanDirectories = _scanDirectories.Value,
				WhitelistedHashes = _whitelistedHashes.Value,
				DumpFullIlReports = _dumpFullIlReports.Value,
				Scan = new ScanConfig
				{
					DeveloperMode = _developerMode.Value
				},
				EnableReportUpload = _enableReportUpload.Value,
				ReportUploadConsentAsked = _reportUploadConsentAsked.Value,
				ReportUploadConsentPending = _reportUploadConsentPending.Value,
				PendingReportUploadPath = _pendingReportUploadPath.Value,
				PendingReportUploadVerdictKind = _pendingReportUploadVerdictKind.Value,
				ReportUploadApiBaseUrl = _reportUploadApiBaseUrl.Value,
				UploadedReportHashes = NormalizeHashes(_uploadedReportHashes.Value),
				IncludeMods = _includeMods.Value,
				IncludePlugins = _includePlugins.Value,
				IncludeUserLibs = _includeUserLibs.Value,
				IncludePatchers = false,
				IncludeThunderstoreProfiles = _includeThunderstoreProfiles.Value,
				AdditionalTargetRoots = (_additionalTargetRoots.Value ?? Array.Empty<string>()),
				ExcludedTargetRoots = (_excludedTargetRoots.Value ?? Array.Empty<string>())
			};
		}

		public void SaveConfig(MLVScanConfig newConfig)
		{
			try
			{
				_enableAutoScan.Value = newConfig.EnableAutoScan;
				_enableAutoDisable.Value = newConfig.EnableAutoDisable;
				_enableScanCache.Value = newConfig.EnableScanCache;
				_blockKnownThreats.Value = newConfig.BlockKnownThreats;
				_blockSuspicious.Value = newConfig.BlockSuspicious;
				_blockIncompleteScans.Value = newConfig.BlockIncompleteScans;
				_scanDirectories.Value = newConfig.ScanDirectories;
				_whitelistedHashes.Value = newConfig.WhitelistedHashes;
				_dumpFullIlReports.Value = newConfig.DumpFullIlReports;
				_developerMode.Value = newConfig.Scan?.DeveloperMode ?? false;
				_enableReportUpload.Value = newConfig.EnableReportUpload;
				_reportUploadConsentAsked.Value = newConfig.ReportUploadConsentAsked;
				_reportUploadConsentPending.Value = newConfig.ReportUploadConsentPending;
				_pendingReportUploadPath.Value = newConfig.PendingReportUploadPath ?? string.Empty;
				_pendingReportUploadVerdictKind.Value = newConfig.PendingReportUploadVerdictKind ?? string.Empty;
				_reportUploadApiBaseUrl.Value = newConfig.ReportUploadApiBaseUrl;
				_uploadedReportHashes.Value = NormalizeHashes(newConfig.UploadedReportHashes);
				_includeMods.Value = newConfig.IncludeMods;
				_includePlugins.Value = newConfig.IncludePlugins;
				_includeUserLibs.Value = newConfig.IncludeUserLibs;
				_includeThunderstoreProfiles.Value = newConfig.IncludeThunderstoreProfiles;
				_additionalTargetRoots.Value = newConfig.AdditionalTargetRoots ?? Array.Empty<string>();
				_excludedTargetRoots.Value = newConfig.ExcludedTargetRoots ?? Array.Empty<string>();
				PersistPreferences();
				_logger.Msg("Configuration saved successfully");
			}
			catch (Exception ex)
			{
				_logger.Error("Error saving configuration: " + ex.Message);
				Config = newConfig;
			}
		}

		public string[] GetWhitelistedHashes()
		{
			return _whitelistedHashes.Value;
		}

		public void SetWhitelistedHashes(string[] hashes)
		{
			if (hashes != null)
			{
				string[] array = (from h in hashes
					where !string.IsNullOrWhiteSpace(h)
					select h.ToLowerInvariant()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
				_whitelistedHashes.Value = array;
				PersistPreferences();
				UpdateConfigFromPreferences();
				_logger.Msg($"Updated whitelist with {array.Length} hash(es)");
			}
		}

		public bool IsHashWhitelisted(string hash)
		{
			if (string.IsNullOrWhiteSpace(hash))
			{
				return false;
			}
			return Config.WhitelistedHashes.Contains<string>(hash.ToLowerInvariant(), StringComparer.OrdinalIgnoreCase);
		}

		public string GetReportUploadApiBaseUrl()
		{
			return _reportUploadApiBaseUrl.Value;
		}

		public bool IsReportHashUploaded(string hash)
		{
			if (string.IsNullOrWhiteSpace(hash))
			{
				return false;
			}
			return NormalizeHashes(_uploadedReportHashes.Value).Contains<string>(hash.ToLowerInvariant(), StringComparer.OrdinalIgnoreCase);
		}

		public void MarkReportHashUploaded(string hash)
		{
			if (HashUtility.IsValidHash(hash))
			{
				string[] array = NormalizeHashes((_uploadedReportHashes.Value ?? Array.Empty<string>()).Append(hash));
				int num = array.Length;
				string[] value = _uploadedReportHashes.Value;
				if (num != ((value != null) ? value.Length : 0) || !IsReportHashUploaded(hash))
				{
					_uploadedReportHashes.Value = array;
					PersistPreferences();
					UpdateConfigFromPreferences();
					_logger.Msg("Recorded uploaded report hash: " + hash);
				}
			}
		}

		private void PersistPreferences()
		{
			MelonPreferences.Save();
			CleanupLegacyPreferenceEntries();
		}

		private void CleanupLegacyPreferenceEntries()
		{
			try
			{
				string filePath = Path.Combine(MelonEnvironment.UserDataDirectory, "MelonPreferences.cfg");
				if (LegacyConfigCleanup.TryRemoveObsoleteIniEntries(filePath, "MLVScan", out var removedKeys))
				{
					_logger.Msg("Removed legacy config keys from MelonPreferences.cfg: " + string.Join(", ", removedKeys));
				}
			}
			catch
			{
			}
		}

		private static string[] NormalizeHashes(IEnumerable<string> hashes)
		{
			return (from h in hashes ?? Array.Empty<string>()
				where !string.IsNullOrWhiteSpace(h)
				select h.ToLowerInvariant()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
		}
	}
	public class MelonPlatformEnvironment : IPlatformEnvironment
	{
		private readonly string _gameRoot;

		private readonly string _dataDir;

		private readonly string _reportsDir;

		public string GameRootDirectory => _gameRoot;

		public string[] PluginDirectories => new string[2]
		{
			Path.Combine(_gameRoot, "Mods"),
			Path.Combine(_gameRoot, "Plugins")
		};

		public string DataDirectory
		{
			get
			{
				EnsureDataDirectory();
				return _dataDir;
			}
		}

		public string ReportsDirectory
		{
			get
			{
				EnsureDataDirectory();
				if (!Directory.Exists(_reportsDir))
				{
					Directory.CreateDirectory(_reportsDir);
				}
				return _reportsDir;
			}
		}

		public string ManagedDirectory
		{
			get
			{
				try
				{
					string[] directories = Directory.GetDirectories(_gameRoot, "*_Data");
					string[] array = directories;
					foreach (string path in array)
					{
						string text = Path.Combine(path, "Managed");
						if (Directory.Exists(text))
						{
							return text;
						}
					}
				}
				catch
				{
				}
				string text2 = Path.Combine(_gameRoot, "MelonLoader", "Managed");
				if (Directory.Exists(text2))
				{
					return text2;
				}
				return string.Empty;
			}
		}

		public string SelfAssemblyPath
		{
			get
			{
				try
				{
					return typeof(MelonPlatformEnvironment).Assembly.Location;
				}
				catch
				{
					return string.Empty;
				}
			}
		}

		public string PlatformName => "MelonLoader";

		public MelonPlatformEnvironment()
		{
			_gameRoot = MelonEnvironment.GameRootDirectory;
			_dataDir = Path.Combine(MelonEnvironment.UserDataDirectory, "MLVScan");
			_reportsDir = Path.Combine(_dataDir, "Reports");
		}

		private void EnsureDataDirectory()
		{
			if (!Directory.Exists(_dataDir))
			{
				Directory.CreateDirectory(_dataDir);
			}
		}
	}
	public class MelonPluginScanner : PluginScannerBase
	{
		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <>c__DisplayClass2_0
		{
			public HashSet<string> emitted;

			public MelonPluginScanner <>4__this;

			public HashSet<string> builtInRoots;
		}

		[CompilerGenerated]
		private sealed class <GetScanDirectories>d__2 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			public MelonPluginScanner <>4__this;

			private <>c__DisplayClass2_0 <>8__1;

			private string[] <>s__2;

			private int <>s__3;

			private string <scanDir>5__4;

			private IEnumerator<string> <>s__5;

			private string <thunderstoreRoot>5__6;

			private HashSet<string>.Enumerator <>s__7;

			private string <path>5__8;

			string IEnumerator<string>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetScanDirectories>d__2(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = System.Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>8__1 = default(<>c__DisplayClass2_0);
				<>s__2 = null;
				<scanDir>5__4 = null;
				<>s__5 = null;
				<thunderstoreRoot>5__6 = null;
				<>s__7 = default(HashSet<string>.Enumerator);
				<path>5__8 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>8__1.<>4__this = <>4__this;
						<>8__1.emitted = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
						<>8__1.builtInRoots = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
						{
							Path.GetFullPath(Path.Combine(<>4__this._environment.GameRootDirectory, "Mods")),
							Path.GetFullPath(Path.Combine(<>4__this._environment.GameRootDirectory, "Plugins")),
							Path.GetFullPath(Path.Combine(<>4__this._environment.GameRootDirectory, "UserLibs"))
						};
						if (<>4__this.Config.IncludeMods)
						{
							<>4__this.<GetScanDirectories>g__AddIfPresent|2_0(Path.Combine(<>4__this._environment.GameRootDirectory, "Mods"), ref <>8__1);
						}
						if (<>4__this.Config.IncludePlugins)
						{
							<>4__this.<GetScanDirectories>g__AddIfPresent|2_0(Path.Combine(<>4__this._environment.GameRootDirectory, "Plugins"), ref <>8__1);
						}
						if (<>4__this.Config.IncludeUserLibs)
						{
							<>4__this.<GetScanDirectories>g__AddIfPresent|2_0(Path.Combine(<>4__this._environment.GameRootDirectory, "UserLibs"), ref <>8__1);
						}
						<>s__2 = <>4__this.Config.ScanDirectories ?? Array.Empty<string>();
						for (<>s__3 = 0; <>s__3 < <>s__2.Length; <>s__3++)
						{
							<scanDir>5__4 = <>s__2[<>s__3];
							<>4__this.<GetScanDirectories>g__AddLegacyIfPresent|2_1(<scanDir>5__4, ref <>8__1);
							<scanDir>5__4 = null;
						}
						<>s__2 = null;
						if (<>4__this.Config.IncludeThunderstoreProfiles)
						{
							<>s__5 = GetThunderstoreDirectories().GetEnumerator();
							try
							{
								while (<>s__5.MoveNext())
								{
									<thunderstoreRoot>5__6 = <>s__5.Current;
									<>4__this.<GetScanDirectories>g__AddIfPresent|2_0(<thunderstoreRoot>5__6, ref <>8__1);
									<thunderstoreRoot>5__6 = null;
								}
							}
							finally
							{
								if (<>s__5 != null)
								{
									<>s__5.Dispose();
								}
							}
							<>s__5 = null;
						}
						<>s__7 = <>8__1.emitted.GetEnumerator();
						<>1__state = -3;
						break;
					case 1:
						<>1__state = -3;
						<path>5__8 = null;
						break;
					}
					if (<>s__7.MoveNext())
					{
						<path>5__8 = <>s__7.Current;
						<>2__current = <path>5__8;
						<>1__state = 1;
						return true;
					}
					<>m__Finally1();
					<>s__7 = default(HashSet<string>.Enumerator);
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				((IDisposable)<>s__7).Dispose();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				<GetScanDirectories>d__2 result;
				if (<>1__state == -2 && <>l__initialThreadId == System.Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					result = this;
				}
				else
				{
					result = new <GetScanDirectories>d__2(0)
					{
						<>4__this = <>4__this
					};
				}
				return result;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<string>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <GetThunderstoreDirectories>d__5 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private string <appDataPath>5__1;

			private string <thunderstoreBasePath>5__2;

			private IEnumerator<string> <>s__3;

			private string <gameFolder>5__4;

			private string <profilesPath>5__5;

			private IEnumerator<string> <>s__6;

			private string <profileFolder>5__7;

			private string <modsPath>5__8;

			private string <pluginsPath>5__9;

			string IEnumerator<string>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetThunderstoreDirectories>d__5(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = System.Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if ((uint)(num - -4) <= 1u || (uint)(num - 1) <= 1u)
				{
					try
					{
						if (num == -4 || (uint)(num - 1) <= 1u)
						{
							try
							{
							}
							finally
							{
								<>m__Finally2();
							}
						}
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<appDataPath>5__1 = null;
				<thunderstoreBasePath>5__2 = null;
				<>s__3 = null;
				<gameFolder>5__4 = null;
				<profilesPath>5__5 = null;
				<>s__6 = null;
				<profileFolder>5__7 = null;
				<modsPath>5__8 = null;
				<pluginsPath>5__9 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						if (!RuntimeInformationHelper.IsWindows)
						{
							return false;
						}
						<appDataPath>5__1 = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
						<thunderstoreBasePath>5__2 = Path.Combine(<appDataPath>5__1, "Thunderstore Mod Manager", "DataFolder");
						if (!Directory.Exists(<thunderstoreBasePath>5__2))
						{
							return false;
						}
						<>s__3 = SafeGetDirectories(<thunderstoreBasePath>5__2).GetEnumerator();
						<>1__state = -3;
						goto IL_01fe;
					case 1:
						<>1__state = -4;
						goto IL_0173;
					case 2:
						{
							<>1__state = -4;
							goto IL_01bb;
						}
						IL_01bb:
						<modsPath>5__8 = null;
						<pluginsPath>5__9 = null;
						<profileFolder>5__7 = null;
						goto IL_01d1;
						IL_01d1:
						if (<>s__6.MoveNext())
						{
							<profileFolder>5__7 = <>s__6.Current;
							<modsPath>5__8 = Path.Combine(<profileFolder>5__7, "Mods");
							if (Directory.Exists(<modsPath>5__8))
							{
								<>2__current = <modsPath>5__8;
								<>1__state = 1;
								return true;
							}
							goto IL_0173;
						}
						<>m__Finally2();
						<>s__6 = null;
						<profilesPath>5__5 = null;
						<gameFolder>5__4 = null;
						goto IL_01fe;
						IL_0173:
						<pluginsPath>5__9 = Path.Combine(<profileFolder>5__7, "Plugins");
						if (Directory.Exists(<pluginsPath>5__9))
						{
							<>2__current = <pluginsPath>5__9;
							<>1__state = 2;
							return true;
						}
						goto IL_01bb;
						IL_01fe:
						do
						{
							if (<>s__3.MoveNext())
							{
								<gameFolder>5__4 = <>s__3.Current;
								<profilesPath>5__5 = Path.Combine(<gameFolder>5__4, "profiles");
								continue;
							}
							<>m__Finally1();
							<>s__3 = null;
							return false;
						}
						while (!Directory.Exists(<profilesPath>5__5));
						<>s__6 = SafeGetDirectories(<profilesPath>5__5).GetEnumerator();
						<>1__state = -4;
						goto IL_01d1;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>s__3 != null)
				{
					<>s__3.Dispose();
				}
			}

			private void <>m__Finally2()
			{
				<>1__state = -3;
				if (<>s__6 != null)
				{
					<>s__6.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == System.Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <GetThunderstoreDirectories>d__5(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<string>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <SafeGetDirectories>d__6 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private string path;

			public string <>3__path;

			private string[] <directories>5__1;

			private string[] <>s__2;

			private int <>s__3;

			private string <directory>5__4;

			string IEnumerator<string>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SafeGetDirectories>d__6(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = System.Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<directories>5__1 = null;
				<>s__2 = null;
				<directory>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					try
					{
						<directories>5__1 = Directory.GetDirectories(path);
					}
					catch (UnauthorizedAccessException)
					{
						return false;
					}
					catch (DirectoryNotFoundException)
					{
						return false;
					}
					catch (IOException)
					{
						return false;
					}
					<>s__2 = <directories>5__1;
					<>s__3 = 0;
					break;
				case 1:
					<>1__state = -1;
					<directory>5__4 = null;
					<>s__3++;
					break;
				}
				if (<>s__3 < <>s__2.Length)
				{
					<directory>5__4 = <>s__2[<>s__3];
					<>2__current = <directory>5__4;
					<>1__state = 1;
					return true;
				}
				<>s__2 = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				<SafeGetDirectories>d__6 <SafeGetDirectories>d__;
				if (<>1__state == -2 && <>l__initialThreadId == System.Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<SafeGetDirectories>d__ = this;
				}
				else
				{
					<SafeGetDirectories>d__ = new <SafeGetDirectories>d__6(0);
				}
				<SafeGetDirectories>d__.path = <>3__path;
				return <SafeGetDirectories>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<string>)this).GetEnumerator();
			}
		}

		private readonly MelonPlatformEnvironment _environment;

		public MelonPluginScanner(IScanLogger logger, IAssemblyResolverProvider resolverProvider, MLVScanConfig config, IConfigManager configManager, MelonPlatformEnvironment environment, LoaderScanTelemetryHub telemetry)
			: base(logger, resolverProvider, config, configManager, environment, telemetry)
		{
			_environment = environment ?? throw new ArgumentNullException("environment");
		}

		[IteratorStateMachine(typeof(<GetScanDirectories>d__2))]
		protected override IEnumerable<string> GetScanDirectories()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetScanDirectories>d__2(-2)
			{
				<>4__this = this
			};
		}

		protected override bool IsSelfAssembly(string filePath)
		{
			try
			{
				string selfAssemblyPath = _environment.SelfAssemblyPath;
				if (string.IsNullOrEmpty(selfAssemblyPath))
				{
					return false;
				}
				return Path.GetFullPath(filePath).Equals(Path.GetFullPath(selfAssemblyPath), StringComparison.OrdinalIgnoreCase);
			}
			catch
			{
				return false;
			}
		}

		protected override IEnumerable<string> GetResolverDirectories()
		{
			HashSet<string> emitted = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			AddIfPresent(Path.Combine(_environment.GameRootDirectory, "Mods"));
			AddIfPresent(Path.Combine(_environment.GameRootDirectory, "Plugins"));
			AddIfPresent(Path.Combine(_environment.GameRootDirectory, "UserLibs"));
			string[] array = Config.ScanDirectories ?? Array.Empty<string>();
			foreach (string text in array)
			{
				AddIfPresent(Path.IsPathRooted(text) ? text : Path.Combine(_environment.GameRootDirectory, text));
			}
			if (Config.IncludeThunderstoreProfiles)
			{
				foreach (string thunderstoreDirectory in GetThunderstoreDirectories())
				{
					AddIfPresent(thunderstoreDirectory);
				}
			}
			return emitted;
			void AddIfPresent(string path)
			{
				if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
				{
					emitted.Add(Path.GetFullPath(path));
				}
			}
		}

		[IteratorStateMachine(typeof(<GetThunderstoreDirectories>d__5))]
		private static IEnumerable<string> GetThunderstoreDirectories()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetThunderstoreDirectories>d__5(-2);
		}

		[IteratorStateMachine(typeof(<SafeGetDirectories>d__6))]
		private static IEnumerable<string> SafeGetDirectories(string path)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SafeGetDirectories>d__6(-2)
			{
				<>3__path = path
			};
		}

		[CompilerGenerated]
		private void <GetScanDirectories>g__AddIfPresent|2_0(string path, ref <>c__DisplayClass2_0 P_1)
		{
			if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
			{
				P_1.emitted.Add(Path.GetFullPath(path));
			}
		}

		[CompilerGenerated]
		private void <GetScanDirectories>g__AddLegacyIfPresent|2_1(string scanDir, ref <>c__DisplayClass2_0 P_1)
		{
			if (!string.IsNullOrWhiteSpace(scanDir))
			{
				string fullPath = Path.GetFullPath(Path.IsPathRooted(scanDir) ? scanDir : Path.Combine(_environment.GameRootDirectory, scanDir));
				if (!P_1.builtInRoots.Contains(fullPath))
				{
					<GetScanDirectories>g__AddIfPresent|2_0(fullPath, ref P_1);
				}
			}
		}
	}
	public class MelonPluginDisabler : PluginDisablerBase
	{
		private const string DisabledExtension = ".disabled";

		public MelonPluginDisabler(IScanLogger logger, MLVScanConfig config)
			: base(logger, config)
		{
		}

		protected override string GetDisabledExtension()
		{
			return ".disabled";
		}

		protected override string GetDisabledPath(string originalPath)
		{
			return Path.ChangeExtension(originalPath, ".disabled");
		}
	}
}
namespace MLVScan.Services
{
	internal static class ConsentMessageHelper
	{
		public static string GetUploadConsentMessage(string modName, string verdictKind, bool wasBlocked = true)
		{
			string text = (string.IsNullOrWhiteSpace(modName) ? "this mod" : modName);
			if (string.Equals(verdictKind, ThreatVerdictKind.KnownMaliciousSample.ToString(), StringComparison.Ordinal) || string.Equals(verdictKind, ThreatVerdictKind.KnownMalwareFamily.ToString(), StringComparison.Ordinal))
			{
				return wasBlocked ? ("MLVScan identified " + text + " as likely malware and disabled it.") : ("MLVScan identified " + text + " as likely malware, but it was not blocked by the current configuration.");
			}
			if (string.IsNullOrWhiteSpace(verdictKind) || string.Equals(verdictKind, ThreatVerdictKind.None.ToString(), StringComparison.Ordinal))
			{
				return wasBlocked ? ("MLVScan blocked " + text + " because it could not complete full analysis and manual review is required.") : ("MLVScan could not complete full analysis of " + text + ", so manual review is required before you trust it.");
			}
			return wasBlocked ? ("MLVScan blocked " + text + " because it triggered suspicious behavior. It may still be a false positive.") : ("MLVScan flagged " + text + " because it triggered suspicious behavior, but it was not blocked by the current configuration. It may still be a false positive.");
		}
	}
	public abstract class PluginScannerBase
	{
		protected readonly IScanLogger Logger;

		protected readonly IAssemblyResolverProvider ResolverProvider;

		protected readonly MLVScanConfig Config;

		protected readonly IConfigManager ConfigManager;

		protected readonly IPlatformEnvironment Environment;

		protected readonly AssemblyScanner AssemblyScanner;

		protected readonly ThreatVerdictBuilder ThreatVerdictBuilder;

		private const long MaxAssemblyScanBytes = 268435456L;

		private readonly IFileIdentityProvider _fileIdentityProvider;

		private readonly IResolverCatalogProvider _resolverCatalogProvider;

		private readonly LoaderScanTelemetryHub _telemetry;

		private readonly TargetAssemblyScopeFilter _scopeFilter = new TargetAssemblyScopeFilter();

		private readonly string _scannerFingerprint;

		private readonly string _selfAssemblyHash;

		private IScanCacheStore _cacheStore;

		private bool _cacheUnavailable;

		protected PluginScannerBase(IScanLogger logger, IAssemblyResolverProvider resolverProvider, MLVScanConfig config, IConfigManager configManager, IPlatformEnvironment environment, LoaderScanTelemetryHub telemetry)
		{
			Logger = logger ?? throw new ArgumentNullException("logger");
			ResolverProvider = resolverProvider ?? throw new ArgumentNullException("resolverProvider");
			Config = config ?? throw new ArgumentNullException("config");
			ConfigManager = configManager ?? throw new ArgumentNullException("configManager");
			Environment = environment ?? throw new ArgumentNullException("environment");
			_telemetry = telemetry ?? throw new ArgumentNullException("telemetry");
			_fileIdentityProvider = new CrossPlatformFileIdentityProvider();
			_resolverCatalogProvider = resolverProvider as IResolverCatalogProvider;
			IScanRule[] rules = RuleFactory.CreateDefaultRules().ToArray();
			AssemblyScanner = new AssemblyScanner(rules, Config.Scan, ResolverProvider);
			ThreatVerdictBuilder = new ThreatVerdictBuilder();
			_scannerFingerprint = ComputeScannerFingerprint(Config.Scan, (IReadOnlyCollection<IScanRule>)(object)rules);
			_selfAssemblyHash = GetSelfAssemblyHash(environment.SelfAssemblyPath);
		}

		protected abstract IEnumerable<string> GetScanDirectories();

		protected abstract bool IsSelfAssembly(string filePath);

		protected virtual IEnumerable<string> GetResolverDirectories()
		{
			return GetScanDirectories();
		}

		protected virtual void OnScanComplete(Dictionary<string, ScannedPluginResult> results)
		{
		}

		public Dictionary<string, ScannedPluginResult> ScanAllPlugins(bool forceScanning = false)
		{
			Dictionary<string, ScannedPluginResult> dictionary = new Dictionary<string, ScannedPluginResult>(StringComparer.OrdinalIgnoreCase);
			if (!forceScanning && !Config.EnableAutoScan)
			{
				Logger.Info("Automatic scanning is disabled in configuration");
				return dictionary;
			}
			_telemetry.BeginRun($"{Environment.PlatformName}-{DateTime.UtcNow:yyyyMMdd-HHmmss}");
			string[] array = GetScanDirectories().ToArray();
			foreach (string item in array.Where((string directory) => !Directory.Exists(directory)))
			{
				Logger.Warning("Directory not found: " + item);
			}
			long startTimestamp = _telemetry.StartTimestamp();
			IReadOnlyList<string> readOnlyList = _scopeFilter.BuildEffectiveRoots(array, Config);
			_telemetry.AddPhaseElapsed("Scope.BuildEffectiveRoots", startTimestamp);
			StringComparer pathComparer = GetPathComparer();
			string[] effectiveRoots = (from root in GetResolverDirectories().Concat(Config.AdditionalTargetRoots)
				where !string.IsNullOrWhiteSpace(root)
				select root).Select(Path.GetFullPath).Distinct<string>(pathComparer).ToArray();
			string resolverFingerprint = BuildResolverCatalog((IReadOnlyCollection<string>)(object)effectiveRoots);
			string[] array2 = readOnlyList.SelectMany(EnumerateCandidateFiles).Distinct<string>(pathComparer).ToArray();
			HashSet<string> activeCanonicalPaths = new HashSet<string>(pathComparer);
			HashSet<string> processedCanonicalPaths = new HashSet<string>(pathComparer);
			string[] array3 = array2;
			foreach (string text in array3)
			{
				try
				{
					ScanSingleFile(text, readOnlyList, dictionary, activeCanonicalPaths, processedCanonicalPaths, resolverFingerprint);
				}
				catch (Exception ex)
				{
					Logger.Error("Error scanning " + Path.GetFileName(text) + ": " + ex.Message);
				}
			}
			GetCacheStore()?.PruneMissingEntries(activeCanonicalPaths);
			OnScanComplete(dictionary);
			_telemetry.CompleteRun(readOnlyList.Count, array2.Length, dictionary.Count);
			string text2 = TryGetDataDirectory();
			if (!string.IsNullOrWhiteSpace(text2))
			{
				string text3 = _telemetry.TryWriteArtifact(Path.Combine(text2, "Diagnostics"));
				if (!string.IsNullOrWhiteSpace(text3))
				{
					Logger.Debug("Wrote loader profile artifact: " + text3);
				}
			}
			return dictionary;
		}

		protected virtual IEnumerable<string> EnumerateCandidateFiles(string directoryPath)
		{
			Logger.Info("Scanning directory: " + directoryPath);
			return Directory.EnumerateFiles(directoryPath, "*.dll", SearchOption.AllDirectories);
		}

		protected virtual void ScanSingleFile(string filePath, IReadOnlyCollection<string> effectiveRoots, Dictionary<string, ScannedPluginResult> results, ISet<string> activeCanonicalPaths, ISet<string> processedCanonicalPaths, string resolverFingerprint)
		{
			long startTimestamp = _telemetry.StartTimestamp();
			string fileName = Path.GetFileName(filePath);
			using FileProbe fileProbe = _fileIdentityProvider.OpenProbe(filePath);
			if (!_scopeFilter.IsTargetAssembly(fileProbe.CanonicalPath, effectiveRoots, Config))
			{
				_telemetry.IncrementCounter("Files.OutOfScope", 1L);
				return;
			}
			activeCanonicalPaths.Add(fileProbe.CanonicalPath);
			if (!processedCanonicalPaths.Add(fileProbe.CanonicalPath))
			{
				_telemetry.IncrementCounter("Files.DuplicateCanonicalPath", 1L);
				return;
			}
			if (IsSelfAssembly(fileProbe.CanonicalPath))
			{
				Logger.Debug("Skipping self: " + fileName);
				_telemetry.IncrementCounter("Files.Self", 1L);
				return;
			}
			if (Config.EnableScanCache && TryReuseByPathCache(fileProbe, filePath, resolverFingerprint, results))
			{
				_telemetry.RecordFileSample(filePath, startTimestamp, "cache-hit:path", 0L, 0);
				return;
			}
			bool flag = fileProbe.Stream.CanSeek && fileProbe.Stream.Length > 268435456;
			long num = 0L;
			byte[] array = null;
			string text;
			if (flag)
			{
				Logger.Warning($"Manual review required for {fileName}: it exceeds the loader scan limit of {256L} MB and cannot be fully analyzed in memory.");
				_telemetry.IncrementCounter("Files.TooLarge", 1L);
				num = (fileProbe.Stream.CanSeek ? fileProbe.Stream.Length : 0);
				if (num > 0)
				{
					_telemetry.IncrementCounter("Bytes.Read", num);
				}
				long startTimestamp2 = _telemetry.StartTimestamp();
				text = CalculateStreamHash(fileProbe.Stream);
				_telemetry.AddPhaseElapsed("Hash.CalculateSha256", startTimestamp2);
			}
			else
			{
				long startTimestamp3 = _telemetry.StartTimestamp();
				array = ReadFileBytes(fileProbe.Stream);
				_telemetry.AddPhaseElapsed("File.ReadBytes", startTimestamp3);
				num = array.Length;
				_telemetry.IncrementCounter("Bytes.Read", num);
				long startTimestamp4 = _telemetry.StartTimestamp();
				text = HashUtility.CalculateBytesHash(array);
				_telemetry.AddPhaseElapsed("Hash.CalculateSha256", startTimestamp4);
			}
			if (IsExactSelfCopy(text))
			{
				Logger.Debug("Skipping self copy: " + fileName);
				_telemetry.IncrementCounter("Files.SelfCopy", 1L);
			}
			else
			{
				if (IsHashWhitelisted(fileName, text))
				{
					return;
				}
				if (Config.EnableScanCache && TryReuseByHashCache(text, fileProbe, filePath, resolverFingerprint, results))
				{
					_telemetry.RecordFileSample(filePath, startTimestamp, "cache-hit:hash", num, 0);
					return;
				}
				ScannedPluginResult scannedPluginResult = ThreatVerdictBuilder.Build(filePath, text, new List<ScanFinding>());
				if (scannedPluginResult.ThreatVerdict.Kind == ThreatVerdictKind.KnownMaliciousSample)
				{
					UpsertCacheEntry(fileProbe, text, resolverFingerprint, scannedPluginResult);
					RegisterResultIfNeeded(fileName, scannedPluginResult, results);
					_telemetry.RecordFileSample(filePath, startTimestamp, "hash-only-known-sample", num, 0);
					return;
				}
				if (!flag)
				{
					long startTimestamp5 = _telemetry.StartTimestamp();
					using MemoryStream assemblyStream = new MemoryStream(array, writable: false);
					List<ScanFinding> source = AssemblyScanner.Scan(assemblyStream, filePath).ToList();
					_telemetry.AddPhaseElapsed("Scan.Assembly", startTimestamp5);
					List<ScanFinding> list = source.Where((ScanFinding finding) => finding.Location != "Assembly scanning").ToList();
					ScannedPluginResult scannedPluginResult2 = ThreatVerdictBuilder.Build(filePath, text, list);
					UpsertCacheEntry(fileProbe, text, resolverFingerprint, scannedPluginResult2);
					RegisterResultIfNeeded(fileName, scannedPluginResult2, results);
					_telemetry.RecordFileSample(filePath, startTimestamp, "scan", num, list.Count);
					return;
				}
				ScannedPluginResult scannedPluginResult3 = CreateOversizedAssemblyResult(filePath, text, num);
				UpsertCacheEntry(fileProbe, text, resolverFingerprint, scannedPluginResult3);
				RegisterResultIfNeeded(fileName, scannedPluginResult3, results);
				_telemetry.RecordFileSample(filePath, startTimestamp, "oversized:review-required", num, scannedPluginResult3.Findings.Count);
			}
		}

		private bool TryReuseByPathCache(FileProbe probe, string filePath, string resolverFingerprint, Dictionary<string, ScannedPluginResult> results)
		{
			IScanCacheStore cacheStore = GetCacheStore();
			if (cacheStore == null)
			{
				return false;
			}
			ScanCacheEntry scanCacheEntry = cacheStore.TryGetByPath(probe.CanonicalPath);
			if (scanCacheEntry == null)
			{
				_telemetry.IncrementCounter("Cache.PathMiss", 1L);
				return false;
			}
			if (!scanCacheEntry.CanReuseStrictly(probe, _scannerFingerprint, resolverFingerprint, cacheStore.CanTrustCleanEntries))
			{
				_telemetry.IncrementCounter("Cache.PathRejected", 1L);
				return false;
			}
			_telemetry.IncrementCounter("Cache.PathHit", 1L);
			RegisterResultIfNeeded(Path.GetFileName(filePath), scanCacheEntry.CloneResultForPath(filePath), results);
			return true;
		}

		private bool TryReuseByHashCache(string hash, FileProbe probe, string filePath, string resolverFingerprint, Dictionary<string, ScannedPluginResult> results)
		{
			IScanCacheStore cacheStore = GetCacheStore();
			if (cacheStore == null)
			{
				return false;
			}
			ScanCacheEntry scanCacheEntry = cacheStore.TryGetByHash(hash);
			if (scanCacheEntry == null)
			{
				_telemetry.IncrementCounter("Cache.HashMiss", 1L);
				return false;
			}
			if (!string.Equals(scanCacheEntry.ScannerFingerprint, _scannerFingerprint, StringComparison.Ordinal) || !string.Equals(scanCacheEntry.ResolverFingerprint, resolverFingerprint, StringComparison.Ordinal))
			{
				_telemetry.IncrementCounter("Cache.HashRejected", 1L);
				return false;
			}
			if (!ScanResultFacts.HasThreatVerdict(scanCacheEntry.Result) && !cacheStore.CanTrustCleanEntries)
			{
				_telemetry.IncrementCounter("Cache.HashRejected", 1L);
				return false;
			}
			ScannedPluginResult scannedPluginResult = scanCacheEntry.CloneResultForPath(filePath);
			UpsertCacheEntry(probe, hash, resolverFingerprint, scannedPluginResult);
			_telemetry.IncrementCounter("Cache.HashHit", 1L);
			RegisterResultIfNeeded(Path.GetFileName(filePath), scannedPluginResult, results);
			return true;
		}

		private void RegisterResultIfNeeded(string fileName, ScannedPluginResult scannedResult, Dictionary<string, ScannedPluginResult> results)
		{
			if (scannedResult == null || IsHashWhitelisted(fileName, scannedResult.FileHash) || !ScanResultFacts.RequiresAttention(scannedResult))
			{
				return;
			}
			results[scannedResult.FilePath] = scannedResult;
			if (scannedResult.ThreatVerdict.Kind == ThreatVerdictKind.KnownMaliciousSample || scannedResult.ThreatVerdict.Kind == ThreatVerdictKind.KnownMalwareFamily)
			{
				string text = scannedResult.ThreatVerdict.PrimaryFamily?.DisplayName;
				if (!string.IsNullOrWhiteSpace(text))
				{
					Logger.Warning("Detected likely malware in " + fileName + " - " + scannedResult.ThreatVerdict.Title + ": " + text);
				}
				else
				{
					Logger.Warning("Detected likely malware in " + fileName + " - " + scannedResult.ThreatVerdict.Title);
				}
			}
			else if (scannedResult.ThreatVerdict.Kind == ThreatVerdictKind.Suspicious)
			{
				Logger.Warning("Detected suspicious behavior in " + fileName + " - " + scannedResult.ThreatVerdict.Title);
			}
			else
			{
				Logger.Warning("Manual review required for " + fileName + " - " + scannedResult.ScanStatus.Title);
			}
		}

		private bool IsHashWhitelisted(string fileName, string fileHash)
		{
			if (!ConfigManager.IsHashWhitelisted(fileHash))
			{
				return false;
			}
			Logger.Debug("Skipping whitelisted: " + fileName);
			_telemetry.IncrementCounter("Files.Whitelisted", 1L);
			return true;
		}

		private void UpsertCacheEntry(FileProbe probe, string hash, string resolverFingerprint, ScannedPluginResult result)
		{
			GetCacheStore()?.Upsert(new ScanCacheEntry
			{
				CanonicalPath = probe.CanonicalPath,
				RealPath = probe.OriginalPath,
				FileIdentity = probe.Identity,
				Sha256 = hash,
				ScannerFingerprint = _scannerFingerprint,
				ResolverFingerprint = resolverFingerprint,
				Result = result
			});
		}

		private static ScannedPluginResult CreateOversizedAssemblyResult(string filePath, string fileHash, long fileSizeBytes)
		{
			long num = 256L;
			double num2 = ((fileSizeBytes > 0) ? Math.Ceiling((double)fileSizeBytes / 1048576.0) : 0.0);
			List<ScanFinding> findings = new List<ScanFinding>
			{
				new ScanFinding("Loader scan preflight", $"Assembly exceeds the loader scan limit ({num2:0.#} MB > {num} MB). SHA-256 and exact known-malicious sample checks still ran, but full IL analysis was skipped and the file requires manual review.", Severity.Medium)
				{
					RuleId = "OversizedAssembly"
				}
			};
			return new ScannedPluginResult
			{
				FilePath = (filePath ?? string.Empty),
				FileHash = (fileHash ?? string.Empty),
				Findings = findings,
				ThreatVerdict = new ThreatVerdictInfo
				{
					Kind = ThreatVerdictKind.None,
					Title = "No threat verdict",
					Summary = "No retained malicious verdict was produced before the loader hit a scan-completeness limit.",
					Confidence = 0.0,
					ShouldBypassThreshold = false
				},
				ScanStatus = new ScanStatusInfo
				{
					Kind = ScanStatusKind.RequiresReview,
					Title = "Manual review required",
					Summary = $"This file exceeds the loader scan size limit ({num} MB). MLVScan calculated its SHA-256 hash and checked exact known-malicious sample matches, but full IL analysis was skipped to avoid loading the entire assembly into memory."
				}
			};
		}

		private string BuildResolverCatalog(IReadOnlyCollection<string> effectiveRoots)
		{
			if (_resolverCatalogProvider == null)
			{
				return "resolver-provider-unavailable";
			}
			long startTimestamp = _telemetry.StartTimestamp();
			_resolverCatalogProvider.BuildCatalog(effectiveRoots);
			_telemetry.AddPhaseElapsed("Resolver.BuildCatalog", startTimestamp);
			return _resolverCatalogProvider.ContextFingerprint;
		}

		private IScanCacheStore GetCacheStore()
		{
			if (!Config.EnableScanCache || _cacheUnavailable)
			{
				return null;
			}
			if (_cacheStore != null)
			{
				return _cacheStore;
			}
			try
			{
				_cacheStore = CreateDefaultCacheStore(Environment);
			}
			catch (Exception ex)
			{
				_cacheUnavailable = true;
				Logger.Warning("Scan cache unavailable; continuing without cache reuse: " + ex.Message);
			}
			return _cacheStore;
		}

		private bool IsExactSelfCopy(string hash)
		{
			return !string.IsNullOrWhiteSpace(_selfAssemblyHash) && _selfAssemblyHash.Equals(hash, StringComparison.OrdinalIgnoreCase);
		}

		private string TryGetDataDirectory()
		{
			try
			{
				return Environment.DataDirectory;
			}
			catch (Exception ex)
			{
				Logger.Warning("Diagnostics output unavailable: " + ex.Message);
				return null;
			}
		}

		private static string GetSelfAssemblyHash(string selfAssemblyPath)
		{
			try
			{
				if (string.IsNullOrWhiteSpace(selfAssemblyPath) || !File.Exists(selfAssemblyPath))
				{
					return string.Empty;
				}
				return HashUtility.CalculateFileHash(selfAssemblyPath);
			}
			catch
			{
				return string.Empty;
			}
		}

		private static byte[] ReadFileBytes(FileStream stream)
		{
			stream.Position = 0L;
			using MemoryStream memoryStream = new MemoryStream((int)(stream.CanSeek ? stream.Length : 0));
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static string CalculateStreamHash(FileStream stream)
		{
			stream.Position = 0L;
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(stream);
			return BitConverter.ToString(array).Replace("-", string.Empty).ToLowerInvariant();
		}

		private static IScanCacheStore CreateDefaultCacheStore(IPlatformEnvironment environment)
		{
			string cacheDirectory = Path.Combine(environment.DataDirectory, "Cache");
			return new SecureScanCacheStore(cacheDirectory, new ScanCacheSigner(cacheDirectory));
		}

		private static string ComputeScannerFingerprint(ScanConfig config, IReadOnlyCollection<IScanRule> rules)
		{
			List<string> list = new List<string>
			{
				"MLVScan.MelonLoader",
				"2.0.2",
				MLVScanVersions.CoreVersion,
				"1.2.0".ToString(),
				$"EnableMultiSignalDetection={config.EnableMultiSignalDetection}",
				$"AnalyzeExceptionHandlers={config.AnalyzeExceptionHandlers}",
				$"AnalyzeLocalVariables={config.AnalyzeLocalVariables}",
				$"AnalyzePropertyAccessors={config.AnalyzePropertyAccessors}",
				$"DetectAssemblyMetadata={config.DetectAssemblyMetadata}",
				$"EnableCrossMethodAnalysis={config.EnableCrossMethodAnalysis}",
				$"MaxCallChainDepth={config.MaxCallChainDepth}",
				$"EnableReturnValueTracking={config.EnableReturnValueTracking}",
				$"EnableRecursiveResourceScanning={config.EnableRecursiveResourceScanning}",
				$"MaxRecursiveResourceSizeMB={config.MaxRecursiveResourceSizeMB}",
				$"MinimumEncodedStringLength={config.MinimumEncodedStringLength}"
			};
			list.AddRange(from rule in rules.OrderBy<IScanRule, string>((IScanRule rule) => rule.RuleId, StringComparer.Ordinal).ThenBy<IScanRule, string>((IScanRule rule) => rule.GetType().FullName, StringComparer.Ordinal)
				select $"{rule.RuleId}|{rule.Severity}|{rule.GetType().FullName}");
			return HashUtility.CalculateBytesHash(Encoding.UTF8.GetBytes(string.Join("\n", list)));
		}

		private static StringComparer GetPathComparer()
		{
			return (RuntimeInformationHelper.IsWindows || RuntimeInformationHelper.IsMacOs) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
		}
	}
	public class DisabledPluginInfo
	{
		public string OriginalPath { get; }

		public string DisabledPath { get; }

		public string FileHash { get; }

		public ThreatVerdictInfo ThreatVerdict { get; }

		public ScanStatusInfo ScanStatus { get; }

		public DisabledPluginInfo(string originalPath, string disabledPath, string fileHash, ThreatVerdictInfo threatVerdict, ScanStatusInfo scanStatus)
		{
			OriginalPath = originalPath;
			DisabledPath = disabledPath;
			FileHash = fileHash;
			ThreatVerdict = threatVerdict ?? new ThreatVerdictInfo();
			ScanStatus = scanStatus ?? new ScanStatusInfo();
		}
	}
	public abstract class PluginDisablerBase
	{
		protected readonly IScanLogger Logger;

		protected readonly MLVScanConfig Config;

		protected virtual string GetDisabledExtension()
		{
			return ".disabled";
		}

		protected PluginDisablerBase(IScanLogger logger, MLVScanConfig config)
		{
			Logger = logger ?? throw new ArgumentNullException("logger");
			Config = config ?? throw new ArgumentNullException("config");
		}

		protected virtual string GetDisabledPath(string originalPath)
		{
			return Path.ChangeExtension(originalPath, GetDisabledExtension());
		}

		protected virtual void OnPluginDisabled(string originalPath, string disabledPath, string hash)
		{
		}

		public List<DisabledPluginInfo> DisableSuspiciousPlugins(Dictionary<string, ScannedPluginResult> scanResults, bool forceDisable = false)
		{
			if (!forceDisable && !Config.EnableAutoDisable)
			{
				Logger.Info("Automatic disabling is turned off in configuration");
				return new List<DisabledPluginInfo>();
			}
			List<DisabledPluginInfo> list = new List<DisabledPluginInfo>();
			foreach (var (text2, scannedPluginResult2) in scanResults)
			{
				if (!ScanResultFacts.RequiresAttention(scannedPluginResult2))
				{
					continue;
				}
				ThreatVerdictInfo threatVerdict = scannedPluginResult2?.ThreatVerdict ?? new ThreatVerdictInfo();
				ScanStatusInfo scanStatus = scannedPluginResult2?.ScanStatus ?? new ScanStatusInfo();
				if (!ShouldDisable(scannedPluginResult2))
				{
					Logger.Info("Plugin " + Path.GetFileName(text2) + " requires attention (" + GetOutcomeTitle(scannedPluginResult2) + ") but blocking for this outcome is disabled in configuration");
					continue;
				}
				try
				{
					DisabledPluginInfo disabledPluginInfo = DisablePlugin(text2, threatVerdict, scanStatus);
					if (disabledPluginInfo != null)
					{
						list.Add(disabledPluginInfo);
						OnPluginDisabled(disabledPluginInfo.OriginalPath, disabledPluginInfo.DisabledPath, disabledPluginInfo.FileHash);
					}
				}
				catch (Exception ex)
				{
					Logger.Error("Failed to disable " + Path.GetFileName(text2) + ": " + ex.Message);
				}
			}
			return list;
		}

		private bool ShouldDisable(ScannedPluginResult scanResult)
		{
			ThreatVerdictInfo threatVerdictInfo = scanResult?.ThreatVerdict ?? new ThreatVerdictInfo();
			ThreatVerdictKind kind = threatVerdictInfo.Kind;
			if (1 == 0)
			{
			}
			bool result = kind switch
			{
				ThreatVerdictKind.KnownMaliciousSample => Config.BlockKnownThreats, 
				ThreatVerdictKind.KnownMalwareFamily => Config.BlockKnownThreats, 
				ThreatVerdictKind.Suspicious => Config.BlockSuspicious, 
				_ => ShouldDisable(scanResult?.ScanStatus ?? new ScanStatusInfo()), 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private bool ShouldDisable(ScanStatusInfo scanStatus)
		{
			ScanStatusKind scanStatusKind = scanStatus?.Kind ?? ScanStatusKind.Complete;
			if (1 == 0)
			{
			}
			bool result = scanStatusKind == ScanStatusKind.RequiresReview && Config.BlockIncompleteScans;
			if (1 == 0)
			{
			}
			return result;
		}

		private static string GetOutcomeTitle(ScannedPluginResult scanResult)
		{
			if (ScanResultFacts.HasThreatVerdict(scanResult))
			{
				return scanResult.ThreatVerdict.Title;
			}
			if (ScanResultFacts.RequiresManualReview(scanResult))
			{
				return scanResult.ScanStatus.Title;
			}
			return "No action required";
		}

		protected virtual DisabledPluginInfo DisablePlugin(string pluginPath, ThreatVerdictInfo threatVerdict, ScanStatusInfo scanStatus)
		{
			string fileHash = HashUtility.CalculateFileHash(pluginPath);
			string disabledPath = GetDisabledPath(pluginPath);
			if (File.Exists(disabledPath))
			{
				File.Delete(disabledPath);
			}
			File.Move(pluginPath, disabledPath);
			Logger.Warning("BLOCKED: " + Path.GetFileName(pluginPath));
			return new DisabledPluginInfo(pluginPath, disabledPath, fileHash, threatVerdict, scanStatus);
		}
	}
	public class DeveloperReportGenerator
	{
		private readonly IScanLogger _logger;

		public DeveloperReportGenerator(IScanLogger logger)
		{
			_logger = logger ?? throw new ArgumentNullException("logger");
		}

		public void GenerateConsoleReport(string modName, List<ScanFinding> findings)
		{
			if (