using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.Kirshoo.ModVerifier")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("com.github.Kirshoo.ModVerifier")]
[assembly: AssemblyTitle("ModVerifier")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[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 BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace ModVerifier
{
public enum SourceType
{
Web,
LocalFile
}
public enum ReportPosition
{
TopLeft,
Top,
TopRight,
Left,
Right,
BottomLeft,
Bottom,
BottomRight
}
[BepInPlugin("com.github.Kirshoo.ModVerifier", "ModVerifier", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
public class WhitelistEntry
{
public string guid = string.Empty;
public string name = string.Empty;
public string version = string.Empty;
public string sha256 = string.Empty;
}
[CompilerGenerated]
private sealed class <>c__DisplayClass19_0
{
public string fontname;
internal bool <LoadFontCoroutine>b__0(TMP_FontAsset asset)
{
return ((Object)asset).name == fontname;
}
}
[CompilerGenerated]
private sealed class <LoadFontCoroutine>d__19 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public string fontname;
private <>c__DisplayClass19_0 <>8__1;
public Action<TMP_FontAsset> callback;
private int <retries>5__2;
private TMP_FontAsset <font>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadFontCoroutine>d__19(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>8__1 = null;
<font>5__3 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>8__1 = new <>c__DisplayClass19_0();
<>8__1.fontname = fontname;
<retries>5__2 = 5;
<font>5__3 = null;
break;
case 1:
<>1__state = -1;
break;
}
if (<retries>5__2 > 0)
{
<font>5__3 = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset asset) => ((Object)asset).name == <>8__1.fontname));
if (!((Object)(object)<font>5__3 != (Object)null))
{
<retries>5__2--;
Logger.LogDebug((object)$"Failed to load {<>8__1.fontname} font, retrying after {(float)(5 - <retries>5__2) * 0.05f}s...");
<>2__current = (object)new WaitForSeconds((float)(5 - <retries>5__2) * 0.05f);
<>1__state = 1;
return true;
}
Logger.LogDebug((object)("Found " + <>8__1.fontname + "! Proceeding..."));
}
if ((Object)(object)<font>5__3 == (Object)null)
{
Logger.LogWarning((object)("Failed to load " + <>8__1.fontname + " font, fallback to built in font..."));
<font>5__3 = Resources.GetBuiltinResource<TMP_FontAsset>("LiberationSans SDF");
}
callback?.Invoke(<font>5__3);
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();
}
}
private const string WhitelistURL = "https://4ufps.github.io/peakverify.json";
private static readonly string LogPath = Path.Combine(Paths.BepInExRootPath, "verifier_result.log");
private static readonly StringBuilder LogBuffer = new StringBuilder();
private const int MAX_LOAD_FONT_RETRIES = 5;
private const float BACKOFF_DELAY_MILLISECONDS = 0.05f;
private static int TotalAmount = 0;
private static int Unauthorized = 0;
private GameObject canvas_go;
private TextMeshProUGUI report_content;
private TMP_FontAsset font_asset;
private ConfigEntry<ReportPosition> reportPos;
private ConfigEntry<float> padding;
public const string Id = "com.github.Kirshoo.ModVerifier";
internal static ManualLogSource Logger { get; private set; } = null;
public static string Name => "ModVerifier";
public static string Version => "1.0.0";
private async void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
BindConfig();
CreateCanvas();
Logger.LogInfo((object)"Starting verification...");
try
{
bool flag = await VerifyRunningPlugins(SourceType.Web, "https://4ufps.github.io/peakverify.json");
Logger.LogInfo((object)string.Format("Verification finished with {0} at {1:O}", flag ? "success" : "fail", DateTime.UtcNow));
Logger.LogInfo((object)("Writing report into " + LogPath));
File.WriteAllText(LogPath, LogBuffer.ToString());
UpdateText(report_content, TotalAmount, Unauthorized);
}
catch (Exception ex)
{
Logger.LogError((object)("Verification failed: " + ex.Message));
((TMP_Text)report_content).text = "Error making report:\n" + ex.Message;
}
}
private void BindConfig()
{
reportPos = ((BaseUnityPlugin)this).Config.Bind<ReportPosition>("VerifierReport", "ReportPosition", ReportPosition.TopLeft, "The position of the mod verifier report.\r\nUse ReportPadding to make sure its not obstructed by other text.");
padding = ((BaseUnityPlugin)this).Config.Bind<float>("VerifierReport", "ReportPadding", 20f, "Padding of the text from all sides.");
}
[IteratorStateMachine(typeof(<LoadFontCoroutine>d__19))]
private IEnumerator LoadFontCoroutine(string fontname, Action<TMP_FontAsset> callback)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadFontCoroutine>d__19(0)
{
fontname = fontname,
callback = callback
};
}
private void CreateCanvas()
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Expected O, but got Unknown
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)canvas_go != (Object)null)
{
return;
}
canvas_go = new GameObject("ModVerifierCanvas");
Canvas val = canvas_go.AddComponent<Canvas>();
val.renderMode = (RenderMode)0;
val.sortingOrder = 1000;
CanvasScaler val2 = canvas_go.AddComponent<CanvasScaler>();
val2.uiScaleMode = (ScaleMode)1;
val2.referenceResolution = new Vector2(1920f, 1080f);
GameObject val3 = new GameObject("VerifierReport");
val3.transform.SetParent(canvas_go.transform, false);
report_content = val3.AddComponent<TextMeshProUGUI>();
((TMP_Text)report_content).text = "Loading report...";
if ((Object)(object)font_asset != (Object)null)
{
SetFont(report_content, font_asset);
}
else
{
((MonoBehaviour)this).StartCoroutine(LoadFontCoroutine("DarumaDropOne-Regular SDF", delegate(TMP_FontAsset font)
{
font_asset = font;
SetFont(report_content, font_asset);
}));
}
TextAlignmentOptions alignment;
switch (reportPos.Value)
{
case ReportPosition.Top:
case ReportPosition.Bottom:
alignment = (TextAlignmentOptions)514;
break;
case ReportPosition.TopLeft:
case ReportPosition.Left:
case ReportPosition.BottomLeft:
alignment = (TextAlignmentOptions)513;
break;
case ReportPosition.TopRight:
case ReportPosition.Right:
case ReportPosition.BottomRight:
alignment = (TextAlignmentOptions)516;
break;
default:
alignment = (TextAlignmentOptions)513;
break;
}
((TMP_Text)report_content).alignment = alignment;
RectTransform component = val3.GetComponent<RectTransform>();
PlaceInPosition(component, reportPos.Value, padding.Value);
Object.DontDestroyOnLoad((Object)(object)canvas_go);
}
private void PlaceInPosition(RectTransform rect, ReportPosition pos, float padding)
{
//IL_0149: Unknown result type (might be due to invalid IL or missing references)
//IL_0150: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
Vector2 val = default(Vector2);
Vector2 anchoredPosition = default(Vector2);
switch (pos)
{
case ReportPosition.TopLeft:
((Vector2)(ref val))..ctor(0f, 1f);
((Vector2)(ref anchoredPosition))..ctor(padding, 0f - padding);
break;
case ReportPosition.Top:
((Vector2)(ref val))..ctor(0.5f, 1f);
((Vector2)(ref anchoredPosition))..ctor(0f, 0f - padding);
break;
case ReportPosition.TopRight:
((Vector2)(ref val))..ctor(1f, 1f);
((Vector2)(ref anchoredPosition))..ctor(0f - padding, 0f - padding);
break;
case ReportPosition.Left:
((Vector2)(ref val))..ctor(0f, 0.5f);
((Vector2)(ref anchoredPosition))..ctor(padding, 0f);
break;
case ReportPosition.Right:
((Vector2)(ref val))..ctor(1f, 0.5f);
((Vector2)(ref anchoredPosition))..ctor(0f - padding, 0f);
break;
case ReportPosition.BottomLeft:
((Vector2)(ref val))..ctor(0f, 0f);
((Vector2)(ref anchoredPosition))..ctor(padding, padding);
break;
case ReportPosition.Bottom:
((Vector2)(ref val))..ctor(0.5f, 0f);
((Vector2)(ref anchoredPosition))..ctor(0f, padding);
break;
case ReportPosition.BottomRight:
((Vector2)(ref val))..ctor(1f, 0f);
((Vector2)(ref anchoredPosition))..ctor(0f - padding, padding);
break;
default:
((Vector2)(ref val))..ctor(0f, 1f);
((Vector2)(ref anchoredPosition))..ctor(padding, 0f - padding);
break;
}
rect.anchorMin = val;
rect.anchorMax = val;
rect.pivot = val;
rect.anchoredPosition = anchoredPosition;
}
private static void UpdateText(TextMeshProUGUI textMesh, int total, int unauthorized)
{
string text = $"Running with a total of {total} mods";
if (unauthorized > 0)
{
text += $"\nof which {unauthorized} are unauthorized";
}
((TMP_Text)textMesh).text = text;
}
private static void SetFont(TextMeshProUGUI textMesh, TMP_FontAsset font)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
((TMP_Text)textMesh).font = font;
((TMP_Text)textMesh).fontSize = 20f;
((TMP_Text)textMesh).overflowMode = (TextOverflowModes)0;
((TMP_Text)textMesh).textWrappingMode = (TextWrappingModes)0;
((Graphic)textMesh).color = Color.white;
((TMP_Text)textMesh).outlineWidth = 0.075f;
((TMP_Text)textMesh).outlineColor = Color32.op_Implicit(Color.black);
}
private async Task<bool> VerifyRunningPlugins(SourceType type, string source)
{
string pluginDir = Paths.PluginPath;
LogBuffer.AppendLine("=== Verification Report (" + Plugin.Name + " v" + Version + ") ===");
LogBuffer.AppendLine($"Timestamp: {DateTime.UtcNow:O}");
LogBuffer.AppendLine($"Whitelist source: {source} of type {type}\n");
Logger.LogDebug((object)("Fetching whitelist from " + source));
WhitelistEntry[] source2;
try
{
source2 = await FetchWhitelist(type, source);
}
catch (JsonSerializationException val)
{
JsonSerializationException innerException = val;
throw new Exception("Serializing the whitelist: malformed whitelist", (Exception?)(object)innerException);
}
catch (HttpRequestException ex)
{
throw new Exception("Fetching the whitelist: " + ex.Message, ex);
}
string[] files = Directory.GetFiles(pluginDir, "*.dll", SearchOption.AllDirectories);
foreach (string text in files)
{
if (!GetMetadata(text, out string guid, out string Name, out string version))
{
Logger.LogWarning((object)("Unable to retrieve metadata from " + text));
LogBuffer.AppendLine("Unknown plugin: " + Path.GetFileNameWithoutExtension(text));
continue;
}
TotalAmount++;
string sha = ComputeSha256(text);
bool flag = source2.Any((WhitelistEntry wMod) => string.Equals(wMod.guid, guid, StringComparison.OrdinalIgnoreCase) && string.Equals(wMod.version, version, StringComparison.OrdinalIgnoreCase) && string.Equals(wMod.sha256, sha, StringComparison.OrdinalIgnoreCase));
LogBuffer.AppendLine(Name + " (" + guid + ") v" + version);
LogBuffer.AppendLine(" SHA256: " + sha);
LogBuffer.AppendLine(" Status: " + (flag ? "Allowed" : "Unauthorized!"));
if (!flag)
{
Unauthorized++;
Logger.LogWarning((object)("Unauthorized mod: " + Name + " (" + version + ")"));
}
}
return Unauthorized == 0;
}
private async Task<WhitelistEntry[]> FetchWhitelist(SourceType type, string source)
{
return type switch
{
SourceType.Web => await FetchWhitelistAsync(source),
SourceType.LocalFile => await LoadWhitelistFileAsync(source),
_ => throw new InvalidDataException($"Unknown source type: {type}"),
};
}
private async Task<WhitelistEntry[]> FetchWhitelistAsync(string web_url)
{
using HttpClient client = new HttpClient();
return JsonConvert.DeserializeObject<WhitelistEntry[]>(await client.GetStringAsync(web_url));
}
private async Task<WhitelistEntry[]> LoadWhitelistFileAsync(string filepath)
{
return JsonConvert.DeserializeObject<WhitelistEntry[]>(await File.ReadAllTextAsync(filepath));
}
private static bool GetMetadata(string filepath, out string GUID, out string Name, out string Version)
{
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
ModuleDefinition val = ModuleDefinition.ReadModule(filepath);
GUID = (Name = (Version = ""));
TypeDefinition val2 = ((IEnumerable<TypeDefinition>)val.Types).FirstOrDefault((Func<TypeDefinition, bool>)((TypeDefinition t) => ((IEnumerable<CustomAttribute>)t.CustomAttributes).Any((CustomAttribute a) => ((MemberReference)a.AttributeType).FullName == "BepInEx.BepInPlugin")));
if (val2 == null)
{
return false;
}
CustomAttribute val3 = ((IEnumerable<CustomAttribute>)val2.CustomAttributes).First((CustomAttribute a) => ((MemberReference)a.AttributeType).FullName == "BepInEx.BepInPlugin");
CustomAttributeArgument val4 = val3.ConstructorArguments[0];
GUID = ((CustomAttributeArgument)(ref val4)).Value?.ToString() ?? string.Empty;
val4 = val3.ConstructorArguments[1];
Name = ((CustomAttributeArgument)(ref val4)).Value?.ToString() ?? string.Empty;
val4 = val3.ConstructorArguments[2];
Version = ((CustomAttributeArgument)(ref val4)).Value?.ToString() ?? string.Empty;
return true;
}
private static string ComputeSha256(string filepath)
{
using SHA256 sHA = SHA256.Create();
using FileStream inputStream = File.OpenRead(filepath);
byte[] array = sHA.ComputeHash(inputStream);
return BitConverter.ToString(array).Replace("-", "").ToUpperInvariant();
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}