using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HarmonyLib;
using Il2Cpp;
using Il2CppInterop.Common;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using MelonLoader;
using Microsoft.CodeAnalysis;
using SkipIntro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(SkipIntroMod), "Skip Intro", "1.0.0", "Relsev", null)]
[assembly: MelonGame("BoltBlasterGames", "TheSpellBrigade")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SkipIntro")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SkipIntro")]
[assembly: AssemblyTitle("SkipIntro")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[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 SkipIntro
{
public class SkipIntroMod : MelonMod
{
public static Instance Log;
public override void OnInitializeMelon()
{
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Expected O, but got Unknown
Log = ((MelonBase)this).LoggerInstance;
try
{
MethodInfo methodInfo = AccessTools.Method(typeof(OpeningFlowManager), "Start", (Type[])null, (Type[])null) ?? throw new MissingMethodException("OpeningFlowManager", "Start");
List<string> list = SharedCodeGuard.FindMethodsSharingCode(methodInfo);
if (list.Count > 0)
{
Log.Warning($"skipped OpeningFlowManager.Start: its native code is shared with {list.Count} other method(s), intro stays");
}
else
{
((MelonBase)this).HarmonyInstance.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(SkipIntroMod), "StartPostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Log.Msg("ready — logos and intro videos are skipped");
}
}
catch (Exception ex)
{
Log.Error("failed to hook OpeningFlowManager.Start, intro stays: " + ex.GetType().Name + ": " + ex.Message);
}
}
private static void StartPostfix(OpeningFlowManager __instance)
{
MelonCoroutines.Start(SkipAll(__instance));
}
private static IEnumerator SkipAll(OpeningFlowManager manager)
{
HashSet<IntPtr> skipped = new HashSet<IntPtr>();
for (int frame = 0; frame < 600; frame++)
{
if (!((Object)(object)manager != (Object)null))
{
break;
}
try
{
OpeningFlow openingFlow = manager.openingFlow;
IOpeningStep val = ((openingFlow != null) ? openingFlow.currentStep : null);
while (val != null && skipped.Add(((Il2CppObjectBase)val).Pointer))
{
openingFlow.SkipCurrentStep();
val = openingFlow.currentStep;
}
}
catch (Exception value)
{
Log.Error($"skip failed, rest of the intro plays as usual: {value}");
yield break;
}
yield return null;
}
if (skipped.Count > 0)
{
Log.Msg($"intro skipped ({skipped.Count} step(s))");
}
}
}
internal static class SharedCodeGuard
{
private static Dictionary<IntPtr, List<IntPtr>> _methodsByCode;
public static List<string> FindMethodsSharingCode(MethodBase generatedMethod)
{
List<string> list = new List<string>();
if (!(Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(generatedMethod)?.GetValue(null) is IntPtr intPtr) || intPtr == IntPtr.Zero)
{
return list;
}
IntPtr intPtr2 = Marshal.ReadIntPtr(intPtr);
if (intPtr2 == IntPtr.Zero)
{
return list;
}
if (_methodsByCode == null)
{
_methodsByCode = BuildIndex();
}
if (!_methodsByCode.TryGetValue(intPtr2, out var value))
{
return list;
}
foreach (IntPtr item in value)
{
if (item != intPtr)
{
list.Add(Describe(item));
}
}
return list;
}
private static string Describe(IntPtr method)
{
IntPtr intPtr = IL2CPP.il2cpp_method_get_class(method);
string text = IL2CPP.il2cpp_class_get_namespace_(intPtr);
return (string.IsNullOrEmpty(text) ? IL2CPP.il2cpp_class_get_name_(intPtr) : (text + "." + IL2CPP.il2cpp_class_get_name_(intPtr))) + "::" + IL2CPP.il2cpp_method_get_name_(method);
}
private unsafe static Dictionary<IntPtr, List<IntPtr>> BuildIndex()
{
Dictionary<IntPtr, List<IntPtr>> dictionary = new Dictionary<IntPtr, List<IntPtr>>();
uint num = 0u;
IntPtr* ptr = IL2CPP.il2cpp_domain_get_assemblies(IL2CPP.il2cpp_domain_get(), ref num);
for (uint num2 = 0u; num2 < num; num2++)
{
IntPtr intPtr = IL2CPP.il2cpp_assembly_get_image(ptr[num2]);
uint num3 = IL2CPP.il2cpp_image_get_class_count(intPtr);
for (uint num4 = 0u; num4 < num3; num4++)
{
IntPtr intPtr2 = IL2CPP.il2cpp_image_get_class(intPtr, num4);
if (intPtr2 == IntPtr.Zero)
{
continue;
}
IntPtr zero = IntPtr.Zero;
IntPtr intPtr3;
while ((intPtr3 = IL2CPP.il2cpp_class_get_methods(intPtr2, ref zero)) != IntPtr.Zero)
{
IntPtr intPtr4 = Marshal.ReadIntPtr(intPtr3);
if (!(intPtr4 == IntPtr.Zero))
{
if (!dictionary.TryGetValue(intPtr4, out var value))
{
value = (dictionary[intPtr4] = new List<IntPtr>(1));
}
value.Add(intPtr3);
}
}
}
}
return dictionary;
}
}
}