using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; 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; using AK; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Hook; using BepInEx.Unity.IL2CPP.Utils; using CellMenu; using Clonesoft.Json; using Clonesoft.Json.Converters; using Clonesoft.Json.Serialization; using CullingSystem; using Enemies; using GameData; using GameEvent; using Gear; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.Runtime; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.IO; using Il2CppSystem.Reflection; using InControl; using JetBrains.Annotations; using LevelGeneration; using Localization; using Microsoft.CodeAnalysis; using Mono.Cecil; using Player; using SNetwork; using SemanticVersioning; using TMPro; using TheArchive.Core; using TheArchive.Core.Attributes; using TheArchive.Core.Attributes.Feature; using TheArchive.Core.Attributes.Feature.Members; using TheArchive.Core.Attributes.Feature.Patches; using TheArchive.Core.Attributes.Feature.Settings; using TheArchive.Core.Bootstrap; using TheArchive.Core.Definitions; using TheArchive.Core.Definitions.Data; using TheArchive.Core.Exceptions; using TheArchive.Core.FeaturesAPI; using TheArchive.Core.FeaturesAPI.Components; using TheArchive.Core.FeaturesAPI.Groups; using TheArchive.Core.FeaturesAPI.Settings; using TheArchive.Core.Interop; using TheArchive.Core.Localization; using TheArchive.Core.Localization.Data; using TheArchive.Core.Localization.Services; using TheArchive.Core.Managers; using TheArchive.Core.Models; using TheArchive.Core.ModulesAPI; using TheArchive.Core.Settings; using TheArchive.Interfaces; using TheArchive.Loader; using TheArchive.Utilities; using UnityEngine; using UnityEngine.Analytics; using UnityEngine.CrashReportHandler; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyFileVersion("0.0.838")] [assembly: AssemblyInformationalVersion("0.0.838-doing-things+a4eb55a")] [assembly: ModSettingsDisplayName("TheArchive")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("TheArchive.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyProduct("TheArchive.Core")] [assembly: AssemblyTitle("TheArchive.Core")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.838.0")] [module: UnverifiableCode] 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; } } } internal class ThisAssembly { public class Git { public class BaseVersion { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "0"; } public class SemVer { public const string Major = "0"; public const string Minor = "0"; public const string Patch = "838"; public const string Label = ""; public const string DashLabel = ""; public const string Source = "Default"; } public const bool IsDirty = false; public const string IsDirtyString = "false"; public const string RepositoryUrl = "https://github.com/Tuesday1028/GTFO_TheArchive"; public const string Branch = "doing-things"; public const string Commit = "a4eb55a"; public const string Sha = "a4eb55a7e97569a44df6831acc6074b0f084b6eb"; public const string CommitDate = "2026-05-07T19:14:52+08:00"; public const string Commits = "838"; public const string Tag = ""; public const string BaseTag = ""; } } internal class ManifestInfo { internal const string TSName = "TheArchive_Core"; internal const string TSDescription = "The main Archive mod, adds the ModSettings menu."; internal const string TSVersion = ""; internal const string TSAuthor = "AuriRex"; internal const string TSWebsite = "https://github.com/AuriRex/GTFO_TheArchive"; } namespace JetBrains.Annotations { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)] internal sealed class CanBeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] internal sealed class ItemNotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] internal sealed class ItemCanBeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)] internal sealed class StringFormatMethodAttribute : Attribute { [NotNull] public string FormatParameterName { get; } public StringFormatMethodAttribute([NotNull] string formatParameterName) { FormatParameterName = formatParameterName; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class StructuredMessageTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)] internal sealed class ValueProviderAttribute : Attribute { [NotNull] public string Name { get; } public ValueProviderAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate, AllowMultiple = true)] internal sealed class ValueRangeAttribute : Attribute { public object From { get; } public object To { get; } public ValueRangeAttribute(long from, long to) { From = from; To = to; } public ValueRangeAttribute(ulong from, ulong to) { From = from; To = to; } public ValueRangeAttribute(long value) { From = (To = value); } public ValueRangeAttribute(ulong value) { From = (To = value); } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] internal sealed class NonNegativeValueAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class InvokerParameterNameAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute { [CanBeNull] public string ParameterName { get; } public NotifyPropertyChangedInvocatorAttribute() { } public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class ContractAnnotationAttribute : Attribute { [NotNull] public string Contract { get; } public bool ForceFullStates { get; } public ContractAnnotationAttribute([NotNull] string contract) : this(contract, forceFullStates: false) { } public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) { Contract = contract; ForceFullStates = forceFullStates; } } [AttributeUsage(AttributeTargets.All)] internal sealed class LocalizationRequiredAttribute : Attribute { public bool Required { get; } public LocalizationRequiredAttribute() : this(required: true) { } public LocalizationRequiredAttribute(bool required) { Required = required; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)] internal sealed class DefaultEqualityUsageAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [BaseTypeRequired(typeof(Attribute))] internal sealed class BaseTypeRequiredAttribute : Attribute { [NotNull] public Type BaseType { get; } public BaseTypeRequiredAttribute([NotNull] Type baseType) { BaseType = baseType; } } [AttributeUsage(AttributeTargets.All)] internal sealed class UsedImplicitlyAttribute : Attribute { public ImplicitUseKindFlags UseKindFlags { get; } public ImplicitUseTargetFlags TargetFlags { get; } public string Reason { get; set; } public UsedImplicitlyAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter | AttributeTargets.GenericParameter)] internal sealed class MeansImplicitUseAttribute : Attribute { [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } public MeansImplicitUseAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } } [Flags] internal enum ImplicitUseKindFlags { Default = 7, Access = 1, Assign = 2, InstantiatedWithFixedConstructorSignature = 4, InstantiatedNoFixedConstructorSignature = 8 } [Flags] internal enum ImplicitUseTargetFlags { Default = 1, Itself = 1, Members = 2, WithInheritors = 4, WithMembers = 3 } [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] [AttributeUsage(AttributeTargets.All, Inherited = false)] internal sealed class PublicAPIAttribute : Attribute { [CanBeNull] public string Comment { get; } public PublicAPIAttribute() { } public PublicAPIAttribute([NotNull] string comment) { Comment = comment; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class InstantHandleAttribute : Attribute { public bool RequireAwait { get; set; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class PureAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class MustUseReturnValueAttribute : Attribute { [CanBeNull] public string Justification { get; } public bool IsFluentBuilderMethod { get; set; } public MustUseReturnValueAttribute() { } public MustUseReturnValueAttribute([NotNull] string justification) { Justification = justification; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Parameter)] internal sealed class MustDisposeResourceAttribute : Attribute { public bool Value { get; } public MustDisposeResourceAttribute() { Value = true; } public MustDisposeResourceAttribute(bool value) { Value = value; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class HandlesResourceDisposalAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class RequireStaticDelegateAttribute : Attribute { public bool IsError { get; set; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.GenericParameter)] internal sealed class ProvidesContextAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class PathReferenceAttribute : Attribute { [CanBeNull] public string BasePath { get; } public PathReferenceAttribute() { } public PathReferenceAttribute([NotNull][PathReference] string basePath) { BasePath = basePath; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class SourceTemplateAttribute : Attribute { public SourceTemplateTargetExpression Target { get; set; } } internal enum SourceTemplateTargetExpression { Inner, Outer } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)] internal sealed class MacroAttribute : Attribute { [CanBeNull] public string Expression { get; set; } public int Editable { get; set; } [CanBeNull] public string Target { get; set; } } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.ReturnValue)] internal sealed class CollectionAccessAttribute : Attribute { public CollectionAccessType CollectionAccessType { get; } public CollectionAccessAttribute(CollectionAccessType collectionAccessType) { CollectionAccessType = collectionAccessType; } } [Flags] internal enum CollectionAccessType { None = 0, Read = 1, ModifyExistingContent = 2, UpdatedContent = 6 } [AttributeUsage(AttributeTargets.Method)] internal sealed class AssertionMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AssertionConditionAttribute : Attribute { public AssertionConditionType ConditionType { get; } public AssertionConditionAttribute(AssertionConditionType conditionType) { ConditionType = conditionType; } } internal enum AssertionConditionType { IS_TRUE, IS_FALSE, IS_NULL, IS_NOT_NULL } [Obsolete("Use [ContractAnnotation('=> halt')] instead")] [AttributeUsage(AttributeTargets.Method)] internal sealed class TerminatesProgramAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class LinqTunnelAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class NoEnumerationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class RegexPatternAttribute : Attribute { } internal enum InjectedLanguage { CSS, HTML, JAVASCRIPT, JSON, XML } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] internal sealed class LanguageInjectionAttribute : Attribute { public InjectedLanguage InjectedLanguage { get; } [CanBeNull] public string InjectedLanguageName { get; } [CanBeNull] public string Prefix { get; set; } [CanBeNull] public string Suffix { get; set; } public LanguageInjectionAttribute(InjectedLanguage injectedLanguage) { InjectedLanguage = injectedLanguage; } public LanguageInjectionAttribute([NotNull] string injectedLanguage) { InjectedLanguageName = injectedLanguage; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, AllowMultiple = true)] internal sealed class NoReorderAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] internal sealed class CodeTemplateAttribute : Attribute { public string SearchTemplate { get; } public string Message { get; set; } public string ReplaceTemplate { get; set; } public string ReplaceMessage { get; set; } public bool FormatAfterReplace { get; set; } = true; public bool MatchSimilarConstructs { get; set; } public bool ShortenReferences { get; set; } public string SuppressionKey { get; set; } public CodeTemplateAttribute(string searchTemplate) { SearchTemplate = searchTemplate; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class IgnoreSpellingAndGrammarErrorsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspChildControlTypeAttribute : Attribute { [NotNull] public string TagName { get; } [NotNull] public Type ControlType { get; } public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) { TagName = tagName; ControlType = controlType; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] internal sealed class AspDataFieldAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] internal sealed class AspDataFieldsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class AspMethodPropertyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] internal sealed class AspRequiredAttributeAttribute : Attribute { [NotNull] public string Attribute { get; } public AspRequiredAttributeAttribute([NotNull] string attribute) { Attribute = attribute; } } [AttributeUsage(AttributeTargets.Property)] internal sealed class AspTypePropertyAttribute : Attribute { public bool CreateConstructorReferences { get; } public AspTypePropertyAttribute(bool createConstructorReferences) { CreateConstructorReferences = createConstructorReferences; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaViewComponentViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaViewComponentViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcMasterLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcMasterLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcViewComponentViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcViewComponentViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] internal sealed class AspMvcViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcActionAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcActionAttribute() { } public AspMvcActionAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcAreaAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcAreaAttribute() { } public AspMvcAreaAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcControllerAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcControllerAttribute() { } public AspMvcControllerAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcMasterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspMvcModelTypeAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcPartialViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcEditorTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcViewComponentAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class AspMvcViewComponentViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] internal sealed class AspMvcActionSelectorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class RouteTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] internal sealed class RouteParameterConstraintAttribute : Attribute { [NotNull] public string ConstraintName { get; } [CanBeNull] public Type ProposedType { get; set; } public RouteParameterConstraintAttribute([NotNull] string constraintName) { ConstraintName = constraintName; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class UriStringAttribute : Attribute { [CanBeNull] public string HttpVerb { get; } public UriStringAttribute() { } public UriStringAttribute(string httpVerb) { HttpVerb = httpVerb; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class AspRouteConventionAttribute : Attribute { [CanBeNull] public string PredefinedPattern { get; } public AspRouteConventionAttribute() { } public AspRouteConventionAttribute(string predefinedPattern) { PredefinedPattern = predefinedPattern; } } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspDefaultRouteValuesAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspRouteValuesConstraintsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] internal sealed class AspRouteOrderAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] internal sealed class AspRouteVerbsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] internal sealed class AspAttributeRoutingAttribute : Attribute { public string HttpVerb { get; set; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class AspMinimalApiDeclarationAttribute : Attribute { public string HttpVerb { get; set; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class AspMinimalApiGroupAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class AspMinimalApiHandlerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class AspMinimalApiImplicitEndpointDeclarationAttribute : Attribute { public string HttpVerb { get; set; } public string RouteTemplate { get; set; } public Type BodyType { get; set; } public string QueryParameters { get; set; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class HtmlElementAttributesAttribute : Attribute { [CanBeNull] public string Name { get; } public HtmlElementAttributesAttribute() { } public HtmlElementAttributesAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] internal sealed class HtmlAttributeValueAttribute : Attribute { [NotNull] public string Name { get; } public HtmlAttributeValueAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] internal sealed class RazorSectionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorImportNamespaceAttribute : Attribute { [NotNull] public string Name { get; } public RazorImportNamespaceAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorInjectionAttribute : Attribute { [NotNull] public string Type { get; } [NotNull] public string FieldName { get; } public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) { Type = type; FieldName = fieldName; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorDirectiveAttribute : Attribute { [NotNull] public string Directive { get; } public RazorDirectiveAttribute([NotNull] string directive) { Directive = directive; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class RazorPageBaseTypeAttribute : Attribute { [NotNull] public string BaseType { get; } [CanBeNull] public string PageName { get; } public RazorPageBaseTypeAttribute([NotNull] string baseType) { BaseType = baseType; } public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) { BaseType = baseType; PageName = pageName; } } [AttributeUsage(AttributeTargets.Method)] internal sealed class RazorHelperCommonAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class RazorLayoutAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] internal sealed class RazorWriteMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] internal sealed class RazorWriteMethodParameterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] internal sealed class XamlItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] internal sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] internal sealed class XamlOneWayBindingModeByDefaultAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] internal sealed class XamlTwoWayBindingModeByDefaultAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface, AllowMultiple = true)] internal sealed class TestSubjectAttribute : Attribute { [NotNull] public Type Subject { get; } public TestSubjectAttribute([NotNull] Type subject) { Subject = subject; } } [AttributeUsage(AttributeTargets.GenericParameter)] internal sealed class MeansTestSubjectAttribute : Attribute { } } namespace TheArchive { [HideInModSettings] [DisallowInGameToggle] [DoNotSaveToConfig] [EnableFeatureByDefault] internal class ArchiveBootstrap : Feature { [ArchivePatch(typeof(GameDataInit), "Initialize", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GameDataInit__Initialize__Patch { public static void Postfix() { try { InvokeGameDataInitialized(); } catch (ReflectionTypeLoadException ex) { ArchiveLogger.Error("Exception thrown in ArchiveBootstrap"); ArchiveLogger.Msg(ConsoleColor.Green, "Oh no, seems like someone's referencing game types from an older/newer game version that do not exist anymore! :c"); ArchiveLogger.Exception(ex); ArchiveLogger.Warning($"{ex.Types.Length} Types loaded."); ArchiveLogger.Notice("Exceptions:"); Exception[] loaderExceptions = ex.LoaderExceptions; foreach (Exception ex2 in loaderExceptions) { if (ex2 != null) { ArchiveLogger.Error(ex2.Message); } } } catch (Exception ex3) { ArchiveLogger.Error("Exception thrown in ArchiveBootstrap"); ArchiveLogger.Exception(ex3); if (ex3.InnerException != null) { ArchiveLogger.Exception(ex3?.InnerException); } } } } [RundownConstraint(Utils.RundownFlags.RundownSix, Utils.RundownFlags.Latest)] [ArchivePatch("Setup", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class LocalizationManager__Setup__Patch { public static Type Type() { return typeof(LocalizationManager); } public static void Postfix() { InvokeDataBlocksReady(); } } [ArchivePatch(typeof(GameStateManager), "ChangeState", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GameStateManager__ChangeState__Patch { public static void Postfix(eGameStateName nextState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected I4, but got Unknown ArchiveMod.InvokeGameStateChanged((int)nextState); } } [RundownConstraint(Utils.RundownFlags.RundownAltOne, Utils.RundownFlags.Latest)] [ArchivePatch("OnBtnPress", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class CM_RundownSelection__OnBtnPress__Patch { public static Type Type() { return typeof(CM_RundownSelection); } public static void Postfix(CM_RundownSelection __instance) { ArchiveMod.CurrentlySelectedRundownKey = __instance.RundownKey; } } [RundownConstraint(Utils.RundownFlags.RundownAltOne, Utils.RundownFlags.Latest)] [ArchivePatch(typeof(CM_PageRundown_New), "Setup", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class CM_PageRundown_New__Setup__Patch { public static void Postfix(CM_PageRundown_New __instance) { __instance.m_selectRundownButton.AddCMItemEvents(delegate { ArchiveMod.CurrentlySelectedRundownKey = string.Empty; }); } } [ArchivePatch(typeof(InControlManager), "OnApplicationFocus", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class EventSystem__OnApplicationFocus__Patch { public static void Postfix(bool focusState) { ArchiveMod.InvokeApplicationFocusChanged(focusState); } } public override string Name => "ArchiveBootstrap"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Hooks into a bunch of important game code in order for this mod to work."; public override bool RequiresRestart => true; private static void InvokeGameDataInitialized() { ArchiveMod.InvokeGameDataInitialized(); if (ArchiveMod.CurrentRundown.IsIncludedIn(Utils.RundownFlags.RundownFour | Utils.RundownFlags.RundownFive)) { InvokeDataBlocksReady(); } } private static void InvokeDataBlocksReady() { if (SharedUtils.TryGetRundownDataBlock(out var block)) { ArchiveMod.CurrentlySelectedRundownKey = $"Local_{((GameDataBlockBase)(object)block).persistentID}"; } ArchiveMod.InvokeDataBlocksReady(); } } public static class ArchiveMod { public const string GUID = "dev.AuriRex.gtfo.TheArchive"; public const string MOD_NAME = "TheArchive"; public const string ABBREVIATION = "Ar"; public const string AUTHOR = "AuriRex"; public const string VERSION_STRING = "0.0.838"; public const string GITHUB_REPOSITORY_NAME = "GTFO_TheArchive"; public const string GITHUB_OWNER_NAME = "AuriRex"; public const string GITHUB_LINK = "https://github.com/AuriRex/GTFO_TheArchive"; public static readonly bool GIT_IS_DIRTY = false; public const string GIT_COMMIT_SHORT_HASH = "a4eb55a"; public const string GIT_COMMIT_DATE = "2026-05-07T19:14:52+08:00"; public const string GIT_BASE_TAG = ""; public const uint GTFO_STEAM_APPID = 493520u; public const string MTFO_GUID = "com.dak.MTFO"; public static readonly string CORE_PATH = Assembly.GetAssembly(typeof(ArchiveMod)).Location; private static JsonSerializerSettings _jsonSerializerSettings = null; private static string _currentlySelectedRundownKey = string.Empty; private static IArchiveModule _mainModule; private static readonly HashSet _typesToInitOnDataBlocksReady = new HashSet(); private static readonly HashSet _typesToInitOnGameDataInit = new HashSet(); private static readonly HashSet _moduleAssemblies = new HashSet(); private static readonly List _moduleTypes = new List(); private const string ARCHIVE_SETTINGS_FILE = "TheArchive_Settings.json"; private static Harmony _harmonyInstance; internal static ArchiveSettings Settings { get; private set; } = new ArchiveSettings(); internal static JsonSerializerSettings JsonSerializerSettings { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown if (_jsonSerializerSettings != null) { return _jsonSerializerSettings; } _jsonSerializerSettings = new JsonSerializerSettings { Formatting = (Formatting)1 }; _jsonSerializerSettings.Converters.Add((JsonConverter)new StringEnumConverter()); _jsonSerializerSettings.ContractResolver = (IContractResolver)(object)ArchiveContractResolver.Instance; return _jsonSerializerSettings; } } public static bool IsPlayingModded { get; private set; } = false; public static Utils.RundownID CurrentRundown { get; private set; } = Utils.RundownID.RundownUnitialized; public static GameBuildInfo CurrentBuildInfo { get; private set; } public static int CurrentGameState { get; private set; } public static bool IsOnALTBuild { get; private set; } private static bool IsInitialized { get; set; } public static string CurrentlySelectedRundownKey { get { return _currentlySelectedRundownKey; } internal set { _currentlySelectedRundownKey = value; ArchiveLogger.Debug($"Setting {"CurrentlySelectedRundownKey"} to \"{_currentlySelectedRundownKey}\"."); if (string.IsNullOrEmpty(_currentlySelectedRundownKey)) { CurrentlySelectedRundownPersistentID = 0u; return; } try { CurrentlySelectedRundownPersistentID = uint.Parse(_currentlySelectedRundownKey.Replace("Local_", "")); } catch (Exception ex) { ArchiveLogger.Error($"Failed to parse selected rundown persistentId from {"CurrentlySelectedRundownKey"} \"{CurrentlySelectedRundownKey}\"!"); ArchiveLogger.Exception(ex); } } } public static uint CurrentlySelectedRundownPersistentID { get; set; } = 0u; public static HashSet Modules { get; } = new HashSet(); public static Type IL2CPP_BaseType { get; private set; } = null; public static event Action GameDataInitialized; public static event Action DataBlocksReady; public static event Action GameStateChanged; public static event Action ApplicationFocusStateChanged; internal static event Action OnNewModuleRegistered; internal static void OnApplicationStart(IArchiveLogger logger, Harmony harmonyInstance) { ArchiveLogger.Logger = logger; _harmonyInstance = harmonyInstance; if (GIT_IS_DIRTY) { ArchiveLogger.Warning("Git is dirty, this is a development build!"); } if (LoaderWrapper.IsGameIL2CPP()) { IL2CPP_BaseType = ImplementationManager.FindTypeInCurrentAppDomain("Il2CppSystem.Object", exactMatch: true); ArchiveLogger.Debug("IL2CPP_BaseType: " + IL2CPP_BaseType?.FullName); if (IL2CPP_BaseType == null) { ArchiveLogger.Error("IL2CPP base type \"Il2CppSystem.Object\" could not be resolved!"); } } LoadConfig(); if (LoaderWrapper.IsModInstalled("com.dak.MTFO")) { IsPlayingModded = true; } GTFOLogger.Logger = LoaderWrapper.CreateLoggerInstance("GTFO-Internals", ConsoleColor.DarkGray); CurrentRundown = BuildDB.GetCurrentRundownID(BuildDB.BuildNumber); ArchiveLogger.Msg(ConsoleColor.DarkMagenta, $"Current game revision determined to be {BuildDB.BuildNumber}! ({CurrentRundown})"); GameBuildInfo currentBuildInfo = default(GameBuildInfo); currentBuildInfo.BuildNumber = BuildDB.BuildNumber; currentBuildInfo.Rundown = CurrentRundown; CurrentBuildInfo = currentBuildInfo; IsOnALTBuild = CurrentRundown.IsIncludedIn(Utils.RundownFlags.RundownAltOne.ToLatest()); string path = Path.Combine(LoaderWrapper.GameDirectory, "steam_appid.txt"); if (!File.Exists(path)) { ArchiveLogger.Notice("Creating \"steam_appid.txt\" in GTFO folder ..."); File.WriteAllText(path, $"{493520}"); } AddInternalAttribution(); FeatureManager.Internal_Init(); try { _mainModule = CreateAndInitModule(typeof(MainArchiveModule)); } catch (ReflectionTypeLoadException ex) { ArchiveLogger.Error("Failed loading main module!!"); ArchiveLogger.Exception(ex); ArchiveLogger.Notice($"Loader Exceptions ({ex.LoaderExceptions.Length}):"); Exception[] loaderExceptions = ex.LoaderExceptions; foreach (Exception ex2 in loaderExceptions) { if (ex2 != null) { ArchiveLogger.Warning(ex2.Message); ArchiveLogger.Debug(ex2.StackTrace); } } ArchiveLogger.Info("-------------"); } InitializeArchiveModuleChainloader(); } internal static void OnApplicationQuit() { InitSingletonBase.Instance.OnApplicationQuit(); CustomSettingManager.OnApplicationQuit(); } private static void LoadConfig() { LoadConfig(Path.Combine(LocalFiles.ModLocalLowPath, "TheArchive_Settings.json")); } private static void LoadConfig(string path) { try { ArchiveLogger.Info("Loading config file ... [" + path + "]"); if (File.Exists(path)) { Settings = JsonConvert.DeserializeObject(File.ReadAllText(path), JsonSerializerSettings); } SaveConfig(path); } catch (Exception ex) { ArchiveLogger.Exception(ex); } } private static void SaveConfig(string path) { ArchiveLogger.Debug("Saving config file ... [" + path + "]"); File.WriteAllText(path, JsonConvert.SerializeObject((object)Settings, JsonSerializerSettings)); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static bool RegisterArchiveModule(Assembly asm) { return RegisterArchiveModule(asm.GetTypes().FirstOrDefault((Type t) => typeof(IArchiveModule).IsAssignableFrom(t))); } public static bool RegisterArchiveModule(Type moduleType) { if (moduleType == null) { throw new ArgumentException("Module can't be null!"); } if (_moduleTypes.Contains(moduleType)) { throw new ArgumentException("Module \"" + moduleType.Name + "\" is already registered!"); } if (!typeof(IArchiveModule).IsAssignableFrom(moduleType)) { throw new ArgumentException($"Type \"{moduleType.Name}\" does not implement {"IArchiveModule"}!"); } IArchiveModule archiveModule = CreateAndInitModule(moduleType); Utils.SafeInvoke(ArchiveMod.OnNewModuleRegistered, archiveModule); if (CurrentRundown != Utils.RundownID.RundownUnitialized) { return true; } return false; } private static void InitializeArchiveModuleChainloader() { ((BaseChainloader)(object)IL2CPPChainloader.Instance).Finished += ArchiveModuleChainloader.Initialize; } internal static void InvokeGameDataInitialized() { if (IsInitialized) { ArchiveLogger.Info("Reload triggered, skipping init."); return; } IsInitialized = true; ArchiveLogger.Info("GameData has been initialized, invoking event."); foreach (Type item in _typesToInitOnGameDataInit) { try { InitInitializables(item, out var _); } catch (Exception ex) { ArchiveLogger.Error("Trying to Init \"" + item.FullName + "\" threw an exception:"); ArchiveLogger.Exception(ex); } } InitSingletonBase.Instance.OnGameDataInitialized(); Utils.SafeInvoke(ArchiveMod.GameDataInitialized, CurrentRundown); } internal static void InvokeDataBlocksReady() { try { DataBlockManager.Setup(); } catch (Exception ex) { ArchiveLogger.Exception(ex); } ArchiveLogger.Info("DataBlocks should be ready to be interacted with, invoking event."); foreach (Type item in _typesToInitOnDataBlocksReady) { try { InitInitializables(item, out var _); } catch (Exception ex2) { ArchiveLogger.Error("Trying to Init \"" + item.FullName + "\" threw an exception:"); ArchiveLogger.Exception(ex2); } } InitSingletonBase.Instance.OnDatablocksReady(); CustomSettingManager.OnGameDataInited(); Interop.OnDataBlocksReady(); Utils.SafeInvoke(ArchiveMod.DataBlocksReady); } internal static void InvokeGameStateChanged(int eGameState_state) { CurrentGameState = eGameState_state; Utils.SafeInvoke(ArchiveMod.GameStateChanged, eGameState_state); } internal static void InvokeApplicationFocusChanged(bool focus) { Utils.SafeInvoke(ArchiveMod.ApplicationFocusStateChanged, focus); } private static void InitInitializables(Type type, out IInitializable initializable) { ArchiveLogger.Debug("Creating instance of: \"" + type.FullName + "\"."); IInitializable initializable2 = (initializable = (IInitializable)Activator.CreateInstance(type)); bool flag = true; Type type2 = typeof(InitSingletonBase<>).MakeGenericType(type); bool flag2 = type2.IsAssignableFrom(type); if (flag2) { type2.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SetValue(null, initializable2); } if (typeof(IInitCondition).IsAssignableFrom(type)) { IInitCondition initCondition = (IInitCondition)initializable2; try { flag = initCondition.InitCondition(); } catch (Exception ex) { ArchiveLogger.Error("InitCondition method on Type \"" + type.FullName + "\" failed!"); ArchiveLogger.Warning("This IInitializable won't be initialized."); ArchiveLogger.Exception(ex); flag = false; } } if (!flag) { return; } try { initializable2.Init(); if (flag2) { type2.GetProperty("HasBeenInitialized", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SetValue(null, true); } } catch (Exception ex2) { ArchiveLogger.Error("Init method on Type \"" + type.FullName + "\" failed!"); ArchiveLogger.Exception(ex2); } } private static void InspectType(Type type, IArchiveModule module) { if (typeof(Feature).IsAssignableFrom(type) && type != typeof(Feature)) { InitSingletonBase.Instance.InitFeature(type, module); return; } if (typeof(IInitImmediately).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { try { InitInitializables(type, out var _); return; } catch (Exception ex) { ArchiveLogger.Error("Trying to Init \"" + type.FullName + "\" (immediately) threw an exception:"); ArchiveLogger.Exception(ex); return; } } if (typeof(IInitAfterGameDataInitialized).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { _typesToInitOnGameDataInit.Add(type); } else if (typeof(IInitAfterDataBlocksReady).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract) { _typesToInitOnDataBlocksReady.Add(type); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static IArchiveModule CreateAndInitModule(Type moduleType) { if (moduleType == null) { throw new ArgumentException("Parameter moduleType can not be null!"); } Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); _moduleTypes.Add(moduleType); ArchiveLogger.Info("Initializing module \"" + moduleType.FullName + "\" ..."); IArchiveModule archiveModule = (IArchiveModule)Activator.CreateInstance(moduleType); DefinitionManager.LoadModuleDefinitions(archiveModule); IArchiveLogger archiveLogger2 = (archiveModule.Logger = LoaderWrapper.CreateLoggerInstance(moduleType.Assembly.GetName().Name, ConsoleColor.DarkMagenta)); IArchiveLogger logger = archiveLogger2; ModuleLocalizationService moduleLocalizationService = (ModuleLocalizationService)(archiveModule.LocalizationService = new ModuleLocalizationService(archiveModule, moduleType, logger)); try { moduleLocalizationService.Setup(); } catch (Exception ex) { ArchiveLogger.Error("Error while trying to setup module localization for \"" + moduleType.FullName + "\"!"); ArchiveLogger.Exception(ex); } try { archiveModule.Init(); } catch (Exception ex2) { ArchiveLogger.Error("Error while trying to init \"" + moduleType.FullName + "\"!"); ArchiveLogger.Exception(ex2); } Type[] types = moduleType.Assembly.GetTypes(); for (int i = 0; i < types.Length; i++) { InspectType(types[i], archiveModule); } _moduleAssemblies.Add(moduleType.Assembly); Modules.Add(archiveModule); stopwatch.Stop(); ArchiveLogger.Debug($"Creation of \"{moduleType.FullName}\" took {stopwatch.Elapsed:ss\\.fff} seconds."); return archiveModule; } internal static void OnUpdate() { InitSingletonBase.Instance.OnUpdate(); } internal static void OnLateUpdate() { InitSingletonBase.Instance.OnLateUpdate(); } private static void AddInternalAttribution() { try { string @string = Encoding.UTF8.GetString(Utils.LoadFromResource("TheArchive.Resources.LICENSE")); Attribution.Add(new Attribution.AttributionInfo("TheArchive License", @string ?? "") { Origin = "TheArchive.Core", Comment = "Huge thanks to everyone that has contributed! - Check out the repo on GitHub!" }); string string2 = Encoding.UTF8.GetString(Utils.LoadFromResource("TheArchive.Resources.LICENSE_BepInEx")); Attribution.Add(new Attribution.AttributionInfo("BepInEx Info + License", ("This project contains parts of BepInEx code, denoted in source files.\n\nLICENSE (Truncated, see repository):\n\n" + string2).Substring(0, 619) + "\n\n[...]") { Origin = "TheArchive.Core" }); string content = "Material Symbols used in ThunderStore mod icons licensed under Apache License Version 2.0\n\n> https://github.com/google/material-design-icons\n> https://www.apache.org/licenses/LICENSE-2.0.txt"; Attribution.Add(new Attribution.AttributionInfo("Mod Icon(s) Info + License", content) { Origin = "TheArchive.Core" }); string string3 = Encoding.UTF8.GetString(Utils.LoadFromResource("TheArchive.Resources.LICENSE_JBA")); Attribution.Add(new Attribution.AttributionInfo("JetBrains.Annotations License", string3 ?? "") { Origin = "TheArchive.Core" }); } catch (Exception ex) { ArchiveLogger.Error("Error while trying to add internal AttributionInfos"); ArchiveLogger.Exception(ex); } } } [ArchiveModule("dev.AuriRex.gtfo.TheArchive", "TheArchive", "0.0.838")] internal class MainArchiveModule : IArchiveModule { public ILocalizationService LocalizationService { get; set; } public IArchiveLogger Logger { get; set; } static MainArchiveModule() { ImplementationManagerExtensions.RegisterSelf(typeof(EnemyDataBlock)); ImplementationManagerExtensions.RegisterSelf(typeof(GameDataBlockBase<>)); ImplementationManagerExtensions.RegisterSelf(typeof(GameDataBlockWrapper<>)); ImplementationManagerExtensions.RegisterSelf(typeof(eGameStateName)); ImplementationManagerExtensions.RegisterSelf(typeof(LG_Area)); typeof(List<>).RegisterForIdentifier("GenericList"); } public void Init() { ArchiveLocalizationService.Setup(LocalizationService); CrashReportHandler.SetUserMetadata("Modded", "true"); CrashReportHandler.enableCaptureExceptions = false; } } } namespace TheArchive.Utilities { public static class AccessorExtensions { public static IValueAccessor OrAlternative(this IValueAccessor self, Func> func) { if (self != null && self.HasMember) { return self; } if (func == null) { throw new NullReferenceException("Parameter func may not be null!"); } return func(); } } public interface IValueAccessor { bool CanGet { get; } bool CanSet { get; } bool HasMember { get; } MT Get(T instance); void Set(T instance, MT value); } public interface IStaticValueAccessor : IValueAccessor { bool IsStatic { get; } MT GetStaticValue(); void SetStaticValue(MT value); } public abstract class AccessorBase { protected static readonly Dictionary Accessors = new Dictionary(); protected static object[] NoParams { get; } = Array.Empty(); protected static BindingFlags AnyBindingFlags => BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public string Identifier { get; private set; } public bool IgnoreErrors { get; private set; } public abstract bool HasMemberBeenFound { get; } protected AccessorBase(string identifier, bool ignoreErrors) { Identifier = identifier; IgnoreErrors = ignoreErrors; } public static IValueAccessor GetValueAccessor(string memberName, bool throwOnError = false) { if (LoaderWrapper.IsGameIL2CPP() && LoaderWrapper.IsIL2CPPType(typeof(T))) { return PropertyAccessor.GetAccessor(memberName, !throwOnError); } MemberInfo memberInfo = typeof(T).GetMember(memberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(); if (!(memberInfo is PropertyInfo)) { if (memberInfo is FieldInfo) { return FieldAccessor.GetAccessor(memberName, !throwOnError); } if (throwOnError) { throw new ArgumentException($"Member with name \"{memberName}\" could not be found in type \"{typeof(T).Name}\" or isn't {"IValueAccessor"} compatible.", "memberName"); } return null; } return PropertyAccessor.GetAccessor(memberName, !throwOnError); } public static IStaticValueAccessor GetStaticValueAccessor(string memberName, bool throwOnError = false) { IValueAccessor valueAccessor = GetValueAccessor(memberName, throwOnError); if (valueAccessor != null) { IStaticValueAccessor staticValueAccessor = valueAccessor as IStaticValueAccessor; if (throwOnError && !staticValueAccessor.IsStatic) { throw new ArgumentException("Member with name \"" + memberName + "\" is not static!", "memberName"); } return staticValueAccessor; } return null; } } public class FieldAccessor : AccessorBase, IValueAccessor, IStaticValueAccessor { private readonly FieldInfo _field; public override bool HasMemberBeenFound => _field != null; public bool CanGet => true; public bool CanSet => true; public bool HasMember => HasMemberBeenFound; public bool IsStatic => _field?.IsStatic ?? false; public static FieldAccessor GetAccessor(string fieldName, bool ignoreErrors = false) { string text = "Field_" + typeof(T).FullName + "_" + fieldName; if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (FieldAccessor)value; } value = new FieldAccessor(text, fieldName, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (FieldAccessor)value; } private FieldAccessor(string identifier, string fieldName, bool ignoreErrors = false) : base(identifier, ignoreErrors) { _field = typeof(T).GetField(fieldName, AccessorBase.AnyBindingFlags); } public FT Get(T instance) { try { return (FT)_field.GetValue(instance); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (!HasMemberBeenFound) { ArchiveLogger.Warning($"NullReferenceException while getting {"FieldAccessor"} field \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); return default(FT); } ArchiveLogger.Error($"NullReferenceException while getting {"FieldAccessor"} field \"{base.Identifier}\"!"); throw; } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while getting {"FieldAccessor"} field \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } return default(FT); } public void Set(T instance, FT value) { try { _field.SetValue(instance, value); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (HasMemberBeenFound) { ArchiveLogger.Error($"NullReferenceException while setting {"FieldAccessor"} field \"{base.Identifier}\"!"); throw; } ArchiveLogger.Warning($"NullReferenceException while setting {"FieldAccessor"} field \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while setting {"FieldAccessor"} field \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } } public FT GetStaticValue() { return Get(default(T)); } public void SetStaticValue(FT value) { Set(default(T), value); } } public class PropertyAccessor : AccessorBase, IValueAccessor, IStaticValueAccessor { private readonly PropertyInfo _property; public override bool HasMemberBeenFound => _property != null; public bool CanGet => _property?.GetGetMethod(nonPublic: true) != null; public bool CanSet => _property?.GetSetMethod(nonPublic: true) != null; public bool HasMember => HasMemberBeenFound; public bool IsStatic => (_property?.GetGetMethod(nonPublic: true) ?? _property?.GetSetMethod(nonPublic: true))?.IsStatic ?? false; public static PropertyAccessor GetAccessor(string propertyName, bool ignoreErrors = false) { string text = "Property_" + typeof(T).FullName + "_" + propertyName; if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (PropertyAccessor)value; } value = new PropertyAccessor(text, propertyName, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (PropertyAccessor)value; } private PropertyAccessor(string identifier, string propertyName, bool ignoreErrors = false) : base(identifier, ignoreErrors) { _property = typeof(T).GetProperty(propertyName, AccessorBase.AnyBindingFlags); } public PT Get(T instance) { try { return (PT)_property.GetValue(instance); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (!HasMemberBeenFound) { ArchiveLogger.Warning($"NullReferenceException while getting {"PropertyAccessor"} property \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); return default(PT); } ArchiveLogger.Error($"NullReferenceException while getting {"PropertyAccessor"} property \"{base.Identifier}\"!"); throw; } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while getting {"PropertyAccessor"} property \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } return default(PT); } public void Set(T instance, PT value) { try { _property.SetValue(instance, value); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (HasMemberBeenFound) { ArchiveLogger.Error($"NullReferenceException while setting {"PropertyAccessor"} property \"{base.Identifier}\"!"); throw; } ArchiveLogger.Warning($"NullReferenceException while setting {"PropertyAccessor"} property \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while setting {"PropertyAccessor"} property \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } } public PT GetStaticValue() { return Get(default(T)); } public void SetStaticValue(PT value) { Set(default(T), value); } } public class MethodAccessor : AccessorBase { private readonly MethodInfo _method; public bool IsMethodStatic => _method.IsStatic; public override bool HasMemberBeenFound => _method != null; public static MethodAccessor GetAccessor(string methodName, Type[] parameterTypes = null, bool ignoreErrors = false) { string text = $"Method_{typeof(T).FullName}_{typeof(RT)}_{methodName}"; if (parameterTypes != null) { text = text + "_" + string.Join("_", parameterTypes.Select((Type pt) => pt.Name)); } if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (MethodAccessor)value; } value = new MethodAccessor(text, methodName, parameterTypes, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (MethodAccessor)value; } private MethodAccessor(string identifier, string methodName, Type[] parameterTypes, bool ignoreErrors = false) : base(identifier, ignoreErrors) { try { if (parameterTypes == null) { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags); } else { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags, null, parameterTypes, null); } } catch (Exception ex) { ArchiveLogger.Error($"Method \"{methodName}\" in Type {typeof(T).FullName} could not be resolved on {ex.Source}!"); ArchiveLogger.Exception(ex); } } public RT Invoke(T instance, params object[] parameters) { try { object obj = _method.Invoke(instance, parameters ?? AccessorBase.NoParams); if (obj == null) { return default(RT); } return (RT)obj; } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (!HasMemberBeenFound) { ArchiveLogger.Warning($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); return default(RT); } ArchiveLogger.Error($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); throw; } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); } return default(RT); } public RT Invoke(T instance) { return Invoke(instance, null); } } public class MethodAccessor : AccessorBase { private readonly MethodInfo _method; public bool IsMethodStatic => _method.IsStatic; public override bool HasMemberBeenFound => _method != null; public int ParameterCount { get; private set; } public static MethodAccessor GetAccessor(string methodName, Type[] parameterTypes = null, bool ignoreErrors = false) { string text = "Method_" + typeof(T).FullName + "_void_" + methodName; if (parameterTypes != null && parameterTypes != Array.Empty()) { text = text + "_" + string.Join("_", parameterTypes.Select((Type pt) => pt.Name)); } if (AccessorBase.Accessors.TryGetValue(text, out var value)) { return (MethodAccessor)value; } value = new MethodAccessor(text, methodName, parameterTypes, ignoreErrors); AccessorBase.Accessors.Add(text, value); return (MethodAccessor)value; } private MethodAccessor(string identifier, string methodName, Type[] parameterTypes, bool ignoreErrors = false) : base(identifier, ignoreErrors) { try { if (parameterTypes == null) { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags); } else { _method = typeof(T).GetMethod(methodName, AccessorBase.AnyBindingFlags, null, parameterTypes, null); } if (!ignoreErrors && _method == null) { throw new Exception("Method not found!"); } if (_method != null) { ParameterCount = _method.GetParameters().Length; } } catch (Exception ex) { if (parameterTypes == null) { parameterTypes = Array.Empty(); } ArchiveLogger.Error($"Method \"{methodName}\" in Type {typeof(T).FullName} could not be resolved on {ex.Source}!"); ArchiveLogger.Exception(ex); ArchiveLogger.Debug($"Constructor debug data:\nidentifier:{identifier}\nmethodName:{methodName}\nparameterTypes:{string.Join(", ", parameterTypes.Select((Type p) => p.FullName))}"); StackFrame frame = new StackTrace().GetFrame(2); ArchiveLogger.Debug($"FileName:{frame.GetFileName()} {frame.GetFileLineNumber()}\nMethod:{frame.GetMethod()?.DeclaringType?.FullName ?? "Unknown"}:{frame.GetMethod()?.Name ?? "Unknown"}"); PrintDebug(); } } private void PrintDebug() { if (!(_method == null)) { ArchiveLogger.Debug($"Method debug data:\nName:{_method.Name}\nDeclaringType:{_method.DeclaringType?.FullName ?? "Unknown"}\nReturnType:{_method.ReturnType}\nParameter Count:{_method.GetParameters().Length}\nParameters:{string.Join(", ", from p in _method.GetParameters() select p.ParameterType.FullName)}"); } } public void Invoke(T instance, params object[] parameters) { try { if (parameters != null && parameters.Length > ParameterCount) { parameters = parameters.Take(ParameterCount).ToArray(); } _method.Invoke(instance, parameters ?? AccessorBase.NoParams); } catch (NullReferenceException) { if (!base.IgnoreErrors) { if (HasMemberBeenFound) { ArchiveLogger.Error($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); throw; } ArchiveLogger.Warning($"NullReferenceException while calling {"MethodAccessor"} method \"{base.Identifier}\"! If this is intentional consider setting {"IgnoreErrors"} to true."); } } catch (Exception ex2) { ArchiveLogger.Error($"Exception while calling {"MethodAccessor"} method \"{base.Identifier}\"!"); ArchiveLogger.Exception(ex2); PrintDebug(); } } public void Invoke(T instance) { Invoke(instance, null); } } internal static class ArchiveLogger { internal static IArchiveLogger Logger; public static void Success(string msg) { Logger.Msg(ConsoleColor.Green, msg); } public static void Notice(string msg) { Logger.Msg(ConsoleColor.Cyan, msg); } public static void Msg(ConsoleColor col, string msg) { Logger.Msg(col, msg); } public static void Debug(string msg) { Logger.Msg(ConsoleColor.DarkGray, msg); } public static void Info(string msg) { Logger.Info(msg); } public static void Warning(string msg) { Logger.Msg(ConsoleColor.DarkYellow, msg); } public static void Error(string msg) { Logger.Error(msg); } public static void Msg(string v) { Info(v); } public static void Exception(Exception ex) { Error($"{ex}: {ex.Message}\n{ex.StackTrace}"); } } public static class EnumExtensions { [CompilerGenerated] private sealed class d__2 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable where T : struct, Enum { private int <>1__state; private T <>2__current; private int <>l__initialThreadId; private T value; public T <>3__value; private ulong 5__2; private IEnumerator <>7__wrap2; T IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__2(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = Convert.ToUInt64(value); <>7__wrap2 = Enum.GetValues(typeof(T)).GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } while (<>7__wrap2.MoveNext()) { T val = (T)<>7__wrap2.Current; ulong num = Convert.ToUInt64(val); if (num != 0L && (5__2 & num) == num) { <>2__current = val; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap2 = null; 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; if (<>7__wrap2 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__2 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__2(0); } d__.value = <>3__value; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__3 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private object value; public object <>3__value; private ulong 5__2; private IEnumerator <>7__wrap2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__3(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; if (value == null) { throw new ArgumentNullException("value"); } 5__2 = Convert.ToUInt64(value); Type type = value.GetType(); <>7__wrap2 = Enum.GetValues(type).GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } while (<>7__wrap2.MoveNext()) { object current = <>7__wrap2.Current; ulong num = Convert.ToUInt64(current); if (num != 0L && (5__2 & num) == num) { <>2__current = current; <>1__state = 1; return true; } } <>m__Finally1(); <>7__wrap2 = null; 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; if (<>7__wrap2 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__3 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__3(0); } d__.value = <>3__value; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } public static T ToFlags(this List enums) where T : struct, Enum { ulong num = 0uL; foreach (T @enum in enums) { num |= Convert.ToUInt64(@enum); } return (T)Enum.ToObject(typeof(T), num); } public static T GetHighestLevel(this T value) where T : struct, Enum { ulong num = Convert.ToUInt64(value); if (num == 0L) { return value; } ulong value2 = 0uL; for (ulong num2 = num; num2 != 0L; num2 &= num2 - 1) { value2 = num2 & (~num2 + 1); } return (T)Enum.ToObject(typeof(T), value2); } [IteratorStateMachine(typeof(d__2<>))] public static IEnumerable GetFlags(this T value) where T : struct, Enum { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__2(-2) { <>3__value = value }; } [IteratorStateMachine(typeof(d__3))] public static IEnumerable GetFlags(object value) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__3(-2) { <>3__value = value }; } } internal static class GTFOLogger { private static readonly HashSet _ignoreListExact = new HashSet { "show crosshair", "Setting and getting Body Position/Rotation, IK Goals, Lookat and BoneLocalRotation should only be done in OnAnimatorIK or OnStateIK" }; private static readonly HashSet _ignoreListStartsWith = new HashSet { "Wielding new item in slot", "Backend.", "BE.OnGameEvent" }; internal static IArchiveLogger Logger { private get; set; } public static void Ignore(string str) { _ignoreListExact.Add(str); } public static void Log(string message) { if (!_ignoreListExact.Contains(message) && !_ignoreListStartsWith.Any((string s) => message.StartsWith(s))) { Logger.Info(message); } } public static void Warn(string message) { if (!_ignoreListExact.Contains(message) && !_ignoreListStartsWith.Any((string s) => message.StartsWith(s))) { Logger.Warning(message); } } public static void Error(string message) { if (!_ignoreListExact.Contains(message) && !_ignoreListStartsWith.Any((string s) => message.StartsWith(s))) { Logger.Error(message); } } } internal static class ImplementationManagerExtensions { public static void RegisterForIdentifier(this Type type, string identifier) { ImplementationManager.RegisterGameType(identifier, type); } public static void RegisterSelf(this T type) where T : Type { if (type.IsGenericTypeDefinition) { type.RegisterForIdentifier(type.Name.Split('`')[0] + "<" + ((type.GenericTypeArguments.Length > 1) ? string.Join(",", new string[type.GenericTypeArguments.Length - 1]) : string.Empty) + ">"); } else { type.RegisterForIdentifier(type.Name); } } } public static class LocalFiles { public const string GTFO_SETTINGS_JSON = "GTFO_Settings.json"; public const string GTFO_FAVORITES_JSON = "GTFO_Favorites.json"; public const string GTFO_BOT_FAVORITES_JSON = "GTFO_BotFavorites.json"; private static string _modLocalLowPath; private static string _modDefaultGameLogsAndCachePath; private static string _modDefaultSaveDataPath; private static string _dataBlockDumpPath; private static string _savePath; private static string _gameLogsAndCacheSavePath; private static string _versionSpecificLogsAndCachePath; private static string _versionSpecificSavePath; private static string _otherConfigsPath; private static string _featureConfigsPath; private static string _filesPath; private static string _settingsPath; private static string _favoritesPath; private static string _botFavoritesPath; private static readonly FileStreamOptions Options = new FileStreamOptions { Access = FileAccess.Write, Mode = FileMode.Create, Options = FileOptions.WriteThrough }; public static string ModLocalLowPath { get { if (_modLocalLowPath != null) { return _modLocalLowPath; } _modLocalLowPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "GTFO_TheArchive"); if (!Directory.Exists(_modLocalLowPath)) { Directory.CreateDirectory(_modLocalLowPath); } return _modLocalLowPath; } } public static string ModDefaultGameLogsAndCachePath { get { if (_modDefaultGameLogsAndCachePath != null) { return _modDefaultGameLogsAndCachePath; } _modDefaultGameLogsAndCachePath = Path.Combine(ModLocalLowPath, "GameLogsAndCache"); if (!Directory.Exists(_modDefaultGameLogsAndCachePath)) { Directory.CreateDirectory(_modDefaultGameLogsAndCachePath); } return _modDefaultGameLogsAndCachePath; } } public static string ModDefaultSaveDataPath { get { if (_modDefaultSaveDataPath != null) { return _modDefaultSaveDataPath; } _modDefaultSaveDataPath = Path.Combine(ModLocalLowPath, "SaveData"); if (!Directory.Exists(_modLocalLowPath)) { Directory.CreateDirectory(_modLocalLowPath); } return _modDefaultSaveDataPath; } } public static string DataBlockDumpPath { get { if (!string.IsNullOrEmpty(_dataBlockDumpPath)) { return _dataBlockDumpPath; } _dataBlockDumpPath = Path.Combine(SaveDirectoryPath, "DataBlocks", $"Build_{BuildDB.BuildNumber}_{ArchiveMod.CurrentRundown}"); if (!Directory.Exists(_dataBlockDumpPath)) { Directory.CreateDirectory(_dataBlockDumpPath); } return _dataBlockDumpPath; } } public static string SaveDirectoryPath { get { if (!string.IsNullOrEmpty(_savePath)) { return _savePath; } _savePath = (string.IsNullOrWhiteSpace(ArchiveMod.Settings.CustomFileSaveLocation) ? ModDefaultSaveDataPath : ArchiveMod.Settings.CustomFileSaveLocation); if (!Directory.Exists(_savePath)) { Directory.CreateDirectory(_savePath); } return _savePath; } } public static string GameLogsAndCachePath { get { if (_gameLogsAndCacheSavePath != null) { return _gameLogsAndCacheSavePath; } _gameLogsAndCacheSavePath = (string.IsNullOrWhiteSpace(ArchiveMod.Settings.CustomLogsAndCacheLocation) ? ModDefaultGameLogsAndCachePath : ArchiveMod.Settings.CustomLogsAndCacheLocation); if (!Directory.Exists(_gameLogsAndCacheSavePath)) { Directory.CreateDirectory(_gameLogsAndCacheSavePath); } return _gameLogsAndCacheSavePath; } } public static string VersionSpecificLogsAndCachePath { get { if (!string.IsNullOrEmpty(_versionSpecificLogsAndCachePath)) { return _versionSpecificLogsAndCachePath; } _versionSpecificLogsAndCachePath = Path.Combine(GameLogsAndCachePath, $"{((int)ArchiveMod.CurrentRundown).ToString().PadLeft(2, '0')}_{ArchiveMod.CurrentRundown}_Data", "appdata"); if (!Directory.Exists(_versionSpecificLogsAndCachePath)) { Directory.CreateDirectory(_versionSpecificLogsAndCachePath); } return _versionSpecificLogsAndCachePath; } } public static string VersionSpecificSaveDirectoryPath { get { if (!string.IsNullOrEmpty(_versionSpecificSavePath)) { return _versionSpecificSavePath; } _versionSpecificSavePath = GetVersionSpecificSaveDirectoryPath(ArchiveMod.CurrentRundown); if (Directory.Exists(_versionSpecificSavePath)) { return _versionSpecificSavePath; } Directory.CreateDirectory(_versionSpecificSavePath); try { if (!CopyMostRecentSaveFiles(ArchiveMod.CurrentRundown - 1, ArchiveMod.CurrentRundown)) { ArchiveLogger.Notice("Creating new game settings file(s)!"); } } catch (Exception ex) { ArchiveLogger.Warning($"Caught an exception while trying to copy over older settings files: {ex}: {ex.Message}"); ArchiveLogger.Debug(ex.StackTrace); } return _versionSpecificSavePath; } } public static string OtherConfigsDirectoryPath { get { if (!string.IsNullOrEmpty(_otherConfigsPath)) { return _otherConfigsPath; } _otherConfigsPath = Path.Combine(SaveDirectoryPath, "OtherConfigs"); if (!Directory.Exists(_otherConfigsPath)) { Directory.CreateDirectory(_otherConfigsPath); } return _otherConfigsPath; } } public static string FeatureConfigsDirectoryPath { get { if (!string.IsNullOrEmpty(_featureConfigsPath)) { return _featureConfigsPath; } _featureConfigsPath = Path.Combine(SaveDirectoryPath, "FeatureSettings"); if (!Directory.Exists(_featureConfigsPath)) { Directory.CreateDirectory(_featureConfigsPath); } return _featureConfigsPath; } } [Obsolete("Legacy path.")] public static string FilesDirectoryPath { get { if (!string.IsNullOrEmpty(_filesPath)) { return _filesPath; } _filesPath = Path.Combine(VersionSpecificSaveDirectoryPath, "Files"); if (!Directory.Exists(_filesPath)) { Directory.CreateDirectory(_filesPath); } return _filesPath; } } public static string SettingsPath { get { if (!string.IsNullOrEmpty(_settingsPath)) { return _settingsPath; } _settingsPath = Path.Combine(VersionSpecificSaveDirectoryPath, "GTFO_Settings.json"); return _settingsPath; } } public static string FavoritesPath { get { if (string.IsNullOrEmpty(_favoritesPath)) { _favoritesPath = Path.Combine(VersionSpecificSaveDirectoryPath, "GTFO_Favorites.json"); } return _favoritesPath; } } public static string BotFavoritesPath { get { if (string.IsNullOrEmpty(_botFavoritesPath)) { _botFavoritesPath = Path.Combine(VersionSpecificSaveDirectoryPath, "GTFO_BotFavorites.json"); } return _botFavoritesPath; } } private static bool CopyFromBaseGameLocation(Utils.RundownID copyTo) { string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "10 Chambers Collective", "GTFO"); string text = Path.Combine(path, "GTFO_Settings.txt"); string text2 = Path.Combine(path, "GTFO_Favorites.txt"); string text3 = Path.Combine(path, "GTFO_BotFavorites.txt"); if (!File.Exists(text)) { return false; } string settingsPath = GetSettingsPath(copyTo); ArchiveLogger.Debug($"Copying vanilla game settings file! (\"{text}\" -> \"{settingsPath}\")"); File.Copy(text, settingsPath); string favoritesPath = GetFavoritesPath(copyTo); string botFavoritesPath = GetBotFavoritesPath(copyTo); if (File.Exists(text2)) { ArchiveLogger.Debug($"Copying vanilla game favorites file! (\"{text2}\" -> \"{favoritesPath}\")"); File.Copy(text2, favoritesPath); } if (File.Exists(text3)) { ArchiveLogger.Debug($"Copying vanilla game bot favorites file! (\"{text3}\" -> \"{botFavoritesPath}\")"); File.Copy(text3, botFavoritesPath); } return true; } private static bool CopyMostRecentSaveFiles(Utils.RundownID copyFrom, Utils.RundownID copyTo, int maxStep = 3) { if (copyFrom < Utils.RundownID.RundownOne) { return CopyFromBaseGameLocation(copyTo); } string settingsPath = GetSettingsPath(copyFrom); if (!File.Exists(settingsPath)) { if (maxStep <= 1) { return CopyFromBaseGameLocation(copyTo); } return CopyMostRecentSaveFiles(copyFrom - 1, copyTo, maxStep - 1); } string settingsPath2 = GetSettingsPath(copyTo); ArchiveLogger.Debug($"Copying most recent settings file! (\"{settingsPath}\" -> \"{settingsPath2}\")"); File.Copy(settingsPath, settingsPath2); if (ArchiveMod.IsPlayingModded) { return true; } string favoritesPath = GetFavoritesPath(copyTo); string favoritesPath2 = GetFavoritesPath(copyFrom); if (File.Exists(favoritesPath2)) { ArchiveLogger.Debug($"Copying most recent favorites file! (\"{favoritesPath2}\" -> \"{favoritesPath}\")"); File.Copy(favoritesPath2, favoritesPath); } string botFavoritesPath = GetBotFavoritesPath(copyTo); string botFavoritesPath2 = GetBotFavoritesPath(copyFrom); if (File.Exists(botFavoritesPath2)) { ArchiveLogger.Debug($"Copying most recent bot favorites file! (\"{botFavoritesPath2}\" -> \"{botFavoritesPath}\")"); File.Copy(botFavoritesPath2, botFavoritesPath); } return true; } public static string GetVersionSpecificSaveDirectoryPath(Utils.RundownID rundown) { string saveDirectoryPath = SaveDirectoryPath; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(6, 2); int num = (int)rundown; defaultInterpolatedStringHandler.AppendFormatted(num.ToString().PadLeft(2, '0')); defaultInterpolatedStringHandler.AppendLiteral("_"); defaultInterpolatedStringHandler.AppendFormatted(rundown); defaultInterpolatedStringHandler.AppendLiteral("_Data"); return Path.Combine(saveDirectoryPath, defaultInterpolatedStringHandler.ToStringAndClear()); } public static string GetSettingsPath(Utils.RundownID rundown) { return Path.Combine(GetVersionSpecificSaveDirectoryPath(rundown), "GTFO_Settings.json"); } public static string GetFavoritesPath(Utils.RundownID rundown) { return Path.Combine(GetVersionSpecificSaveDirectoryPath(rundown), "GTFO_Favorites.json"); } public static string GetBotFavoritesPath(Utils.RundownID rundown) { return Path.Combine(GetVersionSpecificSaveDirectoryPath(rundown), "GTFO_BotFavorites.json"); } public static T LoadConfig(out bool fileExists, bool saveIfNonExistent = true) where T : new() { string path = Path.Combine(OtherConfigsDirectoryPath, typeof(T).Name + ".json"); try { if (!File.Exists(path)) { T val = new T(); if (saveIfNonExistent) { SaveConfig(val); } fileExists = false; return val; } fileExists = true; return JsonConvert.DeserializeObject(File.ReadAllText(path), ArchiveMod.JsonSerializerSettings); } catch (Exception ex) { ArchiveLogger.Error("An error occured while loading config file " + typeof(T).Name + ".json"); ArchiveLogger.Exception(ex); } fileExists = false; return new T(); } public static T LoadConfig(bool saveIfNonExistent = true) where T : new() { bool fileExists; return LoadConfig(out fileExists, saveIfNonExistent); } public static void SaveConfig(T config) { try { File.WriteAllText(Path.Combine(OtherConfigsDirectoryPath, typeof(T).Name + ".json"), JsonConvert.SerializeObject((object)config, ArchiveMod.JsonSerializerSettings)); } catch (Exception ex) { ArchiveLogger.Error("An error occured while saving config file " + typeof(T).Name + ".json"); ArchiveLogger.Exception(ex); } } private static object LoadFeatureConfig(string moduleIdentifier, string featureIdentifier, Type configType, out bool fileExists, bool saveIfNonExistent = true) { if (string.IsNullOrWhiteSpace(featureIdentifier)) { throw new ArgumentException("Parameter featureIdentifier may not be null or whitespace."); } if (configType == null) { throw new ArgumentNullException("configType"); } string text = Path.Combine(FeatureConfigsDirectoryPath, moduleIdentifier); if (!Directory.Exists(text)) { if (moduleIdentifier == "TheArchive.Essentials") { string text2 = Path.Combine(FeatureConfigsDirectoryPath, "TheArchive.IL2CPP"); if (Directory.Exists(text2)) { ArchiveLogger.Msg(ConsoleColor.Green, $"Migrating old config path from \"{text2}\" to \"{text}\""); Directory.Move(text2, text); } else { Directory.CreateDirectory(text); ArchiveLogger.Msg(ConsoleColor.Green, $"Migrating old config files from \"{FeatureConfigsDirectoryPath}\" to \"{text}\""); foreach (string item in Directory.EnumerateFiles(FeatureConfigsDirectoryPath, "*.json", SearchOption.TopDirectoryOnly)) { string text3 = Path.Combine(text, Path.GetFileName(item)); ArchiveLogger.Debug($"Copying \"{item}\" -> \"{text3}\""); File.Copy(item, text3); } } } else { Directory.CreateDirectory(text); } } string path = Path.Combine(text, featureIdentifier + "_" + configType.Name + ".json"); if (!File.Exists(path)) { object obj = Activator.CreateInstance(configType); if (saveIfNonExistent) { SaveFeatureConfig(moduleIdentifier, featureIdentifier, configType, obj); } fileExists = false; return obj; } fileExists = true; try { return JsonConvert.DeserializeObject(File.ReadAllText(path), configType, ArchiveMod.JsonSerializerSettings); } catch { object obj2 = Activator.CreateInstance(configType); if (saveIfNonExistent) { SaveFeatureConfig(moduleIdentifier, featureIdentifier, configType, obj2); } fileExists = false; return obj2; } } internal static object LoadFeatureConfig(string moduleIdentifier, string featureIdentifier, Type configType, bool saveIfNonExistent = true) { bool fileExists; return LoadFeatureConfig(moduleIdentifier, featureIdentifier, configType, out fileExists, saveIfNonExistent); } internal static void SaveFeatureConfig(string moduleIdentifier, string featureIdentifier, Type configType, object configInstance) { if (string.IsNullOrWhiteSpace(featureIdentifier)) { throw new ArgumentException("Parameter featureIdentifier may not be null or whitespace."); } if (configType == null) { throw new ArgumentNullException("configType"); } if (configInstance == null) { throw new ArgumentNullException("configInstance"); } string text = Path.Combine(FeatureConfigsDirectoryPath, moduleIdentifier); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string text2 = Path.Combine(text, featureIdentifier + "_" + configType.Name + ".json"); ArchiveLogger.Debug("Saving Feature Setting to: " + text2); string value = JsonConvert.SerializeObject(configInstance, ArchiveMod.JsonSerializerSettings); try { using StreamWriter streamWriter = new StreamWriter(text2, Encoding.UTF8, Options); streamWriter.Write(value); streamWriter.Flush(); } catch (Exception ex) { ArchiveLogger.Error("Threw an exception while trying to save file '" + text2 + "'."); ArchiveLogger.Exception(ex); } } } public static class MetadataHelper { internal static IEnumerable GetCustomAttributes(TypeDefinition td, bool inherit) where T : Attribute { List list = new List(); Type type = typeof(T); TypeDefinition val = td; do { list.AddRange(((IEnumerable)val.CustomAttributes).Where((CustomAttribute ca) => ((MemberReference)ca.AttributeType).FullName == type.FullName)); TypeReference baseType = val.BaseType; val = ((baseType != null) ? baseType.Resolve() : null); } while (inherit && ((val != null) ? ((MemberReference)val).FullName : null) != typeof(object).FullName); return list; } public static ArchiveModule GetMetadata(Type moduleType) { object[] customAttributes = moduleType.GetCustomAttributes(typeof(ArchiveModule), inherit: false); if (customAttributes.Length == 0) { return null; } return (ArchiveModule)customAttributes[0]; } public static ArchiveModule GetMetadata(object module) { return GetMetadata(module.GetType()); } public static T[] GetAttributes(Type moduleType) where T : Attribute { return (T[])moduleType.GetCustomAttributes(typeof(T), inherit: true); } public static T[] GetAttributes(Assembly assembly) where T : Attribute { return (T[])assembly.GetCustomAttributes(typeof(T), inherit: true); } public static IEnumerable GetAttributes(object module) where T : Attribute { return GetAttributes(module.GetType()); } public static T[] GetAttributes(MemberInfo member) where T : Attribute { return (T[])member.GetCustomAttributes(typeof(T), inherit: true); } public static IEnumerable GetDependencies(Type module) { return module.GetCustomAttributes(typeof(ArchiveDependency), inherit: true).Cast(); } } public static class PresenceFormatter { [AttributeUsage(AttributeTargets.Property)] public class PresenceFormatProvider : Attribute { private PropertyInfo _propertyInfo; public string Identifier { get; private set; } public bool IsValid => _propertyInfo != null; public Type PropertyType => PropertyInfo.PropertyType; public string DebugIdentifier => $"{PropertyInfo.DeclaringType.Name}.{PropertyInfo.Name} (ASM:{PropertyInfo.DeclaringType.Assembly.GetName().Name})"; internal PropertyInfo PropertyInfo => _propertyInfo ?? throw new Exception($"PropertyInfo not set on {"PresenceFormatProvider"} with ID \"{Identifier}\"!"); public PresenceFormatProvider([CallerMemberName] string identifier = "") { Identifier = identifier; } internal void SetPropertyInfo(PropertyInfo propertyInfo) { if (propertyInfo == null) { throw new ArgumentException($"Provided {"PresenceFormatProvider"} {"PropertyInfo"} may not be null! (ID:{Identifier})"); } MethodInfo? getMethod = propertyInfo.GetGetMethod(); if ((object)getMethod == null || !getMethod.IsStatic) { throw new ArgumentException($"Provided {"PresenceFormatProvider"} {"PropertyInfo"} has to implement a static get method! (ID:{Identifier})"); } _propertyInfo = propertyInfo; } public object GetValue() { return PropertyInfo.GetValue(null); } } [AttributeUsage(AttributeTargets.Property)] public class FallbackPresenceFormatProvider : PresenceFormatProvider { public bool NoNotImplementedWarning { get; } public FallbackPresenceFormatProvider(string identifier, bool noNotImplementedWarning = false) : base(identifier) { NoNotImplementedWarning = noNotImplementedWarning; } } private static readonly Dictionary _formatters = new Dictionary(); private static readonly List _typesToCheckForProviders = new List(); private static IArchiveLogger _logger; private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateArSubLoggerInstance("PresenceFormatter", ConsoleColor.DarkMagenta)); public static void Setup() { Logger.Debug("Setting up providers ..."); foreach (Type typesToCheckForProvider in _typesToCheckForProviders) { CheckTypeForProviders(typesToCheckForProvider); } foreach (KeyValuePair item in _formatters.Where((KeyValuePair kvp) => kvp.Value is FallbackPresenceFormatProvider fallbackPresenceFormatProvider && !fallbackPresenceFormatProvider.NoNotImplementedWarning)) { Logger.Warning("Identifier \"" + item.Key + "\" has not been implemented! Using Fallback default values!"); } } private static void CheckTypeForProviders(Type type) { PropertyInfo[] properties = type.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { try { PresenceFormatProvider customAttribute = propertyInfo.GetCustomAttribute(); if (customAttribute != null) { customAttribute.SetPropertyInfo(propertyInfo); RegisterFormatter(customAttribute); } } catch (Exception ex) { Logger.Exception(ex); } } } public static void RegisterAllPresenceFormatProviders(this Type type, bool throwOnDuplicate = true) { if (type == null) { throw new ArgumentException("Type must not be null!"); } if (_typesToCheckForProviders.Contains(type)) { if (throwOnDuplicate) { throw new ArgumentException("Duplicate Type registered: \"" + type.FullName + "\""); } return; } _typesToCheckForProviders.Add(type); if (_formatters.Count > 0) { Logger.Debug($"Late call of {"RegisterAllPresenceFormatProviders"} for {type.FullName} - running checks now."); CheckTypeForProviders(type); } } private static void RegisterFormatter(PresenceFormatProvider pfp) { if (!pfp.IsValid) { return; } bool flag = false; if (_formatters.TryGetValue(pfp.Identifier, out var value)) { if (pfp is FallbackPresenceFormatProvider) { return; } if (!(value is FallbackPresenceFormatProvider)) { throw new ArgumentException($"Duplicate formatter identifier: \"{pfp.Identifier}\" (\"{pfp.DebugIdentifier}\")"); } _formatters.Remove(pfp.Identifier); flag = true; } _formatters.Add(pfp.Identifier, pfp); Logger.Debug($"{(flag ? " (Fallback Overridden)" : ((pfp is FallbackPresenceFormatProvider) ? " (Fallback)" : string.Empty))} Registered: \"{pfp.Identifier}\" => {pfp.DebugIdentifier}"); } public static object Get(string identifier) { _formatters.TryGetValue(identifier, out var value); return value?.GetValue(); } public static T Get(string identifier) { _formatters.TryGetValue(identifier, out var value); if (value == null) { return default(T); } if (!value.PropertyType.IsAssignableFrom(typeof(T)) && typeof(T) != typeof(string)) { throw new ArgumentException($"The property at identifier \"{identifier}\" is not declared as Type \"{typeof(T).Name}\"!"); } return (T)value.GetValue(); } public static string Format(this string formatString, params (string search, string replace)[] extraFormatters) { return FormatPresenceString(formatString, extraFormatters); } public static string FormatPresenceString(string formatString) { return FormatPresenceString(formatString, null); } public static string FormatPresenceString(string formatString, params (string search, string replace)[] extraFormatters) { return FormatPresenceString(formatString, stripAllTMPTags: true, extraFormatters); } public static string FormatPresenceString(string formatString, bool stripAllTMPTags = true, params (string search, string replace)[] extraFormatters) { string text = formatString; foreach (KeyValuePair formatter in _formatters) { if (text.Contains("%" + formatter.Key + "%")) { text = text.ReplaceCaseInsensitive("%" + formatter.Key + "%", formatter.Value.GetValue()?.ToString() ?? "null"); } } if (extraFormatters != null) { for (int i = 0; i < extraFormatters.Length; i++) { (string, string) tuple = extraFormatters[i]; if (text.Contains("%" + tuple.Item1 + "%")) { text = text.ReplaceCaseInsensitive("%" + tuple.Item1 + "%", tuple.Item2); } } } if (stripAllTMPTags) { return Utils.StripTMPTagsRegex(text.Trim()); } return text.Trim(); } } public static class RundownFlagsExtensions { private static IEnumerable _allFlagsOrdered; public static IEnumerable AllFlagsOrdered { get { if (_allFlagsOrdered == null) { _allFlagsOrdered = (from Utils.RundownFlags x in Enum.GetValues(typeof(Utils.RundownFlags)) orderby x select x).Skip(2); } return _allFlagsOrdered; } } public static bool IsIncludedIn(this Utils.RundownID rundownID, Utils.RundownFlags flags) { return Utils.FlagsContain(flags, rundownID); } public static Utils.RundownFlags To(this Utils.RundownFlags flags, Utils.RundownFlags to) { if (to == Utils.RundownFlags.Latest) { return flags.ToLatest(); } if (flags > to) { return Utils.FlagsFromTo(to, flags); } return Utils.FlagsFromTo(flags, to); } public static Utils.RundownFlags ToLatest(this Utils.RundownFlags flags) { return Utils.FlagsFromTo(flags, Utils.RundownFlags.Latest); } public static Utils.RundownFlags LowestRundownFlag(this Utils.RundownFlags flags) { return AllFlagsOrdered.FirstOrDefault((Utils.RundownFlags x) => flags.HasFlag(x)); } public static Utils.RundownFlags HighestRundownFlag(this Utils.RundownFlags flags) { return AllFlagsOrdered.LastOrDefault((Utils.RundownFlags x) => flags.HasFlag(x)); } } public static class SColorExtensions { public static Color ToUnityColor(this SColor col) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); result.r = col.R; result.g = col.G; result.b = col.B; result.a = col.A; return result; } public static Color? ToUnityColor(this SColor? col) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) return col?.ToUnityColor(); } public static SColor ToSColor(this Color col) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) SColor result = default(SColor); result.R = col.r; result.G = col.g; result.B = col.b; result.A = col.a; return result; } public static SColor FromHexString(string col) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) col = EnsureLeadingHash(col); Color col2 = default(Color); if (ColorUtility.TryParseHtmlString(col, ref col2)) { return col2.ToSColor(); } return SColor.WHITE; } public static string EnsureLeadingHash(string hexString) { if (!hexString.StartsWith("#")) { hexString = "#" + hexString; } return hexString; } public static string ToHexString(this SColor col) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return col.ToUnityColor().ToHexString(); } public static string ToHexString(this Color col) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return "#" + ColorUtility.ToHtmlStringRGB(col); } public static string ToShortHexString(this SColor col) { return "#" + ComponentToHex(col.R) + ComponentToHex(col.G) + ComponentToHex(col.B); } public static string ToShortHexString(this Color col) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) return "#" + ComponentToHex(col.r) + ComponentToHex(col.g) + ComponentToHex(col.b); } public static string ComponentToHex(float component) { return $"{Mathf.Clamp((int)(component * 16f), 0, 15):X1}"; } } public static class SharedUtils { public enum IgnoreMode { Match, StartsWith, EndsWith } [CompilerGenerated] private sealed class d__16 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Transform <>2__current; private int <>l__initialThreadId; private Transform trans; public Transform <>3__trans; private int 5__2; Transform IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__16(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = 0; break; case 1: <>1__state = -1; 5__2++; break; } if (5__2 < trans.childCount) { <>2__current = trans.GetChild(5__2); <>1__state = 1; return true; } 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 IEnumerable.GetEnumerator() { d__16 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__16(0); } d__.trans = <>3__trans; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__60 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private LG_Zone <>2__current; private int <>l__initialThreadId; private IEnumerator <>7__wrap1; private Enumerator <>7__wrap2; private Enumerator <>7__wrap3; LG_Zone IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__60(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>7__wrap2 = null; <>7__wrap3 = null; <>1__state = -2; } private bool MoveNext() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (GetGameState() != eGameStateName_InLevel) { return false; } if (Feature.Is.R6OrLater) { <>7__wrap1 = GetAllZonesR6Plus().GetEnumerator(); <>1__state = -3; goto IL_0084; } if (_A_LG_Floor_m_layers == null) { _A_LG_Floor_m_layers = AccessorBase.GetValueAccessor>("m_layers"); } <>7__wrap2 = _A_LG_Floor_m_layers.Get(Builder.CurrentFloor).GetEnumerator(); goto IL_0132; case 1: <>1__state = -3; goto IL_0084; case 2: { <>1__state = -1; goto IL_011e; } IL_011e: if (<>7__wrap3.MoveNext()) { LG_Zone current = <>7__wrap3.Current; <>2__current = current; <>1__state = 2; return true; } <>7__wrap3 = null; goto IL_0132; IL_0132: if (<>7__wrap2.MoveNext()) { LG_Layer current2 = <>7__wrap2.Current; <>7__wrap3 = current2.m_zones.GetEnumerator(); goto IL_011e; } <>7__wrap2 = null; return false; IL_0084: if (<>7__wrap1.MoveNext()) { LG_Zone current3 = <>7__wrap1.Current; <>2__current = current3; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = null; 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; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__60(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__61 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private LG_Zone <>2__current; private int <>l__initialThreadId; private Enumerator <>7__wrap1; private Enumerator <>7__wrap2; private Enumerator <>7__wrap3; LG_Zone IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__61(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>7__wrap2 = null; <>7__wrap3 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_0092; } <>1__state = -1; <>7__wrap1 = Builder.CurrentFloor.m_dimensions.GetEnumerator(); goto IL_00ba; IL_00ba: if (<>7__wrap1.MoveNext()) { Dimension current = <>7__wrap1.Current; <>7__wrap2 = current.Layers.GetEnumerator(); goto IL_00a6; } <>7__wrap1 = null; return false; IL_00a6: if (<>7__wrap2.MoveNext()) { LG_Layer current2 = <>7__wrap2.Current; <>7__wrap3 = current2.m_zones.GetEnumerator(); goto IL_0092; } <>7__wrap2 = null; goto IL_00ba; IL_0092: if (<>7__wrap3.MoveNext()) { LG_Zone current3 = <>7__wrap3.Current; <>2__current = current3; <>1__state = 1; return true; } <>7__wrap3 = null; goto IL_00a6; } 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 IEnumerable.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new d__61(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private static readonly IValueAccessor _A_CM_PageLoadout_m_playerLobbyBars = AccessorBase.GetValueAccessor("m_playerLobbyBars"); private static readonly IValueAccessor _A_CM_PlayerLobbyBar_m_playerSlotIndex = AccessorBase.GetValueAccessor("m_playerSlotIndex"); private static readonly IValueAccessor _A_CM_PlayerLobbyBar_m_playerIndex = AccessorBase.GetValueAccessor("m_playerIndex"); private static readonly IValueAccessor _A_CM_PlayerLobbyBar_m_player = AccessorBase.GetValueAccessor("m_player"); private static readonly MethodAccessor A_CellSoundPlayer_Post_sub_R5 = MethodAccessor.GetAccessor("Post", new Type[1] { typeof(uint) }, ignoreErrors: true); private static readonly PropertyAccessor _rundownIdToLoad = PropertyAccessor.GetAccessor("RundownIdToLoad"); private static GameSetupDataBlock _setupBlock; private static readonly eFocusState eFocusState_ComputerTerminal = Utils.GetEnumFromName("ComputerTerminal"); private static readonly eFocusState eFocusState_Map = Utils.GetEnumFromName("Map"); private static readonly eFocusState eFocusState_Dead = Utils.GetEnumFromName("Dead"); private static readonly eFocusState eFocusState_InElevator = Utils.GetEnumFromName("InElevator"); private static readonly eFocusState eFocusState_Hacking = Utils.GetEnumFromName("Hacking"); private static readonly eGameStateName eGameStateName_InLevel = Utils.GetEnumFromName("InLevel"); private static IValueAccessor> _A_LG_Floor_m_layers; public static bool LocalPlayerIsInTerminal => FocusStateManager.CurrentState == eFocusState_ComputerTerminal; public static bool LocalPlayerIsInMap => FocusStateManager.CurrentState == eFocusState_Map; public static bool LocalPlayerIsDead => FocusStateManager.CurrentState == eFocusState_Dead; public static bool LocalPlayerIsInElevator => FocusStateManager.CurrentState == eFocusState_InElevator; public static bool LocalPlayerIsHacking => FocusStateManager.CurrentState == eFocusState_Hacking; public static List ToSystemList(this List il2List) { List list = new List(); Enumerator enumerator = il2List.GetEnumerator(); while (enumerator.MoveNext()) { T current = enumerator.Current; list.Add(current); } return list; } public static List ToIL2CPPListIfNecessary(this List list) { List val = new List(); foreach (T item in list) { val.Add(item); } return val; } public static List NewListForGame() { return new List(); } public static void ChangeColorTimedExpeditionButton(CM_TimedButton button, Color col) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ChangeColorOnAllChildren(((Component)button).transform, col, new string[1] { "ProgressFill" }); } public static void ChangeColorCMItem(CM_Item item, Color idleColor, Color? hoverColor = null) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) ChangeColorOnSelfAndAllChildren(((Component)item).transform, idleColor); List list = new List(); List list2 = new List(); Color val = idleColor.WithAlpha(0.5f); Color val2 = (Color)(((??)hoverColor) ?? idleColor.WithAlpha(1f)); item.m_spriteColorOrg = val; item.m_spriteColorOut = val; item.m_spriteColorOver = val2; if (item.m_textColorOrg != null) { foreach (Color item2 in (Il2CppArrayBase)(object)item.m_textColorOrg) { _ = item2; list.Add(val); list2.Add(val2); } } item.m_textColorOrg = Il2CppStructArray.op_Implicit(list.ToArray()); item.m_textColorOut = Il2CppStructArray.op_Implicit(list.ToArray()); item.m_textColorOver = Il2CppStructArray.op_Implicit(list2.ToArray()); } public static void ChangeColorOnSelfAndAllChildren(Transform trans, Color col, IList excludeNames = null, IgnoreMode mode = IgnoreMode.StartsWith, Action extraModificationForEachChild = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) ChangeColor(trans, col, excludeNames, mode, extraModificationForEachChild); ChangeColorOnAllChildren(trans, col, excludeNames, mode, extraModificationForEachChild); } public static void ChangeColorOnAllChildren(Transform trans, Color col, IList excludeNames = null, IgnoreMode mode = IgnoreMode.StartsWith, Action extraModificationForEachChild = null) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)trans == (Object)null)) { trans.ForEachChildDo(delegate(Transform child) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) ChangeColor((child != null) ? ((Component)child).transform : null, col, excludeNames, mode, extraModificationForEachChild); }); } } public static void ChangeColor(Transform trans, Color col, IList excludeNames = null, IgnoreMode mode = IgnoreMode.StartsWith, Action extraModificationForEachChild = null) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)trans == (Object)null) { return; } if (excludeNames != null) { switch (mode) { case IgnoreMode.Match: if (excludeNames.Contains(((Object)trans).name)) { return; } break; case IgnoreMode.StartsWith: if (excludeNames.Any((string s) => ((Object)trans).name.StartsWith(s))) { return; } break; case IgnoreMode.EndsWith: if (excludeNames.Any((string s) => ((Object)trans).name.EndsWith(s))) { return; } break; } } SpriteRenderer component = ((Component)trans).GetComponent(); if ((Object)(object)component != (Object)null) { component.color = col; } TextMeshPro component2 = ((Component)trans).GetComponent(); if ((Object)(object)component2 != (Object)null) { ((Graphic)component2).color = col; } extraModificationForEachChild?.Invoke(trans); } public static void RemoveAllEventHandlers(string eventFieldName, object instance = null) { typeof(T).GetProperty(eventFieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SetValue(instance, null); } public static CM_Item AddCMItemEvents(this CM_Item item, Action onButtonPress, Action onButtonHover = null) { if ((Object)(object)item == (Object)null) { throw new ArgumentNullException("item"); } if (onButtonPress != null) { item.OnBtnPressCallback += Action.op_Implicit(onButtonPress); } if (onButtonHover != null) { item.OnBtnHoverChanged += Action.op_Implicit(onButtonHover); } return item; } public static CM_Item SetCMItemEvents(this CM_Item item, Action onButtonPress, Action onButtonHover = null) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if ((Object)(object)item == (Object)null) { throw new ArgumentNullException("item"); } if (item.m_onBtnPress == null) { item.m_onBtnPress = new UnityEvent(); } if (onButtonPress != null) { item.OnBtnPressCallback = Action.op_Implicit(onButtonPress); } if (onButtonHover != null) { item.OnBtnHoverChanged = Action.op_Implicit(onButtonHover); } return item; } public static CM_Item RemoveCMItemEvents(this CM_Item item, bool keepHover = false) { RemoveAllEventHandlers("OnBtnPressCallback", item); if (!keepHover) { RemoveAllEventHandlers("OnBtnHoverChanged", item); } return item; } public static void SetHoldDuration(this CM_TimedButton button, float duration) { button.m_holdButtonDuration = duration; } public static Color WithAlpha(this Color col, float alpha) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return new Color(col.r, col.g, col.b, alpha); } public static void ForEachFirstChildDo(this Transform trans, Action func) { trans.ForEachChildDo(func, recursive: false); } public static void ForEachChildDo(this Transform trans, Action func, bool recursive = true) { if ((Object)(object)trans == (Object)null) { throw new ArgumentNullException("trans"); } for (int i = 0; i < trans.childCount; i++) { Transform child = trans.GetChild(i); func?.Invoke(child); if (recursive) { child.ForEachChildDo(func); } } } [IteratorStateMachine(typeof(d__16))] public static IEnumerable DirectChildren(this Transform trans) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__16(-2) { <>3__trans = trans }; } public static Transform GetChildWithExactName(this Transform trans, string name) { for (int i = 0; i < trans.childCount; i++) { Transform child = trans.GetChild(i); if (((Object)child).name == name) { return child; } } return null; } public static void SafeDestroyGO(this GameObject go) { go.SafeDestroy(); } public static void SafeDestroyGO(this Component comp) { if (comp != null) { comp.gameObject.SafeDestroy(); } } public static void SafeDestroy(this GameObject go) { if (!((Object)(object)go == (Object)null)) { Object.Destroy((Object)(object)go); } } public static void SafeDestroy(this Component comp) { if (!((Object)(object)comp == (Object)null)) { Object.Destroy((Object)(object)comp); } } public static bool TryGetPlayerLobbyBarIndex(CM_PlayerLobbyBar plb, out int index) { index = GetPlayerLobbyBarIndex(plb); return index >= 0; } public static int GetPlayerLobbyBarIndex(CM_PlayerLobbyBar plb) { return _A_CM_PlayerLobbyBar_m_playerSlotIndex?.Get(plb) ?? _A_CM_PlayerLobbyBar_m_playerIndex?.Get(plb) ?? (-1); } public static bool TryGetPlayerByPlayerLobbyBarIndex(int index, out SNet_Player player) { CM_PlayerLobbyBar val = ((IEnumerable)((IEnumerable)CM_PageLoadout.Current.m_playerLobbyBars).ToArray()).FirstOrDefault((Func)((CM_PlayerLobbyBar plb) => GetPlayerLobbyBarIndex(plb) == index)); if ((Object)(object)val == (Object)null) { player = null; return false; } player = _A_CM_PlayerLobbyBar_m_player.Get(val); return (Object)(object)player != (Object)null; } public static bool TryGetPlayerByCharacterIndex(int id, out SNet_Player player) { try { player = ((IEnumerable)SNet.Lobby.Players.ToSystemList()).FirstOrDefault((Func)((SNet_Player ply) => ply.CharacterSlot.index == id)); return (Object)(object)player != (Object)null; } catch (Exception) { player = null; ArchiveLogger.Debug("This shouldn't happen :skull: (TryGetPlayerByCharacterIndex)"); } return false; } public static Bounds GetMaxBounds(this GameObject go) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) Il2CppArrayBase componentsInChildren = go.GetComponentsInChildren(); if (componentsInChildren.Length == 0) { return new Bounds(go.transform.position, Vector3.zero); } Bounds bounds = componentsInChildren[0].bounds; foreach (Renderer item in componentsInChildren) { ((Bounds)(ref bounds)).Encapsulate(item.bounds); } return bounds; } public static bool SafeContains(this IList list, T item) where T : class, new() { if (LoaderWrapper.IsIL2CPPType(typeof(T))) { foreach (T item2 in list) { if (((Il2CppObjectBase)((item2 is Object) ? item2 : null)).Pointer == ((Il2CppObjectBase)((item is Object) ? item : null)).Pointer) { return true; } } return false; } return list.Contains(item); } public static bool SafeIsBot(this SNet_Player player) { if ((Object)(object)player == (Object)null) { return false; } if (Feature.Is.R6OrLater) { return IsBotR6(player); } return false; } public static bool IsFriend(this SNet_Player player) { if ((Object)(object)player == (Object)null) { return false; } SNet_Friend val = default(SNet_Friend); return SNet.Friends.TryGetFriend(player.Lookup, ref val); } [MethodImpl(MethodImplOptions.NoInlining)] private static bool IsBotR6(SNet_Player player) { return player.IsBot; } public static void SafePost(this CellSoundPlayer player, uint eventId, bool isGlobal = true) { if (Feature.Is.R6OrLater) { SafePostR6Plus(player, eventId, isGlobal); return; } A_CellSoundPlayer_Post_sub_R5.Invoke(player, eventId); } [MethodImpl(MethodImplOptions.NoInlining)] private static void SafePostR6Plus(CellSoundPlayer player, uint eventId, bool isGlobal = true) { player.Post(eventId, isGlobal); } public static bool TryGetRundownDataBlock(out RundownDataBlock block) { uint num; if (ArchiveMod.IsOnALTBuild) { if (RundownManager.ActiveExpedition != null) { pActiveExpedition activeExpeditionData = RundownManager.GetActiveExpeditionData(); object obj; if (activeExpeditionData == null) { obj = null; } else { pString25 rundownKey = activeExpeditionData.rundownKey; obj = ((rundownKey != null) ? rundownKey.data : null); } string text = (string)obj; if (text != null && !text.StartsWith("pString") && text != ArchiveMod.CurrentlySelectedRundownKey) { ArchiveMod.CurrentlySelectedRundownKey = text; } } num = ArchiveMod.CurrentlySelectedRundownPersistentID; } else { if (_setupBlock == null) { _setupBlock = GameDataBlockBase.GetBlock(1u); } num = _rundownIdToLoad.Get(_setupBlock); } if (num != 0) { block = GameDataBlockBase.GetBlock(num); return true; } block = null; return false; } public static string GetDataBlockRundownTitle() { string text = null; if (TryGetRundownDataBlock(out var block)) { text = Utils.StripTMPTagsRegex(LocalizedText.op_Implicit(block.StorytellingData.Title)); } if (!ArchiveMod.IsPlayingModded && text != null) { try { string[] array = text.Split("TITLE: "); string text2 = array[1]; string text3 = array[0].Split('#')[1].Split('.')[0]; text = "R" + text3 + " - " + text2; } catch { } } if (ArchiveMod.IsOnALTBuild && text == null) { return "Selecting Rundown"; } return text ?? "Unknown"; } [IteratorStateMachine(typeof(d__60))] public static IEnumerable GetAllZones() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__60(-2); } [MethodImpl(MethodImplOptions.NoInlining)] [IteratorStateMachine(typeof(d__61))] private static IEnumerable GetAllZonesR6Plus() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__61(-2); } public static eGameStateName GetGameState() { return (eGameStateName)(byte)ArchiveMod.CurrentGameState; } public static string GetLocalizedTextSafe(uint localizedTextID, bool forceOverrideText = false, string overrideText = null) { string result = overrideText; if (Feature.Is.R6OrLater && !forceOverrideText) { result = Localization_Text_Get_R6Plus(localizedTextID); } return result; } public static string GetLocalizedTextSafeAndFormat(uint localizedTextID, bool forceOverrideText = false, string overrideText = null, params string[] formatArgs) { string localizedTextSafe = GetLocalizedTextSafe(localizedTextID, forceOverrideText, overrideText); if (formatArgs == null || formatArgs == Array.Empty()) { return localizedTextSafe; } return Utils.UsersafeFormat(localizedTextSafe, formatArgs); } [MethodImpl(MethodImplOptions.NoInlining)] private static string Localization_Text_Get_R6Plus(uint id) { return Text.Get(id); } public static T CastTo(this Il2CppObjectBase value) where T : Il2CppObjectBase { return value.Cast(); } public static T TryCastTo(this Il2CppObjectBase value) where T : Il2CppObjectBase { return value.TryCast(); } public static bool TryCastTo(this Il2CppObjectBase value, out T castedValue) where T : Il2CppObjectBase { castedValue = value.TryCastTo(); return castedValue != null; } } public class SoundEventCache : IInitAfterGameDataInitialized, IInitializable { public class SoundEventNotFoundException : Exception { public SoundEventNotFoundException(string message) : base(message) { } } private static readonly Dictionary _soundIdCache = new Dictionary(); private static readonly Dictionary _reverseSoundIdCache = new Dictionary(); private static IArchiveLogger _logger; public static bool IsReady { get; private set; } private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("SoundEventCache", ConsoleColor.DarkGreen)); public static bool TryResolve(string soundEvent, out uint soundId) { soundId = Resolve(soundEvent); return soundId != 0; } public static uint Resolve(string soundEvent, bool throwIfNotFound = false) { if (!IsReady) { Logger.Error("SoundEventCache isn't ready yet! Try resolving sound events a little later (after GameDataInit for example)!"); return 0u; } if (_soundIdCache.TryGetValue(soundEvent, out var value)) { return value; } string text = "Sound event \"" + soundEvent + "\" could not be resolved!"; if (throwIfNotFound) { throw new SoundEventNotFoundException(text); } Logger.Error(text); return 0u; } public static bool TryReverseResolve(uint id, out string eventName) { eventName = ReverseResolve(id); return !string.IsNullOrEmpty(eventName); } public static string ReverseResolve(uint soundId, bool throwIfNotFound = false) { if (!IsReady) { Logger.Error("SoundEventCache isn't ready yet! Try resolving sound events a little later (after GameDataInit for example)!"); return null; } if (_reverseSoundIdCache.TryGetValue(soundId, out var value)) { return value; } string text = $"Sound id \"{soundId}\" could not be resolved!"; if (throwIfNotFound) { throw new SoundEventNotFoundException(text); } Logger.Error(text); return null; } public void Init() { try { Logger.Debug("Initializing ..."); foreach (PropertyInfo item in from p in typeof(EVENTS).GetProperties() where p.GetMethod?.ReturnType == typeof(uint) select p) { string name = item.Name; uint num = (uint)item.GetValue(null); _soundIdCache.Add(name, num); _reverseSoundIdCache.Add(num, name); } Logger.Debug($"Cached {_soundIdCache.Count} sound events!"); } catch (Exception ex) { Logger.Error("Threw an exception on Init:"); Logger.Exception(ex); } IsReady = true; } public static void DebugLog(IArchiveLogger logger) { logger.Notice($"Logging all cached sound events! ({_soundIdCache.Count})"); foreach (string key in _soundIdCache.Keys) { logger.Info(key); } logger.Notice($"Done logging all cached sound events! ({_soundIdCache.Count})"); } } public static class UnityMessages { public const string Awake = "Awake"; public const string Start = "Start"; public const string Update = "Update"; public const string FixedUpdate = "FixedUpdate"; public const string LateUpdate = "LateUpdate"; public const string OnEnable = "OnEnable"; public const string OnDisable = "OnDisable"; public const string OnDestroy = "OnDestroy"; public const string OnApplicationFocus = "OnApplicationFocus"; public const string OnApplicationQuit = "OnApplicationQuit"; } public static class Utils { public enum RundownID { Latest = -2, RundownUnitialized, RundownUnknown, RundownOne, RundownTwo, RundownThree, RundownFour, RundownFive, RundownSix, RundownSeven, RundownAltOne, RundownAltTwo, RundownAltThree, RundownAltFour, RundownAltFive, RundownAltSix, RundownEight } [Flags] public enum RundownFlags { Latest = -2, None = 0, RundownOne = 1, RundownTwo = 2, RundownThree = 4, RundownFour = 8, RundownFive = 0x10, RundownSix = 0x20, RundownSeven = 0x40, RundownAltOne = 0x80, RundownAltTwo = 0x100, RundownAltThree = 0x200, RundownAltFour = 0x400, RundownAltFive = 0x800, RundownAltSix = 0x1000, RundownEight = 0x2000 } [CompilerGenerated] private sealed class d__31 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Action action; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__31(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; action?.Invoke(); 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(); } } public const BindingFlags AnyBindingFlagss = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public const string EXTENDED_WITHOUT_TMP_TAGS = "://EXTENDED"; public const string EXTENDED = "://EXTENDED"; private static RundownID? _latestRundownID; private const RundownFlags LATEST_RUNDOWN_FLAGS = RundownFlags.RundownEight; public const RundownFlags ALL_RUNDOWN_FLAGS = RundownFlags.RundownOne | RundownFlags.RundownTwo | RundownFlags.RundownThree | RundownFlags.RundownFour | RundownFlags.RundownFive | RundownFlags.RundownSix | RundownFlags.RundownSeven | RundownFlags.RundownAltOne | RundownFlags.RundownAltTwo | RundownFlags.RundownAltThree | RundownFlags.RundownAltFour | RundownFlags.RundownAltFive | RundownFlags.RundownAltSix | RundownFlags.RundownEight; public static string ByteArrayToString(byte[] data) { StringBuilder stringBuilder = new StringBuilder(data.Length * 2); foreach (byte b in data) { stringBuilder.AppendFormat("{0:x2}", b); } return stringBuilder.ToString(); } public static string HashStream(Stream stream) { using SHA256 sHA = SHA256.Create(); byte[] array = new byte[4096]; int inputCount; while ((inputCount = stream.Read(array, 0, array.Length)) > 0) { sHA.TransformBlock(array, 0, inputCount, array, 0); } sHA.TransformFinalBlock(Array.Empty(), 0, 0); return ByteArrayToString(sHA.Hash); } public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, Func loader, out T assembly) where T : class { assembly = null; List list = new List { directory }; if (!Directory.Exists(directory)) { return false; } list.AddRange(Directory.GetDirectories(directory, "*", SearchOption.AllDirectories)); foreach (string item in list) { string[] array = new string[2] { assemblyName.Name + ".dll", assemblyName.Name + ".exe" }; foreach (string path in array) { string text = Path.Combine(item, path); if (File.Exists(text)) { try { assembly = loader(text); } catch (Exception) { continue; } return true; } } } return false; } public static bool TryResolveDllAssembly(AssemblyName assemblyName, string directory, ReaderParameters readerParameters, out AssemblyDefinition assembly) { return TryResolveDllAssembly(assemblyName, directory, (Func)((string s) => AssemblyDefinition.ReadAssembly(s, readerParameters)), out assembly); } public static bool TryParseAssemblyName(string fullName, out AssemblyName assemblyName) { try { assemblyName = new AssemblyName(fullName); return true; } catch (Exception) { assemblyName = null; return false; } } public static IEnumerable TopologicalSort(IEnumerable nodes, Func> dependencySelector) { List sorted_list = new List(); HashSet visited = new HashSet(); HashSet sorted = new HashSet(); foreach (TNode node in nodes) { Stack stack2 = new Stack(); if (!Visit(node, stack2)) { throw new Exception("Cyclic Dependency:\r\n" + stack2.Select((TNode x) => $" - {x}").Aggregate((string a, string b) => a + "\r\n" + b)); } } return sorted_list; bool Visit(TNode node, Stack stack) { if (visited.Contains(node)) { if (!sorted.Contains(node)) { return false; } } else { visited.Add(node); stack.Push(node); if (dependencySelector(node).Any((TNode dep) => !Visit(dep, stack))) { return false; } sorted.Add(node); sorted_list.Add(node); stack.Pop(); } return true; } } public static void SafeInvoke(T action, params object[] args) where T : Delegate { if ((Delegate?)action == (Delegate?)null) { return; } Delegate[] invocationList = action.GetInvocationList(); foreach (Delegate @delegate in invocationList) { try { @delegate.DynamicInvoke(args); } catch (Exception ex) { ArchiveLogger.Warning("Event " + action.Method.Name + " threw an exception: " + ex.Message); ArchiveLogger.Exception(ex); } } } public static T PickRandom(this T[] array) { if (array.Length == 0) { return default(T); } return (T)array.GetValue(Random.Range(0, array.Length)); } public static string StripTMPTagsRegex(string input) { return Regex.Replace(input, "<.*?>", string.Empty); } public static T PickRandom(this List list) { return list.ToArray().PickRandom(); } public static T PickRandomExcept(this T[] array, Func selectFunction) { if (array.Length == 1) { return array[0]; } int num = 0; T val; do { val = array.PickRandom(); num++; } while (!selectFunction(val) && num < 20); return val; } public static T PickRandomExcept(this List list, Func selectFunction) { if (list != null) { return list.ToArray().PickRandomExcept(selectFunction); } return default(T); } public static T GetEnumFromName(string name) where T : struct { if (Enum.TryParse(name, out var result)) { return result; } ArchiveLogger.Warning($"{"GetEnumFromName"} couldn't resolve enum \"{name}\" from type \"{typeof(T).FullName}\"!"); return default(T); } public static bool TryGetEnumFromName(string name, out T value) where T : struct { return Enum.TryParse(name, out value); } [Obsolete("Use \"LoaderWrapper.StartCoroutine()\" instead.")] public static object StartCoroutine(IEnumerator routine) { return LoaderWrapper.StartCoroutine(routine); } [Obsolete("Use \"LoaderWrapper.StopCoroutine()\" instead.")] public static void StopCoroutine(object coroutineToken) { LoaderWrapper.StopCoroutine(coroutineToken); } public static string GetRundownTag(RundownFlags rundowns, bool generalizeLatest = false) { Enum.TryParse(rundowns.LowestRundownFlag().ToString(), out var result); Enum.TryParse(rundowns.HighestRundownFlag().ToString(), out var result2); if (result2 == RundownID.RundownUnknown) { result2 = GetLatestRundownID(); } bool flag = result2 == GetLatestRundownID(); if (result == result2) { string value = "R"; if (result >= RundownID.RundownAltOne && result < RundownID.RundownEight) { value = "A"; result = result - 8 + 1; } if (result >= RundownID.RundownEight) { result -= 6; } return $"{value}{result}"; } string value2 = "R"; if (result >= RundownID.RundownAltOne && result < RundownID.RundownEight) { value2 = "A"; result = result - 8 + 1; } else if (result >= RundownID.RundownEight) { result -= 6; } string text = "R"; if (result2 >= RundownID.RundownAltOne && result2 < RundownID.RundownEight) { text = "A"; result2 = result2 - 8 + 1; } else if (result2 >= RundownID.RundownEight) { result2 -= 6; } DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(1, 3); defaultInterpolatedStringHandler.AppendFormatted(value2); defaultInterpolatedStringHandler.AppendFormatted((int)result); defaultInterpolatedStringHandler.AppendLiteral("-"); object value3; if (!(flag && generalizeLatest)) { string text2 = text; int num = (int)result2; value3 = text2 + num; } else { value3 = "RL"; } defaultInterpolatedStringHandler.AppendFormatted((string?)value3); return defaultInterpolatedStringHandler.ToStringAndClear(); } [Obsolete("Old game versions only.")] public static string GetRundownTitle() { return GetRundownTitle(ArchiveMod.CurrentRundown); } [Obsolete("Old game versions only.")] public static string GetRundownTitle(RundownID rundown) { return rundown switch { RundownID.RundownOne => "Deviation", RundownID.RundownTwo => "Infection", RundownID.RundownThree => "The Vessel", RundownID.RundownFour => "Contact", RundownID.RundownFive => "Rebirth", RundownID.RundownSix => "Destination", RundownID.RundownSeven => "Rise", RundownID.RundownEight => "Duality", _ => "Unknown", }; } public static string GetHash(byte[] bytes) { using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(bytes); StringBuilder stringBuilder = new StringBuilder(); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } public static string UsersafeFormat(string format, params string[] replacementData) { for (int i = 0; i < replacementData.Length; i++) { string text = $"{{{i}}}"; if (format.Contains(text)) { format = format.ReplaceCaseInsensitive(text, replacementData[i]); } } return format; } public static IList ToSystemListSlow(object il2CppList, Type type) { if (LoaderWrapper.IsGameIL2CPP()) { Type type2 = ImplementationManager.GameTypeByIdentifier("GenericList").MakeGenericType(type); IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); IEnumerator enumerator = ((IEnumerable)type2.GetMethod("ToArray", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Invoke(il2CppList, Array.Empty())).GetEnumerator(); while (enumerator.MoveNext()) { list.Add(enumerator.Current); } return list; } return (IList)il2CppList; } public static string ToRoman(int number) { if (number >= 100) { if (number < 500) { if (number >= 400) { return "CD" + ToRoman(number - 400); } return "C" + ToRoman(number - 100); } if (number < 1000) { if (number >= 900) { return "CM" + ToRoman(number - 900); } return "D" + ToRoman(number - 500); } if (number <= 3999) { return "M" + ToRoman(number - 1000); } } else { if (number >= 10) { if (number >= 50) { if (number >= 90) { return "XC" + ToRoman(number - 90); } return "L" + ToRoman(number - 50); } if (number >= 40) { return "XL" + ToRoman(number - 40); } return "X" + ToRoman(number - 10); } if (number >= 5) { if (number >= 9) { return "IX" + ToRoman(number - 9); } return "V" + ToRoman(number - 5); } if (number >= 1) { if (number >= 4) { return "IV" + ToRoman(number - 4); } return "I" + ToRoman(number - 1); } if (number >= 0) { return string.Empty; } } throw new ArgumentOutOfRangeException("number", "Value has to be between 1 and 3999"); } public static bool IsPowerOfTwo(ulong x) { if (x != 0L) { return (x & (x - 1)) == 0; } return false; } public static string ReplaceCaseInsensitive(this string input, string search, string replacement) { return Regex.Replace(input, Regex.Escape(search), replacement.Replace("$", "$$"), RegexOptions.IgnoreCase); } public static byte[] LoadFromResource(string resourcePath) { return GetResource(Assembly.GetCallingAssembly(), resourcePath); } public static byte[] GetResource(Assembly assembly, string resourcePath) { Stream manifestResourceStream = assembly.GetManifestResourceStream(resourcePath); if (manifestResourceStream == null || !manifestResourceStream.CanRead) { throw new ArgumentException("Resource could not be loaded.", "resourcePath"); } byte[] array = new byte[manifestResourceStream.Length]; manifestResourceStream.Read(array, 0, (int)manifestResourceStream.Length); return array; } [Obsolete("Old game versions only.")] public static string GetStartupTextForRundown(RundownID currentRundownID) { StringBuilder stringBuilder = new StringBuilder(); switch (currentRundownID) { case RundownID.RundownOne: stringBuilder.Append("Rundown #001"); break; case RundownID.RundownTwo: stringBuilder.Append("Rundown #002 Infection"); break; case RundownID.RundownThree: stringBuilder.Append("Rundown #003 The Vessel"); break; case RundownID.RundownFour: stringBuilder.Append("Rundown #004 Contact"); break; case RundownID.RundownFive: stringBuilder.Append("Rundown #005 Rebirth"); break; default: return "Rundown #??? Yo Waddup?!\n\nThis shouldn't happen unless you somehow modified the datablocks in R1 to R5 builds ...\nAnyways, things are probably gonna break :)"; } stringBuilder.Append("\n"); stringBuilder.Append("The Archive active.\n\n"); return stringBuilder.ToString(); } [IteratorStateMachine(typeof(d__31))] public static IEnumerator NextFrame(Action action) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__31(0) { action = action }; } [MethodImpl(MethodImplOptions.NoInlining)] public static RundownID GetLatestRundownID() { RundownID valueOrDefault = _latestRundownID.GetValueOrDefault(); if (!_latestRundownID.HasValue) { valueOrDefault = GetEnumFromName(GetLatestRundownFlags().ToString()); _latestRundownID = valueOrDefault; return valueOrDefault; } return valueOrDefault; } [MethodImpl(MethodImplOptions.NoInlining)] public static RundownFlags GetLatestRundownFlags() { return RundownFlags.RundownEight; } public static bool FlagsContain(RundownFlags flags, RundownID id) { if (flags == RundownFlags.None) { return false; } if (id == RundownID.RundownUnknown) { return false; } RundownFlags latestRundownFlags = GetLatestRundownFlags(); if (flags == RundownFlags.Latest) { flags = latestRundownFlags; } if (id == RundownID.Latest) { id = GetLatestRundownID(); } if (!Enum.TryParse(id.ToString(), out var result)) { return false; } return (flags & result) == result; } public static RundownFlags FlagsFromTo(RundownFlags from, RundownFlags to) { if (from == RundownFlags.Latest) { from = GetLatestRundownFlags(); } if (to == RundownFlags.Latest) { to = GetLatestRundownFlags(); } if (from == to) { return from; } if (from > to) { throw new ArgumentException($"{"from"} ({from}) may not be larger than {"to"} ({to})!"); } if (!IsPowerOfTwo((ulong)from) || !IsPowerOfTwo((ulong)to) || from > to) { return RundownFlags.None; } RundownFlags? rundownFlags = null; for (int num = (int)from; num <= (int)to; num *= 2) { rundownFlags = (RundownFlags?)((!rundownFlags.HasValue) ? ((ValueType)new RundownFlags?((RundownFlags)num)) : ((ValueType)((uint?)rundownFlags | (uint)num))); } return rundownFlags.Value; } public static bool AnyRundownConstraintMatches(MemberInfo memberInfo) { RundownConstraint[] array = memberInfo.GetCustomAttributes().ToArray(); if (array.Length == 0) { return true; } RundownID rundown = ArchiveMod.CurrentBuildInfo.Rundown; RundownConstraint[] array2 = array; for (int i = 0; i < array2.Length; i++) { if (array2[i].Matches(rundown)) { return true; } } return false; } public static bool AnyBuildConstraintMatches(MemberInfo memberInfo) { BuildConstraint[] array = memberInfo.GetCustomAttributes().ToArray(); if (array.Length == 0) { return true; } int buildNumber = ArchiveMod.CurrentBuildInfo.BuildNumber; BuildConstraint[] array2 = array; for (int i = 0; i < array2.Length; i++) { if (array2[i].Matches(buildNumber)) { return true; } } return false; } public static HashSet GetNestedClasses(Type type) { List list = new List { type }; Type[] nestedTypes = type.GetNestedTypes(); foreach (Type type2 in nestedTypes) { if (type2.IsClass) { list.AddRange(GetNestedClasses(type2)); } } return list.ToHashSet(); } public static string HashString(this string input) { using SHA256 sHA = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(input); return BitConverter.ToString(sHA.ComputeHash(bytes)).Replace("-", string.Empty).ToLower(); } public static bool TryParseRundownKey(string rundownKey, out uint rundownID) { string[] array = rundownKey.Split('_'); if (array.Length < 2) { rundownID = 0u; return false; } return uint.TryParse(array[1], out rundownID); } public static Dictionary GetEmptyLanguageDictionary() { Dictionary dictionary = new Dictionary(); Language[] values = Enum.GetValues(); foreach (Language key in values) { dictionary[key] = null; } return dictionary; } public static Dictionary GetEmptyLanguageDictionary(Func generator) { Dictionary dictionary = new Dictionary(); Language[] values = Enum.GetValues(); foreach (Language key in values) { dictionary[key] = generator(); } return dictionary; } public static Dictionary GetEmptyEnumDictionary() where T : struct, Enum { Dictionary dictionary = new Dictionary(); T[] values = Enum.GetValues(); for (int i = 0; i < values.Length; i++) { T val = values[i]; dictionary[val.ToString()] = null; } return dictionary; } } } namespace TheArchive.Loader { public static class LoaderWrapper { public static class ClassInjector { public static IntPtr DerivedConstructorPointer() { return ClassInjector.DerivedConstructorPointer(); } public static void DerivedConstructorBody(Il2CppObjectBase objectBase) { ClassInjector.DerivedConstructorBody(objectBase); } public static void RegisterTypeInIl2Cpp(bool logSuccess = false) where T : class { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown RegisterTypeOptions val = new RegisterTypeOptions(); val.set_LogSuccess(logSuccess); ClassInjector.RegisterTypeInIl2Cpp(val); } public static void RegisterTypeInIl2CppWithInterfaces(bool logSuccess = false, params Type[] interfaces) where T : class { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown RegisterTypeOptions val = new RegisterTypeOptions(); val.set_Interfaces(Il2CppInterfaceCollection.op_Implicit(interfaces)); val.set_LogSuccess(logSuccess); ClassInjector.RegisterTypeInIl2Cpp(val); } } public static string GameDirectory => Paths.GameRootPath; public static bool IsIL2CPPType(Type type) { if (!IsGameIL2CPP()) { return false; } return ArchiveMod.IL2CPP_BaseType.IsAssignableFrom(type); } public static bool IsGameIL2CPP() { return true; } public static IArchiveLogger CreateLoggerInstance(string name, ConsoleColor col = ConsoleColor.White) { return new BIE_LogWrapper((ManualLogSource)(object)new ArManualLogSource(name, col)); } public static IArchiveLogger CreateArSubLoggerInstance(string name, ConsoleColor col = ConsoleColor.White) { return CreateLoggerInstance("Ar::" + name, col); } public static IArchiveLogger WrapLogger(ManualLogSource loggerInstance, ConsoleColor? col = null) { return new BIE_LogWrapper((ManualLogSource)(object)new ArManualLogSource(loggerInstance.SourceName, col)); } public static object StartCoroutine(IEnumerator routine) { return MonoBehaviourExtensions.StartCoroutine(BIE_ArchiveMod.MainComponent, routine); } public static void StopCoroutine(object coroutineToken) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown BIE_ArchiveMod.MainComponent.StopCoroutine((Coroutine)coroutineToken); } public unsafe static void* GetIl2CppMethod(string methodName, string returnTypeName, bool isGeneric, params string[] argTypes) where T : Il2CppObjectBase { void** ptr = (void**)IL2CPP.GetIl2CppMethod(Il2CppClassPointerStore.NativeClassPtr, isGeneric, methodName, returnTypeName, argTypes).ToPointer(); if (ptr == null) { return ptr; } return *ptr; } public unsafe static TDelegate GetIl2CppMethod(string methodName, string returnTypeName, bool isGeneric, params string[] argTypes) where T : Il2CppObjectBase where TDelegate : Delegate { void* il2CppMethod = GetIl2CppMethod(methodName, returnTypeName, isGeneric, argTypes); if (il2CppMethod == null) { return null; } return Marshal.GetDelegateForFunctionPointer((IntPtr)il2CppMethod); } public unsafe static INativeDetour ApplyNativeHook(string methodName, string returnType, string[] paramTypes, TDelegate to, out TDelegate original) where TClass : Object where TDelegate : Delegate { //IL_0042: Unknown result type (might be due to invalid IL or missing references) IntPtr nativeClassPtr = Il2CppClassPointerStore.NativeClassPtr; if (nativeClassPtr == IntPtr.Zero) { throw new ArgumentException(typeof(TClass).Name + " does not exist in il2cpp domain"); } return INativeDetour.CreateAndApply(UnityVersionHandler.Wrap((Il2CppMethodInfo*)(void*)IL2CPP.il2cpp_method_get_from_reflection(((Il2CppObjectBase)new MethodInfo(IL2CPP.il2cpp_method_get_object(IL2CPP.GetIl2CppMethod(nativeClassPtr, false, methodName, returnType, paramTypes), nativeClassPtr))).Pointer)).MethodPointer, to, ref original); } public unsafe static INativeDetour ApplyNativeHook(string methodName, string returnType, string[] paramTypes, Type[] genericArguments, TDelegate to, out TDelegate original) where TClass : Object where TDelegate : Delegate { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) IntPtr nativeClassPtr = Il2CppClassPointerStore.NativeClassPtr; if (nativeClassPtr == IntPtr.Zero) { throw new ArgumentException(typeof(TClass).Name + " does not exist in il2cpp domain"); } MethodInfo val = new MethodInfo(IL2CPP.il2cpp_method_get_object(IL2CPP.GetIl2CppMethod(nativeClassPtr, true, methodName, returnType, paramTypes), nativeClassPtr)); val.MakeGenericMethod(((IEnumerable)genericArguments).Select((Func)Il2CppType.From).ToArray()); return INativeDetour.CreateAndApply(UnityVersionHandler.Wrap((Il2CppMethodInfo*)(void*)IL2CPP.il2cpp_method_get_from_reflection(((Il2CppObjectBase)val).Pointer)).MethodPointer, to, ref original); } public static bool IsModInstalled(string guid) { ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue(guid, out var value); return value != null; } } [BepInPlugin("dev.AuriRex.gtfo.TheArchive", "TheArchive", "0.0.838")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [UsedImplicitly] public class BIE_ArchiveMod : BasePlugin { internal class TheArchive_BIE_Controller : MonoBehaviour { public TheArchive_BIE_Controller(IntPtr ptr) : base(ptr) { } public void Awake() { Object.DontDestroyOnLoad((Object)(object)this); ((Object)this).hideFlags = (HideFlags)61; } public void Update() { ArchiveMod.OnUpdate(); } public void LateUpdate() { ArchiveMod.OnLateUpdate(); } } internal static MonoBehaviour MainComponent { get; private set; } public override void Load() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown Harmony harmonyInstance = new Harmony("dev.AuriRex.gtfo.TheArchive"); ArchiveMod.OnApplicationStart(LoaderWrapper.WrapLogger(((BasePlugin)this).Log, ConsoleColor.DarkMagenta), harmonyInstance); Application.quitting += Action.op_Implicit((Action)delegate { ArchiveMod.OnApplicationQuit(); }); MainComponent = (MonoBehaviour)(object)((BasePlugin)this).AddComponent(); } public override bool Unload() { ArchiveMod.OnApplicationQuit(); return ((BasePlugin)this).Unload(); } } public class ArManualLogSource : ManualLogSource { public ConsoleColor? Color { get; } public ArManualLogSource(string sourceName, ConsoleColor? col = null) : base(sourceName) { Color = col; } } public static class BIE_LogSourceColorLookup { private static readonly Dictionary _colorLookup = new Dictionary(); public static void SetColor(ManualLogSource logger, ConsoleColor color) { _colorLookup[logger] = color; } public static bool TryGetColor(ManualLogSource logger, out ConsoleColor color, bool clearColor = true) { bool num = _colorLookup.TryGetValue(logger, out color); if (num && clearColor) { _colorLookup.Remove(logger); } return num; } } internal class BIE_LogWrapper : IArchiveLogger { private readonly ManualLogSource _logger; public BIE_LogWrapper(ManualLogSource logger) { _logger = logger; if (!Logger.Sources.Contains((ILogSource)(object)_logger)) { Logger.Sources.Add((ILogSource)(object)_logger); } } public void Success(string msg) { BIE_LogSourceColorLookup.SetColor(_logger, ConsoleColor.Green); _logger.LogMessage((object)msg); } public void Notice(string msg) { BIE_LogSourceColorLookup.SetColor(_logger, ConsoleColor.Cyan); _logger.LogMessage((object)msg); } public void Info(string msg) { _logger.LogInfo((object)msg); } public void Fail(string msg) { BIE_LogSourceColorLookup.SetColor(_logger, ConsoleColor.Red); _logger.LogInfo((object)msg); } public void Msg(ConsoleColor col, string msg) { BIE_LogSourceColorLookup.SetColor(_logger, col); _logger.LogMessage((object)msg); } public void Msg(string msg) { _logger.LogMessage((object)msg); } public void Debug(string msg) { _logger.LogDebug((object)msg); } public void Warning(string msg) { _logger.LogWarning((object)msg); } public void Error(string msg) { _logger.LogError((object)msg); } public void Exception(Exception ex) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ManualLogSource logger = _logger; bool flag = default(bool); BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(3, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.StackTrace); } logger.LogError(val); } } } namespace TheArchive.Interfaces { public interface IArchiveLogger { void Success(string msg); void Notice(string msg); void Msg(ConsoleColor col, string msg); void Info(string msg); void Fail(string msg); void Debug(string msg); void Warning(string msg); void Error(string msg); void Exception(Exception ex); } public interface IInitAfterDataBlocksReady : IInitializable { } public interface IInitAfterGameDataInitialized : IInitializable { } public interface IInitCondition { bool InitCondition(); } public interface IInitializable { void Init(); } public interface IInitImmediately : IInitializable { } } namespace TheArchive.Features.Dev { [EnableFeatureByDefault] [HideInModSettings] internal class AAColoredLogMessages : Feature { [ArchivePatch(typeof(ConsoleLogListener), "LogEvent", null, ArchivePatch.PatchMethodType.Method, -1)] public static class ConsoleLogListener__LogEvent__Patch { public static bool Prefix(object sender, LogEventArgs eventArgs) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!(sender is ArManualLogSource arManualLogSource)) { return true; } if (!BIE_LogSourceColorLookup.TryGetColor((ManualLogSource)(object)arManualLogSource, out var color)) { return true; } if (ConsoleManager.ConsoleStream == null) { return true; } ConsoleColor consoleColor = LogLevelExtensions.GetConsoleColor(eventArgs.Level); ConsoleColor valueOrDefault = arManualLogSource.Color.GetValueOrDefault(consoleColor); ConsoleManager.SetConsoleColor(consoleColor); ConsoleManager.ConsoleStream.Write("["); ConsoleManager.SetConsoleColor(valueOrDefault); ConsoleManager.ConsoleStream.Write(GetLevelAndSourceString(eventArgs)); ConsoleManager.SetConsoleColor(consoleColor); ConsoleManager.ConsoleStream.Write("] "); ConsoleManager.SetConsoleColor(color); ConsoleManager.ConsoleStream.Write($"{eventArgs.Data}{Environment.NewLine}"); ConsoleManager.SetConsoleColor(ConsoleColor.Gray); return false; } private static string GetLevelAndSourceString(LogEventArgs eventArgs) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) return $"{eventArgs.Level,-7}:{eventArgs.Source.SourceName,10}"; } } public override string Name => "Colored Log Messages"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Allows for colored log messages to be logged."; } [EnableFeatureByDefault] [HideInModSettings] internal class AnalyticsBlocker : Feature { [ArchivePatch(typeof(AnalyticsManager), "OnGameEvent", new Type[] { typeof(GameEventData) }, ArchivePatch.PatchMethodType.Method, -1)] internal static class AnalyticsManager_OnGameEventPatch { public static bool Prefix() { return false; } } public override string Name => "Block Game Analytics"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Prevent analytics data from being sent.\n(Recommended to keep enabled)"; public override void OnEnable() { Analytics.enabled = false; } public override void OnDisable() { Analytics.enabled = true; } } [HideInModSettings] [DoNotSaveToConfig] [ForceDisable("Needs work.")] internal class CullingHook : Feature { [ArchivePatch(typeof(C_Node), "Show", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class C_Node_Show_Patch { private static bool _IsShown; public static void Prefix(C_Node __instance) { _IsShown = ((C_Cullable)__instance).IsShown; } public static void Postfix(C_Node __instance) { if (!_IsShown) { CallUpdate(((C_CullBucket)__instance).m_owner, active: true); } } } [ArchivePatch(typeof(C_Node), "Hide", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class C_Node_Hide_Patch { private static bool _IsShown; public static void Prefix(C_Node __instance) { _IsShown = ((C_Cullable)__instance).IsShown; } public static void Postfix(C_Node __instance) { if (_IsShown) { CallUpdate(((C_CullBucket)__instance).m_owner, active: false); } } } public override string Name => "CullingHook"; public override GroupBase Group => GroupManager.Dev; public new static IArchiveLogger FeatureLogger { get; set; } public static void CallUpdate(IC_CullbucketOwner owner, bool active) { LG_Area val = ((Il2CppObjectBase)(object)owner).TryCastTo(); if (!((Object)(object)val == (Object)null)) { InitSingletonBase.Instance.OnLGAreaCullUpdate(val, active); } } } [HideInModSettings] internal class DamageLog : Feature { [ArchivePatch(typeof(BulletWeapon), "Fire", null, ArchivePatch.PatchMethodType.Method, -1)] public static class BulletWeapon_Fire_Patch { public static void Prefix() { BulletWeaponFired = true; ShotID++; FeatureLogger.Debug("BulletWeapon shot start"); } public static void Postfix() { BulletWeaponFired = false; FeatureLogger.Debug("BulletWeapon shot end"); } } [ArchivePatch(typeof(Shotgun), "Fire", null, ArchivePatch.PatchMethodType.Method, -1)] public static class Shotgun_Fire_Patch { public static void Prefix() { ShotgunWeaponFired = true; ShotID++; FeatureLogger.Debug("Shotgun shot start"); } public static void Postfix() { ShotgunWeaponFired = false; FeatureLogger.Debug("Shotgun shot end"); } } [ArchivePatch(typeof(BulletWeapon), "BulletHit", null, ArchivePatch.PatchMethodType.Method, -1)] public static class BulletWeapon_BulletHit_Patch { public static uint currentShotID; public static int penCount; public static bool isFirstRay; public static GameObject hitGO; public static IDamageable damageable; public static Agent hitAgent; public static EnemyAgent hitEnemyAgent; public static float enemyHealth; public static bool HasHitAnyAgent => (Object)(object)hitAgent != (Object)null; public static bool HasHitEnemy => (Object)(object)hitEnemyAgent != (Object)null; [IsPrefix] [RundownConstraint(Utils.RundownFlags.RundownOne, Utils.RundownFlags.RundownTwo)] public static void PrefixPreR3(WeaponHitData weaponRayData) { PrefixR3(weaponRayData, 0f); } [IsPrefix] [RundownConstraint(Utils.RundownFlags.RundownThree, Utils.RundownFlags.Latest)] public static void PrefixR3(WeaponHitData weaponRayData, float additionalDis) { //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) isFirstRay = additionalDis == 0f; if (currentShotID != ShotID || isFirstRay) { penCount = 0; } currentShotID = ShotID; object obj; if (weaponRayData == null) { obj = null; } else { RaycastHit rayHit = weaponRayData.rayHit; Collider collider = ((RaycastHit)(ref rayHit)).collider; obj = ((collider != null) ? ((Component)collider).gameObject : null); } hitGO = (GameObject)obj; GameObject obj2 = hitGO; object obj3; if (obj2 == null) { obj3 = null; } else { ColliderMaterial component = obj2.GetComponent(); obj3 = ((component != null) ? component.Damageable : null); } if (obj3 == null) { GameObject obj4 = hitGO; obj3 = ((obj4 != null) ? obj4.GetComponent() : null); } damageable = (IDamageable)obj3; IDamageable obj5 = damageable; hitAgent = ((obj5 != null) ? obj5.GetBaseAgent() : null); hitEnemyAgent = ((Il2CppObjectBase)(object)hitAgent)?.TryCastTo(); if (HasHitEnemy) { EnemyAgent obj6 = hitEnemyAgent; float? obj7; if (obj6 == null) { obj7 = null; } else { Dam_EnemyDamageBase damage = obj6.Damage; obj7 = ((damage != null) ? new float?(((Dam_SyncedDamageBase)damage).Health) : null); } float? num = obj7; enemyHealth = num.GetValueOrDefault(-1f); } else { enemyHealth = -1f; } } public static void Postfix(WeaponHitData weaponRayData, bool doDamage) { IArchiveLogger featureLogger = FeatureLogger; GameObject obj = hitGO; featureLogger.Debug("HitGO Name: " + ((obj != null) ? ((Object)obj).name : null)); if (!doDamage) { return; } string text = "| (" + ((penCount > 0) ? $"Pen: {penCount}" : "Origin") + ")"; if (!HasHitAnyAgent) { FeatureLogger.Info("Hit World Geometry! " + text); return; } if (!HasHitEnemy) { IArchiveLogger featureLogger2 = FeatureLogger; Agent obj2 = hitAgent; object obj3; if (obj2 == null) { obj3 = null; } else { GameObject gameObject = ((Component)obj2).gameObject; obj3 = ((gameObject != null) ? ((Object)gameObject).name : null); } featureLogger2.Info("Hit Other Agent! | AgentGOName: " + (string?)obj3 + " " + text); return; } IArchiveLogger featureLogger3 = FeatureLogger; int col = (isFirstRay ? 11 : 3); DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(44, 5); defaultInterpolatedStringHandler.AppendLiteral("Hit: "); EnemyAgent obj4 = hitEnemyAgent; object value; if (obj4 == null) { value = null; } else { GameObject gameObject2 = ((Component)obj4).gameObject; value = ((gameObject2 != null) ? ((Object)gameObject2).name : null); } defaultInterpolatedStringHandler.AppendFormatted((string?)value); defaultInterpolatedStringHandler.AppendLiteral(": HP_Before: "); defaultInterpolatedStringHandler.AppendFormatted(enemyHealth); defaultInterpolatedStringHandler.AppendLiteral(" | BaseDMG: "); defaultInterpolatedStringHandler.AppendFormatted(weaponRayData.damage); defaultInterpolatedStringHandler.AppendLiteral(" | HP_After: "); defaultInterpolatedStringHandler.AppendFormatted(((Dam_SyncedDamageBase)hitEnemyAgent.Damage).Health); defaultInterpolatedStringHandler.AppendLiteral(" "); defaultInterpolatedStringHandler.AppendFormatted(text); featureLogger3.Msg((ConsoleColor)col, defaultInterpolatedStringHandler.ToStringAndClear()); penCount++; } } private static uint _shotID; public override string Name => "Damage Log"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Log what all hitboxes/enemies were hit by your shot to console.\n\n(Probably only works correctly as master)\n(Disable for normal gameplay)"; public new static IArchiveLogger FeatureLogger { get; set; } public static bool BulletWeaponFired { get; private set; } public static bool ShotgunWeaponFired { get; private set; } public static uint ShotID { get { return _shotID; } set { if (_shotID >= uint.MaxValue) { _shotID = 0u; } else { _shotID = value; } } } public static bool AnyWeaponFired { get { if (!BulletWeaponFired) { return ShotgunWeaponFired; } return true; } } } [DoNotSaveToConfig] [DisallowInGameToggle] [HideInModSettings] internal class FeaturesMarkdownCreator : Feature { public class ReadmeCreatorSettings { [FSDisplayName("Use Localized Texts")] public FButton UseLocalizedTextsButton { get; set; } = new FButton("False", "use_localized_texts"); [FSDisplayName("Switch Assembly")] public FButton NextAssemblyButton { get; set; } = new FButton("Next ASM", "next_asm"); [FSDisplayName("Selected ASM:")] public FLabel SelectedAssemblyLabel { get; set; } = new FLabel("///"); [FSSpacer] [FSDisplayName("Create Markdown")] public FButton CreateMarkdownButton { get; set; } = new FButton("Create Markdown (Clipboard)", "create_markdown"); } public const char NEWLINE = '\n'; private static Assembly _selectedAssembly; private static bool _useLocalizedTexts; private static int _index; private static Assembly[] _loadedModules; public override string Name => "Features Markdown Creator"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Used to automatically generate a markdown text containing all Features.\n\n(Copied to clipboard)"; public new static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static ReadmeCreatorSettings Settings { get; set; } public override void OnDatablocksReady() { DiscoverAssemblies(); } public override void OnButtonPressed(ButtonSetting setting) { switch (setting.ButtonID) { case "use_localized_texts": ToggleUseLocalizedTexts(); break; case "next_asm": NextAssembly(); break; case "create_markdown": CreateReadme(_selectedAssembly); break; } } private void ToggleUseLocalizedTexts() { _useLocalizedTexts = !_useLocalizedTexts; ReadmeCreatorSettings settings = Settings; if (settings == null) { return; } FButton useLocalizedTextsButton = settings.UseLocalizedTextsButton; if (useLocalizedTextsButton != null) { TextMeshPro secondaryText = useLocalizedTextsButton.SecondaryText; if (secondaryText != null) { ((TMP_Text)secondaryText).SetText($"[ {(_useLocalizedTexts ? "" : "")}{_useLocalizedTexts} ]", true); } } } private static void DiscoverAssemblies() { _loadedModules = ArchiveMod.Modules.Select((IArchiveModule m) => m.GetType().Assembly).Distinct().ToArray(); SelectAssembly(_loadedModules[0]); } public static void NextAssembly() { _index++; if (_index >= _loadedModules.Length) { _index = 0; } SelectAssembly(_loadedModules[_index]); } public static void SelectAssembly(Assembly asm) { _selectedAssembly = asm; ReadmeCreatorSettings settings = Settings; if (settings == null) { return; } FLabel selectedAssemblyLabel = settings.SelectedAssemblyLabel; if (selectedAssemblyLabel != null) { TextMeshPro primaryText = selectedAssemblyLabel.PrimaryText; if (primaryText != null) { ((TMP_Text)primaryText).SetText("Current ASM: " + (asm?.GetName().Name ?? "None") + "", true); } } } public static void CreateReadme(Assembly asmFilter = null) { Dictionary> dictionary = new Dictionary>(); StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair> groupedFeature in InitSingletonBase.Instance.GroupedFeatures) { if (!GroupManager.TryGetGroup(groupedFeature.Key, out var group)) { FeatureLogger.Warning("Attempted to resolve unknown group \"" + groupedFeature.Key + "\"."); continue; } if (group.IsHidden) { FeatureLogger.Info("Skipping hidden group \"" + groupedFeature.Key + "\"."); continue; } if (group == GroupManager.LocalProgression) { FeatureLogger.Info("Skipping LocalProgression"); continue; } Feature[] array = groupedFeature.Value.Where((Feature f) => !f.IsHidden).ToArray(); if (asmFilter != null) { array = array.Where((Feature f) => f.GetType().Assembly == asmFilter).ToArray(); } if (!array.Any()) { FeatureLogger.Info($"Skipping Group \"{group}\": All Features are hidden!"); } else if (array.Length != 0) { dictionary.Add(group, array); } } KeyValuePair>[] array2 = dictionary.OrderBy((KeyValuePair> kvp) => Utils.StripTMPTagsRegex(kvp.Key.Identifier)).ToArray(); KeyValuePair>[] array3 = array2; foreach (KeyValuePair> keyValuePair in array3) { GroupBase key = keyValuePair.Key; StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(5, 1, stringBuilder2); handler.AppendLiteral(" * "); handler.AppendFormatted(CreateQuickLink(key)); handler.AppendLiteral("\n"); stringBuilder2.Append(ref handler); } stringBuilder.Append('\n'); array3 = array2; for (int i = 0; i < array3.Length; i++) { KeyValuePair> keyValuePair2 = array3[i]; GroupBase key2 = keyValuePair2.Key; IOrderedEnumerable orderedEnumerable = keyValuePair2.Value.OrderBy((Feature f) => Utils.StripTMPTagsRegex(f.Name)); stringBuilder.Append('\n'); stringBuilder.Append(CreateGroupEntry(key2)); foreach (Feature item in orderedEnumerable) { stringBuilder.Append(CreateFeatureEntry(item)); } } string text2 = (GUIUtility.systemCopyBuffer = stringBuilder.Replace("\\n", '\n'.ToString()).ToString()); FeatureLogger.Notice($"Copied {text2.Length} characters to clipboard!"); } public static string CreateQuickLink(GroupBase group) { string text = Utils.StripTMPTagsRegex(_useLocalizedTexts ? group.DisplayName : group.Identifier); string value = "#" + text.ToLower().Replace(" ", "-").Replace("/", string.Empty); return $"[{text}]({value})"; } public static string CreateGroupEntry(GroupBase group) { StringBuilder stringBuilder; StringBuilder stringBuilder2 = (stringBuilder = new StringBuilder()); StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder); handler.AppendLiteral("## "); handler.AppendFormatted(Utils.StripTMPTagsRegex(_useLocalizedTexts ? group.DisplayName : group.Identifier)); stringBuilder.Append(ref handler); stringBuilder2.Append('\n'); stringBuilder2.Append('\n'); return stringBuilder2.ToString(); } public static string CreateFeatureEntry(Feature feature) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder2); handler.AppendLiteral("### "); handler.AppendFormatted(Utils.StripTMPTagsRegex(_useLocalizedTexts ? feature.FeatureInternal.DisplayName : feature.Name)); stringBuilder3.Append(ref handler); if (feature.AppliesToRundowns != 0) { string rundownTag = Utils.GetRundownTag(feature.AppliesToRundowns, generalizeLatest: true); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2); handler.AppendLiteral(" - `["); handler.AppendFormatted(rundownTag); handler.AppendLiteral("]`"); stringBuilder4.Append(ref handler); } stringBuilder.Append('\n'); stringBuilder.Append('\n'); if (!string.IsNullOrWhiteSpace(_useLocalizedTexts ? feature.FeatureInternal.DisplayDescription : feature.Description)) { stringBuilder.Append(Utils.StripTMPTagsRegex(_useLocalizedTexts ? feature.FeatureInternal.DisplayDescription : feature.Description)); } else { stringBuilder.Append(Tagged("description missing!")); } stringBuilder.Append('\n'); stringBuilder.Append('\n'); return stringBuilder.ToString(); } private static string Tagged(string tag, bool todo = true) { return "[ " + (todo ? "TODO: " : string.Empty) + tag + " ]"; } } [HideInModSettings] internal class ForcedSeed : Feature { public class ForcedSeedSettings { [FSDisplayName("Force Session Seed")] public bool ForceSessionSeed { get; set; } = true; [FSDisplayName("Session Seed Override")] [FSDescription("The main seed used for level randomization.")] public int SessionSeed { get; set; } [FSDisplayName("Force HostID Seed")] public bool ForceHostIDSeed { get; set; } [FSDisplayName("HostID Seed Override")] public int HostIDSeed { get; set; } } [ArchivePatch(typeof(ExpeditionInTierData), "SetSeeds", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class RundownManager_SetActiveExpedition_Patch { public static void Prefix(ref int hostIDSeed, ref int sessionSeed) { if (SNet.IsMaster) { if (Settings.ForceSessionSeed) { FeatureLogger.Notice($"Forcing seed: {"SessionSeed"}: {Settings.SessionSeed}"); sessionSeed = Settings.SessionSeed; } if (Settings.ForceHostIDSeed) { FeatureLogger.Notice($"Forcing seed: {"HostIDSeed"}: {Settings.HostIDSeed}"); hostIDSeed = Settings.HostIDSeed; } } } } public override string Name => "Force Expedition Seeds"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Force Seeds used to randomize objectives, boxes and enemies spawns.\n\n<#F00>(Master only!)\n(Probably doesn't work in multiplayer, idk ... haven't tested it :p)"; public new static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] public static ForcedSeedSettings Settings { get; set; } } [EnableFeatureByDefault] [HideInModSettings] [ForceDisable("Not needed as BepInEx already redirects unity debug logs itself.")] internal class GameDebugLogRedirect : Feature { [ArchivePatch(typeof(Debug), "Log", new Type[] { typeof(Object) }, ArchivePatch.PatchMethodType.Method, -1)] internal static class Debug__Log__Patch { public static void Prefix(Object message) { GTFOLogger.Log(message.ToString()); } } [ArchivePatch(typeof(Debug), "LogWarning", new Type[] { typeof(Object) }, ArchivePatch.PatchMethodType.Method, -1)] internal static class Debug__LogWarning__Patch { public static void Prefix(Object message) { GTFOLogger.Warn(message.ToString()); } } [ArchivePatch(typeof(Debug), "LogError", new Type[] { typeof(Object) }, ArchivePatch.PatchMethodType.Method, -1)] internal static class Debug__LogError__Patch { public static void Prefix(Object message) { GTFOLogger.Error(message.ToString()); } } public override string Name => "Game Logs Redirect"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Prints Unity debug logs into the MelonLoader console."; } [EnableFeatureByDefault] [HideInModSettings] [DoNotSaveToConfig] [RundownConstraint(Utils.RundownFlags.RundownSix, Utils.RundownFlags.Latest)] internal class GTFOApi_Compat : Feature { [ArchivePatch("Setup_Prefix", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GearManager_Patches_Setup_Prefix_Patch { public static Type Type() { return ImplementationManager.FindTypeInCurrentAppDomain("GTFO.API.Patches.GearManager_Patches", exactMatch: true); } public static bool Prefix() { return false; } } public override string Name => "GTFOApi_Compat"; public override GroupBase Group => GroupManager.Dev; public override bool ShouldInit() { if (!LoaderWrapper.IsModInstalled("dev.gtfomodding.gtfo-api")) { RequestDisable("GTFO-Api not installed!"); return false; } if (Feature.IsPlayingModded) { RequestDisable("MTFO is installed, not using our favorites location."); return false; } return true; } } [HideInModSettings] internal class IconRenderSettings : Feature { public class IconRenderSettingsSettings { [FSDisplayName("Resolution Multiplier")] public int ResolutionMultiplier { get; set; } = 5; } [ArchivePatch(typeof(GearIconRendering), "RenderIconPrep", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GearIconRendering_RenderIconPrepPatch { public static void Prefix(ref IconRenderJob job) { IconRenderSettings settings = ((Il2CppArrayBase)(object)job.datas)[job.currentIconIndex].settings; settings.resX *= Settings.ResolutionMultiplier; settings.resY *= Settings.ResolutionMultiplier; } } public override string Name => "IconRenderSettings"; public override GroupBase Group => GroupManager.Dev; [FeatureConfig] public static IconRenderSettingsSettings Settings { get; set; } } [AutomatedFeature] [DoNotSaveToConfig] internal class InputDisabler : Feature { [ArchivePatch(typeof(InputMapper), "GetButtonKeyMouseGamepad", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class InputMapper_GetButtonKeyMouseGamepad_Patch { public static bool Prefix(ref bool __result) { return Skip(ref __result); } } [ArchivePatch(typeof(InputMapper), "GetButtonDownKeyMouseGamepad", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class InputMapper_GetButtonDownKeyMouseGamepad_Patch { public static bool Prefix(ref bool __result) { return Skip(ref __result); } } [ArchivePatch(typeof(InputMapper), "GetButtonUpKeyMouseGamepad", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class InputMapper_GetButtonUpKeyMouseGamepad_Patch { public static bool Prefix(ref bool __result) { return Skip(ref __result); } } [ArchivePatch(typeof(InputMapper), "GetAxisKeyMouseGamepad", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class InputMapper_GetAxisKeyMouseGamepad_Patch { public static bool Prefix(ref float __result) { __result = 0f; return false; } } public override string Name => "InputDisabler"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Used to disable input programatically."; public static bool Skip(ref bool __result) { __result = false; return false; } } [HideInModSettings] [DoNotSaveToConfig] [AutomatedFeature] internal class InternalUAudioListenerHelper : Feature { [RundownConstraint(Utils.RundownFlags.RundownOne, Utils.RundownFlags.RundownFive)] [ArchivePatch(typeof(PlayerAgent), "Setup", null, ArchivePatch.PatchMethodType.Method, -1)] public static class PlayerAgent_Setup_Patch { public static void Postfix(PlayerAgent __instance) { AddOrEnableAudioListener((__instance != null) ? ((Component)__instance).gameObject : null); } } [RundownConstraint(Utils.RundownFlags.RundownSix, Utils.RundownFlags.Latest)] [ArchivePatch(null, "Setup", null, ArchivePatch.PatchMethodType.Method, -1)] public static class LocalPlayerAgent_Setup_Patch { public static Type Type() { return typeof(LocalPlayerAgent); } public static void Postfix(LocalPlayerAgent __instance) { AddOrEnableAudioListener((__instance != null) ? ((Component)__instance).gameObject : null); } } public override string Name => "InternalUAudioListenerHelper"; public override GroupBase Group => GroupManager.Dev; public new static IArchiveLogger FeatureLogger { get; set; } public static void AddOrEnableAudioListener(GameObject go) { if (!((Object)(object)go == (Object)null) && (Object)(object)go.GetComponent() == (Object)null) { go.AddComponent(); FeatureLogger.Success($"Added {"AudioListener"} to local player! ({((Object)go).name})"); } } public static void DestroyAudioListener(GameObject go) { if (!((Object)(object)go == (Object)null)) { AudioListener component = go.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); FeatureLogger.Fail($"Removed {"AudioListener"} from local player! ({((Object)go).name})"); } } } public override void OnEnable() { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); AddOrEnableAudioListener((localPlayerAgent != null) ? ((Component)localPlayerAgent).gameObject : null); } public override void OnDisable() { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); DestroyAudioListener((localPlayerAgent != null) ? ((Component)localPlayerAgent).gameObject : null); } } [EnableFeatureByDefault] [HideInModSettings] [RundownConstraint(Utils.RundownFlags.RundownOne, Utils.RundownFlags.RundownAltSix)] internal class MaulGavelChecksumPatch : Feature { [ArchivePatch(typeof(GearIDRange), "GetChecksum", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GearIDRange_GetChecksumPatch { public static void Prefix(GearIDRange __instance) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (__instance.m_checksum == 0) { ChecksumGenerator_32 val = new ChecksumGenerator_32(); for (int i = 0; i < ((Il2CppArrayBase)(object)__instance.m_comps).Length; i++) { val.Insert((uint)((Il2CppArrayBase)(object)__instance.m_comps)[i]); } val.Insert("name", __instance.PublicGearName); __instance.m_checksum = val.Checksum; } } } public override string Name => "Maul/Gavel Checksum Patch"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Fixes the Maul and Gavel hammers having the same icon."; } [HideInModSettings] [EnableFeatureByDefault] [DisallowInGameToggle] internal class ModLanguage : Feature { [RundownConstraint(Utils.RundownFlags.RundownSix, Utils.RundownFlags.Latest)] [ArchivePatch(null, "SetCurrentLanguage", null, ArchivePatch.PatchMethodType.Method, -1)] private class GameDataTextLocalizationService__SetCurrentLanguage__Patch { public static Type Type() { return typeof(GameDataTextLocalizationService); } private static void Postfix(Language language) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Language num = GameLanguageToModLanguage(language); ModLanguageLegacy.StoreLanguage(num); TrySetLanguage(num); } } public override string Name => "Mod Language"; public override string Description => "Change Language of ModSettings"; public override GroupBase Group => GroupManager.Dev; public new static IArchiveLogger FeatureLogger { get; set; } public static bool TrySetLanguage(Language targetLanguage) { if (targetLanguage != ArchiveLocalizationService.CurrentLanguage) { FeatureLogger.Notice($"Setting Language to {targetLanguage}."); ArchiveLocalizationService.SetCurrentLanguage(targetLanguage); FeatureInternal.ReloadAllFeatureSettings(); ModSettings.RegenerateModSettingsPage(); return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Language GameLanguageToModLanguage(Language gameLanguage) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown switch (gameLanguage - 1) { case 10: case 11: return Language.Chinese; case 1: return Language.French; case 2: return Language.Italian; case 3: return Language.German; case 4: return Language.Spanish; case 5: return Language.Russian; case 6: return Language.Portuguese_Brazil; case 7: return Language.Polish; case 8: return Language.Japanese; case 9: return Language.Korean; default: return Language.English; } } } [HideInModSettings] [EnableFeatureByDefault] [DisallowInGameToggle] internal class ModLanguageLegacy : Feature { public class ModLanguageLegacySettings { [FSRundownHint(Utils.RundownFlags.RundownOne, Utils.RundownFlags.RundownFive)] [FSDisplayName("Legacy Version Language")] [FSDescription("The Language used for ModSettings when playing Original Rundowns 1 to 5.\n\nUpdates automatically whenever you choose your Language on the latest game version!")] public Language Language { get; set; } } public override string Name => "Mod Language Legacy"; public override string Description => "Change Language of ModSettings for OG Rundowns 1 to 5"; public override GroupBase Group => GroupManager.Dev; public override bool InlineSettingsIntoParentMenu => true; public override Type[] ExternalLocalizedTypes => new Type[1] { typeof(Language) }; [FeatureConfig] public static ModLanguageLegacySettings Settings { get; set; } public static void StoreLanguage(Language language) { if (Settings.Language != language) { Settings.Language = language; } } public override void OnDatablocksReady() { TrySetLanguageLegacy(); } public override void OnFeatureSettingChanged(FeatureSetting setting) { TrySetLanguageLegacy(); } private static void TrySetLanguageLegacy() { if (!Is.R6OrLater) { ModLanguage.TrySetLanguage(Settings.Language); } } } [EnableFeatureByDefault] [HideInModSettings] public class ModSettings : Feature { internal class Attribution : IDisposable { private readonly SubMenu _submenu; internal Attribution() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) _submenu = new DynamicSubMenu(ArchiveLocalizationService.GetById(60u, "Attribution & Licenses"), BuildSubMenu, "Attribution and Licenses Menu"); using (_submenu.GetPersistentContentAdditionToken()) { SettingsCreationHelper.CreateSettingsItem("<<< " + ArchiveLocalizationService.GetById(38u, "Back") + " <<<", out var cm_settingsItem, null, null, (TooltipPositionType)0, RED, _submenu); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); ((CM_Item)(object)cm_settingsItem).SetCMItemEvents(delegate { _submenu.Close(); }); SettingsCreationHelper.CreateSpacer(_submenu); } } private void BuildSubMenu(DynamicSubMenu subMenu) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) foreach (TheArchive.Core.Attribution.AttributionInfo attributionInfo in TheArchive.Core.Attribution.AttributionInfos) { SettingsCreationHelper.CreateHeader(attributionInfo.Name, out var cm_settingsItem, ORANGE, bold: true, subMenu); SettingsCreationHelper.CreateFSHoverAndSetButtonAction(new DescriptionPanelData { Title = attributionInfo.Name, Description = attributionInfo.Content, CriticalInfo = "<#FFF>" + attributionInfo.Comment + "", FeatureOrigin = attributionInfo.Origin }, cm_settingsItem, null); } } internal void InsertMenuButton() { SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(60u, "Attribution & Licenses"), ArchiveLocalizationService.GetById(28u, "Open"), delegate { _submenu.Show(); }); } public void Dispose() { _submenu?.Dispose(); } } [UsedImplicitly] internal class ModSettingsSettings { public class SearchOptions { [FSDisplayName("Search Titles")] public bool SearchTitles { get; set; } = true; [FSDisplayName("Search Descrption")] public bool SearchDescriptions { get; set; } [FSDisplayName("Search Sub Setting Titles")] public bool SearchSubSettingsTitles { get; set; } = true; [FSDisplayName("Search Sub Settings Description")] public bool SearchSubSettingsDescription { get; set; } } [FSDisplayName("Search Option")] public SearchOptions Search { get; set; } = new SearchOptions(); } internal static class CM_SettingScrollReceiver_GetFloatDisplayText_Patch { public static bool OverrideDisplayValue { get; set; } public static float Value { get; set; } public static void Prefix(ref float val) { if (OverrideDisplayValue) { val = Value; OverrideDisplayValue = false; } } } [ArchivePatch(typeof(CM_SettingsInputField), "OnBtnPressAnywhere", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_SettingsInputField__OnBtnPressAnywhere__Patch { private static void Postfix(CM_SettingsInputField __instance, iCellMenuInputHandler inputHandler, List hoverItems) { if (!SubMenu.ScrollWindowClickAnyWhereListeners.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { return; } foreach (iCellMenuCursorInputAnywhereItem item in value) { if (item != null) { item.OnBtnPressAnywhere(inputHandler, hoverItems); } } } } [ArchivePatch(typeof(CM_PopupOverlay), "OnBtnPressAnywhere", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_PopupOverlay__OnBtnPressAnywhere__Patch { private static void Postfix(CM_PopupOverlay __instance, iCellMenuInputHandler inputHandler, List hoverItems) { if (!SubMenu.ScrollWindowClickAnyWhereListeners.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { return; } foreach (iCellMenuCursorInputAnywhereItem item in value) { if (item != null) { item.OnBtnPressAnywhere(inputHandler, hoverItems); } } } } [ArchivePatch(typeof(PlayerChatManager), "ExitChatMode", null, ArchivePatch.PatchMethodType.Method, -1)] private class PlayerChatManager__ExitChatMode__Patch { private static void Postfix() { Input.imeCompositionMode = (IMECompositionMode)2; } } [ArchivePatch(typeof(CM_SettingsInputField), "OnBtnPress", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_SettingsInputField__OnBtnPress__Patch { private static void Postfix() { Input.imeCompositionMode = (IMECompositionMode)1; } } [ArchivePatch(typeof(CM_PageSettings), "OnEnable", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_PageSettings__OnEnable__Patch { private static void Postfix() { PlayerChatManager.TextChatInputEnabled = false; } } [ArchivePatch(typeof(CM_PageSettings), "OnDisable", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_PageSettings__OnDisable__Patch { private static void Postfix() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) PlayerChatManager.OnFocusStateChanged(FocusStateManager.CurrentState); } } [ArchivePatch(typeof(CM_SettingsInputField), "Update", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_SettingsInputField__Update__Patch { private static void Postfix(CM_SettingsInputField __instance) { if (__instance.m_readingActive) { TextMeshPro text = __instance.m_text; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(0, 2); defaultInterpolatedStringHandler.AppendFormatted(__instance.m_currentValue); defaultInterpolatedStringHandler.AppendFormatted((__instance.m_readingActive && __instance.m_blink) ? ((object)'_') : string.Empty); ((TMP_Text)text).text = defaultInterpolatedStringHandler.ToStringAndClear(); } } } [ArchivePatch(typeof(CM_SettingsInputField), "SetReadingActive", null, ArchivePatch.PatchMethodType.Method, -1)] private class CM_SettingsInputField__SetReadingActive__Patch { private static void Postfix(CM_SettingsInputField __instance, bool active) { __instance.ResetValue(); } } [ArchivePatch(typeof(CM_PageSettings), "Setup", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class CM_PageSettings__Setup__Patch { private static Stopwatch _setupStopwatch = new Stopwatch(); private static TextMeshPro _restartInfoText; public static IValueAccessor A_CM_PageSettings_m_subMenuItemOffset; private static int _versionClickedCounter; private static float _lastVersionClickedTime; private const int VERSION_CLICK_MIN = 3; private const float VERSION_CLICK_TIME = 1.5f; public static void SetRestartInfoText(bool value) { if (value) { SetRestartInfoText(ArchiveLocalizationService.GetById(59u, "Restart required for some settings to apply!")); } else { SetRestartInfoText(string.Empty); } } public static void SetRestartInfoText(string str) { if (!((Object)(object)_restartInfoText == (Object)null)) { ((TMP_Text)_restartInfoText).SetText(str, true); JankTextMeshProUpdaterOnce.UpdateMesh(_restartInfoText); } } public static void Init() { A_CM_PageSettings_m_subMenuItemOffset = AccessorBase.GetValueAccessor("m_subMenuItemOffset"); } public static void Postfix(CM_PageSettings __instance, MainMenuGuiLayer guiLayer) { PageSettingsData.SubMenuButtonPrefab = __instance.m_subMenuButtonPrefab; RectTransform movingContentHolder = ((CM_PageBase)__instance).m_movingContentHolder; GameObject scrollwindowPrefab = __instance.m_scrollwindowPrefab; try { PageSettingsData.SettingsPageInstance = __instance; PageSettingsData.PopupWindow = __instance.m_popupWindow; PageSettingsData.SettingsItemPrefab = __instance.m_settingsItemPrefab; PageSettingsData.ScrollWindowPrefab = scrollwindowPrefab; PageSettingsData.MMGuiLayer = guiLayer; PageSettingsData.MovingContentHolder = movingContentHolder; SetupMainModSettingsPage(); } catch (Exception ex) { FeatureLogger.Exception(ex); } try { UIHelper.Setup(); } catch (Exception ex2) { FeatureLogger.Exception(ex2); } } public static void DestroyModSettingsPage() { ((Component)(object)PageSettingsData.MainModSettingsButton).SafeDestroyGO(); ((Component)(object)PageSettingsData.MainModSettingsScrollWindow).SafeDestroyGO(); PageSettingsData.TheDescriptionPanel.Dispose(); PageSettingsData.TheDescriptionPanel = null; PageSettingsData.TheColorPicker.Dispose(); PageSettingsData.TheColorPicker = null; PageSettingsData.TheSearchMenu.Dispose(); PageSettingsData.TheSearchMenu = null; PageSettingsData.AttributionPage.Dispose(); PageSettingsData.AttributionPage = null; foreach (CM_ScrollWindow allSubmenuScrollWindow in PageSettingsData.AllSubmenuScrollWindows) { RemoveFromAllSettingsWindows(allSubmenuScrollWindow); ((Component)(object)allSubmenuScrollWindow).SafeDestroyGO(); } PageSettingsData.AllSubmenuScrollWindows.Clear(); float num = A_CM_PageSettings_m_subMenuItemOffset.Get(PageSettingsData.SettingsPageInstance); A_CM_PageSettings_m_subMenuItemOffset.Set(PageSettingsData.SettingsPageInstance, num + 80f); } public static void SetupMainModSettingsPage() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0119: 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_0132: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_04fc: Unknown result type (might be due to invalid IL or missing references) //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) FeatureLogger.Debug("SetupMainModSettingsPage: Starting Setup Stopwatch."); _setupStopwatch.Reset(); _setupStopwatch.Start(); PageSettingsData.ScrollWindowContentElements.Clear(); string byId = ArchiveLocalizationService.GetById(3u, "Mod Settings"); float num = A_CM_PageSettings_m_subMenuItemOffset.Get(PageSettingsData.SettingsPageInstance); PageSettingsData.MainModSettingsButton = ((Il2CppObjectBase)(object)((GuiLayer)PageSettingsData.MMGuiLayer).AddRectComp(PageSettingsData.SubMenuButtonPrefab, (GuiAnchor)1, new Vector2(70f, num), (Transform)(object)PageSettingsData.MovingContentHolder)).TryCastTo(); ((RectTransformComp)PageSettingsData.MainModSettingsButton).SetScaleFactor(0.85f); PageSettingsData.MainModSettingsButton.UpdateColliderOffset(); A_CM_PageSettings_m_subMenuItemOffset.Set(PageSettingsData.SettingsPageInstance, num - 80f); PageSettingsData.MainModSettingsButton.SetText(byId); SharedUtils.ChangeColorCMItem(PageSettingsData.MainModSettingsButton, Color.magenta); PageSettingsData.MainModSettingsScrollWindow = SettingsCreationHelper.CreateScrollWindow(byId); PageSettingsData.MainScrollWindowTransform = ((Component)PageSettingsData.MainModSettingsScrollWindow).transform; TextMeshPro componentInChildren = ((Component)PageSettingsData.MainModSettingsScrollWindow).GetComponentInChildren(); _restartInfoText = Object.Instantiate(componentInChildren, componentInChildren.transform.parent); ((Object)_restartInfoText).name = "ModSettings_RestartInfoText"; Transform transform = _restartInfoText.transform; transform.localPosition += new Vector3(300f, 0f, 0f); ((TMP_Text)_restartInfoText).SetText("", true); ((TMP_Text)_restartInfoText).text = ""; ((TMP_Text)_restartInfoText).enableWordWrapping = false; ((TMP_Text)_restartInfoText).fontSize = 16f; ((TMP_Text)_restartInfoText).fontSizeMin = 16f; JankTextMeshProUpdaterOnce.UpdateMesh(_restartInfoText); PageSettingsData.TheDescriptionPanel = new DescriptionPanel(); PageSettingsData.TheColorPicker = new ColorPicker(); if (Feature.DevMode) { SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(56u, "Dev Mode enabled - Hidden Features shown!"), DISABLED); } PageSettingsData.TheSearchMenu = new SearchMainPage(); PageSettingsData.AttributionPage = new Attribution(); BuildFeatureGroup(((IEnumerable)GroupManager.TopLevelGroups.Values).ToHashSet()); bool flag = GroupManager.ModuleGroups.Values.All((ModuleGroup g) => g.IsHidden || g.Features.Count == 0 || g.Features.All((Feature f) => f.IsHidden)); if (GroupManager.ModuleGroups.Count > 1 && !flag) { SettingsCreationHelper.CreateSpacer(); SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(58u, "Add-ons"), GREEN); } BuildFeatureGroup(((IEnumerable)GroupManager.ModuleGroups.Values).ToHashSet()); IEnumerable source = ((!Feature.DevMode) ? InitSingletonBase.Instance.RegisteredFeatures.Where((Feature f) => !f.IsHidden) : InitSingletonBase.Instance.RegisteredFeatures); source = source.Where((Feature f) => !f.BelongsToGroup); source = source.OrderBy((Feature f) => f.GetType().Assembly.GetName().Name + f.Name); _ = from asm in source.Select((Feature f) => f.GetType().Assembly).ToHashSet() orderby asm.GetName().Name select asm; SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(26u, "Info")); SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(27u, "Open saves folder"), ArchiveLocalizationService.GetById(28u, "Open"), delegate { Process.Start(new ProcessStartInfo { Arguments = Path.GetFullPath(LocalFiles.SaveDirectoryPath), UseShellExecute = true, FileName = "explorer.exe" }); }); SettingsCreationHelper.CreateHeader("> " + Path.GetFullPath(LocalFiles.SaveDirectoryPath), WHITE_GRAY, bold: false); SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(34u, "Open mod github"), ArchiveLocalizationService.GetById(35u, "Open in Browser"), delegate { Application.OpenURL("https://github.com/AuriRex/GTFO_TheArchive"); }); SettingsCreationHelper.CreateHeader("TheArchive v0.0.838", WHITE_GRAY, bold: false, null, VersionClicked); if (ArchiveMod.GIT_IS_DIRTY) { SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(30u, "Built with uncommitted changes | Git is dirty"), ORANGE, bold: false); } if (Feature.DevMode) { SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.Format(32u, "Last Commit Hash: {0}", "a4eb55a"), WHITE_GRAY, bold: false); SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.Format(33u, "Last Commit Date: {0}", "2026-05-07T19:14:52+08:00"), WHITE_GRAY, bold: false); } PageSettingsData.AttributionPage.InsertMenuButton(); SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.Format(31u, "Currently running GTFO {0}, build {1}", Feature.BuildInfo.Rundown, Feature.BuildInfo.BuildNumber), WHITE_GRAY, bold: false); AddToAllSettingsWindows(PageSettingsData.MainModSettingsScrollWindow); PageSettingsData.MainModSettingsButton.SetCMItemEvents(ShowMainModSettingsWindow); PageSettingsData.MainModSettingsScrollWindow.SetContentItems(PageSettingsData.ScrollWindowContentElements.ToIL2CPPListIfNecessary(), 5f); _setupStopwatch.Stop(); FeatureLogger.Debug($"It took {_setupStopwatch.Elapsed:ss\\.fff} seconds to run {"SetupMainModSettingsPage"}!"); } private static void BuildFeatureGroup(HashSet groups, SubMenu parentMenu = null) { GroupBase[] array = groups.OrderBy((GroupBase g) => g.Identifier).ToArray(); int num = 0; GroupBase[] array2 = array; foreach (GroupBase groupBase in array2) { num++; _ = groupBase.Identifier; Feature[] array3 = groupBase.Features.OrderBy((Feature fs) => fs.Name).ToArray(); if ((groupBase.IsHidden && !Feature.DevMode) || (!groupBase.SubGroups.Any() && !groupBase.Features.Any()) || (!Feature.DevMode && array3.All((Feature f) => f.IsHidden) && !groupBase.SubGroups.Any())) { continue; } string displayName = groupBase.DisplayName; SubMenu subMenu = parentMenu; SettingsCreationHelper.CreateHeader(displayName, out var cm_settingsItem, null, bold: true, subMenu); if (!string.IsNullOrWhiteSpace(groupBase.Description)) { SettingsCreationHelper.CreateFSHoverAndSetButtonAction(new DescriptionPanelData { Title = groupBase.DisplayName, Description = groupBase.Description, FeatureOrigin = groupBase.Identifier }, cm_settingsItem, null); } SubMenu subMenu2 = new SubMenu(groupBase.DisplayName, groupBase.Identifier); int num2 = array3.Count((Feature f) => !f.IsHidden || Feature.DevMode); int num3 = groupBase.SubGroups.Count((GroupBase g) => (!g.IsHidden || Feature.DevMode) && g.Features.Any((Feature f) => !f.IsHidden || Feature.DevMode)); string item = ArchiveLocalizationService.Format(24u, "{0} Feature{1}", num2, (num2 == 1) ? string.Empty : "s"); string item2 = ArchiveLocalizationService.Format(57u, "{0} Subgroup{1}", num3, (num3 == 1) ? string.Empty : "s"); string empty = string.Empty; List list = new List(); if (num2 > 0) { list.Add(item); } if (num3 > 0) { list.Add(item2); } empty = string.Join(", ", list.Where((string p) => !string.IsNullOrWhiteSpace(p))); subMenu = parentMenu; string menuEntryLabelText = empty; SettingsCreationHelper.CreateSubMenuControls(subMenu2, null, menuEntryLabelText, subMenu); string displayName2 = groupBase.DisplayName; subMenu = subMenu2; SettingsCreationHelper.CreateHeader(displayName2, null, bold: true, subMenu); Feature[] array4 = array3; for (int j = 0; j < array4.Length; j++) { SetupEntriesForFeature(array4[j], subMenu2); } if (num2 > 0 && num3 > 0) { SettingsCreationHelper.CreateSpacer(subMenu2); } BuildFeatureGroup(groupBase.SubGroups, subMenu2); subMenu2?.Build(); if (num != array.Length) { SettingsCreationHelper.CreateSpacer(parentMenu); } } if (parentMenu == null) { SettingsCreationHelper.CreateSpacer(); } } private static void VersionClicked(int _) { if (_versionClickedCounter >= 3) { ArchiveMod.Settings.FeatureDevMode = !ArchiveMod.Settings.FeatureDevMode; FeatureLogger.Notice("TheArchive Developer mode has been " + (Feature.DevMode ? "enabled" : "disabled") + " temporarily!"); _versionClickedCounter = 0; DestroyModSettingsPage(); SetupMainModSettingsPage(); ShowMainModSettingsWindow(0); } else { float time = Time.time; if (time - _lastVersionClickedTime >= 1.5f) { _lastVersionClickedTime = time; _versionClickedCounter = 0; } else { _versionClickedCounter++; _lastVersionClickedTime = time; } } } internal static void SetupEntriesForFeature(Feature feature, SubMenu groupSubMenu) { //IL_0055: 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) if (!Feature.DevMode && feature.IsHidden) { return; } CM_Item toggleButton_cm_item; TextMeshPro toggleButtonText; CM_Item sub_toggleButton_cm_item; TextMeshPro sub_toggleButtonText; try { Color? val = null; string text; if (feature.IsHidden) { text = "[H] " + feature.FeatureInternal.DisplayName; val = DISABLED; } else { text = feature.FeatureInternal.DisplayName; } if (feature.RequiresRestart) { text = "[!] " + text; } SubMenu subMenu = null; if (!feature.InlineSettingsIntoParentMenu && feature.HasAdditionalSettings && !feature.AllAdditionalSettingsAreHidden) { subMenu = new SubMenu(text, feature.Identifier); text = "" + text + ""; } SettingsCreationHelper.CreateSettingsItem(text, out var cm_settingsItem, null, null, (TooltipPositionType)0, val, groupSubMenu); SettingsCreationHelper.SetupToggleButton(cm_settingsItem, out toggleButton_cm_item, out toggleButtonText); CM_SettingsItem cm_settingsItem2 = null; if (subMenu != null) { SettingsCreationHelper.CreateSubMenuControls(subMenu, val, "> Settings", groupSubMenu); SettingsCreationHelper.CreateSettingsItem(text, out cm_settingsItem2, null, null, (TooltipPositionType)0, ORANGE, subMenu); } sub_toggleButton_cm_item = null; sub_toggleButtonText = null; if (subMenu != null) { SettingsCreationHelper.SetupToggleButton(cm_settingsItem2, out sub_toggleButton_cm_item, out sub_toggleButtonText); } if (feature.DisableModSettingsButton) { ((Component)toggleButton_cm_item).gameObject.SetActive(false); CM_Item obj = sub_toggleButton_cm_item; if (obj != null) { ((Component)obj).gameObject.SetActive(false); } } DescriptionPanelData descriptionData; if (feature.IsAutomated || feature.DisableModSettingsButton) { toggleButton_cm_item.SetCMItemEvents(delegate { }); sub_toggleButton_cm_item?.SetCMItemEvents(delegate { }); } else { descriptionData = new DescriptionPanelData { Title = feature.FeatureInternal.DisplayName, Description = feature.FeatureInternal.DisplayDescription, CriticalInfo = feature.FeatureInternal.CriticalInfo, FeatureOrigin = feature.FeatureInternal.AsmDisplayName }; ((CM_Item)(object)cm_settingsItem).SetCMItemEvents(delegate { }, OnButtonHover); ((CM_Item)(object)cm_settingsItem2)?.SetCMItemEvents(delegate { }, OnButtonHover); toggleButton_cm_item.SetCMItemEvents(OnButtonPress, OnButtonHover); sub_toggleButton_cm_item?.SetCMItemEvents(OnButtonPress, OnButtonHover); } SettingsCreationHelper.SetFeatureItemTextAndColor(feature, toggleButton_cm_item, toggleButtonText); if ((Object)(object)sub_toggleButton_cm_item != (Object)null) { SettingsCreationHelper.SetFeatureItemTextAndColor(feature, sub_toggleButton_cm_item, sub_toggleButtonText); } SettingsCreationHelper.CreateRundownInfoTextForItem(cm_settingsItem, feature.AppliesToRundowns); if ((Object)(object)cm_settingsItem2 != (Object)null) { SettingsCreationHelper.CreateRundownInfoTextForItem(cm_settingsItem2, feature.AppliesToRundowns); } ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); if (cm_settingsItem2 != null) { ((RectTransformComp)cm_settingsItem2).ForcePopupLayer(true, (GameObject)null); } if (feature.HasAdditionalSettings) { foreach (FeatureSettingsHelper settingsHelper in feature.SettingsHelpers) { SettingsCreationHelper.SetupItemsForSettingsHelper(settingsHelper, subMenu ?? groupSubMenu); } } subMenu?.Build(); void OnButtonHover(int id, bool hovering) { if (hovering) { PageSettingsData.TheDescriptionPanel.Show(descriptionData); } else { PageSettingsData.TheDescriptionPanel.Hide(); } } } catch (Exception ex) { FeatureLogger.Exception(ex); } void OnButtonPress(int id) { FeatureManager.ToggleFeature(feature); SettingsCreationHelper.SetFeatureItemTextAndColor(feature, toggleButton_cm_item, toggleButtonText); if ((Object)(object)sub_toggleButton_cm_item != (Object)null) { SettingsCreationHelper.SetFeatureItemTextAndColor(feature, sub_toggleButton_cm_item, sub_toggleButtonText); } } } } public class KeyListener : MonoBehaviour { public static readonly KeyCode[] allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode)); [HideFromIl2Cpp] public KeySetting ActiveKeySetting { get; private set; } public KeyListener(IntPtr ptr) : base(ptr) { } [HideFromIl2Cpp] public void StartListening(KeySetting setting) { FeatureLogger.Debug("[KeyListener] Starting listener, disabling all input!"); setting.UpdateKeyText("<#F00>" + ArchiveLocalizationService.GetById(51u, "Press any Key!") + ""); ActiveKeySetting = setting; FeatureManager.EnableAutomatedFeature(typeof(InputDisabler)); ((Behaviour)this).enabled = true; } public void StopListening() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) KeyCode key = (KeyCode)0; if (ActiveKeySetting != null) { key = ActiveKeySetting.GetCurrent(); } OnKeyFound(key); } private void OnKeyFound(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if ((int)key == 27) { key = (KeyCode)0; } if (ActiveKeySetting != null) { FeatureLogger.Debug($"[{"KeyListener"}] Key found (\"{key}\"), re-enabling all input!"); ActiveKeySetting.SetValue(key); } ActiveKeySetting = null; FeatureManager.DisableAutomatedFeature(typeof(InputDisabler)); ((Behaviour)this).enabled = false; } public void Update() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (ActiveKeySetting == null) { OnKeyFound((KeyCode)0); return; } KeyCode[] array = allKeys; foreach (KeyCode val in array) { if (Input.GetKeyDown(val)) { OnKeyFound(val); break; } } } public void OnDisable() { StopListening(); } public void OnDestroy() { if (!Feature.IsApplicationQuitting) { StopListening(); } } internal static void ActivateKeyListener(KeySetting setting) { GameObject gameObject = ((Component)PageSettingsData.SettingsPageInstance).gameObject; (gameObject.GetComponent() ?? gameObject.AddComponent()).StartListening(setting); } } public class OnDisabledListener : MonoBehaviour { public Action OnDisabledSelf; public OnDisabledListener(IntPtr ptr) : base(ptr) { } public void OnDisable() { OnDisabledSelf?.Invoke(((Component)this).gameObject); } } public class OnEnabledListener : MonoBehaviour { public Action OnEnabledSelf; public OnEnabledListener(IntPtr ptr) : base(ptr) { } public void OnEnable() { OnEnabledSelf?.Invoke(((Component)this).gameObject); } } public class JankTextMeshProUpdaterOnce : MonoBehaviour { public JankTextMeshProUpdaterOnce(IntPtr ptr) : base(ptr) { } public void Awake() { UpdateMesh(((Component)this).GetComponent()); Object.Destroy((Object)(object)this); } public static void UpdateMesh(TextMeshPro textMesh) { if (Is.R4 || Is.R5) { ForceUpdateMesh(textMesh); } } public static void ForceUpdateMesh(TextMeshPro textMesh) { if (!((Object)(object)textMesh == (Object)null)) { if (Is.R6OrLater) { A_TextMeshPro_ForceMeshUpdate.Invoke(textMesh, true, false); } else { A_TextMeshPro_ForceMeshUpdate.Invoke(textMesh); } } } [HideFromIl2Cpp] public static JankTextMeshProUpdaterOnce Apply(TextMeshPro tmp) { if (Is.R4 || Is.R5) { return ((Component)tmp).gameObject.AddComponent(); } return null; } } public class JankCellMenuSettingsItemLocalizedTextUpdaterWrapper : MonoBehaviour, ILocalizedTextUpdater { private Func _titleText; private Func<(string, string)> _tooltipText; private CM_SettingsItem _settingsItem; private TextMeshPro _title; [HideFromIl2Cpp] public static void CreateAndApply(CM_SettingsItem settingsItem, Func titleText, Func<(string, string)> tooltipText = null) { if (!((Object)(object)settingsItem == (Object)null) && titleText != null) { ((Component)settingsItem).gameObject.AddComponent().Setup(settingsItem, titleText, tooltipText); } } public JankCellMenuSettingsItemLocalizedTextUpdaterWrapper(IntPtr ptr) : base(ptr) { } [HideFromIl2Cpp] private void Setup(CM_SettingsItem settingsItem, Func titleText, Func<(string, string)> tooltipText = null) { _settingsItem = settingsItem; _titleText = titleText; _tooltipText = tooltipText; _title = ((Component)((Component)settingsItem).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).gameObject.GetComponent(); UpdateText(); ArchiveLocalizationService.AddTextUpdater(this); } private void OnDestroy() { ArchiveLocalizationService.RemoveTextUpdater(this); } public void UpdateText() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if (!((Object)(object)_settingsItem == (Object)null)) { string text = _titleText(); ((TMP_Text)_title).text = text; ((TMP_Text)_title).SetText(text, true); if (_tooltipText != null) { TooltipInfo val = new TooltipInfo(); val.UseTooptip = true; TooltipInfo val2 = val; (string, string) tuple = _tooltipText(); val.TooltipHeader = tuple.Item1; val2.TooltipText = tuple.Item2; ((CM_Item)_settingsItem).TooltipInfo = val; } } } } internal class ColorPicker : IDisposable { private readonly CM_ScrollWindow _backgroundPanel; private readonly TextMeshPro _headerText; private SpriteRenderer _previewRenderer; private ColorSetting _currentSetting; private SpriteRenderer _currentSetting_previewRenderer; private CM_SettingsInputField _hexField; private float _hue = 0.5f; private float _saturation = 0.5f; private float _value = 0.5f; private readonly CM_SettingScrollReceiver _sr_hue; private readonly CM_SettingScrollReceiver _sr_saturation; private readonly CM_SettingScrollReceiver _sr_value; public bool IsActive { get { CM_ScrollWindow backgroundPanel = _backgroundPanel; bool? obj; if (backgroundPanel == null) { obj = null; } else { GameObject gameObject = ((Component)backgroundPanel).gameObject; obj = ((gameObject != null) ? new bool?(gameObject.activeInHierarchy) : null); } bool? flag = obj; return flag.GetValueOrDefault(); } } public float Hue { get { return _hue; } private set { _hue = value; } } public float Saturation { get { return _saturation; } private set { _saturation = value; } } public float Value { get { return _value; } private set { _value = value; } } public Color CurrentColor => Color.HSVToRGB(Hue, Saturation, Value); public ColorPicker() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) _backgroundPanel = SettingsCreationHelper.CreateScrollWindow(ArchiveLocalizationService.GetById(41u, "Color Picker")); ((Component)_backgroundPanel).transform.localPosition = ((Component)_backgroundPanel).transform.localPosition + new Vector3(1050f, 0f, 0f); GameObject val2 = Object.Instantiate(PageSettingsData.SettingsItemPrefab, ((Component)_backgroundPanel).transform); _headerText = ((Component)((Component)val2.GetComponentInChildren()).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).gameObject.GetComponent(); ((Graphic)_headerText).color = ORANGE; ((TMP_Text)_headerText).SetText("Header Text", true); RectTransform component = ((Component)_headerText).gameObject.GetComponent(); component.sizeDelta = new Vector2(component.sizeDelta.x * 2f, component.sizeDelta.y); SettingsCreationHelper.CreateHeader(string.Empty, out var cm_settingsItem, null, bold: true, null, delegate { }, delegate { }, placeInNoMenu: true); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); ((Component)(object)cm_settingsItem).SafeDestroy(); Transform childWithExactName = ((Component)cm_settingsItem).transform.GetChildWithExactName("Background"); _previewRenderer = ((Component)childWithExactName).GetComponent(); Action onValueUpdated = delegate(float val) { Hue = val; UpdatePreviewColor(); }; Func getValue = (float oldValOrZero) => Hue; SettingsCreationHelper.CreateSimpleNumberField(ArchiveLocalizationService.GetById(52u, "Hue"), 0.5f, onValueUpdated, out var cm_settingsItem2, out var cm_settingsInputField, out _sr_hue, getValue, new FSSlider(0f, 1f), null, null, placeInNoMenu: true); Action onValueUpdated2 = delegate(float val) { Saturation = val; UpdatePreviewColor(); }; Func getValue2 = (float oldValOrZero) => Saturation; SettingsCreationHelper.CreateSimpleNumberField(ArchiveLocalizationService.GetById(53u, "Saturation"), 0.5f, onValueUpdated2, out var cm_settingsItem3, out cm_settingsInputField, out _sr_saturation, getValue2, new FSSlider(0f, 1f), null, null, placeInNoMenu: true); Action onValueUpdated3 = delegate(float val) { Value = val; UpdatePreviewColor(); }; Func getValue3 = (float oldValOrZero) => Value; SettingsCreationHelper.CreateSimpleNumberField(ArchiveLocalizationService.GetById(54u, "Value"), 0.5f, onValueUpdated3, out var cm_settingsItem4, out cm_settingsInputField, out _sr_value, getValue3, new FSSlider(0f, 1f), null, null, placeInNoMenu: true); SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(42u, "Apply Color"), $"<{GREEN.ToHexString()}>{ArchiveLocalizationService.GetById(43u, "Apply")}", OnApplyPress, out var cmItem, null, placeInNoMenu: true); SettingsCreationHelper.CreateSpacer(out var cm_settingsItem5, null, placeInNoMenu: true); SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(44u, "Close Color Picker"), ArchiveLocalizationService.GetById(45u, "Cancel"), OnCancelPress, out var cmItem2, null, placeInNoMenu: true); SettingsCreationHelper.CreateSpacer(out var cm_settingsItem6, null, placeInNoMenu: true); SettingsCreationHelper.CreateSpacer(out var cm_settingsItem7, null, placeInNoMenu: true); Action onValueUpdated4 = delegate(string val) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) FeatureLogger.Debug("Setting Value to: " + val); if (TryGetColorFromHexString(val, out var color)) { Color.RGBToHSV(color, ref _hue, ref _saturation, ref _value); ResetAllScrollReceivers(); UpdatePreviewColor(); } }; Func getValue4 = (string oldValOrZero) => CurrentColor.ToHexString(); SettingsCreationHelper.CreateSimpleTextField(ArchiveLocalizationService.GetById(46u, "Hex Code"), "#COLORS", onValueUpdated4, out var cm_settingsItem8, out _hexField, getValue4, 7, null, null, placeInNoMenu: true); SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(47u, "Copy Color Hex Code"), ArchiveLocalizationService.GetById(49u, "Copy"), OnCopyColor, out var cmItem3, null, placeInNoMenu: true); SettingsCreationHelper.CreateSimpleButton(ArchiveLocalizationService.GetById(48u, "Paste Color Hex Code"), ArchiveLocalizationService.GetById(50u, "Paste"), OnPasteColor, out var cmItem4, null, placeInNoMenu: true); _backgroundPanel.SetContentItems(new List { val2.GetComponentInChildren(), ((Component)cm_settingsItem).GetComponentInChildren(), ((Component)cm_settingsItem2).GetComponentInChildren(), ((Component)cm_settingsItem3).GetComponentInChildren(), ((Component)cm_settingsItem4).GetComponentInChildren(), ((Component)cmItem).GetComponentInChildren(), ((Component)cm_settingsItem5).GetComponentInChildren(), ((Component)cmItem2).GetComponentInChildren(), ((Component)cm_settingsItem6).GetComponentInChildren(), ((Component)cm_settingsItem7).GetComponentInChildren(), ((Component)cm_settingsItem8).GetComponentInChildren(), ((Component)cmItem3).GetComponentInChildren(), ((Component)cmItem4).GetComponentInChildren() }.ToIL2CPPListIfNecessary(), 5f); AddToAllSettingsWindows(_backgroundPanel); } private static bool TryGetColorFromHexString(string colorString, out Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) color = Color.white; colorString = colorString?.Trim(); if (string.IsNullOrWhiteSpace(colorString)) { return false; } if (colorString.Length > 7) { return false; } colorString = SColorExtensions.EnsureLeadingHash(colorString); if (ColorUtility.TryParseHtmlString(colorString, ref color)) { return true; } return false; } private void OnPasteColor() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) string systemCopyBuffer = GUIUtility.systemCopyBuffer; if (TryGetColorFromHexString(systemCopyBuffer, out var color)) { FeatureLogger.Info("Pasted in color from clipboard: " + systemCopyBuffer); Color.RGBToHSV(color, ref _hue, ref _saturation, ref _value); ResetAllScrollReceivers(); } } private void OnCopyColor() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) string text2 = (GUIUtility.systemCopyBuffer = Color.HSVToRGB(Hue, Saturation, Value).ToHexString()); FeatureLogger.Info("Copied color to clipboard: " + text2); } private void OnApplyPress() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (_currentSetting != null) { Color currentColor = CurrentColor; _currentSetting.SetValue(currentColor.ToSColor()); _currentSetting_previewRenderer.color = currentColor; Hide(); } } private void OnCancelPress() { Hide(); } private void ResetAllScrollReceivers() { ResetScrollReceiver(_sr_hue, Hue); ResetScrollReceiver(_sr_saturation, Saturation); ResetScrollReceiver(_sr_value, Value); } private void ResetScrollReceiver(CM_SettingScrollReceiver receiver, float value) { CM_SettingScrollReceiver_GetFloatDisplayText_Patch.OverrideDisplayValue = true; CM_SettingScrollReceiver_GetFloatDisplayText_Patch.Value = value; if (receiver != null) { receiver.ResetValue(); } } private void UpdatePreviewColor() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _previewRenderer.color = CurrentColor; _hexField.ResetValue(); } public void Show(ColorSetting setting) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) ((Component)_backgroundPanel).gameObject.SetActive(true); Transform val = CustomExtensions.FindChildRecursive(((Component)(CM_SettingsItem)setting.CM_SettingsItem).transform, "ColorPreview", true); _currentSetting = setting; _currentSetting_previewRenderer = ((Component)val).GetComponent(); ((TMP_Text)_headerText).SetText(SettingsCreationHelper.GetNameForSetting(setting), true); JankTextMeshProUpdaterOnce.Apply(_headerText); Color.RGBToHSV(((SColor)setting.GetValue()).ToUnityColor(), ref _hue, ref _saturation, ref _value); ResetAllScrollReceivers(); UpdatePreviewColor(); } public void Hide() { ((Component)_backgroundPanel).gameObject.SetActive(false); } public void Dispose() { RemoveFromAllSettingsWindows(_backgroundPanel); ((Component)(object)_backgroundPanel).SafeDestroyGO(); } } public class DescriptionPanelData { public string Title; public string Description; public string CriticalInfo; public string FeatureOrigin; public bool HasDescription => !string.IsNullOrWhiteSpace(Description); } internal class DescriptionPanel : IDisposable { private CM_ScrollWindow _backgroundPanel; private TextMeshPro _headerText; private TextMeshPro _contentText; private TextMeshPro _infoL1Text; private TextMeshPro _infoL2Text; public DescriptionPanel() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) _backgroundPanel = SettingsCreationHelper.CreateScrollWindow(ArchiveLocalizationService.GetById(40u, "Description")); ((Component)_backgroundPanel).transform.localPosition = ((Component)_backgroundPanel).transform.localPosition + new Vector3(1050f, 0f, 0f); CreateItem("Header Text", ORANGE, ((Component)_backgroundPanel).transform, out var scrollWindowContent, out var rectTrans, out _headerText); rectTrans.sizeDelta = new Vector2(rectTrans.sizeDelta.x * 2f, rectTrans.sizeDelta.y); CreateItem("Content Text", WHITE_GRAY, ((Component)_backgroundPanel).transform, out var scrollWindowContent2, out var rectTrans2, out _contentText); rectTrans2.sizeDelta = new Vector2(rectTrans2.sizeDelta.x * 2f, rectTrans2.sizeDelta.y * 10f); CreateItem("Info One", RED, ((Component)_backgroundPanel).transform, out var scrollWindowContent3, out var rectTrans3, out _infoL1Text); rectTrans3.sizeDelta = new Vector2(rectTrans3.sizeDelta.x * 2f, rectTrans3.sizeDelta.y); ((Transform)rectTrans3).localPosition = ((Transform)rectTrans3).localPosition + new Vector3(0f, -660f, 0f); CreateItem("Info Two", ORANGE, ((Component)_backgroundPanel).transform, out var scrollWindowContent4, out var rectTrans4, out _infoL2Text); rectTrans4.sizeDelta = new Vector2(rectTrans4.sizeDelta.x * 2f, rectTrans4.sizeDelta.y); ((Transform)rectTrans4).localPosition = ((Transform)rectTrans4).localPosition + new Vector3(0f, -640f, 0f); _backgroundPanel.SetContentItems(new List { scrollWindowContent, scrollWindowContent2, scrollWindowContent3, scrollWindowContent4 }.ToIL2CPPListIfNecessary(), 5f); AddToAllSettingsWindows(_backgroundPanel); } private static void CreateItem(string text, Color col, Transform parent, out iScrollWindowContent scrollWindowContent, out RectTransform rectTrans, out TextMeshPro tmp) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate(PageSettingsData.SettingsItemPrefab, parent); tmp = ((Component)((Component)val.GetComponentInChildren()).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).gameObject.GetComponent(); ((Graphic)tmp).color = col; ((TMP_Text)tmp).SetText(text, true); rectTrans = ((Component)tmp).gameObject.GetComponent(); scrollWindowContent = val.GetComponentInChildren(); } public void Dispose() { RemoveFromAllSettingsWindows(_backgroundPanel); ((Component)(object)_backgroundPanel).SafeDestroyGO(); } public void Show(DescriptionPanelData data) { if (!PageSettingsData.TheColorPicker.IsActive) { ((Component)_backgroundPanel).gameObject.SetActive(true); ((TMP_Text)_headerText).SetText(data.Title, true); JankTextMeshProUpdaterOnce.ForceUpdateMesh(_headerText); ((TMP_Text)_contentText).SetText(data.Description, true); JankTextMeshProUpdaterOnce.ForceUpdateMesh(_contentText); ((TMP_Text)_infoL1Text).SetText(data.CriticalInfo ?? string.Empty, true); JankTextMeshProUpdaterOnce.ForceUpdateMesh(_infoL1Text); ((TMP_Text)_infoL2Text).SetText(data.FeatureOrigin ?? string.Empty, true); JankTextMeshProUpdaterOnce.ForceUpdateMesh(_infoL2Text); } } public void Hide() { ((Component)_backgroundPanel).gameObject.SetActive(false); } } public static class PageSettingsData { internal static GameObject SettingsItemPrefab { get; set; } internal static CM_ScrollWindow MainModSettingsScrollWindow { get; set; } internal static List ScrollWindowContentElements { get; set; } = new List(); internal static List AllSubmenuScrollWindows { get; set; } = new List(); internal static Transform MainScrollWindowTransform { get; set; } public static CM_ScrollWindow PopupWindow { get; internal set; } internal static CM_PageSettings SettingsPageInstance { get; set; } internal static DescriptionPanel TheDescriptionPanel { get; set; } internal static ColorPicker TheColorPicker { get; set; } internal static SearchMainPage TheSearchMenu { get; set; } internal static Attribution AttributionPage { get; set; } internal static CM_Item MainModSettingsButton { get; set; } internal static GameObject SubMenuButtonPrefab { get; set; } internal static HashSet TheStaticSettingsInputFieldJankRemoverHashSet2000 { get; private set; } = new HashSet(); public static MainMenuGuiLayer MMGuiLayer { get; internal set; } public static GameObject ScrollWindowPrefab { get; internal set; } public static RectTransform MovingContentHolder { get; internal set; } } public static class UIHelper { public static GameObject PopupItemPrefab { get; private set; } internal static void Setup() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate(PageSettingsData.SettingsItemPrefab); PopupItemPrefab = Object.Instantiate(GOUtil.SpawnChildAndGetComp(val.GetComponentInChildren().m_enumDropdownInputPrefab, val.transform).m_popupItemPrefab); Object.DontDestroyOnLoad((Object)(object)PopupItemPrefab); ((Object)PopupItemPrefab).hideFlags = (HideFlags)61; PopupItemPrefab.transform.position = new Vector3(-3000f, -3000f, 0f); Object.Destroy((Object)(object)val); } } [UsedImplicitly(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers)] public class CustomStringReceiver : Object { private readonly Func _getValue; private readonly Action _setValue; public CustomStringReceiver(IntPtr ptr) : base(ptr) { } public CustomStringReceiver(Func getFunc, Action setAction) : base(LoaderWrapper.ClassInjector.DerivedConstructorPointer()) { LoaderWrapper.ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this); _getValue = getFunc; _setValue = setAction; } private string GetStringValue(eCellSettingID setting) { return _getValue?.Invoke() ?? string.Empty; } private string SetStringValue(eCellSettingID setting, string value) { _setValue(value); return value; } } [UsedImplicitly(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers)] public class CustomIntReceiver : Object { private readonly Func _getValue; private readonly Action _setValue; public CustomIntReceiver(IntPtr ptr) : base(ptr) { } public CustomIntReceiver(Func getFunc, Action setAction) : base(LoaderWrapper.ClassInjector.DerivedConstructorPointer()) { LoaderWrapper.ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this); _getValue = getFunc; _setValue = setAction; } private int GetIntValue(eCellSettingID setting) { return _getValue?.Invoke() ?? 0; } private int SetIntValue(eCellSettingID setting, int value) { _setValue(value); return value; } } [UsedImplicitly(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers)] public class CustomFloatReceiver : Object { private readonly Func _getValue; private readonly Action _setValue; public CustomFloatReceiver(IntPtr ptr) : base(ptr) { } public CustomFloatReceiver(Func getFunc, Action setAction) : base(LoaderWrapper.ClassInjector.DerivedConstructorPointer()) { LoaderWrapper.ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this); _getValue = getFunc; _setValue = setAction; } private float GetFloatValue(eCellSettingID setting) { return _getValue?.Invoke() ?? 0f; } private float SetFloatValue(eCellSettingID setting, float value) { _setValue(value); return value; } } internal class SearchMainPage : IDisposable { public class SearchQuery { public string QueryString { get; set; } = string.Empty; public bool TitleContains { get; set; } = true; public bool DesciptionContains { get; set; } = true; public bool SubSettingsTitleContains { get; set; } = true; public bool SubSettingsDesciptionContains { get; set; } } private readonly SubMenu _searchMainSubmenu; private readonly DynamicSubMenu _resultsMenu; public SearchQuery Query { get; set; } = new SearchQuery(); internal SearchMainPage() { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) _searchMainSubmenu = new SubMenu(ArchiveLocalizationService.GetById(9u, "Search"), "Feature Search Menu"); Query.TitleContains = Settings.Search.SearchTitles; Query.DesciptionContains = Settings.Search.SearchDescriptions; Query.SubSettingsTitleContains = Settings.Search.SearchSubSettingsTitles; Query.SubSettingsDesciptionContains = Settings.Search.SearchSubSettingsDescription; SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(11u, "Search All Features"), Color.magenta); SettingsCreationHelper.CreateSubMenuControls(_searchMainSubmenu, null, ArchiveLocalizationService.GetById(9u, "Search"), null, ArchiveLocalizationService.GetById(9u, "Search")); SettingsCreationHelper.CreateSpacer(); SettingsCreationHelper.CreateSimpleTextField(ArchiveLocalizationService.GetById(12u, "Query") + ":", Query.QueryString, OnSearchValueUpdate, null, 32, _searchMainSubmenu); SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(10u, "Search Options") + ":", DISABLED, bold: true, _searchMainSubmenu); SettingsCreationHelper.CreateSimpleToggle(ArchiveLocalizationService.GetById(16u, "Title"), Query.TitleContains, TitleContainsToggled, _searchMainSubmenu); SettingsCreationHelper.CreateSimpleToggle(ArchiveLocalizationService.GetById(17u, "Description"), Query.DesciptionContains, DescriptionContainsToggled, _searchMainSubmenu); SettingsCreationHelper.CreateSimpleToggle(ArchiveLocalizationService.GetById(18u, "SubSettings Title"), Query.SubSettingsTitleContains, SubTitleContainsToggled, _searchMainSubmenu); SettingsCreationHelper.CreateSimpleToggle(ArchiveLocalizationService.GetById(19u, "SubSettings Description"), Query.SubSettingsDesciptionContains, SubDescriptionContainsToggled, _searchMainSubmenu); SettingsCreationHelper.CreateSpacer(_searchMainSubmenu); _resultsMenu = new DynamicSubMenu(ArchiveLocalizationService.GetById(13u, "Search Results"), BuildSearchResultsMenu, "Search Results Menu"); SettingsCreationHelper.CreateSubMenuControls(_resultsMenu, null, ArchiveLocalizationService.GetById(14u, "Start Search"), _searchMainSubmenu, ArchiveLocalizationService.GetById(13u, "Search Results"), null, "<<< Back <<<", ArchiveLocalizationService.GetById(36u)); SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(15u, "Search is WIP, Toggling settings here does not visually update the normal buttons currently!"), DISABLED, bold: true, _searchMainSubmenu); using (_resultsMenu.GetPersistentContentAdditionToken()) { SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.GetById(15u, "Search is WIP, Toggling settings here does not visually update the normal buttons currently!"), DISABLED, bold: true, _resultsMenu); } _searchMainSubmenu.Build(); } private void OnSearchValueUpdate(string search) { Query.QueryString = search; } public void Dispose() { _searchMainSubmenu.Dispose(); _resultsMenu.Dispose(); } private void TitleContainsToggled(bool state) { Settings.Search.SearchTitles = state; Query.TitleContains = state; } private void DescriptionContainsToggled(bool state) { Settings.Search.SearchDescriptions = state; Query.DesciptionContains = state; } private void SubTitleContainsToggled(bool state) { Settings.Search.SearchSubSettingsTitles = state; Query.SubSettingsTitleContains = state; } private void SubDescriptionContainsToggled(bool state) { Settings.Search.SearchSubSettingsDescription = state; Query.SubSettingsDesciptionContains = state; } private void BuildSearchResultsMenu(DynamicSubMenu menu) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) IEnumerable enumerable = SearchForFeatures(Query); int num = enumerable.Count(); string title = ((num != 0) ? ArchiveLocalizationService.Format(21u, "Found {0} Feature{1}!", num, (num > 1) ? "s" : string.Empty) : ArchiveLocalizationService.GetById(20u, "Nothing found! :(")); SettingsCreationHelper.CreateHeader(ArchiveLocalizationService.Format(22u, "Query: {0}", Query.QueryString), WHITE_GRAY, bold: true, menu); SettingsCreationHelper.CreateHeader(title, (num == 0) ? RED : GREEN, bold: true, menu); SettingsCreationHelper.CreateSpacer(); foreach (Feature item in enumerable) { CM_PageSettings__Setup__Patch.SetupEntriesForFeature(item, menu); } } private bool IncludeHidden(Feature f) { if (Feature.DevMode) { return true; } return !f.IsHidden; } private bool IncludeHidden(FeatureSetting fs) { if (Feature.DevMode) { return true; } return !fs.HideInModSettings; } private bool ContainsIgnoreCase(string text, string value, bool stripTMPTags = true) { if (string.IsNullOrWhiteSpace(text)) { return false; } if (stripTMPTags) { text = Utils.StripTMPTagsRegex(text); } return text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0; } private IEnumerable SearchForFeatures(SearchQuery query) { IEnumerable source = InitSingletonBase.Instance.RegisteredFeatures.AsEnumerable(); IEnumerable enumerable = new HashSet(); if (query.TitleContains) { enumerable = enumerable.Concat(source.Where((Feature f) => IncludeHidden(f) && ContainsIgnoreCase(f.Name, query.QueryString))); } if (query.DesciptionContains) { enumerable = enumerable.Concat(source.Where((Feature f) => IncludeHidden(f) && ContainsIgnoreCase(f.Description, query.QueryString))); } if (query.SubSettingsTitleContains) { enumerable = enumerable.Concat(source.Where((Feature f) => f.SettingsHelpers.Any((FeatureSettingsHelper fsh) => fsh.Settings.Any((FeatureSetting fs) => IncludeHidden(fs) && ContainsIgnoreCase(fs.DisplayName, query.QueryString))))); } if (query.SubSettingsDesciptionContains) { enumerable = enumerable.Concat(source.Where((Feature f) => f.SettingsHelpers.Any((FeatureSettingsHelper fsh) => fsh.Settings.Any((FeatureSetting fs) => IncludeHidden(fs) && ContainsIgnoreCase(fs.Description, query.QueryString))))); } return from f in enumerable.Distinct() orderby f.Name select f; } } internal static class SettingsCreationHelper { private static class OnlyTouchOnR5AndLater { internal static Dictionary _styleDisplayMap = new Dictionary { { FSSlider.SliderStyle.IntMinMax, Utils.GetEnumFromName("Percent") }, { FSSlider.SliderStyle.FloatPercent, Utils.GetEnumFromName("Percent") }, { FSSlider.SliderStyle.FloatNoDecimal, Utils.GetEnumFromName("Decimal0") }, { FSSlider.SliderStyle.FloatOneDecimal, Utils.GetEnumFromName("Decimal1") }, { FSSlider.SliderStyle.FloatTwoDecimal, Utils.GetEnumFromName("Decimal2") } }; } public const string COLOR_PREVIEW_NAME = "ColorPreview"; private static IValueAccessor _A_m_floatReceiver = AccessorBase.GetValueAccessor("m_floatReceiver"); private static IValueAccessor _A_m_intReceiver = AccessorBase.GetValueAccessor("m_intReceiver"); private static IValueAccessor _A_m_inputType = AccessorBase.GetValueAccessor("m_inputType"); private static IValueAccessor _A_m_scrollRange = AccessorBase.GetValueAccessor("m_scrollRange"); public static void SetupItemsForSettingsHelper(FeatureSettingsHelper settingsHelper, SubMenu placeIntoMenu = null) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) foreach (FeatureSetting setting11 in settingsHelper.Settings) { if (setting11.HeaderAbove != null) { CreateHeader(setting11.HeaderAbove.Title, setting11.HeaderAbove.Color.ToUnityColor(), setting11.HeaderAbove.Bold, placeIntoMenu); } else if (setting11.SeparatorAbove) { SubMenu subMenu = placeIntoMenu; CreateSeparator(null, subMenu); } else if (setting11.SpacerAbove) { CreateSpacer(placeIntoMenu); } if (setting11.HideInModSettings && !Feature.DevMode) { continue; } if (!(setting11 is EnumListSetting setting)) { if (!(setting11 is ColorSetting setting2)) { if (!(setting11 is StringSetting setting3)) { if (!(setting11 is BoolSetting setting4)) { if (!(setting11 is EnumSetting setting5)) { if (!(setting11 is GenericListSetting setting6)) { if (!(setting11 is GenericDictionarySetting setting7)) { if (!(setting11 is NumberSetting setting8)) { if (!(setting11 is KeySetting setting9)) { if (!(setting11 is ButtonSetting setting10)) { if (!(setting11 is LabelSetting ls)) { SubmenuSetting submenuSetting = setting11 as SubmenuSetting; if (submenuSetting != null) { SubMenu subMenu2 = ((!submenuSetting.UseDynamicMenu) ? new SubMenu(submenuSetting.DisplayName, submenuSetting.Identifier) : new DynamicSubMenu(submenuSetting.DisplayName, delegate(DynamicSubMenu dsm) { SetupItemsForSettingsHelper(submenuSetting.SettingsHelper, dsm); }, submenuSetting.Identifier)); DescriptionPanelData descriptionPanelData = new DescriptionPanelData { Title = submenuSetting.DisplayName, Description = submenuSetting.Description, CriticalInfo = submenuSetting.Helper.Feature.FeatureInternal.CriticalInfo, FeatureOrigin = submenuSetting.Helper.Feature.FeatureInternal.AsmDisplayName }; string displayName = submenuSetting.DisplayName; SubMenu subMenu = placeIntoMenu; DescriptionPanelData descriptionPanelData2 = descriptionPanelData; CreateSubMenuControls(subMenu2, null, displayName, subMenu, null, descriptionPanelData2); if (!submenuSetting.UseDynamicMenu) { SetupItemsForSettingsHelper(submenuSetting.SettingsHelper, subMenu2); subMenu2.Build(); } } else { string dEBUG_Path = setting11.DEBUG_Path; SubMenu subMenu = placeIntoMenu; CreateHeader(dEBUG_Path, null, bold: true, subMenu); } } else { CreateLabel(ls, placeIntoMenu); } } else { CreateButton(setting10, placeIntoMenu); } } else { CreateKeySetting(setting9, placeIntoMenu); } } else { CreateNumberSetting(setting8, placeIntoMenu); } } else { CreateGenericDictionarySetting(setting7, placeIntoMenu); } } else { CreateGenericListSetting(setting6, placeIntoMenu); } } else { CreateEnumSetting(setting5, placeIntoMenu); } } else { CreateBoolSetting(setting4, placeIntoMenu); } } else { CreateStringSetting(setting3, placeIntoMenu); } } else { CreateColorSetting(setting2, placeIntoMenu); } } else { CreateEnumListSetting(setting, placeIntoMenu); } } } public static void CreateKeySetting(KeySetting setting, SubMenu subMenu) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) CreateSimpleButton(GetNameForSetting(setting), setting.Key, delegate { KeyListener.ActivateKeyListener(setting); }, out var cmItem, out var buttonTmp, subMenu); ((Graphic)buttonTmp).color = ORANGE; setting.KeyTextUpdated = delegate(string key) { ((TMP_Text)buttonTmp).SetText("[ " + key + " ]", true); JankTextMeshProUpdaterOnce.UpdateMesh(buttonTmp); }; setting.CM_SettingsItem = cmItem; CreateRundownInfoTextForItem(cmItem, setting.RundownHint); CreateFSHoverAndSetButtonAction(setting, cmItem, null); } public static void CreateLabel(LabelSetting ls, SubMenu placeIntoMenu) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) CreateHeader(ls.LabelText, out var cm_settingsItem, WHITE_GRAY, bold: false, placeIntoMenu); ls.FComponent.PrimaryText = ((Component)((Component)cm_settingsItem).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).GetComponent(); } public static CM_ScrollWindow CreateScrollWindow(string title) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) CM_ScrollWindow obj = ((Il2CppObjectBase)(object)((GuiLayer)PageSettingsData.MMGuiLayer).AddRectComp(PageSettingsData.ScrollWindowPrefab, (GuiAnchor)1, new Vector2(420f, -200f), (Transform)(object)PageSettingsData.MovingContentHolder)).TryCastTo(); ((CM_PopupOverlay)obj).SetupCMItem(); ((RectTransformComp)obj).SetSize(new Vector2(1020f, 900f)); ((RectTransformComp)obj).SetVisible(false); obj.SetHeader(title); return obj; } public static void CreateSimpleNumberField(string labelText, float initialValue, Action onValueUpdated, Func getValue = null, FSSlider slider = null, SubMenu placeIntoMenu = null, DescriptionPanelData descriptionPanelData = null, bool placeInNoMenu = false) { CreateSimpleNumberField(labelText, initialValue, onValueUpdated, out var _, out var _, out var _, getValue, slider, placeIntoMenu, descriptionPanelData, placeInNoMenu); } public static void CreateSimpleNumberField(string labelText, float initialValue, Action onValueUpdated, out CM_SettingsItem cm_settingsItem, out CM_SettingsInputField cm_settingsInputField, out CM_SettingScrollReceiver cm_settingScrollReceiver, Func getValue = null, FSSlider slider = null, SubMenu placeIntoMenu = null, DescriptionPanelData descriptionPanelData = null, bool placeInNoMenu = false) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) Action onValueUpdated2 = delegate(string val) { if (float.TryParse(val, out var result3)) { onValueUpdated?.Invoke(result3); } }; float result2; Func getValue2 = (string val) => float.TryParse(val, out result2) ? (getValue?.Invoke(result2).ToString() ?? result2.ToString()) : val; CreateSimpleTextField(labelText, initialValue.ToString(), onValueUpdated2, out cm_settingsItem, out cm_settingsInputField, getValue2, 32, placeIntoMenu, descriptionPanelData, placeInNoMenu); cm_settingScrollReceiver = null; if (slider != null) { cm_settingScrollReceiver = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_sliderInputPrefab, cm_settingsItem.m_inputAlign); eSettingInputType value = (eSettingInputType)((slider.Style == FSSlider.SliderStyle.IntMinMax) ? 5 : 4); _A_m_inputType.Set(cm_settingScrollReceiver, value); if (Is.R5OrLater) { SetSliderFloatDisplayStyle(cm_settingScrollReceiver, slider.Style); } CustomFloatReceiver customFloatReceiver = new CustomFloatReceiver(() => ((getValue?.Invoke(0f)).Value - slider.Min) / (slider.Max - slider.Min), delegate(float delta) { float num = (slider.Max - slider.Min) * delta + slider.Min; if (slider.Rounding != FSSlider.RoundTo.NoRounding) { num = (float)Math.Round(num, (int)slider.Rounding); } num = Math.Min(slider.Max, Math.Max(num, slider.Min)); if ((getValue?.Invoke(0f)).Value != num) { onValueUpdated?.Invoke(num); } CM_SettingScrollReceiver_GetFloatDisplayText_Patch.OverrideDisplayValue = true; CM_SettingScrollReceiver_GetFloatDisplayText_Patch.Value = num; }); cm_settingScrollReceiver.m_floatReceiver = new iFloatInputReceiver(((Il2CppObjectBase)customFloatReceiver).Pointer); _A_m_scrollRange.Set(cm_settingScrollReceiver, cm_settingScrollReceiver.m_handleLocalXPosMinMax.y - cm_settingScrollReceiver.m_handleLocalXPosMinMax.x); CM_SettingScrollReceiver_GetFloatDisplayText_Patch.OverrideDisplayValue = true; if (float.TryParse(getValue?.Invoke(0f).ToString(), out var result)) { CM_SettingScrollReceiver_GetFloatDisplayText_Patch.Value = result; } cm_settingScrollReceiver.ResetValue(); ((Component)cm_settingsInputField).gameObject.SetActive(false); } ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } public static void CreateSimpleTextField(string labelText, string initialValue, Action onValueUpdated, Func getValue = null, int maxLength = 32, SubMenu placeIntoMenu = null, DescriptionPanelData descriptionPanelData = null) { CreateSimpleTextField(labelText, initialValue, onValueUpdated, out var _, out var _, getValue, maxLength, placeIntoMenu, descriptionPanelData); } public static void CreateSimpleTextField(string labelText, string initialValue, Action onValueUpdated, out CM_SettingsItem cm_settingsItem, out CM_SettingsInputField cm_settingsInputField, Func getValue = null, int maxLength = 32, SubMenu placeIntoMenu = null, DescriptionPanelData descriptionPanelData = null, bool placeInNoMenu = false) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown bool placeInNoMenu2 = placeInNoMenu; CreateSettingsItem(labelText, out cm_settingsItem, null, null, (TooltipPositionType)0, null, placeIntoMenu, placeInNoMenu2); cm_settingsInputField = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_textInputPrefab, cm_settingsItem.m_inputAlign); CM_SettingsInputField cm_settingsInputField_bruh = cm_settingsInputField; OnDisabledListener onDisabledListener = ((Component)cm_settingsInputField).gameObject.AddComponent(); PageSettingsData.TheStaticSettingsInputFieldJankRemoverHashSet2000.Add(cm_settingsInputField); onDisabledListener.OnDisabledSelf = delegate { if (_A_CM_SettingsInputField_m_readingActive.Get(cm_settingsInputField_bruh)) { _A_CM_SettingsInputField_SetReadingActive.Invoke(cm_settingsInputField_bruh, false); cm_settingsInputField_bruh.ResetValue(); } }; StringInputSetMaxLength(cm_settingsInputField, maxLength); CreateFSHoverAndSetButtonAction(descriptionPanelData, cm_settingsItem, null); ((TMP_Text)cm_settingsInputField.m_text).SetText(initialValue ?? string.Empty, true); JankTextMeshProUpdaterOnce.Apply(cm_settingsInputField.m_text); CustomStringReceiver customStringReceiver = new CustomStringReceiver(delegate { string text = ((TMP_Text)cm_settingsInputField_bruh.m_text).text; return getValue?.Invoke(text) ?? text; }, delegate(string val) { onValueUpdated?.Invoke(val); }); cm_settingsInputField.m_stringReceiver = new iStringInputReceiver(((Il2CppObjectBase)customStringReceiver).Pointer); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } public static void CreateSimpleToggle(string labelText, bool initialState, Action onPress, SubMenu placeIntoMenu = null, string stateTrue = "<#0F0>[ On ]", string stateFalse = "<#F00>[ Off ]") { if (stateTrue == "<#0F0>[ On ]") { stateTrue = "<#0F0>[ " + ArchiveLocalizationService.GetById(6u, "On") + " ]"; } if (stateFalse == "<#F00>[ Off ]") { stateFalse = "<#F00>[ " + ArchiveLocalizationService.GetById(7u, "Off") + " ]"; } CreateSettingsItem(labelText, out var cm_settingsItem, null, null, (TooltipPositionType)0, null, placeIntoMenu); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); SetupToggleButton(cm_settingsItem, out var toggleButton_cm_item, out var buttonTmp); ((TMP_Text)buttonTmp).SetText(initialState ? stateTrue : stateFalse, true); JankTextMeshProUpdaterOnce.Apply(buttonTmp); toggleButton_cm_item.SetCMItemEvents(delegate { bool flag = ((TMP_Text)buttonTmp).text == stateTrue; flag = !flag; ((TMP_Text)buttonTmp).SetText(flag ? stateTrue : stateFalse, true); JankTextMeshProUpdaterOnce.Apply(buttonTmp); onPress?.Invoke(flag); }); } public static void CreateSimpleButton(string labelText, string buttonText, Action onPress, out CM_SettingsItem cmItem, SubMenu placeIntoMenu = null, bool placeInNoMenu = false) { CreateSimpleButton(labelText, buttonText, onPress, out cmItem, out var _, placeIntoMenu, placeInNoMenu); } public static void CreateSimpleButton(string labelText, string buttonText, Action onPress, out CM_SettingsItem cmItem, out TextMeshPro buttonTmp, SubMenu placeIntoMenu = null, bool placeInNoMenu = false) { bool placeInNoMenu2 = placeInNoMenu; CreateSettingsItem(labelText, out cmItem, null, null, (TooltipPositionType)0, null, placeIntoMenu, placeInNoMenu2); ((RectTransformComp)cmItem).ForcePopupLayer(true, (GameObject)null); SetupToggleButton(cmItem, out var toggleButton_cm_item, out buttonTmp); ((TMP_Text)buttonTmp).SetText("[ " + buttonText + " ]", true); JankTextMeshProUpdaterOnce.Apply(buttonTmp); toggleButton_cm_item.SetCMItemEvents(delegate { onPress?.Invoke(); }); } public static void CreateSimpleButton(string labelText, string buttonText, Action onPress, SubMenu placeIntoMenu = null, bool placeInNoMenu = false) { CreateSimpleButton(labelText, buttonText, onPress, out var _, placeIntoMenu, placeInNoMenu); } public static void CreateSpacer(out CM_SettingsItem cm_settingsItem, SubMenu subMenu = null, bool placeInNoMenu = false) { string empty = string.Empty; bool placeInNoMenu2 = placeInNoMenu; CreateHeader(empty, out cm_settingsItem, null, bold: true, subMenu, null, null, placeInNoMenu2); } public static void CreateSpacer(SubMenu subMenu = null) { CreateHeader(string.Empty, null, bold: true, subMenu); } public static void CreateSeparator(Color? col = null, SubMenu subMenu = null) { CreateSeparator(out var _, col, subMenu); } public static void CreateSeparator(out CM_SettingsItem cm_settingsItem, Color? col = null, SubMenu subMenu = null, bool placeInNoMenu = false) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!col.HasValue) { col = DISABLED; } CreateHeader("------------------------------", out cm_settingsItem, col, bold: false, subMenu, null, null, placeInNoMenu); } public static void CreateHeader(string title, Color? color = null, bool bold = true, SubMenu subMenu = null, Action clickAction = null, Action hoverAction = null) { CreateHeader(title, out var _, color, bold, subMenu, clickAction, hoverAction); } public static void CreateHeader(string title, out CM_SettingsItem cm_settingsItem, Color? color = null, bool bold = true, SubMenu subMenu = null, Action clickAction = null, Action hoverAction = null, bool placeInNoMenu = false) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (!color.HasValue) { color = ORANGE; } if (string.IsNullOrWhiteSpace(title)) { title = string.Empty; } else if (bold) { title = "" + title + ""; } CreateSettingsItem(title, out cm_settingsItem, null, null, (TooltipPositionType)0, color.Value, subMenu, placeInNoMenu); RectTransform component = ((Component)((Component)cm_settingsItem).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).gameObject.GetComponent(); component.sizeDelta = new Vector2(component.sizeDelta.x * 2f, component.sizeDelta.y); if (!string.IsNullOrWhiteSpace(title)) { ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } if (clickAction != null || hoverAction != null) { ((CM_Item)(object)cm_settingsItem).SetCMItemEvents(clickAction, hoverAction); } } public static void CreateSettingsItem(Func getTitleText, out CM_SettingsItem cm_settingsItem, Func<(string, string)> getTooltipText = null, TooltipPositionType positionType = 0, Color? titleColor = null, SubMenu subMenu = null, bool placeInNoMenu = false) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (getTitleText == null) { throw new ArgumentNullException("getTitleText"); } var (tooltipHeader, tooltipText) = getTooltipText?.Invoke() ?? (null, null); CreateSettingsItem(getTitleText(), out cm_settingsItem, tooltipText, tooltipHeader, positionType, titleColor, subMenu, placeInNoMenu); JankCellMenuSettingsItemLocalizedTextUpdaterWrapper.CreateAndApply(cm_settingsItem, getTitleText, getTooltipText); } public static void CreateSettingsItem(string titleText, out CM_SettingsItem cm_settingsItem, string tooltipText = null, string tooltipHeader = null, TooltipPositionType positionType = 0, Color? titleColor = null, SubMenu subMenu = null, bool placeInNoMenu = false) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown GameObject val = Object.Instantiate(PageSettingsData.SettingsItemPrefab, subMenu?.WindowTransform ?? PageSettingsData.MainScrollWindowTransform); if (subMenu != null) { subMenu.AppendContent(val); } else if (!placeInNoMenu) { PageSettingsData.ScrollWindowContentElements.Add(val.GetComponentInChildren()); } cm_settingsItem = val.GetComponentInChildren(); TextMeshPro component = ((Component)((Component)cm_settingsItem).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).gameObject.GetComponent(); if (titleColor.HasValue) { ((Graphic)component).color = titleColor.Value; } ((TMP_Text)component).m_text = titleText; ((TMP_Text)component).SetText(titleText, true); JankTextMeshProUpdaterOnce.Apply(component); ((CM_Item)cm_settingsItem).TooltipInfo = new TooltipInfo { TooltipHeader = tooltipHeader, TooltipText = tooltipText, UseTooptip = (!string.IsNullOrWhiteSpace(tooltipHeader) || !string.IsNullOrWhiteSpace(tooltipText)), PositionType = positionType }; } public static void SetupToggleButton(CM_SettingsItem cm_settingsItem, out CM_Item toggleButton_cm_item, out TextMeshPro toggleButtonText) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) CM_SettingsToggleButton val = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_toggleInputPrefab, cm_settingsItem.m_inputAlign); toggleButton_cm_item = ((Component)val).gameObject.AddComponent(); Object.Destroy((Object)(object)val); toggleButtonText = ((Component)toggleButton_cm_item).GetComponentInChildren(); BoxCollider2D component = ((Component)toggleButton_cm_item).GetComponent(); component.size = new Vector2(550f, 50f); ((Collider2D)component).offset = new Vector2(250f, -25f); } public static void CreateSubMenuControls(SubMenu subMenu, Color? entryItemColor = null, string menuEntryLabelText = "> Settings", SubMenu placeIntoMenu = null, string headerText = null, DescriptionPanelData descriptionPanelData = null, string backButtonText = "<<< Back <<<", string enterButtonText = "> ENTER <") { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) if (subMenu == null) { return; } if (menuEntryLabelText == "> Settings") { menuEntryLabelText = "> " + ArchiveLocalizationService.GetById(37u, "Settings"); } if (backButtonText == "<<< Back <<<") { backButtonText = "<<< " + ArchiveLocalizationService.GetById(38u, "Back") + " <<<"; } if (enterButtonText == "> ENTER <") { enterButtonText = "> " + ArchiveLocalizationService.GetById(39u, "ENTER") + " <"; } using (subMenu.GetPersistentContentAdditionToken()) { CreateSettingsItem(backButtonText, out var cm_settingsItem, null, null, (TooltipPositionType)0, RED, subMenu); CreateSpacer(subMenu); if (!string.IsNullOrWhiteSpace(headerText)) { SubMenu subMenu2 = subMenu; CreateHeader(headerText, null, bold: true, subMenu2); } CreateSettingsItem(menuEntryLabelText, out var cm_settingsItem2, null, null, (TooltipPositionType)0, entryItemColor, placeIntoMenu); CreateFSHoverAndSetButtonAction(descriptionPanelData, cm_settingsItem2, null); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); ((CM_Item)(object)cm_settingsItem).SetCMItemEvents(delegate { subMenu.Close(); }); SetupToggleButton(cm_settingsItem2, out var toggleButton_cm_item, out var toggleButtonText); SharedUtils.ChangeColorCMItem(toggleButton_cm_item, ORANGE); toggleButton_cm_item.SetCMItemEvents(delegate { subMenu.Show(); }); ((RectTransformComp)cm_settingsItem2).ForcePopupLayer(true, (GameObject)null); ((TMP_Text)toggleButtonText).SetText(enterButtonText, true); JankTextMeshProUpdaterOnce.Apply(toggleButtonText); } } public static void CreateColorSetting(ColorSetting setting, SubMenu subMenu = null, bool useLegacyColorInputField = false) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Expected O, but got Unknown CM_SettingsItem cm_settingsItem; if (useLegacyColorInputField) { CreateSettingsItem(GetNameForSetting(setting), out cm_settingsItem, setting.TooltipText, setting.TooltipHeader, setting.TooltipPositionType, null, subMenu); } else { Action onPress = delegate { if (!setting.Readonly) { PageSettingsData.TheColorPicker.Show(setting); } }; CreateSimpleButton(GetNameForSetting(setting), ArchiveLocalizationService.GetById(8u, "Pick Color"), onPress, out cm_settingsItem, subMenu); } setting.CM_SettingsItem = cm_settingsItem; CreateRundownInfoTextForItem(cm_settingsItem, setting.RundownHint); CM_SettingsInputField val2 = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_textInputPrefab, cm_settingsItem.m_inputAlign); if (useLegacyColorInputField) { StringInputSetMaxLength(val2, 7); } CreateFSHoverAndSetButtonAction(setting, cm_settingsItem, null); if (useLegacyColorInputField) { RectTransform component = ((Component)val2.m_background).GetComponent(); component.anchoredPosition = new Vector2(-175f, 0f); ((Transform)component).localScale = new Vector3(0.19f, 1f, 1f); if (setting.Readonly) { ((Behaviour)((Component)val2).GetComponent()).enabled = false; } } GameObject val3 = Object.Instantiate(((Component)val2.m_background).gameObject, cm_settingsItem.m_inputAlign, true); if (!useLegacyColorInputField) { ((Component)(object)val2).SafeDestroyGO(); } val3.transform.SetParent(cm_settingsItem.m_inputAlign); val3.transform.localScale = new Vector3(0.07f, 1f, 1f); int num = (useLegacyColorInputField ? (-225) : (-100)); val3.GetComponent().anchoredPosition = new Vector2((float)num, -25f); ((Object)val3).name = "ColorPreview"; SpriteRenderer renderer = val3.GetComponent(); SColor col = (SColor)setting.GetValue(); renderer.color = col.ToUnityColor(); if (useLegacyColorInputField) { ((TMP_Text)val2.m_text).SetText(col.ToHexString(), true); JankTextMeshProUpdaterOnce.Apply(val2.m_text); CustomStringReceiver customStringReceiver = new CustomStringReceiver(delegate { //IL_008d: Unknown result type (might be due to invalid IL or missing references) FeatureLogger.Debug($"[{"CustomStringReceiver"}({"ColorSetting"})] Gotten value of \"{setting.DEBUG_Path}\"!"); SColor col2 = (SColor)setting.GetValue(); renderer.color = col2.ToUnityColor(); return col2.ToHexString(); }, delegate(string val) { FeatureLogger.Debug($"[{"CustomStringReceiver"}({"ColorSetting"})] Set value of \"{setting.DEBUG_Path}\" to \"{val}\""); SColor sColor = SColorExtensions.FromHexString(val); setting.SetValue(sColor); }); val2.m_stringReceiver = new iStringInputReceiver(((Il2CppObjectBase)customStringReceiver).Pointer); } ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } public static void CreateStringSetting(StringSetting setting, SubMenu subMenu = null) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown CreateSettingsItem(GetNameForSetting(setting), out var cm_settingsItem, setting.TooltipText, setting.TooltipHeader, setting.TooltipPositionType, null, subMenu); setting.CM_SettingsItem = cm_settingsItem; CreateRundownInfoTextForItem(cm_settingsItem, setting.RundownHint); CM_SettingsInputField cm_settingsInputField = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_textInputPrefab, cm_settingsItem.m_inputAlign); PageSettingsData.TheStaticSettingsInputFieldJankRemoverHashSet2000.Add(cm_settingsInputField); ((Component)cm_settingsInputField).gameObject.AddComponent().OnDisabledSelf = delegate { if (_A_CM_SettingsInputField_m_readingActive.Get(cm_settingsInputField)) { _A_CM_SettingsInputField_SetReadingActive.Invoke(cm_settingsInputField, false); cm_settingsInputField.ResetValue(); } }; StringInputSetMaxLength(cm_settingsInputField, setting.MaxInputLength); CreateFSHoverAndSetButtonAction(setting, cm_settingsItem, null); ((TMP_Text)cm_settingsInputField.m_text).SetText(setting.GetValue()?.ToString() ?? string.Empty, true); if (setting.Readonly) { ((Behaviour)((Component)cm_settingsInputField).GetComponent()).enabled = false; ((Component)cm_settingsInputField.m_background).gameObject.SetActive(false); } JankTextMeshProUpdaterOnce.Apply(cm_settingsInputField.m_text); CustomStringReceiver customStringReceiver = new CustomStringReceiver(delegate { FeatureLogger.Debug($"[{"CustomStringReceiver"}] Gotten value of \"{setting.DEBUG_Path}\"!"); return setting.GetValue()?.ToString() ?? string.Empty; }, delegate(string val) { FeatureLogger.Debug($"[{"CustomStringReceiver"}] Set value of \"{setting.DEBUG_Path}\" to \"{val}\""); setting.SetValue(val); }); cm_settingsInputField.m_stringReceiver = new iStringInputReceiver(((Il2CppObjectBase)customStringReceiver).Pointer); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } public static void StringInputSetMaxLength(CM_SettingsInputField sif, int maxLength) { sif.m_maxLen = maxLength; } public static void CreateBoolSetting(BoolSetting setting, SubMenu subMenu = null) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) CreateSettingsItem(GetNameForSetting(setting), out var cm_settingsItem, setting.TooltipText, setting.TooltipHeader, setting.TooltipPositionType, null, subMenu); setting.CM_SettingsItem = cm_settingsItem; CreateRundownInfoTextForItem(cm_settingsItem, setting.RundownHint); CM_SettingsToggleButton val = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_toggleInputPrefab, cm_settingsItem.m_inputAlign); CM_Item toggleButton_cm_item = ((Component)val).gameObject.AddComponent(); if (setting.Readonly) { ((Behaviour)((Component)toggleButton_cm_item).GetComponent()).enabled = false; } Object.Destroy((Object)(object)val); TextMeshPro toggleButtonText = ((Component)toggleButton_cm_item).GetComponentInChildren(); CreateFSHoverAndSetButtonAction(setting, cm_settingsItem, toggleButton_cm_item, delegate { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) bool flag2 = !(bool)setting.GetValue(); setting.SetValue(flag2); ((TMP_Text)toggleButtonText).SetText(flag2 ? ArchiveLocalizationService.GetById(6u, "On") : ArchiveLocalizationService.GetById(7u, "Off"), true); Color idleColor2 = (flag2 ? GREEN : RED); SharedUtils.ChangeColorCMItem(toggleButton_cm_item, idleColor2); }); bool flag = (bool)setting.GetValue(); ((TMP_Text)toggleButtonText).SetText(flag ? ArchiveLocalizationService.GetById(6u, "On") : ArchiveLocalizationService.GetById(7u, "Off"), true); Color idleColor = (flag ? GREEN : RED); SharedUtils.ChangeColorCMItem(toggleButton_cm_item, idleColor); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } public static void CreateEnumListSetting(EnumListSetting setting, SubMenu subMenu = null) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) CreateSettingsItem(GetNameForSetting(setting), out var cm_settingsItem, setting.TooltipText, setting.TooltipHeader, setting.TooltipPositionType, null, subMenu); setting.CM_SettingsItem = cm_settingsItem; CreateRundownInfoTextForItem(cm_settingsItem, setting.RundownHint); CM_SettingsEnumDropdownButton cm_settingsEnumDropdownButton = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_enumDropdownInputPrefab, cm_settingsItem.m_inputAlign); CM_Item enumButton_cm_item = ((Component)cm_settingsEnumDropdownButton).gameObject.AddComponent(); ((RectTransformComp)enumButton_cm_item).Setup(); CreateFSHoverAndSetButtonAction(setting, cm_settingsItem, enumButton_cm_item, delegate { CreateAndShowEnumListPopup(setting, enumButton_cm_item, cm_settingsEnumDropdownButton); }); if (setting.Readonly) { ((Behaviour)((Component)enumButton_cm_item).GetComponent()).enabled = false; } SharedUtils.ChangeColorCMItem(enumButton_cm_item, ORANGE); Transform childWithExactName = ((Component)enumButton_cm_item).gameObject.transform.GetChildWithExactName("Background"); if ((Object)(object)childWithExactName != (Object)null) { Object.Destroy((Object)(object)((Component)childWithExactName).gameObject); } enumButton_cm_item.SetText(GetEnumListItemName(setting)); BoxCollider2D component = ((Component)enumButton_cm_item).GetComponent(); component.size = new Vector2(550f, 50f); ((Collider2D)component).offset = new Vector2(250f, -25f); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); ((Behaviour)cm_settingsEnumDropdownButton).enabled = false; Object.Destroy((Object)(object)cm_settingsEnumDropdownButton); cm_settingsEnumDropdownButton.m_popupWindow = PageSettingsData.PopupWindow; } public static void CreateAndShowEnumListPopup(EnumListSetting setting, CM_Item enumButton_cm_item, CM_SettingsEnumDropdownButton cm_settingsEnumDropdownButton) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) List val = SharedUtils.NewListForGame(); object[] source = setting.CurrentSelectedValues(); List list = new List(); foreach (KeyValuePair item in setting.Map) { iScrollWindowContent val2 = GOUtil.SpawnChildAndGetComp(cm_settingsEnumDropdownButton.m_popupItemPrefab, ((Component)enumButton_cm_item).transform); val.Add(val2); string key = item.Key; object value = item.Value; CM_Item val3 = ((Il2CppObjectBase)(object)val2).TryCastTo(); if ((Object)(object)val3 != (Object)null) { ((RectTransformComp)val3).Setup(); val3.SetText(key); ((RectTransformComp)val3).SetScaleFactor(1f); ((Object)val3).name = key; SharedUtils.ChangeColorCMItem(val3, source.Contains(value) ? ORANGE : DISABLED); ((RectTransformComp)val3).ForcePopupLayer(true, (GameObject)null); list.Add(val3); } } foreach (CM_Item cm_Item in list) { cm_Item.SetCMItemEvents(delegate { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) string name = ((Object)cm_Item).name; object enumValueFor = setting.GetEnumValueFor(name); bool flag = setting.ToggleInList(enumValueFor); enumButton_cm_item.SetText(GetEnumListItemName(setting)); SharedUtils.ChangeColorCMItem(cm_Item, flag ? ORANGE : DISABLED); }); } ((CM_PopupOverlay)PageSettingsData.PopupWindow).SetupFromButton(((Il2CppObjectBase)(object)cm_settingsEnumDropdownButton).TryCastTo(), (CM_PageBase)(object)PageSettingsData.SettingsPageInstance); ((Component)PageSettingsData.PopupWindow).transform.position = cm_settingsEnumDropdownButton.m_popupWindowAlign.position; PageSettingsData.PopupWindow.SetContentItems(val, 5f); PageSettingsData.PopupWindow.SetHeader(setting.DisplayName); ((RectTransformComp)PageSettingsData.PopupWindow).SetVisible(true); } public static string GetEnumListItemName(EnumListSetting setting) { string text = string.Join(", ", setting.CurrentSelectedValuesName()); if (string.IsNullOrWhiteSpace(text)) { return "[" + ArchiveLocalizationService.GetById(5u, "None") + "]"; } if (text.Length > 36) { return text.Substring(0, 36) + " ..."; } return text; } public static void CreateEnumSetting(EnumSetting setting, SubMenu subMenu = null) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) CreateSettingsItem(GetNameForSetting(setting), out var cm_settingsItem, setting.TooltipText, setting.TooltipHeader, setting.TooltipPositionType, null, subMenu); setting.CM_SettingsItem = cm_settingsItem; CreateRundownInfoTextForItem(cm_settingsItem, setting.RundownHint); CM_SettingsEnumDropdownButton cm_settingsEnumDropdownButton = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_enumDropdownInputPrefab, cm_settingsItem.m_inputAlign); CM_Item enumButton_cm_item = ((Component)cm_settingsEnumDropdownButton).gameObject.AddComponent(); ((RectTransformComp)enumButton_cm_item).Setup(); CreateFSHoverAndSetButtonAction(setting, cm_settingsItem, enumButton_cm_item, delegate { CreateAndShowEnumPopup(setting, enumButton_cm_item, cm_settingsEnumDropdownButton); }); if (setting.Readonly) { ((Behaviour)((Component)enumButton_cm_item).GetComponent()).enabled = false; } SharedUtils.ChangeColorCMItem(enumButton_cm_item, ORANGE); Transform childWithExactName = ((Component)enumButton_cm_item).gameObject.transform.GetChildWithExactName("Background"); if ((Object)(object)childWithExactName != (Object)null) { Object.Destroy((Object)(object)((Component)childWithExactName).gameObject); } enumButton_cm_item.SetText(setting.GetCurrentEnumKey()); BoxCollider2D component = ((Component)enumButton_cm_item).GetComponent(); component.size = new Vector2(550f, 50f); ((Collider2D)component).offset = new Vector2(250f, -25f); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); ((Behaviour)cm_settingsEnumDropdownButton).enabled = false; Object.Destroy((Object)(object)cm_settingsEnumDropdownButton); cm_settingsEnumDropdownButton.m_popupWindow = PageSettingsData.PopupWindow; } public static void CreateAndShowEnumPopup(EnumSetting setting, CM_Item enumButton_cm_item, CM_SettingsEnumDropdownButton cm_settingsEnumDropdownButton) { //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) List val = SharedUtils.NewListForGame(); string currentEnumKey = setting.GetCurrentEnumKey(); foreach (KeyValuePair kvp in setting.Map) { iScrollWindowContent val2 = GOUtil.SpawnChildAndGetComp(cm_settingsEnumDropdownButton.m_popupItemPrefab, ((Component)enumButton_cm_item).transform); val.Add(val2); string enumKey = kvp.Key; CM_Item val3 = ((Il2CppObjectBase)(object)val2).TryCastTo(); if ((Object)(object)val3 != (Object)null) { ((RectTransformComp)val3).Setup(); val3.SetText(enumKey); ((RectTransformComp)val3).SetScaleFactor(1f); SharedUtils.ChangeColorCMItem(val3, (currentEnumKey == enumKey) ? ORANGE : DISABLED); ((RectTransformComp)val3).ForcePopupLayer(true, (GameObject)null); val3.SetCMItemEvents(delegate { setting.SetValue(kvp.Value); enumButton_cm_item.SetText(enumKey); SetPopupVisible(visible: false); }); } } ((CM_PopupOverlay)PageSettingsData.PopupWindow).SetupFromButton(((Il2CppObjectBase)(object)cm_settingsEnumDropdownButton).CastTo(), (CM_PageBase)(object)PageSettingsData.SettingsPageInstance); ((Component)PageSettingsData.PopupWindow).transform.position = cm_settingsEnumDropdownButton.m_popupWindowAlign.position; PageSettingsData.PopupWindow.SetContentItems(val, 5f); PageSettingsData.PopupWindow.SetHeader(setting.DisplayName); ((RectTransformComp)PageSettingsData.PopupWindow).SetVisible(true); } public static void CreateGenericListSetting(GenericListSetting setting, SubMenu subMenu = null) { DynamicSubMenu subMenu2 = new DynamicSubMenu(setting.DisplayName, delegate(DynamicSubMenu dynamicMenu) { foreach (GenericListSetting.ListEntry entry in setting.GetEntries()) { SetupItemsForSettingsHelper(entry.Helper, dynamicMenu); } }, setting.Identifier); string displayName = setting.DisplayName; string displayName2 = setting.DisplayName; CreateSubMenuControls(subMenu2, null, displayName, subMenu, displayName2); } public static void CreateGenericDictionarySetting(GenericDictionarySetting setting, SubMenu subMenu = null) { DynamicSubMenu subMenu2 = new DynamicSubMenu(setting.DisplayName, delegate(DynamicSubMenu dynamicMenu) { foreach (GenericDictionarySetting.DictEntry entry in setting.GetEntries()) { if (setting.TopLevelReadonly || setting.Readonly) { string keyName = entry.KeyName; SubMenu subMenu3 = dynamicMenu; CreateHeader(keyName, null, bold: true, subMenu3); } else { string title = "EDITABLE=TODO: " + entry.Key.ToString(); SubMenu subMenu3 = dynamicMenu; CreateHeader(title, null, bold: true, subMenu3); } SetupItemsForSettingsHelper(entry.Helper, dynamicMenu); } }, setting.Identifier); string displayName = setting.DisplayName; string displayName2 = setting.DisplayName; CreateSubMenuControls(subMenu2, null, displayName, subMenu, displayName2); } [MethodImpl(MethodImplOptions.NoInlining)] private static bool GetSliderFloatDisplayValue(FSSlider.SliderStyle style, out int val) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown eDisplayFloatValueAs value; bool result = OnlyTouchOnR5AndLater._styleDisplayMap.TryGetValue(style, out value); val = (int)value; return result; } [MethodImpl(MethodImplOptions.NoInlining)] private static void SetSliderFloatDisplayStyle(CM_SettingScrollReceiver cm_settingScrollReceiver, FSSlider.SliderStyle style) { if (GetSliderFloatDisplayValue(style, out var val)) { cm_settingScrollReceiver.m_displayAs = (eDisplayFloatValueAs)val; } } public static void CreateNumberSetting(NumberSetting setting, SubMenu subMenu) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Expected O, but got Unknown //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) CreateSettingsItem(GetNameForSetting(setting), out var cm_settingsItem, setting.TooltipText, setting.TooltipHeader, setting.TooltipPositionType, null, subMenu); setting.CM_SettingsItem = cm_settingsItem; CreateRundownInfoTextForItem(cm_settingsItem, setting.RundownHint); CM_SettingsInputField val2 = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_textInputPrefab, cm_settingsItem.m_inputAlign); StringInputSetMaxLength(val2, 30); CreateFSHoverAndSetButtonAction(setting, cm_settingsItem, null); ((TMP_Text)val2.m_text).SetText(setting.GetValue()?.ToString() ?? string.Empty, true); CM_SettingScrollReceiver val3 = null; if (setting.Readonly) { ((Behaviour)((Component)val2).GetComponent()).enabled = false; ((Component)val2.m_background).gameObject.SetActive(false); } else if (setting.HasSlider) { FSSlider slider = setting.Slider; val3 = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_sliderInputPrefab, cm_settingsItem.m_inputAlign); eSettingInputType value = (eSettingInputType)((slider.Style == FSSlider.SliderStyle.IntMinMax) ? 5 : 4); _A_m_inputType.Set(val3, value); if (Is.R5OrLater) { SetSliderFloatDisplayStyle(val3, slider.Style); } CustomIntReceiver customIntReceiver = new CustomIntReceiver(() => (int)setting.GetValue(), delegate(int val) { if ((int)setting.GetValue() != val) { setting.SetValue(val); } }); CustomFloatReceiver customFloatReceiver = new CustomFloatReceiver(() => ((float)setting.GetValue() - slider.Min) / (slider.Max - slider.Min), delegate(float delta) { float num = (slider.Max - slider.Min) * delta + slider.Min; if (slider.Rounding != FSSlider.RoundTo.NoRounding) { num = (float)Math.Round(num, (int)slider.Rounding); } num = Math.Min(slider.Max, Math.Max(num, slider.Min)); if ((float)setting.GetValue() != num) { FeatureLogger.Debug($"{setting.DEBUG_Path}: setting to {num} (delta={delta})"); setting.SetValue(num); } CM_SettingScrollReceiver_GetFloatDisplayText_Patch.OverrideDisplayValue = true; CM_SettingScrollReceiver_GetFloatDisplayText_Patch.Value = num; }); val3.m_intReceiver = new iIntInputReceiver(((Il2CppObjectBase)customIntReceiver).Pointer); val3.m_floatReceiver = new iFloatInputReceiver(((Il2CppObjectBase)customFloatReceiver).Pointer); _A_m_scrollRange.Set(val3, val3.m_handleLocalXPosMinMax.y - val3.m_handleLocalXPosMinMax.x); CM_SettingScrollReceiver_GetFloatDisplayText_Patch.OverrideDisplayValue = true; if (float.TryParse(setting.GetValue().ToString(), out var result)) { CM_SettingScrollReceiver_GetFloatDisplayText_Patch.Value = result; } val3.ResetValue(); ((Component)val2).gameObject.SetActive(false); } JankTextMeshProUpdaterOnce.Apply(val2.m_text); CustomStringReceiver customStringReceiver = new CustomStringReceiver(delegate { string text = setting.GetValue()?.ToString() ?? string.Empty; FeatureLogger.Debug($"[{"CustomStringReceiver"}] Gotten value of \"{setting.DEBUG_Path}\"! ({text})"); return text; }, delegate(string val) { FeatureLogger.Debug($"[{"CustomStringReceiver"}] Attempting to set value of \"{setting.DEBUG_Path}\" to \"{val}\""); setting.SetValue(val); FeatureLogger.Debug($"[{"CustomStringReceiver"}] Set value of \"{setting.DEBUG_Path}\" to \"{setting.GetValue()}\""); }); val2.m_stringReceiver = new iStringInputReceiver(((Il2CppObjectBase)customStringReceiver).Pointer); ((RectTransformComp)cm_settingsItem).ForcePopupLayer(true, (GameObject)null); } public static void CreateButton(ButtonSetting setting, SubMenu subMenu) { CreateSimpleButton(GetNameForSetting(setting), setting.ButtonText, delegate { FeatureManager.InvokeButtonPressed(setting.Helper.Feature, setting); if (setting.RefreshSubMenu) { subMenu.Refresh(); } }, out var cmItem, out var buttonTmp, subMenu); setting.CM_SettingsItem = cmItem; setting.FComponent.PrimaryText = ((Component)((Component)cmItem).transform.GetChildWithExactName("Title").GetChildWithExactName("TitleText")).gameObject.GetComponent(); setting.FComponent.SecondaryText = buttonTmp; CreateRundownInfoTextForItem(cmItem, setting.RundownHint); CreateFSHoverAndSetButtonAction(setting, cmItem, null); } public static void CreateRundownInfoTextForItem(CM_SettingsItem cm_settingsItem, Utils.RundownFlags rundowns) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) CM_SettingsToggleButton obj = GOUtil.SpawnChildAndGetComp(cm_settingsItem.m_toggleInputPrefab, cm_settingsItem.m_inputAlign); TextMeshPro componentInChildren = ((Component)obj).GetComponentInChildren(); JankTextMeshProUpdaterOnce.Apply(componentInChildren); Object.Destroy((Object)(object)obj); Object.Destroy((Object)(object)((Component)obj).GetComponent()); ((Component)componentInChildren).GetComponent().sizeDelta = new Vector2(520f, 50f); if (rundowns == Utils.RundownFlags.None) { ((TMP_Text)componentInChildren).SetText(string.Empty, true); } else { string rundownTag = Utils.GetRundownTag(rundowns); ((TMP_Text)componentInChildren).SetText("" + rundownTag + "", true); } ((Graphic)componentInChildren).color = ORANGE; } public static void CreateFSHoverAndSetButtonAction(FeatureSetting setting, CM_SettingsItem cm_settingsItem, CM_Item toggleButton_cm_item, Action buttonAction = null) { CreateFSHoverAndSetButtonAction(new DescriptionPanelData { Title = setting.DisplayName, Description = setting.Description, CriticalInfo = setting.Helper.Feature.FeatureInternal.CriticalInfo, FeatureOrigin = setting.Helper.Feature.FeatureInternal.AsmDisplayName }, cm_settingsItem, toggleButton_cm_item, buttonAction); } public static void CreateFSHoverAndSetButtonAction(DescriptionPanelData data, CM_SettingsItem cm_settingsItem, CM_Item toggleButton_cm_item, Action buttonAction = null) { ((CM_Item)(object)cm_settingsItem)?.SetCMItemEvents(delegate { }, OnButtonHover); toggleButton_cm_item?.SetCMItemEvents(buttonAction ?? ((Action)delegate { }), OnButtonHover); void OnButtonHover(int id, bool hovering) { if (hovering) { if (data != null) { PageSettingsData.TheDescriptionPanel.Show(data); } } else { PageSettingsData.TheDescriptionPanel.Hide(); } } } public static void SetFeatureItemTextAndColor(Feature feature, CM_Item buttonItem, TextMeshPro text) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (feature.IsAutomated) { ((TMP_Text)text).SetText(ArchiveLocalizationService.GetById(4u, "Automated"), true); SharedUtils.ChangeColorCMItem(buttonItem, DISABLED); return; } bool flag = ((feature.IsLoadedAndNotDisabledInternally && !feature.RequiresRestart) ? feature.Enabled : FeatureManager.IsEnabledInConfig(feature)); ((TMP_Text)text).SetText(flag ? ArchiveLocalizationService.GetById(1u, "Enabled") : ArchiveLocalizationService.GetById(2u, "Disabled"), true); SetFeatureItemColor(feature, buttonItem); JankTextMeshProUpdaterOnce.UpdateMesh(text); } public static void SetFeatureItemColor(Feature feature, CM_Item item) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!feature.IsLoadedAndNotDisabledInternally) { SharedUtils.ChangeColorCMItem(item, DISABLED); } else if (feature.RequiresRestart) { Color idleColor = (FeatureManager.IsEnabledInConfig(feature) ? GREEN : RED); SharedUtils.ChangeColorCMItem(item, idleColor); } else { Color idleColor = (feature.Enabled ? GREEN : RED); SharedUtils.ChangeColorCMItem(item, idleColor); } } public static void ShowPopupWindow(string header, Vector2 pos) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) PageSettingsData.PopupWindow.SetHeader(header); ((Component)PageSettingsData.PopupWindow).transform.position = Vector2.op_Implicit(pos); ((RectTransformComp)PageSettingsData.PopupWindow).SetVisible(true); } public static void SetPopupVisible(bool visible) { ((RectTransformComp)PageSettingsData.PopupWindow).SetVisible(visible); } public static string GetNameForSetting(FeatureSetting setting) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (setting.HideInModSettings) { return $"<{DISABLED.ToSColor().ToHexString()}>[H] {setting.DisplayName}"; } return setting.DisplayName; } } public class DynamicSubMenu : SubMenu { private readonly Action _buildMenuAction; public DynamicSubMenu(string title, Action buildMenuAction, string identifier) : base(title, identifier) { _buildMenuAction = buildMenuAction; } public override bool Build() { ClearItems(); _buildMenuAction?.Invoke(this); base.Build(); base.HasBeenBuilt = false; return true; } public override void Show() { Build(); base.Show(); } public void ClearItems() { foreach (SubMenuEntry item in persistentContent) { item.GameObject.transform.SetParent(base.WindowTransform); } foreach (SubMenuEntry item2 in content) { Object.Destroy((Object)(object)item2.GameObject); } content.Clear(); } } public class SubMenu : IDisposable { public class SubMenuEntry { public GameObject GameObject { get; private set; } public iScrollWindowContent IScrollWindowContent { get; private set; } public SubMenuEntry(GameObject go) { GameObject = go; IScrollWindowContent = go.GetComponentInChildren(); if (IScrollWindowContent == null) { throw new ArgumentException("Passed GameObject does not contain a Component inheriting from iScrollWindowContent in its children!", "go"); } } } public class PersistentContentAdditionToken : IDisposable { private readonly SubMenu _subMenu; private readonly bool _previousAddAsPersistent; internal PersistentContentAdditionToken(SubMenu menu) { _subMenu = menu; _previousAddAsPersistent = menu._addContentAsPersistent; menu._addContentAsPersistent = true; } public void Dispose() { _subMenu._addContentAsPersistent = _previousAddAsPersistent; GC.SuppressFinalize(this); } } [CompilerGenerated] private sealed class d__33 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public SubMenu <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__33(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown int num = <>1__state; SubMenu subMenu = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; case 1: { <>1__state = -1; foreach (iCellMenuCursorItem componentsInChild in ((Component)subMenu.ScrollWindow).GetComponentsInChildren()) { if (componentsInChild.ID == 0) { componentsInChild.ID = CM_PageBase.NextCellItemID(); componentsInChild.SetupCMItem(); } } List list = ((IEnumerable)((Component)subMenu.ScrollWindow).GetComponentsInChildren(true)).ToList(); list.RemoveAt(0); ScrollWindowClickAnyWhereListeners[((Object)subMenu.ScrollWindow).GetInstanceID()] = list; 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(); } } internal readonly string Identifier; internal static readonly Stack openMenus = new Stack(); internal static Dictionary> ScrollWindowClickAnyWhereListeners = new Dictionary>(); protected readonly List persistentContent = new List(); protected readonly List content = new List(); private bool _addContentAsPersistent; public string Title { get; private set; } public bool HasBeenBuilt { get; protected set; } public float Padding { get; set; } = 5f; public Transform WindowTransform { get { CM_ScrollWindow scrollWindow = ScrollWindow; if (scrollWindow == null) { return null; } return ((Component)scrollWindow).transform; } } public CM_ScrollWindow ScrollWindow { get; private set; } public SubMenu(string title, string identifier) { Identifier = identifier ?? title; Title = title; ScrollWindow = SettingsCreationHelper.CreateScrollWindow(title); AddToAllSettingsWindows(ScrollWindow); } public void Dispose() { if (!((Object)(object)ScrollWindow == (Object)null)) { RemoveFromAllSettingsWindows(ScrollWindow); ScrollWindowClickAnyWhereListeners.Remove(((Object)ScrollWindow).GetInstanceID()); ((Component)(object)ScrollWindow).SafeDestroyGO(); } } public PersistentContentAdditionToken GetPersistentContentAdditionToken() { return new PersistentContentAdditionToken(this); } public bool AppendContent(GameObject go) { return AppendContent(new SubMenuEntry(go)); } public virtual bool AppendContent(SubMenuEntry item) { if (HasBeenBuilt) { return false; } if (_addContentAsPersistent) { persistentContent.Add(item); } else { content.Add(item); } return true; } public virtual bool Build() { if (HasBeenBuilt) { return false; } List list = new List(); if (persistentContent.Count > 0) { list.AddRange(persistentContent.Select((SubMenuEntry sme) => sme.IScrollWindowContent)); } if (content.Count > 0) { list.AddRange(content.Select((SubMenuEntry sme) => sme.IScrollWindowContent)); } ScrollWindow.SetContentItems(list.ToIL2CPPListIfNecessary(), Padding); LoaderWrapper.StartCoroutine(UpdateCellMenuCursorItems()); HasBeenBuilt = true; return true; } public virtual void Show() { FeatureLogger.Debug($"Opening SubMenu \"{Identifier}\" (Title: {Title}) ..."); ShowScrollWindow(ScrollWindow); openMenus.Push(this); } public virtual void Refresh() { Close(); Show(); } public void Close() { if (openMenus.Count > 0) { openMenus.Pop(); } if (openMenus.Count > 0) { openMenus.Pop().Show(); } else { ShowMainModSettingsWindow(0); } } [IteratorStateMachine(typeof(d__33))] private IEnumerator UpdateCellMenuCursorItems() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__33(0) { <>4__this = this }; } } private static MethodAccessor A_TextMeshPro_ForceMeshUpdate; private static readonly MethodAccessor _A_CM_SettingsInputField_SetReadingActive = MethodAccessor.GetAccessor("SetReadingActive", new Type[1] { typeof(bool) }); private static readonly IValueAccessor _A_CM_SettingsInputField_m_readingActive = AccessorBase.GetValueAccessor("m_readingActive"); public static Color ORANGE_WHITE = new Color(1f, 0.85f, 0.75f, 0.8f); public static Color ORANGE = new Color(1f, 0.5f, 0.05f, 0.8f); public static Color WHITE_GRAY = new Color(0.7358f, 0.7358f, 0.7358f, 0.7686f); public static Color RED = new Color(0.8f, 0.1f, 0.1f, 0.8f); public static Color GREEN = new Color(0.1f, 0.8f, 0.1f, 0.8f); public static Color DISABLED = new Color(0.3f, 0.3f, 0.3f, 0.8f); public override string Name => "Mod Settings (this)"; public override GroupBase Group => GroupManager.Dev; public override string Description => "WARNING! Disabling this makes you unable to change settings in game via this very menu after a restart!"; public override bool RequiresRestart => true; public new static IArchiveLogger FeatureLogger { get; set; } [FeatureConfig] internal static ModSettingsSettings Settings { get; set; } public override void Init() { LoaderWrapper.ClassInjector.RegisterTypeInIl2Cpp(); LoaderWrapper.ClassInjector.RegisterTypeInIl2Cpp(); LoaderWrapper.ClassInjector.RegisterTypeInIl2Cpp(); LoaderWrapper.ClassInjector.RegisterTypeInIl2Cpp(); LoaderWrapper.ClassInjector.RegisterTypeInIl2Cpp(); RegisterReceiverTypesInIL2CPP(); if (Is.R6OrLater) { A_TextMeshPro_ForceMeshUpdate = MethodAccessor.GetAccessor("ForceMeshUpdate", new Type[2] { typeof(bool), typeof(bool) }); } else { A_TextMeshPro_ForceMeshUpdate = MethodAccessor.GetAccessor("ForceMeshUpdate", Array.Empty()); } } public override void OnEnable() { InitSingletonBase.Instance.OnFeatureRestartRequestChanged += OnFeatureRestartRequestChanged; } public override void OnDisable() { InitSingletonBase.Instance.OnFeatureRestartRequestChanged -= OnFeatureRestartRequestChanged; } private void OnFeatureRestartRequestChanged(Feature feature, bool restartRequested) { CM_PageSettings__Setup__Patch.SetRestartInfoText(InitSingletonBase.Instance.AnyFeatureRequestingRestart); } public static void ShowMainModSettingsWindow(int _) { ShowScrollWindow(PageSettingsData.MainModSettingsScrollWindow); } public static void AddToAllSettingsWindows(CM_ScrollWindow scrollWindow) { PageSettingsData.AllSubmenuScrollWindows.Add(scrollWindow); PageSettingsData.SettingsPageInstance.m_allSettingsWindows.Add(scrollWindow); AddToClickAnywhereListeners(((Il2CppObjectBase)scrollWindow).Cast()); } public static void RemoveFromAllSettingsWindows(CM_ScrollWindow scrollWindow) { PageSettingsData.SettingsPageInstance.m_allSettingsWindows.Remove(scrollWindow); RemoveFromClickAnywhereListeners(((Il2CppObjectBase)scrollWindow).Cast()); } public static void AddToClickAnywhereListeners(iCellMenuCursorInputAnywhereItem item) { if (!((CM_PageBase)PageSettingsData.SettingsPageInstance).m_clickAnywhereListeners.Contains(item)) { ((CM_PageBase)PageSettingsData.SettingsPageInstance).m_clickAnywhereListeners.Add(item); } } public static void RemoveFromClickAnywhereListeners(iCellMenuCursorInputAnywhereItem item) { ((CM_PageBase)PageSettingsData.SettingsPageInstance).m_clickAnywhereListeners.Remove(item); } public static void RegenerateModSettingsPage() { if (!((Object)(object)PageSettingsData.MainModSettingsScrollWindow == (Object)null)) { CM_PageSettings__Setup__Patch.DestroyModSettingsPage(); CM_PageSettings__Setup__Patch.SetupMainModSettingsPage(); ((CM_PageBase)PageSettingsData.SettingsPageInstance).UpdateCellMenuCursorItems(); } } public static void ShowScrollWindow(CM_ScrollWindow window) { if ((Object)(object)window == (Object)(object)PageSettingsData.MainModSettingsScrollWindow) { SubMenu.openMenus.Clear(); } CM_PageSettings.ToggleAudioTestLoop(false); PageSettingsData.SettingsPageInstance.ResetAllInputFields(); PageSettingsData.SettingsPageInstance.ResetAllValueHolders(); PageSettingsData.SettingsPageInstance.m_currentSubMenuId = (eSettingsSubMenuId)0; PageSettingsData.SettingsPageInstance.ShowSettingsWindow(window); } private static void RegisterReceiverTypesInIL2CPP() { LoaderWrapper.ClassInjector.RegisterTypeInIl2CppWithInterfaces(logSuccess: true, new Type[1] { typeof(iStringInputReceiver) }); LoaderWrapper.ClassInjector.RegisterTypeInIl2CppWithInterfaces(logSuccess: true, new Type[1] { typeof(iIntInputReceiver) }); LoaderWrapper.ClassInjector.RegisterTypeInIl2CppWithInterfaces(logSuccess: true, new Type[1] { typeof(iFloatInputReceiver) }); } } [HideInModSettings] [EnableFeatureByDefault] [DisallowInGameToggle] [RundownConstraint(Utils.RundownFlags.RundownFive, Utils.RundownFlags.Latest)] internal class PageRundownPopupManager : Feature { [CompilerGenerated] private sealed class d__19 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__19(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (_runningAllPopups) { return false; } _runningAllPopups = true; <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return true; case 1: <>1__state = -1; while (_popupQueue.Count > 0) { try { GlobalPopupMessageManager.ShowPopup(_popupQueue.Dequeue()); } catch (Exception ex) { FeatureLogger.Error("Failed to show popup."); FeatureLogger.Exception(ex); } } _runningAllPopups = false; 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 static readonly Queue _popupQueue = new Queue(); private static bool _runningAllPopups = false; public override string Name => "PopupQueue"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Popups, yay!"; public new static IArchiveLogger FeatureLogger { get; set; } public static Action EmptyAction { get; private set; } = Empty; private static void Empty() { } public static void ShowPopup(PopupMessage popupMessage) { MainMenuGuiLayer current = MainMenuGuiLayer.Current; if ((Object)(object)((current != null) ? current.PageRundownNew : null) == (Object)null) { FeatureLogger.Error("Called too early!"); return; } if (!((Behaviour)MainMenuGuiLayer.Current.PageRundownNew).isActiveAndEnabled) { GameObject gameObject = ((Component)MainMenuGuiLayer.Current.PageRundownNew).gameObject; if ((Object)(object)gameObject.GetComponent() == (Object)null) { ModSettings.OnEnabledListener onEnabledListener = gameObject.AddComponent(); onEnabledListener.OnEnabledSelf = (Action)Delegate.Combine(onEnabledListener.OnEnabledSelf, new Action(PageRundownEnabled)); } _popupQueue.Enqueue(popupMessage); return; } try { GlobalPopupMessageManager.ShowPopup(popupMessage); } catch (Exception ex) { FeatureLogger.Error("Failed to show single popup."); FeatureLogger.Exception(ex); } } private static void PageRundownEnabled(GameObject go) { if (_popupQueue.Count > 0 && !_runningAllPopups) { LoaderWrapper.StartCoroutine(ShowAllPopups()); } } [IteratorStateMachine(typeof(d__19))] private static IEnumerator ShowAllPopups() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__19(0); } } [EnableFeatureByDefault] [HideInModSettings] public class PlayerDialogFilter : Feature { [ArchivePatch(typeof(PlayerVoiceManager), "DoSayLine", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class PlayerVoiceManager__DoSayLine__Patch { public static bool Prefix(pSayLine data) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (_soundEventsToFilter.Contains(data.eventID)) { return false; } return true; } } private static readonly List _soundEventsToFilter = new List(); public override string Name => "Player Dialog Filter"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Remove unwanted player sound events."; public static bool AddEventToFilter(string soundEvent) { if (!SoundEventCache.TryResolve(soundEvent, out var soundId)) { return false; } if (_soundEventsToFilter.Contains(soundId)) { return true; } _soundEventsToFilter.Add(soundId); return true; } public static bool RemoveEventFromFilter(string soundEvent) { if (!SoundEventCache.TryResolve(soundEvent, out var soundId)) { return false; } return _soundEventsToFilter.Remove(soundId); } } [EnableFeatureByDefault] [HideInModSettings] internal class RedirectSettings : Feature { [ArchivePatch(typeof(Application), "persistentDataPath", null, ArchivePatch.PatchMethodType.Getter, -1)] public static class Application_persistentDataPath_Patch { public static bool Prefix(ref string __result) { __result = LocalFiles.VersionSpecificLogsAndCachePath; return false; } } [ArchivePatch(typeof(Path), "Combine", new Type[] { typeof(string), typeof(string) }, ArchivePatch.PatchMethodType.Method, -1)] public static class Il2CppSystem_IO_Path_Combine_Patch { public static bool Prefix(ref string __result, ref string path1, ref string path2) { switch (path2) { case "GTFO_Settings.txt": __result = LocalFiles.SettingsPath; return false; case "GTFO_Favorites.txt": if (Feature.IsPlayingModded) { return true; } __result = LocalFiles.FavoritesPath; return false; case "GTFO_BotFavorites.txt": if (Feature.IsPlayingModded) { return true; } __result = LocalFiles.BotFavoritesPath; return false; default: return true; } } } public override string Name => "Redirect Settings"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Redirect settings load/save location"; public override bool RequiresRestart => true; } [HideInModSettings] [EnableFeatureByDefault] internal class SeedDebuggerNicknamePathFix : Feature { [ArchivePatch(typeof(LG_SeedDebugPrinter), "WriteLogToDisk", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class LG_SeedDebugPrinter__WriteLogToDisk__Patch { public static void Prefix(ref string filename) { try { char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char c in invalidFileNameChars) { if (filename.Contains(c)) { filename = filename.Replace(c, '_'); } } } catch (Exception ex) { FeatureLogger.Warning("This should not happen."); FeatureLogger.Exception(ex); } } } public override string Name => "SeedDebugger-Nickname-Path-Fix"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Makes sure GenerationChecksumFails and other SeedDebugger related log files properly get saved even if the local player has invalid path characters in their name."; public new static IArchiveLogger FeatureLogger { get; set; } } [HideInModSettings] [DoNotSaveToConfig] internal class SettingsDebug : Feature { public class SettingsDebugSettings { public class Sliders { [FSSlider(0f, 1f, FSSlider.SliderStyle.FloatPercent, FSSlider.RoundTo.TwoDecimal)] public float SliderPercent0_1 { get; set; } = 0.5f; [FSSlider(0.1f, 2f, FSSlider.SliderStyle.FloatTwoDecimal, FSSlider.RoundTo.TwoDecimal)] public float SliderFloat01_20 { get; set; } = 0.5f; [FSSlider(0.1f, 2f, FSSlider.SliderStyle.FloatOneDecimal, FSSlider.RoundTo.OneDecimal)] public float SliderFloat01_20_OneDec { get; set; } = 0.5f; [FSSlider(0f, 100f, FSSlider.SliderStyle.FloatNoDecimal, FSSlider.RoundTo.NoDecimal)] public float SliderFloat0_100_NoDec { get; set; } = 50f; [FSSlider(0f, 100f, FSSlider.SliderStyle.IntMinMax, FSSlider.RoundTo.TwoDecimal)] public int SliderInt0_100_NoDec { get; set; } = 50; [FSSlider(0f, 3f, FSSlider.SliderStyle.FloatPercent, FSSlider.RoundTo.TwoDecimal)] public float SliderPercent0_3 { get; set; } = 0.5f; [FSSlider(0.2f, 2f, FSSlider.SliderStyle.FloatPercent, FSSlider.RoundTo.TwoDecimal)] public float SliderPercent02_20 { get; set; } = 0.5f; } [FSDisplayName("Slider Settings")] public Sliders SliderSettings { get; set; } = new Sliders(); } public override string Name => "SettingsDebug"; public override GroupBase Group => GroupManager.Dev; [FeatureConfig] public static SettingsDebugSettings Settings { get; set; } } [EnableFeatureByDefault] internal class SmartFavoritesSaving : Feature { [ArchivePatch(typeof(GearManager), "SaveFavoritesData", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GearManager_SaveFavoritesData_Patch { private static bool _shouldRun; private static MethodAccessor _A_SaveFavoritesData; public static void Init() { _A_SaveFavoritesData = MethodAccessor.GetAccessor("SaveFavoritesData"); } public static bool Prefix() { if (_shouldRun) { return true; } if (Feature.DevMode) { FeatureLogger.Debug("Skipping SaveFavoritesData!"); } return false; } public static void InvokeOriginal() { _shouldRun = true; _A_SaveFavoritesData.Invoke(null); _shouldRun = false; } } [RundownConstraint(Utils.RundownFlags.RundownSix, Utils.RundownFlags.Latest)] [ArchivePatch(typeof(GearManager), "SaveBotFavoritesData", null, ArchivePatch.PatchMethodType.Method, -1)] internal static class GearManager_SaveBotFavoritesData_Patch { private static bool _shouldRun; public static bool Prefix() { if (_shouldRun) { return true; } if (Feature.DevMode) { FeatureLogger.Debug("Skipping SaveBotFavoritesData!"); } return false; } [MethodImpl(MethodImplOptions.NoInlining)] public static void InvokeOriginal() { _shouldRun = true; GearManager.SaveBotFavoritesData(); _shouldRun = false; } } private static readonly eGameStateName _eGameStateName_Generating = Utils.GetEnumFromName("Generating"); public override string Name => "Smart Favorites Saving"; public override GroupBase Group => GroupManager.Dev; public override string Description => "Only save selected weapons/vanity on drop or game quit."; public new static IArchiveLogger FeatureLogger { get; set; } public void OnGameStateChanged(eGameStateName state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (state == _eGameStateName_Generating) { SaveFavoritesFiles(); } } public override void OnQuit() { SaveFavoritesFiles(); } private static void SaveFavoritesFiles() { FeatureLogger.Notice("Saving Favorites file(s)!"); GearManager_SaveFavoritesData_Patch.InvokeOriginal(); if (Is.R6OrLater) { GearManager_SaveBotFavoritesData_Patch.InvokeOriginal(); } } } [EnableFeatureByDefault] [HideInModSettings] [RundownConstraint(Utils.RundownFlags.RundownOne, Utils.RundownFlags.Latest)] internal class StartupscreenOverride : Feature { [ArchivePatch(typeof(PlayFabManager), "TryGetStartupScreenData", null, ArchivePatch.PatchMethodType.Method, -1)] internal class PlayFabManager__TryGetStartupScreenData__Patch { public static bool Prefix(eStartupScreenKey key, out StartupScreenData data, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown StartupScreenData val = new StartupScreenData(); val.AllowedToStartGame = true; val.IntroText = Utils.GetStartupTextForRundown(Feature.BuildInfo.Rundown); val.ShowDiscordButton = false; val.ShowBugReportButton = false; val.ShowRoadmapButton = false; val.ShowIntroText = true; __result = true; data = val; return false; } } public override string Name => "StartupscreenOverride"; public override GroupBase Group => GroupManager.Dev; } [HideInModSettings] [ForceDisable("Might be causing overall issues.")] internal class UnityRandomOverrider : Feature { [ArchivePatch(typeof(Random), "Range", new Type[] { typeof(float), typeof(float) }, ArchivePatch.PatchMethodType.Method, -1)] internal static class UnityEngine__Random__Range__Patch { public static void Postfix(ref float __result) { if (SetSeed != 0) { __result = SetSeed; FeatureLogger.Notice($"RNG Seed set to: {(uint)__result}"); if (ResetSeedAfterUse) { SetSeed = 0u; } } } } public override string Name => "UnityRandomOverrider"; public override GroupBase Group => GroupManager.Dev; public new static IArchiveLogger FeatureLogger { get; set; } public static uint SetSeed { get; set; } = 0u; public static bool ResetSeedAfterUse { get; set; } = true; } } namespace TheArchive.Core { internal class ArchiveContractResolver : DefaultContractResolver { public static readonly ArchiveContractResolver Instance = new ArchiveContractResolver(); protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) JsonProperty val = ((DefaultContractResolver)this).CreateProperty(member, memberSerialization); if (typeof(ISettingsComponent).IsAssignableFrom(val.PropertyType)) { val.Ignored = true; } return val; } } [Obsolete("Remove this")] public class ArchiveLegacyPatcher { } public class ArchiveSettings { public string CustomFileSaveLocation { get; set; } = string.Empty; public string CustomLogsAndCacheLocation { get; set; } = string.Empty; public bool DumpDataBlocks { get; set; } public bool AlwaysOverrideDataBlocks { get; set; } public bool FeatureDevMode { get; set; } } public static class Attribution { public record AttributionInfo(string Name, string Content, string Comment = "", string Origin = ""); internal static HashSet AttributionInfos { get; } = new HashSet(); public static void Add(AttributionInfo attribution) { AttributionInfos.Add(attribution); } } public static class BuildDB { private static int _buildNumber = -1; public static readonly Dictionary RundownIDMapping = new Dictionary { { 19715, Utils.RundownID.RundownOne }, { 20472, Utils.RundownID.RundownTwo }, { 20869, Utils.RundownID.RundownThree }, { 21989, Utils.RundownID.RundownFour }, { 25829, Utils.RundownID.RundownFive }, { 29742, Utils.RundownID.RundownSix }, { 31994, Utils.RundownID.RundownSeven }, { 32283, Utils.RundownID.RundownAltOne }, { 32416, Utils.RundownID.RundownAltTwo }, { 32577, Utils.RundownID.RundownAltThree }, { 32823, Utils.RundownID.RundownAltFour }, { 33054, Utils.RundownID.RundownAltFive }, { 34156, Utils.RundownID.RundownAltSix } }; public static int BuildNumber { get { if (_buildNumber != -1) { return _buildNumber; } try { _buildNumber = (int)(ImplementationManager.FindTypeInCurrentAppDomain("CellBuildData")?.GetMethod("GetRevision", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(null, null) ?? ((object)(-1))); if (_buildNumber <= 0) { string text = Path.Combine(LoaderWrapper.GameDirectory, "revision.txt"); if (!File.Exists(text)) { throw new Exception("File doesn't exist: \"" + text + "\""); } _buildNumber = int.Parse(File.ReadAllLines(text)[0].Replace(" ", "")); } if (_buildNumber <= 0) { throw new Exception("Build / Revision number couldn't be found ..."); } } catch (Exception ex) { _buildNumber = 0; ArchiveLogger.Error("Couldn't load the current build / revision number from CellBuildData or revisions.txt!"); ArchiveLogger.Exception(ex); } return _buildNumber; } } public static Utils.RundownID GetCurrentRundownID(int buildNumber) { foreach (KeyValuePair item in RundownIDMapping) { if (buildNumber <= item.Key) { return item.Value; } } return Utils.GetLatestRundownID(); } } public interface IArchiveModule { ILocalizationService LocalizationService { get; set; } IArchiveLogger Logger { get; set; } void Init(); } public abstract class InitSingletonBase where T : class { private static T _instance; public static bool HasBeenInitialized { get; private set; } public static T Instance { get { if (_instance == null) { ArchiveLogger.Warning("Instance on \"" + typeof(T).FullName + "\" not set even though it's being accessed."); } return _instance; } protected set { _instance = value; } } } } namespace TheArchive.Core.Settings { public class EnabledFeatures { public Dictionary Features { get; set; } = new Dictionary(); } } namespace TheArchive.Core.ModulesAPI { [PublicAPI] public class CustomSetting : ICustomSetting where T : new() { internal string FullPath { get; } internal string FileName { get; } public T Value { get; set; } private Action AfterLoad { get; } public bool SaveOnQuit { get; } public LoadingTime LoadingTime { get; } public CustomSetting(string fileName, T defaultValue, Action afterLoad = null, LoadingTime loadingTime = LoadingTime.Immediately, bool saveOnQuit = true) { FileName = fileName; FullPath = Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), "Settings", FileName + ".json"); Value = defaultValue; AfterLoad = afterLoad; SaveOnQuit = saveOnQuit; LoadingTime = loadingTime; CustomSettingManager.RegisterModuleSetting(this); } public void Load() { string directoryName = Path.GetDirectoryName(FullPath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } if (File.Exists(FullPath)) { Value = JsonConvert.DeserializeObject(File.ReadAllText(FullPath), ArchiveMod.JsonSerializerSettings); } AfterLoad?.Invoke(Value); } public void Save() { string directoryName = Path.GetDirectoryName(FullPath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } string input = string.Empty; if (File.Exists(FullPath)) { input = File.ReadAllText(FullPath); } string text = JsonConvert.SerializeObject((object)Value, ArchiveMod.JsonSerializerSettings); if (input.HashString() != text.HashString()) { File.WriteAllText(FullPath, text); } } } public interface ICustomSetting { LoadingTime LoadingTime { get; } bool SaveOnQuit { get; } void Load(); void Save(); } public enum LoadingTime { None, Immediately, AfterGameDataInited } internal static class CustomSettingManager { private static readonly IArchiveLogger _logger = LoaderWrapper.CreateArSubLoggerInstance("CustomSettingManager", ConsoleColor.DarkRed); private static readonly HashSet _moduleSettings = new HashSet(); public static void RegisterModuleSetting(ICustomSetting setting) { if (setting.LoadingTime == LoadingTime.Immediately || (setting.LoadingTime == LoadingTime.AfterGameDataInited && Feature.GameDataInited)) { try { setting.Load(); } catch (Exception ex) { _logger.Error($"Exception thrown in {"RegisterModuleSetting"}! {ex}: {ex.Message}"); _logger.Exception(ex); } } _moduleSettings.Add(setting); } public static void OnApplicationQuit() { foreach (ICustomSetting moduleSetting in _moduleSettings) { try { if (moduleSetting.SaveOnQuit) { moduleSetting.Save(); } } catch (Exception ex) { _logger.Error($"Exception thrown in {"OnApplicationQuit"}! {ex}: {ex.Message}"); _logger.Exception(ex); } } } public static void OnGameDataInited() { foreach (ICustomSetting moduleSetting in _moduleSettings) { try { if (moduleSetting.LoadingTime == LoadingTime.AfterGameDataInited) { moduleSetting.Load(); } } catch (Exception ex) { _logger.Error($"Exception thrown in {"OnGameDataInited"}! {ex}: {ex.Message}"); _logger.Exception(ex); } } } } } namespace TheArchive.Core.Models { public struct GameBuildInfo { public Utils.RundownID Rundown { get; init; } public int BuildNumber { get; init; } } public enum PresenceGameState { Startup, NoLobby, InLobby, Dropping, LevelGenerationFinished, InLevel, ExpeditionFailed, ExpeditionSuccess } public struct SColor { public static readonly SColor WHITE = new SColor(1f, 1f, 1f); public static readonly SColor BLACK = new SColor(0f, 0f, 0f); public static readonly SColor ORANGE = new SColor(1f, 0.5f, 0.05f, 1f); public static readonly SColor RED = new SColor(0.8f, 0.1f, 0.1f, 1f); public static readonly SColor GREEN = new SColor(0.1f, 0.8f, 0.1f, 1f); public static readonly SColor DARK_PURPLE = new SColor(0.3f, 0.03f, 0.6f, 1f); public static readonly SColor DARK_ORANGE = new SColor(0.8f, 0.3f, 0.03f, 1f); public float R { get; set; } public float G { get; set; } public float B { get; set; } public float A { get; set; } public SColor(float r, float g, float b, float? a = null) { R = r; G = g; B = b; if (a.HasValue) { A = a.Value; } else { A = 1f; } } public readonly SColor WithAlpha(float alpha) { SColor result = this; result.A = alpha; return result; } } } namespace TheArchive.Core.Managers { public static class DataBlockManager { private interface ITransformationData { int Priority { get; } Type DBType { get; } MethodBase OriginMethod { get; } Type DeclaringType { get; } Assembly DeclaringAssembly { get; } internal void Invoke(IList list); } private class TransformationData : ITransformationData { private readonly MethodInfo _method; private readonly object _target; public int Priority { get; private set; } public Type DBType => typeof(T); public MethodBase OriginMethod { get; private set; } public Type DeclaringType => OriginMethod.DeclaringType; public Assembly DeclaringAssembly => DeclaringType.Assembly; public TransformationData(Action> transform, int priority = 0, MethodBase originMethod = null) { _method = transform.Method; _target = transform.Target; OriginMethod = originMethod; Priority = priority; } public void Invoke(IList list) { _method.Invoke(_target, new object[1] { list }); } } private static IArchiveLogger _logger; private static HashSet _dataBlockTypes; private static readonly List _transformationDataToApply = new List(); public static bool HasBeenSetup { get; private set; } private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateArSubLoggerInstance("DataBlockManager", ConsoleColor.Green)); public static HashSet DataBlockTypes { get { if (_dataBlockTypes == null) { GetAllDataBlockTypes(); } return _dataBlockTypes; } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void GetAllDataBlockTypes() { Type[] types; try { types = Assembly.GetAssembly(ImplementationManager.GameTypeByIdentifier("EnemyDataBlock")).GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; } _dataBlockTypes = types.Where((Type x) => x != null && !x.IsAbstract && !x.IsInterface && x.BaseType != null && x.BaseType.IsGenericType && x.BaseType.GetGenericTypeDefinition() == ImplementationManager.GameTypeByIdentifier("GameDataBlockBase<>")).ToHashSet(); } public static void RegisterTransformationFor(Action> action, int priority = 0) where T : class { if (HasBeenSetup) { throw new Exception("Transformations have to be registered before DataBlocks have been initialized!"); } StackFrame frame = new StackTrace().GetFrame(1); TransformationData transformationData = new TransformationData(action, priority, frame.GetMethod()); _transformationDataToApply.Add(transformationData); Logger.Debug($"Transform for {typeof(T).Name} Registered: '{transformationData.DeclaringType?.FullName ?? "Null"}', Method: '{transformationData.OriginMethod?.Name ?? "Null"}' (Asm:{transformationData.DeclaringAssembly?.GetName()?.Name ?? "Null"}) [Priority:{transformationData.Priority}]"); } private static object GetWrapper(Type type, out Type wrapperType) { Type type2 = ImplementationManager.GameTypeByIdentifier("GameDataBlockBase<>").MakeGenericType(type); wrapperType = ImplementationManager.GameTypeByIdentifier("GameDataBlockWrapper<>").MakeGenericType(type); return type2.GetProperty("Wrapper", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) ?? type2.GetField("Wrapper", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null); } private static object GetAllBlocksFromWrapper(Type wrapperType, object wrapper) { return wrapperType.GetProperty("Blocks").GetValue(wrapper); } public static List GetAllBlocks() { return (List)GetAllBlocks(typeof(T)); } public static IList GetAllBlocks(Type dataBlockType) { if (!IsDataBlockType(dataBlockType)) { throw new InvalidOperationException("The Type \"" + dataBlockType?.FullName + "\" is not a valid DataBlock Type!"); } Type wrapperType; object wrapper = GetWrapper(dataBlockType, out wrapperType); return Utils.ToSystemListSlow(GetAllBlocksFromWrapper(wrapperType, wrapper), dataBlockType); } public static bool IsDataBlockType(Type type) { return DataBlockTypes.Contains(type); } internal static void Setup() { Logger.Debug("Setting up ..."); try { if (ArchiveMod.Settings.DumpDataBlocks && !ArchiveMod.IsPlayingModded) { DumpOriginalDataBlocks(LocalFiles.DataBlockDumpPath, ArchiveMod.Settings.AlwaysOverrideDataBlocks); } if (_transformationDataToApply.Count > 0) { Logger.Msg(ConsoleColor.Green, "Applying DataBlock transformations ..."); foreach (ITransformationData item in _transformationDataToApply.OrderBy((ITransformationData x) => x.Priority)) { try { Logger.Notice($"> Applying transform for {item.DBType.Name} from '{item.DeclaringType?.FullName ?? "Null"}', Method: '{item.OriginMethod?.Name ?? "Null"}' (Asm:{item.DeclaringAssembly?.GetName()?.Name ?? "Null"}) [Priority:{item.Priority}]"); item.Invoke(GetAllBlocks(item.DBType)); } catch (Exception ex) { Logger.Exception(ex); } } } } catch (Exception ex2) { Logger.Exception(ex2); } HasBeenSetup = true; } public static void DumpOriginalDataBlocks(string pathToDumpTo, bool overrideExistingFiles) { if (string.IsNullOrWhiteSpace(pathToDumpTo) || !Directory.Exists(pathToDumpTo)) { throw new ArgumentException("Parameter \"pathToDumpTo\" may not be null or whitespace and has to be a valid directory path!"); } Logger.Msg(ConsoleColor.Green, "Dumping original DataBlocks into \"" + pathToDumpTo + "\""); foreach (Type dataBlockType in DataBlockTypes) { Logger.Msg(ConsoleColor.DarkGreen, "> " + dataBlockType.FullName); string text = Path.Combine(pathToDumpTo, dataBlockType.Name + ".json"); string text2 = (string)ImplementationManager.GameTypeByIdentifier("GameDataBlockBase<>").MakeGenericType(dataBlockType).GetMethod("GetFileContents") .Invoke(null, Array.Empty()); if (string.IsNullOrWhiteSpace(text2)) { Logger.Warning(" X This DataBlock does not have any content!"); } if (overrideExistingFiles || !File.Exists(text)) { Logger.Msg(ConsoleColor.DarkYellow, " > Writing to file: " + text); File.WriteAllText(text, text2); } } } } public static class ImplementationManager { private static Dictionary _gameTypeDictionary = new Dictionary(); private static Dictionary _implementationInstances = new Dictionary(); internal static void RegisterGameType(string identifier, Type type) { if (string.IsNullOrWhiteSpace(identifier)) { throw new ArgumentException("Identifier must not be null or whitespace!"); } if (type == null) { throw new ArgumentException("Type must not be null!"); } if (_gameTypeDictionary.ContainsKey(identifier)) { throw new ArgumentException("Duplicate identifier \"" + identifier + "\" used!"); } _gameTypeDictionary.Add(identifier, type); ArchiveLogger.Debug("Registered: " + identifier + " --> " + type.FullName); } internal static Type GameTypeByIdentifier(string identifier) { if (_gameTypeDictionary.TryGetValue(identifier, out var value)) { return value; } throw new ArgumentException("No type found for identifier \"" + identifier + "\", has it not been registered yet?!"); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static T GetOrFindImplementation() { if (_implementationInstances.TryGetValue(typeof(T), out var value)) { return (T)value; } try { foreach (IArchiveModule module in ArchiveMod.Modules) { Type[] types = module.GetType().Assembly.GetTypes(); foreach (Type type2 in types) { if (typeof(T).IsAssignableFrom(type2) && typeof(T) != type2 && Utils.AnyRundownConstraintMatches(type2) && Utils.AnyBuildConstraintMatches(type2)) { ArchiveLogger.Debug($"Found implementation \"{type2.FullName}\" for \"{typeof(T).FullName}\"!"); object obj = Activator.CreateInstance(type2); _implementationInstances.Add(typeof(T), obj); return (T)obj; } } } } catch (ReflectionTypeLoadException ex) { ArchiveLogger.Error("ReflectionTypeLoadException was thrown! This should not happen! Falling back to loaded types ... (Ignore stack trace if no ArgumentException is thrown afterwards)"); ArchiveLogger.Exception(ex); Type type3 = ex.Types.FirstOrDefault((Type type) => typeof(T).IsAssignableFrom(type) && typeof(T) != type && Utils.AnyRundownConstraintMatches(type) && Utils.AnyBuildConstraintMatches(type)); if (type3 != null) { ArchiveLogger.Debug($"Found implementation \"{type3.FullName}\" for \"{typeof(T).FullName}\"!"); object obj2 = Activator.CreateInstance(type3); _implementationInstances.Add(typeof(T), obj2); return (T)obj2; } } throw new ArgumentException("Could not find implementation for type \"" + typeof(T).FullName + "\"!"); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static Type FindTypeInCurrentAppDomain(string typeName, bool exactMatch = false) { IEnumerable enumerable = new List(); try { foreach (PluginInfo value in ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.Values) { Assembly assembly = value.Instance?.GetType().Assembly; if (!(assembly != null)) { continue; } Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (exactMatch) { if (type.FullName == typeName) { return type; } } else if (type.FullName.Contains(typeName)) { return type; } } } } catch (ReflectionTypeLoadException ex) { enumerable = enumerable.Concat(ex.Types.Where((Type t) => t != null)); } try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type[] types = assemblies[i].GetTypes(); foreach (Type type2 in types) { if (exactMatch) { if (type2.FullName == typeName) { return type2; } } else if (type2.FullName.Contains(typeName)) { return type2; } } } } catch (ReflectionTypeLoadException ex2) { enumerable = enumerable.Concat(ex2.Types.Where((Type t) => t != null)); } if (exactMatch) { return enumerable.FirstOrDefault((Type t) => t?.FullName == typeName); } return enumerable.FirstOrDefault((Type t) => (t?.FullName?.Contains(typeName)).GetValueOrDefault()); } } } namespace TheArchive.Core.Localization { internal static class ArchiveLocalizationService { private static IArchiveLogger _logger; private static BaseLocalizationService _localization; private static HashSet _localizationServices = new HashSet(); private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("ArchiveLocalizationService", ConsoleColor.Yellow)); public static Language CurrentLanguage => _localization.CurrentLanguage; public static void SetCurrentLanguage(Language language) { _localization.SetCurrentLanguage(language); foreach (BaseLocalizationService localizationService in _localizationServices) { try { localizationService.SetCurrentLanguage(language); } catch (Exception ex) { Logger.Error($"Exception has been thrown in {localizationService.Identifier} --> {"SetCurrentLanguage"}. {ex}: {ex.Message}"); Logger.Exception(ex); } } } public static void AddTextSetter(ILocalizedTextSetter textSetter, uint textId) { _localization.AddTextSetter(textSetter, textId); } public static void RemoveTextSetter(ILocalizedTextSetter textSetter) { _localization.RemoveTextSetter(textSetter); } public static void AddTextUpdater(ILocalizedTextUpdater textUpdater) { _localization.AddTextUpdater(textUpdater); } public static void RemoveTextUpdater(ILocalizedTextUpdater textUpdater) { _localization.RemoveTextUpdater(textUpdater); } public static string GetById(uint id, string fallback = null) { return _localization.GetById(id, fallback); } public static string Get(T value) { return _localization.Get(value); } public static string Format(uint id, string fallback = null, params object[] args) { return _localization.Format(id, fallback, args); } public static void RegisterLocalizationService(BaseLocalizationService service) { if (service != _localization) { _localizationServices.Add(service); } } public static void Setup(ILocalizationService localization) { _localization = localization as BaseLocalizationService; } } internal abstract class BaseLocalizationService : ILocalizationService { protected readonly IArchiveLogger Logger; private readonly Dictionary _textSetters = new Dictionary(); private readonly HashSet _textUpdaters = new HashSet(); public string Identifier { get; } public Language CurrentLanguage { get; private set; } internal BaseLocalizationService(IArchiveLogger logger, string identifier) { Logger = logger; Identifier = identifier; } public void SetCurrentLanguage(Language language) { CurrentLanguage = language; UpdateAllTexts(); } public abstract string GetById(uint id, string fallback = null); public virtual string Get(T value) { return Get(typeof(T), value); } public abstract string Get(Type type, object value); public virtual string Format(uint id, string fallback = null, params object[] args) { try { return string.Format(GetById(id, fallback), args); } catch (FormatException ex) { string text = $"{"FormatException"} thrown in {"Format"} for id {id}!"; Logger.Error(text); Logger.Exception(ex); return text; } } public virtual void AddTextSetter(ILocalizedTextSetter textSetter, uint textId, string fallback = null) { textSetter.SetText(GetById(textId, fallback)); _textSetters.Add(textSetter, (textId, fallback)); } public virtual void AddTextUpdater(ILocalizedTextUpdater textUpdater) { textUpdater.UpdateText(); _textUpdaters.Add(textUpdater); } public virtual void RemoveTextSetter(ILocalizedTextSetter textSetter) { _textSetters.Remove(textSetter); } public virtual void RemoveTextUpdater(ILocalizedTextUpdater textUpdater) { _textUpdaters.Remove(textUpdater); } public virtual void UpdateAllTexts() { foreach (KeyValuePair textSetter in _textSetters) { textSetter.Deconstruct(out var key, out var value); (uint, string) tuple = value; ILocalizedTextSetter localizedTextSetter = key; var (id, fallback) = tuple; localizedTextSetter.SetText(GetById(id, fallback)); } foreach (ILocalizedTextUpdater textUpdater in _textUpdaters) { textUpdater.UpdateText(); } } protected string GetSingleEnumText(T value, Dictionary enumTexts) { string text = value.ToString(); if (enumTexts.TryGetValue(text, out var value2) && !string.IsNullOrWhiteSpace(value2)) { return value2; } return text; } protected string GetFlagsEnumText(Type type, T value, Dictionary enumTexts) { long num = Convert.ToInt64(value); if (num == 0L) { return GetSingleEnumText(value, enumTexts); } List list = new List(); object[] array = (from object v in Enum.GetValues(type) where Convert.ToInt64(v) != 0 orderby Convert.ToInt64(v) descending select v).ToArray(); long num2 = num; object[] array2 = array; foreach (object obj in array2) { long num3 = Convert.ToInt64(obj); if ((num2 & num3) == num3) { string key = obj.ToString(); if (!enumTexts.TryGetValue(key, out var value2) || string.IsNullOrWhiteSpace(value2)) { return value.ToString(); } list.Add(value2); num2 &= ~num3; } } if (num2 != 0L) { return value.ToString(); } if (list.Count <= 0) { return value.ToString(); } return string.Join(", ", list); } } internal enum FSType { FName, FDescription, FSDisplayName, FSDescription, FSHeader, FSLabelText, FSButtonText, FSTooltipHeader, FSTooltipText } [AttributeUsage(AttributeTargets.Property)] public class IgnoreLocalization : Attribute { } public interface ILocalizationService { Language CurrentLanguage { get; } string GetById(uint id, string fallback = null); string Get(T value); string Format(uint id, string fallback = null, params object[] args); void AddTextSetter(ILocalizedTextSetter textSetter, uint textId, string fallback = null); void AddTextUpdater(ILocalizedTextUpdater textUpdater); void RemoveTextSetter(ILocalizedTextSetter textSetter); void RemoveTextUpdater(ILocalizedTextUpdater textUpdater); } public interface ILocalizedTextSetter { void SetText(string text); } public interface ILocalizedTextUpdater { void UpdateText(); } [Localized] public enum Language { English, Chinese, French, Italian, German, Spanish, Russian, Portuguese_Brazil, Polish, Japanese, Korean } [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Property, Inherited = true)] public class Localized : Attribute { public string UntranslatedText { get; } protected Localized(string text) { UntranslatedText = text; } public Localized() { } } } namespace TheArchive.Core.Localization.Services { internal class FeatureLocalizationService : BaseLocalizationService { private readonly Dictionary> _genericTexts = new Dictionary>(); private readonly Dictionary>> _featureSettingTexts = new Dictionary>>(); private Dictionary>> _internalEnumTexts = new Dictionary>>(); private Dictionary>> _externalEnumTexts = new Dictionary>>(); public Feature Feature { get; } public FeatureLocalizationService(Feature feature, IArchiveLogger logger) : base(logger, feature.Name) { Feature = feature; } private FeatureLocalizationData LoadFeatureLocalizationText() { string location = Feature.FeatureInternal.OriginAssembly.Location; if (string.IsNullOrWhiteSpace(location)) { Logger.Warning($"Feature \"{Feature.Name}\"'s OriginAssembly.Location is null or whitespace. (ID:{Feature.Identifier})"); Logger.Warning("Localization on dynamic assemblies is not supported currently!"); return new FeatureLocalizationData(); } string text = Path.Combine(Path.GetDirectoryName(location), "Localization"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } string path = Path.Combine(text, "Feature_" + Feature.Identifier + "_Localization.json"); if (!File.Exists(path)) { FeatureLocalizationData featureLocalizationData = FeatureInternal.GenerateFeatureLocalization(Feature); File.WriteAllText(path, JsonConvert.SerializeObject((object)featureLocalizationData, ArchiveMod.JsonSerializerSettings)); return featureLocalizationData; } FeatureLocalizationData featureLocalizationData2 = JsonConvert.DeserializeObject(File.ReadAllText(path), ArchiveMod.JsonSerializerSettings); string input = JsonConvert.SerializeObject((object)featureLocalizationData2, ArchiveMod.JsonSerializerSettings); FeatureLocalizationData featureLocalizationData3 = FeatureInternal.GenerateFeatureLocalization(Feature, featureLocalizationData2); string text2 = JsonConvert.SerializeObject((object)featureLocalizationData3, ArchiveMod.JsonSerializerSettings); if (text2.HashString() != input.HashString()) { File.WriteAllText(path, text2); } return featureLocalizationData3; } public void Setup() { _featureSettingTexts.Clear(); _genericTexts.Clear(); FeatureLocalizationData featureLocalizationData = LoadFeatureLocalizationText(); foreach (KeyValuePair>> featureSettingText in featureLocalizationData.Internal.FeatureSettingTexts) { Dictionary> dictionary = new Dictionary>(); foreach (KeyValuePair> item in featureSettingText.Value) { Dictionary dictionary2 = new Dictionary(); foreach (Language value5 in Enum.GetValues(typeof(Language))) { if (!item.Value.TryGetValue(value5, out var value)) { value = item.Value.FirstOrDefault().Value; } dictionary2[value5] = value; } dictionary[item.Key] = dictionary2; } _featureSettingTexts[featureSettingText.Key] = dictionary; } foreach (KeyValuePair>> featureSettingText2 in featureLocalizationData.External.FeatureSettingTexts) { Dictionary> dictionary3 = new Dictionary>(); foreach (KeyValuePair> item2 in featureSettingText2.Value) { Dictionary dictionary4 = new Dictionary(); foreach (Language value6 in Enum.GetValues(typeof(Language))) { if (!item2.Value.TryGetValue(value6, out var value2)) { value2 = item2.Value.FirstOrDefault().Value; } dictionary4[value6] = value2; } dictionary3[item2.Key] = dictionary4; } _featureSettingTexts[featureSettingText2.Key] = dictionary3; } foreach (KeyValuePair genericText in featureLocalizationData.GenericTexts) { genericText.Deconstruct(out var key3, out var value3); uint key4 = key3; GenericLocalizationData genericLocalizationData = value3; Dictionary dictionary5 = new Dictionary(); foreach (Language value7 in Enum.GetValues(typeof(Language))) { if (!genericLocalizationData.Data.TryGetValue(value7, out var value4)) { genericLocalizationData.Data.TryGetValue(Language.English, out value4); } dictionary5[value7] = value4; } _genericTexts[key4] = dictionary5; } _internalEnumTexts = featureLocalizationData.Internal.EnumTexts; _externalEnumTexts = featureLocalizationData.External.EnumTexts; ArchiveLocalizationService.RegisterLocalizationService(this); } public bool TryGetFSText(string propID, FSType type, out string text) { if (!_featureSettingTexts.TryGetValue(propID, out var value) || !value.TryGetValue(type, out var value2) || !value2.TryGetValue(base.CurrentLanguage, out text) || string.IsNullOrWhiteSpace(text)) { text = null; return false; } return true; } public bool TryGetFSEnumText(Type enumType, out Dictionary enumTexts) { if (enumType == null) { enumTexts = null; return false; } string[] names = Enum.GetNames(enumType); if ((!_internalEnumTexts.TryGetValue(enumType.FullName, out var value) || !value.TryGetValue(base.CurrentLanguage, out enumTexts) || enumTexts.Count != names.Length || enumTexts.Any((KeyValuePair p) => string.IsNullOrWhiteSpace(p.Value))) && (!_externalEnumTexts.TryGetValue(enumType.FullName, out value) || !value.TryGetValue(base.CurrentLanguage, out enumTexts) || enumTexts.Count != names.Length || enumTexts.Any((KeyValuePair p) => string.IsNullOrWhiteSpace(p.Value)))) { enumTexts = null; return false; } return true; } public override string GetById(uint id, string fallback = null) { if (!_genericTexts.TryGetValue(id, out var value) || !value.TryGetValue(base.CurrentLanguage, out var value2)) { if (!string.IsNullOrWhiteSpace(fallback)) { return fallback; } return $"UNKNOWN ID: {id}"; } return value2; } public override string Get(Type type, object value) { if (type.IsEnum) { if (!TryGetFSEnumText(type, out var enumTexts)) { return value.ToString(); } if (type.GetCustomAttribute() != null) { return GetFlagsEnumText(type, value, enumTexts); } return GetSingleEnumText(value, enumTexts); } return value.ToString(); } } internal class ModuleLocalizationService : BaseLocalizationService { private readonly IArchiveModule _archiveModule; private ModuleLocalizationData _localizationData; public Type ModuleType { get; } public ModuleLocalizationService(IArchiveModule archiveModule, Type moduleType, IArchiveLogger logger) : base(logger, moduleType.FullName) { _archiveModule = archiveModule; ModuleType = moduleType; } public void Setup() { string text = ModuleType.Assembly.Location; if (string.IsNullOrWhiteSpace(text)) { text = Path.Combine(Paths.ConfigPath, "Localization/"); } string text2 = Path.Combine(Path.GetDirectoryName(text), "Localization/"); if (!Directory.Exists(text2)) { Directory.CreateDirectory(text2); } string name = ModuleType.Assembly.GetName().Name; string path = "Module_" + name + "_Localization.json"; string path2 = Path.Combine(text2, path); if (!File.Exists(path2)) { File.WriteAllText(path2, JsonConvert.SerializeObject((object)new ModuleLocalizationData(), ArchiveMod.JsonSerializerSettings)); } _localizationData = JsonConvert.DeserializeObject(File.ReadAllText(path2), ArchiveMod.JsonSerializerSettings); ArchiveLocalizationService.RegisterLocalizationService(this); } public override string GetById(uint id, string fallback = null) { if (!_localizationData.TryGetGenericText(id, base.CurrentLanguage, out var value)) { if (!string.IsNullOrWhiteSpace(fallback)) { return fallback; } return $"UNKNOWN ID: {id}"; } return value; } public override string Get(T value) { Type typeFromHandle = typeof(T); if (typeFromHandle.IsEnum) { if (!_localizationData.EnumTexts.TryGetValue(typeFromHandle.FullName, out var value2) || !value2.Entries.TryGetValue(base.CurrentLanguage, out var value3)) { return value.ToString(); } if (typeFromHandle.GetCustomAttribute() != null) { return GetFlagsEnumText(typeFromHandle, value, value3); } return GetSingleEnumText(value, value3); } return Get(typeFromHandle, value); } public override string Get(Type type, object value) { return value.ToString(); } } } namespace TheArchive.Core.Localization.Data { public class EnumLocalizationData { public string Comment { get; set; } = string.Empty; public Dictionary> Entries { get; set; } = new Dictionary>(); public void SetupEmptyForEnum() where T : struct, Enum { Entries = Utils.GetEmptyLanguageDictionary(Utils.GetEmptyEnumDictionary); } } internal class FeatureLocalizationData { public FeatureSettingLocalizationData Internal { get; set; } = new FeatureSettingLocalizationData(); public FeatureSettingLocalizationData External { get; set; } = new FeatureSettingLocalizationData(); public Dictionary GenericTexts { get; set; } } internal class FeatureSettingLocalizationData { public Dictionary>> FeatureSettingTexts { get; set; } = new Dictionary>>(); public Dictionary>> EnumTexts { get; set; } = new Dictionary>>(); } public class GenericLocalizationData { public string Comment { get; set; } = string.Empty; public Dictionary Data { get; set; } = new Dictionary(); public bool TryGet(Language language, out string value, string fallback = null) { if (Data.TryGetValue(language, out value) && !string.IsNullOrWhiteSpace(value)) { return true; } if (Data.TryGetValue(Language.English, out value) && !string.IsNullOrWhiteSpace(value)) { return true; } if (fallback == null) { value = "Error: All values null."; return false; } value = fallback; return true; } } public class GroupLocalizationData { public Dictionary DisplayName { get; set; } = Utils.GetEmptyLanguageDictionary(); public Dictionary Description { get; set; } = Utils.GetEmptyLanguageDictionary(); public string GetLocalizedDisplayName(Language language, string fallback = null) { return GetLocalized(language, DisplayName, fallback); } public string GetLocalizedDescription(Language language, string fallback = null) { return GetLocalized(language, Description, fallback); } private static string GetLocalized(Language language, Dictionary dictionary, string fallback = null) { if (dictionary.TryGetValue(language, out var value) && !string.IsNullOrWhiteSpace(value)) { return value; } if (dictionary.TryGetValue(Language.English, out value) && !string.IsNullOrWhiteSpace(value)) { return value; } return fallback; } public void AppendData(GroupLocalizationData data) { if (data == null) { return; } Language key; string value; foreach (KeyValuePair item in data.DisplayName) { item.Deconstruct(out key, out value); Language key2 = key; string value2 = value; if (!DisplayName.ContainsKey(key2) || string.IsNullOrWhiteSpace(DisplayName[key2])) { DisplayName[key2] = value2; } } foreach (KeyValuePair item2 in data.Description) { item2.Deconstruct(out key, out value); Language key3 = key; string value3 = value; if (!Description.ContainsKey(key3) || string.IsNullOrWhiteSpace(Description[key3])) { Description[key3] = value3; } } } } public class ModuleLocalizationData { public Dictionary GenericTexts { get; set; } = new Dictionary(); public Dictionary EnumTexts { get; set; } = new Dictionary(); public bool TryGetGenericText(uint id, Language language, out string value, string fallback = null) { value = fallback; if (!GenericTexts.TryGetValue(id, out var value2) || value2 == null) { return false; } if (value2.TryGet(language, out var value3) && !string.IsNullOrWhiteSpace(value3)) { value = value3; return true; } if (value2.TryGet(Language.English, out value3) && !string.IsNullOrWhiteSpace(value3)) { value = value3; return true; } return false; } } } namespace TheArchive.Core.Interop { internal static class Interop { internal static void OnDataBlocksReady() { try { LocaliaCoreInterop.TryApplyPatch(); } catch (Exception ex) { ArchiveLogger.Exception(ex); } } } internal static class LocaliaCoreInterop { [CompilerGenerated] private sealed class d__11 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private CodeInstruction <>2__current; private int <>l__initialThreadId; private IEnumerable instructions; public IEnumerable <>3__instructions; private bool 5__2; private IEnumerator <>7__wrap2; private CodeInstruction 5__4; CodeInstruction IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__11(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 2u) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = false; <>7__wrap2 = instructions.GetEnumerator(); <>1__state = -3; goto IL_0135; case 1: <>1__state = -3; goto IL_0135; case 2: <>1__state = -3; 5__2 = true; goto IL_010f; case 3: { <>1__state = -3; 5__4 = null; goto IL_0135; } IL_0135: if (<>7__wrap2.MoveNext()) { 5__4 = <>7__wrap2.Current; if (5__2 || 5__4.opcode != OpCodes.Callvirt) { <>2__current = 5__4; <>1__state = 1; return true; } if (5__4.operand is MethodInfo methodInfo && methodInfo.Name == "ToString") { MethodInfo method = typeof(LocaliaCoreInterop).GetMethod("DoThings", BindingFlags.Static | BindingFlags.NonPublic); <>2__current = new CodeInstruction(OpCodes.Call, (object)method); <>1__state = 2; return true; } goto IL_010f; } <>m__Finally1(); <>7__wrap2 = null; return false; IL_010f: <>2__current = 5__4; <>1__state = 3; return true; } } 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 (<>7__wrap2 != null) { <>7__wrap2.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__11 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__11(0); } d__.instructions = <>3__instructions; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private const string LOCALIA_CORE_GUID = "Localia.LocaliaCore"; private const string LOCALIA_MODLIST_GUID = "Localia.ModList"; private static bool _hasExecuted; private static Harmony _harmonyInstance; private static Assembly _localiaCoreAssembly; private static Assembly _localiaModlistAssembly; private static IArchiveLogger _logger; private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateArSubLoggerInstance("LocaliaCoreInterop")); internal static void TryApplyPatch() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown if (_hasExecuted) { return; } _hasExecuted = true; try { PluginInfo value = ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.FirstOrDefault((KeyValuePair kvp) => kvp.Key == "Localia.LocaliaCore").Value; _localiaCoreAssembly = ((value == null) ? null : value.Instance?.GetType().Assembly); if (_localiaCoreAssembly == null) { return; } _harmonyInstance = new Harmony("dev.AuriRex.gtfo.TheArchive_LocaliaCoreInterop"); MethodInfo methodInfo = AccessTools.GetTypesFromAssembly(_localiaCoreAssembly).FirstOrDefault((Type t) => t.Name == "Patch_DoChangeState")?.GetMethod("Postfix"); MethodInfo method = typeof(LocaliaCoreInterop).GetMethod("Transpiler", BindingFlags.Static | BindingFlags.NonPublic); _harmonyInstance.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null); PluginInfo value2 = ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.FirstOrDefault((KeyValuePair kvp) => kvp.Key == "Localia.ModList").Value; _localiaModlistAssembly = ((value2 == null) ? null : value2.Instance?.GetType().Assembly); if (_localiaModlistAssembly == null) { return; } Dictionary dictionary = (Dictionary)(AccessTools.GetTypesFromAssembly(_localiaModlistAssembly).FirstOrDefault((Type t) => t.Name == "ModList_Manager")?.GetField("myModDic", BindingFlags.Static | BindingFlags.Public)?.GetValue(null)); if (dictionary == null) { return; } foreach (KeyValuePair module in ArchiveModuleChainloader.Instance.Modules) { module.Deconstruct(out var key, out var value3); string text = key; ModuleInfo moduleInfo = value3; string key2 = text.Replace(';', ' '); dictionary.Add(key2, moduleInfo.Metadata.Name); } } catch (Exception ex) { Logger.Error("LocaliaCoreInterop failed!:"); Logger.Exception(ex); } } private static StringBuilder DoThings(StringBuilder stringBuilder) { List list = (List)((_localiaCoreAssembly.GetTypes().FirstOrDefault((Type t) => t.Name == "Network_Manager")?.GetField("myModList", BindingFlags.Static | BindingFlags.Public))?.GetValue(null)); if (list == null) { return stringBuilder; } foreach (KeyValuePair module in ArchiveModuleChainloader.Instance.Modules) { module.Deconstruct(out var key, out var _); string text = key.Replace(';', ' '); list.Add(text); StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder); handler.AppendFormatted(text); handler.AppendLiteral(";"); stringBuilder.Append(ref handler); } return stringBuilder; } [IteratorStateMachine(typeof(d__11))] private static IEnumerable Transpiler(IEnumerable instructions) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__11(-2) { <>3__instructions = instructions }; } } } namespace TheArchive.Core.FeaturesAPI { public class DynamicFeatureSettingsHelper : FeatureSettingsHelper { public DynamicFeatureSettingsHelper(Feature feature, FeatureSettingsHelper parentHelper = null) { base.Feature = feature; base.ParentHelper = parentHelper; } public DynamicFeatureSettingsHelper Initialize(Type typeToCheck, object instance) { PopulateSettings(typeToCheck, instance, string.Empty); return this; } internal override void SetupViaFeatureInstance(object objectInstance) { throw new NotImplementedException(); } public override object GetFeatureInstance() { throw new NotImplementedException(); } } [UsedImplicitly(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithInheritors)] public abstract class Feature { public static class Is { public static bool R1 { get; internal set; } public static bool R1OrLater { get; internal set; } public static bool R2 { get; internal set; } public static bool R2OrLater { get; internal set; } public static bool R3 { get; internal set; } public static bool R3OrLater { get; internal set; } public static bool R4 { get; internal set; } public static bool R4OrLater { get; internal set; } public static bool R5 { get; internal set; } public static bool R5OrLater { get; internal set; } public static bool R6 { get; internal set; } public static bool R6OrLater { get; internal set; } public static bool R7 { get; internal set; } public static bool R7OrLater { get; internal set; } public static bool A1 { get; internal set; } public static bool A1OrLater { get; internal set; } public static bool A2 { get; internal set; } public static bool A2OrLater { get; internal set; } public static bool A3 { get; internal set; } public static bool A3OrLater { get; internal set; } public static bool A4 { get; internal set; } public static bool A4OrLater { get; internal set; } public static bool A5 { get; internal set; } public static bool A5OrLater { get; internal set; } public static bool A6 { get; internal set; } public static bool A6OrLater { get; internal set; } public static bool R8 { get; internal set; } public static bool R8OrLater { get; internal set; } } private string _identifier; private string _guid; public string Identifier => _identifier ?? (_identifier = GetType().Name); public string GUID => _guid ?? (_guid = GetType().FullName); public bool IsHidden => FeatureInternal.HideInModSettings; public bool BelongsToGroup => Group != null; public bool HasAdditionalSettings => FeatureInternal.HasAdditionalSettings; public bool AllAdditionalSettingsAreHidden => FeatureInternal.AllAdditionalSettingsAreHidden; public IEnumerable SettingsHelpers => FeatureInternal.Settings; public ILocalizationService Localization => FeatureInternal.Localization; public virtual Type[] ExternalLocalizedTypes => Array.Empty(); public bool IsAutomated => FeatureInternal.AutomatedFeature; public bool DisableModSettingsButton => FeatureInternal.DisableModSettingsButton; public IArchiveLogger FeatureLogger => FeatureInternal.FeatureLoggerInstance; public bool Enabled { get; internal set; } public bool IsLoadedAndNotDisabledInternally => !FeatureInternal.InternalDisabled; public Utils.RundownFlags AppliesToRundowns => FeatureInternal.Rundowns; public static GameBuildInfo BuildInfo { get; internal set; } public abstract string Name { get; } public virtual string Description => string.Empty; public virtual GroupBase Group => ModuleGroup; public virtual TopLevelGroup TopLevelGroup { get; } protected ModuleGroup ModuleGroup => FeatureInternal.ModuleGroup; public virtual bool RequiresRestart => false; public virtual bool SkipInitialOnEnable => false; public virtual bool RequiresUnityAudioListener => false; public virtual bool InlineSettingsIntoParentMenu => false; internal FeatureInternal FeatureInternal { get; set; } public static bool IsPlayingModded => ArchiveMod.IsPlayingModded; public static bool DevMode => ArchiveMod.Settings.FeatureDevMode; public static bool GameDataInited { get; internal set; } public static bool IsApplicationFocused { get; internal set; } public static bool IsApplicationQuitting { get; internal set; } public static bool DataBlocksReady { get; internal set; } public static int CurrentGameState { get; internal set; } public static int PreviousGameState { get; internal set; } public void RequestRestart() { FeatureManager.RequestRestart(this); } public void RevokeRestartRequest() { FeatureManager.RevokeRestartRequest(this); } protected void RequestDisable(string reason = null) { FeatureInternal.RequestDisable(reason); } public virtual bool ShouldInit() { return true; } public virtual bool LateShouldInit() { return true; } public virtual void Init() { } public virtual void OnEnable() { } public virtual void OnDisable() { } public virtual void OnGameDataInitialized() { } public virtual void OnApplicationFocusChanged(bool focus) { } public virtual void OnDatablocksReady() { } public virtual void Update() { } public virtual void LateUpdate() { } public virtual void OnFeatureSettingChanged(FeatureSetting setting) { } public virtual void OnGameStateChanged(int state) { } [Obsolete("Has not been implemented properly; does not work!")] public virtual void OnAreaCull(object lgArea, bool active) { } public virtual void OnButtonPressed(ButtonSetting setting) { } public virtual void OnQuit() { } public bool MarkSettingsDirty(object settings) { return FeatureInternal.MarkSettingsDirty(settings); } [MethodImpl(MethodImplOptions.NoInlining)] public static bool MarkSettingsAsDirty(object settings, Type featureType = null) { if (featureType == null) { featureType = new StackTrace().GetFrame(1).GetMethod().DeclaringType; ArchiveLogger.Debug(featureType.FullName); int num = 0; while (!typeof(Feature).IsAssignableFrom(featureType) && num <= 5) { if (featureType.IsNested) { featureType = featureType.DeclaringType; } num++; } } if (!typeof(Feature).IsAssignableFrom(featureType)) { throw new InvalidOperationException("Only call from within a Feature class or provide the correct type!"); } return (FeatureManager.GetByType(featureType) ?? throw new InvalidOperationException("Feature Type could not be found!")).MarkSettingsDirty(settings); } public static bool MarkSettingsAsDirty(T settings) { if (!typeof(T).IsNested) { throw new InvalidOperationException("Type must be a nested class of your Feature implementation to use this method!"); } Type declaringType = typeof(T).DeclaringType; return MarkSettingsAsDirty(settings, declaringType); } internal static void SetupIs() { Is.R1 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownOne); Is.R1OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownOne.ToLatest()); Is.R2 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownTwo); Is.R2OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownTwo.ToLatest()); Is.R3 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownThree); Is.R3OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownThree.ToLatest()); Is.R4 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownFour); Is.R4OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownFour.ToLatest()); Is.R5 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownFive); Is.R5OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownFive.ToLatest()); Is.R6 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownSix); Is.R6OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownSix.ToLatest()); Is.R7 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownSeven); Is.R7OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownSeven.ToLatest()); Is.A1 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltOne); Is.A1OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltOne.ToLatest()); Is.A2 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltTwo); Is.A2OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltTwo.ToLatest()); Is.A3 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltThree); Is.A3OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltThree.ToLatest()); Is.A4 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltFour); Is.A4OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltFour.ToLatest()); Is.A5 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltFive); Is.A5OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltFive.ToLatest()); Is.A6 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltSix); Is.A6OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownAltSix.ToLatest()); Is.R8 = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownEight); Is.R8OrLater = BuildInfo.Rundown.IsIncludedIn(Utils.RundownFlags.RundownEight.ToLatest()); } } internal class FeatureInternal { public delegate void Update(); public delegate void LateUpdate(); private class FeaturePatchInfo { internal ArchivePatch ArchivePatchInfo { get; } internal MethodBase OriginalMethod { get; } internal HarmonyMethod HarmonyPrefixMethod { get; } internal HarmonyMethod HarmonyPostfixMethod { get; } internal HarmonyMethod HarmonyTranspilerMethod { get; } internal HarmonyMethod HarmonyFinalizerMethod { get; } internal HarmonyMethod HarmonyILManipulatorMethod { get; } internal HarmonyMethod HarmonyOriginalReverseMethod { get; } internal HarmonyMethod HarmonySnapshotReverseMethod { get; } internal MethodInfo PrefixPatchMethod { get; private set; } internal MethodInfo PostfixPatchMethod { get; private set; } internal MethodInfo TranspilerPatchMethod { get; private set; } internal MethodInfo FinalizerPatchMethod { get; private set; } internal MethodInfo ILManipulatorPatchMethod { get; private set; } internal MethodInfo OriginalReversePatchMethod { get; private set; } internal MethodInfo SnapshotReversePatchMethod { get; private set; } public FeaturePatchInfo(MethodBase original, MethodInfo prefix, MethodInfo postfix, MethodInfo transpiler, MethodInfo finalizer, MethodInfo ilManipulator, MethodInfo originalReverse, MethodInfo snapshotReverse, ArchivePatch archivePatch, bool wrapTryCatch = true) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Expected O, but got Unknown //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Expected O, but got Unknown //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Expected O, but got Unknown //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Expected O, but got Unknown //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Expected O, but got Unknown //IL_039f: Expected O, but got Unknown OriginalMethod = original; PrefixPatchMethod = prefix; PostfixPatchMethod = postfix; TranspilerPatchMethod = transpiler; FinalizerPatchMethod = finalizer; ILManipulatorPatchMethod = ilManipulator; OriginalReversePatchMethod = originalReverse; SnapshotReversePatchMethod = snapshotReverse; ArchivePatchInfo = archivePatch; if (prefix != null) { HarmonyPrefixMethod = new HarmonyMethod(prefix) { wrapTryCatch = wrapTryCatch, priority = archivePatch.Priority, after = prefix.GetCustomAttributes(inherit: true).SelectMany((ArchiveAfter attr) => attr.After).Distinct() .ToArray(), before = prefix.GetCustomAttributes(inherit: true).SelectMany((ArchiveBefore attr) => attr.Before).Distinct() .ToArray() }; } if (postfix != null) { HarmonyPostfixMethod = new HarmonyMethod(postfix) { wrapTryCatch = wrapTryCatch, priority = archivePatch.Priority, after = postfix.GetCustomAttributes(inherit: true).SelectMany((ArchiveAfter attr) => attr.After).Distinct() .ToArray(), before = postfix.GetCustomAttributes(inherit: true).SelectMany((ArchiveBefore attr) => attr.Before).Distinct() .ToArray() }; } if (transpiler != null) { HarmonyTranspilerMethod = new HarmonyMethod(transpiler) { wrapTryCatch = wrapTryCatch, priority = archivePatch.Priority, after = transpiler.GetCustomAttributes(inherit: true).SelectMany((ArchiveAfter attr) => attr.After).Distinct() .ToArray(), before = transpiler.GetCustomAttributes(inherit: true).SelectMany((ArchiveBefore attr) => attr.Before).Distinct() .ToArray() }; } if (finalizer != null) { HarmonyFinalizerMethod = new HarmonyMethod(finalizer) { wrapTryCatch = wrapTryCatch, priority = archivePatch.Priority, after = finalizer.GetCustomAttributes(inherit: true).SelectMany((ArchiveAfter attr) => attr.After).Distinct() .ToArray(), before = finalizer.GetCustomAttributes(inherit: true).SelectMany((ArchiveBefore attr) => attr.Before).Distinct() .ToArray() }; } if (ilManipulator != null) { HarmonyILManipulatorMethod = new HarmonyMethod(ilManipulator) { wrapTryCatch = wrapTryCatch, priority = archivePatch.Priority, after = ilManipulator.GetCustomAttributes(inherit: true).SelectMany((ArchiveAfter attr) => attr.After).Distinct() .ToArray(), before = ilManipulator.GetCustomAttributes(inherit: true).SelectMany((ArchiveBefore attr) => attr.Before).Distinct() .ToArray() }; } if (originalReverse != null) { HarmonyOriginalReverseMethod = new HarmonyMethod(originalReverse) { wrapTryCatch = wrapTryCatch, reversePatchType = (HarmonyReversePatchType)0 }; } if (snapshotReverse != null) { HarmonySnapshotReverseMethod = new HarmonyMethod(snapshotReverse) { wrapTryCatch = wrapTryCatch, reversePatchType = (HarmonyReversePatchType)1 }; } } } [Flags] internal enum InternalDisabledReason { None = 0, RundownConstraintMismatch = 1, BuildConstraintMismatch = 2, MainInitMethodFailed = 4, PatchInitMethodFailed = 8, UpdateMethodFailed = 0x10, LateUpdateMethodFailed = 0x20, ForceDisabled = 0x40, DisabledViaShouldInit = 0x80, DisabledByRequest = 0x100, TypeLoadException = 0x200, Other = 0x400, ShouldInitFailed = 0x800, LateShouldInitFailed = 0x1000, DisabledViaLateShouldInit = 0x2000 } private string _asmDisplayName; private Feature _feature; private Harmony _harmonyInstance; private readonly List _patchTypes = new List(); private readonly HashSet _patchInfos = new HashSet(); private readonly HashSet _settingsHelpers = new HashSet(); private PropertyInfo _isEnabledPropertyInfo; private bool _onGameStateChangedMethodUsesGameEnum; private MethodInfo _onGameStateChangedMethodInfo; private MethodInfo _onLGAreaCullUpdateMethodInfo; private Type _featureType; private PropertyInfo _namePropertyInfo; private PropertyInfo _descriptionPropertyInfo; private bool _doLocalizeName; private bool _doLocalizeDescription; private static readonly HashSet _usedIdentifiers = new HashSet(); private static readonly HashSet _usedGuids = new HashSet(); private static readonly IArchiveLogger _FILogger = LoaderWrapper.CreateArSubLoggerInstance("FeatureInternal", ConsoleColor.DarkYellow); private static Type _gameStateType; private static Type _lgAreaType; internal string ModuleGroupId { get; set; } internal ModuleGroup ModuleGroup { get; private set; } internal FeatureLocalizationService Localization { get; private set; } private static GameBuildInfo BuildInfo => Feature.BuildInfo; internal bool InternalDisabled { get; private set; } private InternalDisabledReason DisabledReason { get; set; } internal bool HasUpdateMethod => UpdateDelegate != null; internal Update UpdateDelegate { get; private set; } internal bool HasLateUpdateMethod => LateUpdateDelegate != null; internal LateUpdate LateUpdateDelegate { get; private set; } internal bool HideInModSettings { get; private set; } internal bool DoNotSaveToConfig { get; private set; } internal bool AutomatedFeature { get; private set; } internal bool DisableModSettingsButton { get; private set; } internal bool HasAdditionalSettings => _settingsHelpers.Count > 0; internal bool AllAdditionalSettingsAreHidden { get; private set; } = true; internal bool InitialEnabledState { get; private set; } internal IEnumerable Settings => _settingsHelpers; internal Utils.RundownFlags Rundowns { get; private set; } internal IArchiveLogger FeatureLoggerInstance { get; private set; } internal Assembly OriginAssembly { get; private set; } internal IArchiveModule ArchiveModule { get; private set; } internal string DisplayName { get { if (!_doLocalizeName) { return _feature.Name; } string propID = _featureType.FullName + ".Name"; if (Localization.TryGetFSText(propID, FSType.FName, out var text)) { return text; } return _feature.Name; } } internal string DisplayDescription { get { if (!_doLocalizeDescription) { return _feature.Description; } string propID = _featureType.FullName + ".Description"; if (Localization.TryGetFSText(propID, FSType.FDescription, out var text)) { return text; } return _feature.Description; } } internal string AsmDisplayName { get { if (string.IsNullOrEmpty(_asmDisplayName)) { _asmDisplayName = OriginAssembly.GetCustomAttribute()?.DisplayName ?? OriginAssembly.GetName().Name; } return _asmDisplayName; } } internal string CriticalInfo { get { if (!InternalDisabled) { return string.Empty; } return "<#F00>" + ArchiveLocalizationService.GetById(2u, "Disabled") + ": " + ArchiveLocalizationService.Get(DisabledReason); } } private FeatureInternal() { } internal static void CreateAndAssign(Feature feature, IArchiveModule module) { if ((object)_gameStateType == null) { _gameStateType = ImplementationManager.GameTypeByIdentifier("eGameStateName"); } if ((object)_lgAreaType == null) { _lgAreaType = ImplementationManager.GameTypeByIdentifier("LG_Area"); } FeatureInternal featureInternal2 = (feature.FeatureInternal = new FeatureInternal()); try { featureInternal2.Init(feature, module); } catch (TypeLoadException ex) { _FILogger.Error("Initialization of " + featureInternal2._feature.Identifier + " failed! - " + ex.GetType().FullName); _FILogger.Warning("!!! PLEASE FIX THIS !!!"); _FILogger.Debug("StackTrace:\n" + ex.Message + "\n" + ex.StackTrace); featureInternal2.InternallyDisableFeature(InternalDisabledReason.TypeLoadException); _FILogger.Msg(ConsoleColor.Magenta, $"Feature \"{featureInternal2._feature.Identifier}\" has been disabled internally! ({featureInternal2.DisabledReason})"); return; } featureInternal2.AfterInit(); } private void Init(Feature feature, IArchiveModule module) { //IL_0a3e: Unknown result type (might be due to invalid IL or missing references) //IL_0a48: Expected O, but got Unknown _feature = feature; ArchiveModule = module; _featureType = _feature.GetType(); OriginAssembly = _featureType.Assembly; string name = ArchiveModule.GetType().Assembly.GetName().Name; ModuleGroupId = name + ".ModuleGroup"; ModuleGroup = GroupManager.GetModuleGroup(name); Localization = new FeatureLocalizationService(feature, feature.FeatureLogger); try { Localization.Setup(); } catch (Exception ex) { _FILogger.Error("Error while trying to setup feature localization for \"" + _feature.Identifier + "\"!"); _FILogger.Exception(ex); } _FILogger.Msg(ConsoleColor.Black, "-"); _FILogger.Msg(ConsoleColor.Green, "Initializing " + _feature.Identifier + " ..."); if (!_usedIdentifiers.Add(_feature.Identifier)) { _FILogger.Warning($"Provided feature id \"{_feature.Identifier}\" has already been registered by {FeatureManager.GetById(_feature.Identifier)}!"); } if (!_usedGuids.Add(_feature.GUID)) { throw new ArchiveFeatureDuplicateIDException($"Provided feature guid \"{_feature.GUID}\" has already been registered by {FeatureManager.GetByGuid(_feature.GUID)}!"); } FeatureLoggerInstance = LoaderWrapper.CreateArSubLoggerInstance("F::" + _feature.Identifier, ConsoleColor.Cyan); HideInModSettings = _featureType.GetCustomAttribute() != null; DoNotSaveToConfig = _featureType.GetCustomAttribute() != null; AutomatedFeature = _featureType.GetCustomAttribute() != null; DisableModSettingsButton = _featureType.GetCustomAttribute() != null; if (_featureType.GetCustomAttribute() != null) { InternalDisabled = true; DisabledReason |= InternalDisabledReason.ForceDisabled; } foreach (RundownConstraint customAttribute2 in _featureType.GetCustomAttributes()) { Rundowns |= customAttribute2.Rundowns; } if (!Utils.AnyRundownConstraintMatches(_featureType)) { InternalDisabled = true; DisabledReason |= InternalDisabledReason.RundownConstraintMismatch; } if (!Utils.AnyBuildConstraintMatches(_featureType)) { InternalDisabled = true; DisabledReason |= InternalDisabledReason.BuildConstraintMismatch; } _namePropertyInfo = _featureType.GetProperty("Name"); _doLocalizeName = _namePropertyInfo?.GetCustomAttribute() == null; _descriptionPropertyInfo = _featureType.GetProperty("Description"); _doLocalizeDescription = _descriptionPropertyInfo?.GetCustomAttribute() == null; try { if (!_feature.ShouldInit()) { InternalDisabled = true; DisabledReason |= InternalDisabledReason.DisabledViaShouldInit; } } catch (Exception ex2) { _FILogger.Error($"{"ShouldInit"} method on {"Feature"} failed: {ex2}: {ex2.Message}"); _FILogger.Exception(ex2); InternalDisabled = true; DisabledReason |= InternalDisabledReason.ShouldInitFailed; } if (InternalDisabled) { _FILogger.Msg(ConsoleColor.Magenta, $"Feature \"{_feature.Identifier}\" has been disabled internally! ({DisabledReason})"); return; } MethodInfo[] methods = _featureType.GetMethods(); Delegate @delegate = methods.FirstOrDefault((MethodInfo mi) => (mi.Name == "Update" || mi.GetCustomAttribute() != null) && mi.GetParameters().Length == 0 && !mi.IsStatic && mi.DeclaringType != typeof(Feature) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi))?.CreateDelegate(typeof(Update), _feature); if ((object)@delegate != null) { _FILogger.Debug("Update method found."); UpdateDelegate = (Update)@delegate; } Delegate delegate2 = methods.FirstOrDefault((MethodInfo mi) => (mi.Name == "LateUpdate" || mi.GetCustomAttribute() != null) && mi.GetParameters().Length == 0 && !mi.IsStatic && mi.DeclaringType != typeof(Feature) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi))?.CreateDelegate(typeof(LateUpdate), _feature); if ((object)delegate2 != null) { _FILogger.Debug("LateUpdate method found."); LateUpdateDelegate = (LateUpdate)delegate2; } PropertyInfo[] properties = _featureType.GetProperties(AccessTools.allDeclared); IEnumerable enumerable = properties.Where((PropertyInfo pi) => pi.GetCustomAttribute() != null); _isEnabledPropertyInfo = properties.FirstOrDefault((PropertyInfo pi) => (pi.Name == "IsEnabled" || pi.GetCustomAttribute() != null) && pi.SetMethod != null && pi.GetMethod != null && pi.GetMethod.IsStatic && pi.GetMethod.ReturnType == typeof(bool)); if (_isEnabledPropertyInfo != null) { _FILogger.Debug($"Found IsEnabled property \"{_isEnabledPropertyInfo.Name}\" on Feature {_feature.Identifier}."); } PropertyInfo propertyInfo = properties.FirstOrDefault((PropertyInfo pi) => (pi.Name == "FeatureLogger" || pi.GetCustomAttribute() != null) && pi.SetMethod != null && pi.GetMethod != null && pi.GetMethod.IsStatic && pi.GetMethod.ReturnType == typeof(IArchiveLogger)); if (propertyInfo != null) { _FILogger.Debug($"Found FeatureLogger property \"{propertyInfo.Name}\" on Feature {_feature.Identifier}. Populating ..."); propertyInfo.SetValue(null, FeatureLoggerInstance); } PropertyInfo propertyInfo2 = properties.FirstOrDefault((PropertyInfo pi) => (pi.Name == "Localization" || pi.GetCustomAttribute() != null) && pi.SetMethod != null && pi.GetMethod != null && pi.GetMethod.IsStatic && pi.GetMethod.ReturnType == typeof(ILocalizationService)); if (propertyInfo2 != null) { _FILogger.Debug($"Found Localization property \"{propertyInfo2.Name}\" on Feature {_feature.Identifier}. Populating ..."); propertyInfo2.SetValue(null, _feature.Localization); } PropertyInfo propertyInfo3 = properties.FirstOrDefault((PropertyInfo pi) => (pi.Name == "Instance" || pi.Name == "Self" || pi.GetCustomAttribute() != null) && pi.SetMethod != null && pi.GetMethod != null && pi.GetMethod.IsStatic && pi.GetMethod.ReturnType.IsAssignableTo(_featureType)); if (propertyInfo3 != null) { _FILogger.Debug($"Found Instance/Self property \"{propertyInfo3.Name}\" on Feature {_feature.Identifier}. Populating ..."); propertyInfo3.SetValue(null, _feature); } _onGameStateChangedMethodInfo = methods.FirstOrDefault((MethodInfo mi) => (mi.Name == "OnGameStateChanged" || mi.GetCustomAttribute() != null) && !mi.IsStatic && mi.DeclaringType != typeof(Feature) && mi.GetParameters().Length == 1 && (mi.GetParameters()[0].ParameterType == _gameStateType || mi.GetParameters()[0].ParameterType == typeof(int))); if (_onGameStateChangedMethodInfo != null) { if (_onGameStateChangedMethodInfo.GetParameters()[0].ParameterType == _gameStateType) { _onGameStateChangedMethodUsesGameEnum = true; } _FILogger.Debug($"Found {"OnGameStateChanged"} method \"{_onGameStateChangedMethodInfo.Name}\" on Feature {_feature.Identifier}. (Uses {(_onGameStateChangedMethodUsesGameEnum ? "eGameStateName" : "int")})"); } _onLGAreaCullUpdateMethodInfo = methods.FirstOrDefault((MethodInfo mi) => (mi.Name == "OnAreaCull" || mi.GetCustomAttribute() != null) && !mi.IsStatic && mi.DeclaringType != typeof(Feature) && mi.GetParameters().Length == 2 && (mi.GetParameters()[0].ParameterType == _lgAreaType || mi.GetParameters()[0].ParameterType == typeof(object)) && mi.GetParameters()[1].ParameterType == typeof(bool)); foreach (PropertyInfo item in enumerable) { MethodInfo? setMethod = item.SetMethod; if ((object)setMethod != null && setMethod.IsStatic) { MethodInfo? getMethod = item.GetMethod; if ((object)getMethod != null && getMethod.IsStatic) { _settingsHelpers.Add(new FeatureSettingsHelper(_feature, item)); continue; } } _FILogger.Warning($"Feature \"{_feature.Identifier}\" has an invalid property \"{item.Name}\" with a {"FeatureConfig"} attribute! Make sure it's static with both a get and set method!"); } _harmonyInstance = new Harmony("TheArchive_FeaturesAPI_" + _feature.GUID); foreach (Type item2 in from nt in _featureType.GetNestedTypes(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where nt.GetCustomAttribute() != null select nt) { if (Utils.AnyRundownConstraintMatches(item2) && Utils.AnyBuildConstraintMatches(item2)) { _patchTypes.Add(item2); } else { _FILogger.Debug(_feature.Identifier + ": ignoring " + item2.FullName + " (Rundown | Build not matching.)"); } } _FILogger.Notice($"Discovered {_patchTypes.Count} Patch{((_patchTypes.Count == 1) ? string.Empty : "es")} matching constraints."); foreach (Type patchType in _patchTypes) { ArchivePatch customAttribute = patchType.GetCustomAttribute(); try { PropertyInfo property = patchType.GetProperty("PatchInfo"); if (property != null && property.GetMethod != null && property.GetMethod.IsStatic && property.GetMethod.ReturnType == typeof(ArchivePatch) && property.SetMethod != null) { property.SetValue(null, customAttribute); _FILogger.Debug("Populated PatchInfo Property for Patch \"" + patchType.FullName + "\"."); } MethodInfo[] methods2 = patchType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!customAttribute.HasType) { MethodInfo methodInfo = methods2.FirstOrDefault((MethodInfo mi) => mi.ReturnType == typeof(Type) && (mi.Name == "Type" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); if (!(methodInfo != null)) { throw new ArchivePatchNoTypeProvidedException($"Patch \"{patchType.FullName}\" has no Type to patch! Add a static method returning Type and decorate it with the {"IsTypeProvider"} Attribute!"); } if (!methodInfo.IsStatic) { throw new ArchivePatchMethodNotStaticException($"Method \"{methodInfo.Name}\" in Feature \"{feature.Identifier}\" must be static!"); } customAttribute.Type = (Type)methodInfo.Invoke(null, null); _FILogger.Debug($"Discovered target Type for Patch \"{patchType.FullName}\" to be \"{customAttribute.Type?.FullName ?? "TYPE NOT FOUND"}\""); } MethodInfo methodInfo2 = methods2.FirstOrDefault((MethodInfo mi) => mi.ReturnType == typeof(Type[]) && (mi.Name == "ParameterTypes" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); if (methodInfo2 != null) { if (!methodInfo2.IsStatic) { throw new ArchivePatchMethodNotStaticException($"Method \"{methodInfo2.Name}\" in Feature \"{feature.Identifier}\" must be static!"); } customAttribute.ParameterTypes = (Type[])methodInfo2.Invoke(null, null); } MethodInfo methodInfo3 = methods2.FirstOrDefault((MethodInfo mi) => mi.ReturnType == typeof(int) && (mi.Name == "Priority" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); if (methodInfo3 != null) { if (!methodInfo3.IsStatic) { throw new ArchivePatchMethodNotStaticException($"Method \"{methodInfo3.Name}\" in Feature \"{feature.Identifier}\" must be static!"); } customAttribute.Priority = (int)methodInfo3.Invoke(null, null); } if (string.IsNullOrWhiteSpace(customAttribute.MethodName)) { MethodInfo methodInfo4 = methods2.FirstOrDefault((MethodInfo mi) => mi.ReturnType == typeof(string) && (mi.Name == "MethodName" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); _FILogger.Debug($"Invoking static MethodNameProvider method {patchType.Name}.{methodInfo4.Name} on {_feature.Identifier}"); customAttribute.MethodName = (string)methodInfo4.Invoke(null, null); } MethodBase methodBase = customAttribute.MethodType switch { ArchivePatch.PatchMethodType.Getter => customAttribute.Type.GetProperty(customAttribute.MethodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetMethod, ArchivePatch.PatchMethodType.Setter => customAttribute.Type.GetProperty(customAttribute.MethodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.SetMethod, ArchivePatch.PatchMethodType.Constructor => customAttribute.Type.GetConstructor(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, customAttribute.ParameterTypes, null), _ => (customAttribute.ParameterTypes == null) ? customAttribute.Type.GetMethod(customAttribute.MethodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : customAttribute.Type.GetMethod(customAttribute.MethodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, customAttribute.ParameterTypes, null), }; if (methodBase == null) { throw new ArchivePatchNoOriginalMethodException($"{customAttribute.MethodType} with name \"{customAttribute.MethodName}\"{((customAttribute.ParameterTypes != null) ? (" with parameters [" + string.Join(", ", customAttribute.ParameterTypes.Select((Type type) => type.Name)) + "]") : string.Empty)} couldn't be found in type \"{customAttribute.Type.FullName}\", PatchClass: {patchType.FullName}."); } bool flag = LoaderWrapper.IsIL2CPPType(methodBase.DeclaringType); MethodInfo methodInfo5 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "Prefix" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); MethodInfo methodInfo6 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "Postfix" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); MethodInfo methodInfo7 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "Finalizer" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); MethodInfo methodInfo8 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "Transpiler" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); MethodInfo methodInfo9 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "ILManipulator" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); MethodInfo methodInfo10 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "Original" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); MethodInfo methodInfo11 = methods2.FirstOrDefault((MethodInfo mi) => (mi.Name == "Snapshot" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); if (methodInfo8 != null && flag) { _FILogger.Error($"Can't apply Transpiler \"{methodInfo8.Name}\" on native method \"{methodBase.Name}\" from IL2CPP Type \"{methodBase.DeclaringType.FullName}\"!"); _FILogger.Warning("This Transpiler is going to be skipped, things might break!"); methodInfo8 = null; } if (methodInfo9 != null && flag) { _FILogger.Error($"Can't apply ILManipulator \"{methodInfo9.Name}\" on native method \"{methodBase.Name}\" from IL2CPP Type \"{methodBase.DeclaringType.FullName}\"!"); _FILogger.Warning("This ILManipulator is going to be skipped, things might break!"); methodInfo9 = null; } if (methodInfo5 == null && methodInfo6 == null && methodInfo7 == null && methodInfo8 == null && methodInfo9 == null && methodInfo10 == null && methodInfo11 == null) { throw new ArchivePatchNoPatchMethodException("Patch class \"" + patchType.FullName + "\" doesn't contain a Prefix, Postfix, Finalizer, Transpiler, ILManipulator, Original or Snapshot method, at least one is required!"); } _patchInfos.Add(new FeaturePatchInfo(methodBase, methodInfo5, methodInfo6, methodInfo8, methodInfo7, methodInfo9, methodInfo10, methodInfo11, customAttribute)); try { MethodInfo methodInfo12 = methods2.FirstOrDefault((MethodInfo mi) => mi.IsStatic && (mi.Name == "Init" || mi.GetCustomAttribute() != null) && Utils.AnyRundownConstraintMatches(mi) && Utils.AnyBuildConstraintMatches(mi)); ParameterInfo[] array = methodInfo12?.GetParameters(); if (methodInfo12 != null) { _FILogger.Debug($"Invoking static Init method {patchType.Name}.{methodInfo12.Name} on {_feature.Identifier}"); if (array.Length == 1 && array[0].ParameterType == typeof(GameBuildInfo)) { methodInfo12.Invoke(null, new object[1] { BuildInfo }); } else { methodInfo12.Invoke(null, null); } } } catch (Exception ex3) { _FILogger.Error($"Static Init method on {_feature.Identifier} failed! - {ex3}: {ex3.Message}"); _FILogger.Exception(ex3); InternallyDisableFeature(InternalDisabledReason.PatchInitMethodFailed); return; } } catch (Exception ex4) { _FILogger.Error($"Patch discovery for \"{customAttribute.Type?.FullName ?? ("TYPE NOT FOUND - PatchType:" + patchType.FullName)}\" failed: {ex4}: {ex4.Message}"); _FILogger.Exception(ex4); } } LoadFeatureSettings(); try { if (!_feature.LateShouldInit()) { InternallyDisableFeature(InternalDisabledReason.DisabledViaLateShouldInit); return; } } catch (Exception ex5) { _FILogger.Error($"{"LateShouldInit"} method on {"Feature"} failed: {ex5}: {ex5.Message}"); _FILogger.Exception(ex5); InternallyDisableFeature(InternalDisabledReason.LateShouldInitFailed); return; } try { _feature.Init(); } catch (Exception ex6) { _FILogger.Error($"Main Feature Init method on {_feature.Identifier} failed! - {ex6}: {ex6.Message}"); _FILogger.Exception(ex6); InternallyDisableFeature(InternalDisabledReason.MainInitMethodFailed); return; } if (FeatureManager.IsEnabledInConfig(_feature)) { Enable(!_feature.SkipInitialOnEnable); } else { _feature.Enabled = false; } } private void AfterInit() { InitialEnabledState = _feature.Enabled; if (_feature.BelongsToGroup) { FeatureManager.AddGroupedFeature(_feature); } } private void LoadFeatureSettings(bool refreshDisplayName = false) { if (InternalDisabled) { return; } foreach (FeatureSettingsHelper settingsHelper in _settingsHelpers) { try { _FILogger.Info($"Loading config {_feature.Identifier} [{settingsHelper.PropertyName}] ({settingsHelper.TypeName}) ..."); object configInstance = LocalFiles.LoadFeatureConfig(ArchiveModule.GetType().Assembly.GetName().Name, _feature.Identifier + "_" + settingsHelper.PropertyName, settingsHelper.SettingType); if (refreshDisplayName) { settingsHelper.RefreshDisplayName(); } settingsHelper.SetupViaFeatureInstance(configInstance); if (settingsHelper.Settings.Any((FeatureSetting fs) => !fs.HideInModSettings)) { AllAdditionalSettingsAreHidden = false; } } catch (Exception ex) { _FILogger.Error($"An error occured while loading config {_feature.Identifier} [{settingsHelper.PropertyName}] ({settingsHelper.TypeName})!"); _FILogger.Exception(ex); } } } internal void SaveFeatureSettings() { if (InternalDisabled) { return; } foreach (FeatureSettingsHelper settingsHelper in _settingsHelpers) { if (!settingsHelper.IsDirty) { _FILogger.Info($"Config {_feature.Identifier} [{settingsHelper.PropertyName}] ({settingsHelper.TypeName}) does not need saving!"); continue; } _FILogger.Success($"Saving config {_feature.Identifier} [{settingsHelper.PropertyName}] ({settingsHelper.TypeName}) ..."); try { object featureInstance = settingsHelper.GetFeatureInstance(); LocalFiles.SaveFeatureConfig(ArchiveModule.GetType().Assembly.GetName().Name, _feature.Identifier + "_" + settingsHelper.PropertyName, settingsHelper.SettingType, featureInstance); } catch (Exception ex) { _FILogger.Error($"Failed to save config file {_feature.Identifier} [{settingsHelper.PropertyName}] ({settingsHelper.TypeName})!"); _FILogger.Exception(ex); } settingsHelper.IsDirty = false; } } private void SaveAndReloadFeatureSettings() { SaveFeatureSettings(); LoadFeatureSettings(refreshDisplayName: true); } private void ApplyPatches() { if (InternalDisabled) { return; } foreach (FeaturePatchInfo patchInfo in _patchInfos) { try { _FILogger.Msg(ConsoleColor.DarkBlue, $"Patching {_feature.Identifier} : {patchInfo.ArchivePatchInfo.Type.FullName}.{patchInfo.ArchivePatchInfo.MethodName}()"); if (patchInfo.HarmonyOriginalReverseMethod != null) { _harmonyInstance.CreateReversePatcher(patchInfo.OriginalMethod, patchInfo.HarmonyOriginalReverseMethod).Patch((HarmonyReversePatchType)0); } if (patchInfo.HarmonySnapshotReverseMethod != null) { _harmonyInstance.CreateReversePatcher(patchInfo.OriginalMethod, patchInfo.HarmonySnapshotReverseMethod).Patch((HarmonyReversePatchType)0); } _harmonyInstance.Patch(patchInfo.OriginalMethod, patchInfo.HarmonyPrefixMethod, patchInfo.HarmonyPostfixMethod, patchInfo.HarmonyTranspilerMethod, patchInfo.HarmonyFinalizerMethod, patchInfo.HarmonyILManipulatorMethod); } catch (Exception ex) { _FILogger.Error($"Patch {patchInfo.ArchivePatchInfo.Type.FullName}.{patchInfo.ArchivePatchInfo.MethodName}() failed! {ex}: {ex.Message}"); _FILogger.Exception(ex); } } } internal bool Enable(bool callOnEnable = true) { if (InternalDisabled) { return false; } if (_feature.Enabled) { return false; } ApplyPatches(); _feature.Enabled = true; _isEnabledPropertyInfo?.SetValue(null, true); if (callOnEnable) { try { _feature.OnEnable(); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnEnable"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } InitSingletonBase.Instance.CheckSpecialFeatures(); return true; } internal bool Disable() { if (InternalDisabled) { return false; } if (!_feature.Enabled) { return false; } _harmonyInstance.UnpatchSelf(); _feature.Enabled = false; _isEnabledPropertyInfo?.SetValue(null, false); try { _feature.OnDisable(); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnDisable"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } InitSingletonBase.Instance.CheckSpecialFeatures(); return true; } internal void GameDataInitialized() { if (InternalDisabled || !_feature.Enabled) { return; } try { _feature.OnGameDataInitialized(); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnGameDataInitialized"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal void DatablocksReady() { if (InternalDisabled || !_feature.Enabled) { return; } try { _feature.OnDatablocksReady(); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnDatablocksReady"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal void FeatureSettingChanged(FeatureSetting setting) { if (!_feature.Enabled) { return; } if (setting.RequiresRestart) { _feature.RequestRestart(); } try { _feature.OnFeatureSettingChanged(setting); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnFeatureSettingChanged"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal bool MarkSettingsDirty(object settings) { foreach (FeatureSettingsHelper setting in Settings) { if (setting.Instance == settings) { setting.IsDirty = true; return true; } } return false; } internal void GameStateChanged(int state) { if (InternalDisabled || !_feature.Enabled) { return; } try { object obj = state; if (_onGameStateChangedMethodUsesGameEnum) { obj = Enum.ToObject(_gameStateType, state); } _onGameStateChangedMethodInfo?.Invoke(_feature, new object[1] { obj }); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnGameStateChanged"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal void ApplicationFocusChanged(bool focus) { if (InternalDisabled || !_feature.Enabled) { return; } try { _feature.OnApplicationFocusChanged(focus); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnApplicationFocusChanged"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal void OnLGAreaCullUpdate(object lgArea, bool active) { if (InternalDisabled || !_feature.Enabled) { return; } try { _onLGAreaCullUpdateMethodInfo?.Invoke(_feature, new object[2] { lgArea, active }); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnAreaCull"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal void OnButtonPressed(ButtonSetting setting) { if (InternalDisabled || !_feature.Enabled) { return; } try { _feature.OnButtonPressed(setting); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnButtonPressed"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal void Quit() { try { _feature.OnQuit(); } catch (Exception ex) { _FILogger.Error($"Exception thrown during {"OnQuit"} in Feature {_feature.Identifier}!"); _FILogger.Exception(ex); } } internal static void ReloadAllFeatureSettings() { foreach (Feature registeredFeature in InitSingletonBase.Instance.RegisteredFeatures) { registeredFeature.FeatureInternal.SaveAndReloadFeatureSettings(); } } private static Dictionary GetFSProperties(Type type) { return (from prop in type.GetProperties() where prop.GetCustomAttribute() == null && (prop.GetCustomAttributes(inherit: true).Any() || (typeof(Feature).IsAssignableFrom(prop.DeclaringType) && prop.DeclaringType.FullName != typeof(Feature).FullName && (prop.Name == "Name" || prop.Name == "Description")) || prop.PropertyType == typeof(FLabel) || prop.PropertyType == typeof(FButton)) select prop).ToDictionary((PropertyInfo prop) => prop.DeclaringType.FullName + "." + prop.Name, (PropertyInfo prop) => prop); } private static FeatureSettingLocalizationData GenerateFeatureSettingLocalizationData(List> allProperties, HashSet enumTypes, FeatureSettingLocalizationData existData) { FeatureSettingLocalizationData featureSettingLocalizationData = new FeatureSettingLocalizationData(); foreach (Dictionary allProperty in allProperties) { foreach (KeyValuePair item in allProperty) { item.Deconstruct(out var key, out var value); string text = key; PropertyInfo propertyInfo = value; Type propertyType = propertyInfo.PropertyType; if (!featureSettingLocalizationData.FeatureSettingTexts.ContainsKey(text)) { featureSettingLocalizationData.FeatureSettingTexts[text] = new Dictionary>(); } FSType[] values = Enum.GetValues(); foreach (FSType fSType in values) { if (ShouldProcessFSType(propertyInfo, propertyType, fSType)) { Dictionary value2 = CreateLanguageDictionary(text, fSType, existData); featureSettingLocalizationData.FeatureSettingTexts[text][fSType] = value2; } } } } ProcessEnumLocalization(featureSettingLocalizationData, enumTypes, existData); return featureSettingLocalizationData; } private static bool ShouldProcessFSType(PropertyInfo prop, Type propType, FSType fstype) { bool flag = typeof(Feature).IsAssignableFrom(prop.DeclaringType); return fstype switch { FSType.FName => flag && prop.Name == "Name", FSType.FDescription => flag && prop.Name == "Description", FSType.FSDisplayName => !IsLabelType(propType) && prop.GetCustomAttribute() != null, FSType.FSDescription => !IsLabelType(propType) && prop.GetCustomAttribute() != null, FSType.FSButtonText => propType == typeof(FButton), FSType.FSLabelText => propType == typeof(FLabel), FSType.FSHeader => prop.GetCustomAttribute() != null, FSType.FSTooltipHeader => prop.GetCustomAttribute() != null, FSType.FSTooltipText => prop.GetCustomAttribute() != null, _ => false, }; static bool IsLabelType(Type type) { return type == typeof(FLabel); } } private static Dictionary CreateLanguageDictionary(string propKey, FSType fstype, FeatureSettingLocalizationData existData) { Dictionary dictionary = new Dictionary(); Language[] values = Enum.GetValues(); Dictionary> value2 = default(Dictionary>); foreach (Language key in values) { string value = null; if (existData != null && (existData.FeatureSettingTexts?.TryGetValue(propKey, out value2)).GetValueOrDefault() && value2.TryGetValue(fstype, out var value3) && value3.TryGetValue(key, out var value4)) { value = value4; } dictionary[key] = value; } return dictionary; } private static void ProcessEnumLocalization(FeatureSettingLocalizationData result, HashSet enumTypes, FeatureSettingLocalizationData existData) { Dictionary> value2 = default(Dictionary>); foreach (Type enumType in enumTypes) { Dictionary> dictionary = new Dictionary>(); result.EnumTexts[enumType.FullName] = dictionary; Language[] values = Enum.GetValues(); foreach (Language key in values) { Dictionary dictionary3 = (dictionary[key] = new Dictionary()); string[] names = Enum.GetNames(enumType); foreach (string key2 in names) { string value = null; if (existData != null && (existData.EnumTexts?.TryGetValue(enumType.FullName, out value2)).GetValueOrDefault() && value2 != null && value2.TryGetValue(key, out var value3) && value3.TryGetValue(key2, out var value4)) { value = value4; } dictionary3[key2] = value; } } } } internal static FeatureLocalizationData GenerateFeatureLocalization(Feature feature, FeatureLocalizationData existData = null) { FeatureLocalizationData obj = new FeatureLocalizationData { GenericTexts = (existData?.GenericTexts ?? new Dictionary()) }; (List> properties, HashSet enumTypes) tuple = CollectInternalTypesData(feature); List> item = tuple.properties; HashSet item2 = tuple.enumTypes; obj.Internal = GenerateFeatureSettingLocalizationData(item, item2, existData?.Internal); (List> properties, HashSet enumTypes) tuple2 = CollectExternalTypesData(feature); List> item3 = tuple2.properties; HashSet item4 = tuple2.enumTypes; obj.External = GenerateFeatureSettingLocalizationData(item3, item4, existData?.External); return obj; } private static (List> properties, HashSet enumTypes) CollectInternalTypesData(Feature feature) { List> list = new List>(); HashSet hashSet = new HashSet(); foreach (Type nestedClass in Utils.GetNestedClasses(feature.GetType())) { CollectNestedEnumTypes(nestedClass, hashSet); list.Add(GetFSProperties(nestedClass)); } return (list, hashSet); } private static (List> properties, HashSet enumTypes) CollectExternalTypesData(Feature feature) { List> list = new List>(); HashSet hashSet = new HashSet(); Type[] externalLocalizedTypes = feature.ExternalLocalizedTypes; foreach (Type type in externalLocalizedTypes) { if (type.IsEnum) { hashSet.Add(type); } else { if (!type.IsClass) { continue; } foreach (Type nestedClass in Utils.GetNestedClasses(type)) { CollectNestedEnumTypes(nestedClass, hashSet); list.Add(GetFSProperties(nestedClass)); } } } return (list, hashSet); } private static void CollectNestedEnumTypes(Type type, HashSet enumTypes) { Type[] nestedTypes = type.GetNestedTypes(); foreach (Type type2 in nestedTypes) { if (type2.IsEnum && HasLocalizedAttribute(type2)) { enumTypes.Add(type2); } } } private static bool HasLocalizedAttribute(Type type) { return type.GetCustomAttributes(inherit: true).Any(); } private static void CollectEnumTypesFromProperty(Type propType, HashSet enumTypes) { if (propType.IsEnum) { enumTypes.Add(propType); } else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>)) { Type underlyingType = Nullable.GetUnderlyingType(propType); if ((object)underlyingType != null && underlyingType.IsEnum) { enumTypes.Add(underlyingType); } } else { if (!propType.IsGenericType) { return; } Type[] genericArguments = propType.GetGenericArguments(); foreach (Type type in genericArguments) { if (type.IsEnum) { enumTypes.Add(type); } } } } internal void RequestDisable(string reason) { _FILogger.Info("Feature " + _feature.Identifier + " has requested to be disabled: " + reason); InternallyDisableFeature(InternalDisabledReason.DisabledByRequest); } private void InternallyDisableFeature(InternalDisabledReason reason) { InternalDisabled = true; DisabledReason |= reason; InitSingletonBase.Instance.DisableFeature(_feature, setConfig: false); } } public class FeatureManager : InitSingletonBase { private record struct UpdateModification(bool SetEnabled, T Value); private readonly EnabledFeatures _enabledFeatures; private readonly HashSet _activeUpdateMethods = new HashSet(); private readonly HashSet _activeLateUpdateMethods = new HashSet(); private readonly Stack> _updateModification = new Stack>(); private readonly Stack> _lateUpdateModification = new Stack>(); private Feature _unityAudioListenerHelper; private readonly IArchiveLogger _logger = LoaderWrapper.CreateArSubLoggerInstance("FeatureManager", ConsoleColor.DarkYellow); public HashSet RegisteredFeatures { get; } = new HashSet(); public HashSet FeaturesRequestingRestart { get; } = new HashSet(); public bool AnyFeatureRequestingRestart => FeaturesRequestingRestart.Count > 0; public Dictionary> GroupedFeatures { get; } = new Dictionary>(); public bool InUpdateCycle { get; private set; } public bool InLateUpdateCycle { get; private set; } private Feature UnityAudioListenerHelper { get { if (_unityAudioListenerHelper == null) { _unityAudioListenerHelper = GetById("InternalUAudioListenerHelper"); } return _unityAudioListenerHelper; } } internal event Action OnFeatureEnabled; internal event Action OnFeatureDisabled; internal event Action OnFeatureRestartRequestChanged; private FeatureManager() { Feature.BuildInfo = ArchiveMod.CurrentBuildInfo; _enabledFeatures = LocalFiles.LoadConfig(); if (_enabledFeatures?.Features == null) { _logger.Warning("EnabledFeatures config might have been corrupted since last time! :( Resetting ..."); _enabledFeatures = new EnabledFeatures(); } ArchiveMod.GameStateChanged += OnGameStateChanged; ArchiveMod.ApplicationFocusStateChanged += OnApplicationFocusChanged; Feature.SetupIs(); } public static bool IsFeatureEnabled(string featureId, bool useGuid = false) { return InitSingletonBase.Instance.RegisteredFeatures.FirstOrDefault((Feature f) => (!useGuid) ? (f.Identifier == featureId) : (f.GUID == featureId))?.Enabled ?? false; } internal void OnGameDataInitialized() { _logger.Debug("OnGameDataInitialized()"); Feature.GameDataInited = true; try { foreach (Feature registeredFeature in RegisteredFeatures) { registeredFeature.FeatureInternal.GameDataInitialized(); } } catch (Exception ex) { _logger.Error($"Exception thrown in {"OnGameDataInitialized"}! {ex}: {ex.Message}"); _logger.Exception(ex); } } internal void OnDatablocksReady() { _logger.Debug("OnDatablocksReady()"); Feature.DataBlocksReady = true; try { foreach (Feature registeredFeature in RegisteredFeatures) { registeredFeature.FeatureInternal.DatablocksReady(); } } catch (Exception ex) { _logger.Error($"Exception thrown in {"OnDatablocksReady"}! {ex}: {ex.Message}"); _logger.Exception(ex); } } internal void OnApplicationQuit() { _logger.Info("OnApplicationQuit()"); try { Feature.IsApplicationQuitting = true; _logger.Info("Saving settings ... "); SaveEnabledFeaturesConfig(); foreach (Feature registeredFeature in RegisteredFeatures) { SaveFeatureConfig(registeredFeature); } foreach (Feature registeredFeature2 in RegisteredFeatures) { registeredFeature2.FeatureInternal.Quit(); } } catch (Exception ex) { _logger.Error($"Exception thrown in {"OnApplicationQuit"}! {ex}: {ex.Message}"); _logger.Exception(ex); } } internal void OnUpdate() { while (_updateModification.Count > 0) { UpdateModification updateModification = _updateModification.Pop(); if (updateModification.SetEnabled) { _activeUpdateMethods.Add(updateModification.Value); } else { _activeUpdateMethods.Remove(updateModification.Value); } } InUpdateCycle = true; foreach (FeatureInternal.Update activeUpdateMethod in _activeUpdateMethods) { try { activeUpdateMethod(); } catch (Exception ex) { _logger.Error($"Update method on {activeUpdateMethod.Target.GetType().FullName} threw an exception! {ex}: {ex.Message}"); _logger.Exception(ex); _logger.Warning("Removing Update method on " + activeUpdateMethod.Target.GetType().FullName + "! (Update won't be called anymore!! Toggle the Feature to re-enable)"); _updateModification.Push(new UpdateModification(SetEnabled: false, activeUpdateMethod)); } } InUpdateCycle = false; } internal void OnLateUpdate() { while (_lateUpdateModification.Count > 0) { UpdateModification updateModification = _lateUpdateModification.Pop(); if (updateModification.SetEnabled) { _activeLateUpdateMethods.Add(updateModification.Value); } else { _activeLateUpdateMethods.Remove(updateModification.Value); } } InLateUpdateCycle = true; foreach (FeatureInternal.LateUpdate activeLateUpdateMethod in _activeLateUpdateMethods) { try { activeLateUpdateMethod(); } catch (Exception ex) { _logger.Error($"LateUpdate method on {activeLateUpdateMethod.Target.GetType().FullName} threw an exception! {ex}: {ex.Message}"); _logger.Exception(ex); _logger.Warning("Removing LateUpdate method on " + activeLateUpdateMethod.Target.GetType().FullName + "! (LateUpdate won't be called anymore!! Toggle the Feature to re-enable)"); _lateUpdateModification.Push(new UpdateModification(SetEnabled: false, activeLateUpdateMethod)); } } InLateUpdateCycle = false; } internal void OnFeatureSettingChanged(FeatureSetting setting) { setting.Helper.Feature.FeatureInternal.FeatureSettingChanged(setting); } private void OnGameStateChanged(int state) { Feature.PreviousGameState = Feature.CurrentGameState; Feature.CurrentGameState = state; foreach (Feature registeredFeature in RegisteredFeatures) { if (registeredFeature.Enabled) { registeredFeature.FeatureInternal.GameStateChanged(state); } } } internal void OnLGAreaCullUpdate(object lg_area, bool active) { foreach (Feature registeredFeature in RegisteredFeatures) { if (registeredFeature.Enabled) { registeredFeature.FeatureInternal.OnLGAreaCullUpdate(lg_area, active); } } } internal void OnApplicationFocusChanged(bool focus) { Feature.IsApplicationFocused = focus; foreach (Feature registeredFeature in RegisteredFeatures) { if (registeredFeature.Enabled) { registeredFeature.FeatureInternal.ApplicationFocusChanged(focus); } } } internal void InitFeature(Type type, IArchiveModule module) { Feature feature = (Feature)Activator.CreateInstance(type); InitFeature(feature, module); CheckSpecialFeatures(); } private void InitFeature(Feature feature, IArchiveModule module) { FeatureInternal.CreateAndAssign(feature, module); if (feature.Enabled) { if (feature.FeatureInternal.HasUpdateMethod) { _activeUpdateMethods.Add(feature.FeatureInternal.UpdateDelegate); } if (feature.FeatureInternal.HasLateUpdateMethod) { _activeLateUpdateMethods.Add(feature.FeatureInternal.LateUpdateDelegate); } } RegisteredFeatures.Add(feature); feature.Group.Features.Add(feature); feature.TopLevelGroup?.Features.Add(feature); } public void EnableFeature(Feature feature, bool setConfig = true) { if (feature != null) { if (setConfig) { SetEnabledInConfig(feature, value: true); } if (!feature.RequiresRestart && !feature.Enabled && feature.IsLoadedAndNotDisabledInternally) { _logger.Msg(ConsoleColor.Green, $"Enabling {(feature.IsAutomated ? "automated " : string.Empty)}{"Feature"} {feature.Identifier} ..."); feature.FeatureInternal.Enable(); SetAllUpdateMethodsEnabled(feature, enable: true); this.OnFeatureEnabled?.Invoke(feature); } } } public void DisableFeature(Feature feature, bool setConfig = true) { if (feature == null) { return; } if (setConfig) { SetEnabledInConfig(feature, value: false); } if (!feature.RequiresRestart && feature.Enabled) { if (feature.IsLoadedAndNotDisabledInternally) { _logger.Msg(ConsoleColor.Red, $"Disabling {"Feature"} {feature.Identifier} ..."); } feature.FeatureInternal.Disable(); SetAllUpdateMethodsEnabled(feature, enable: false); this.OnFeatureDisabled?.Invoke(feature); } } private void SetAllUpdateMethodsEnabled(Feature feature, bool enable) { SetUpdateMethodEnabled(feature, enable); SetLateUpdateMethodEnabled(feature, enable); } private void SetUpdateMethodEnabled(Feature feature, bool enable) { if (feature.FeatureInternal.HasUpdateMethod) { SetUpdateMethodEnabled(feature.FeatureInternal.UpdateDelegate, enable); } } private void SetUpdateMethodEnabled(FeatureInternal.Update update, bool enable) { if (update != null) { if (InUpdateCycle) { _updateModification.Push(new UpdateModification(enable, update)); } else if (enable) { _activeUpdateMethods.Add(update); } else { _activeUpdateMethods.Remove(update); } } } private void SetLateUpdateMethodEnabled(Feature feature, bool enable) { if (feature.FeatureInternal.HasLateUpdateMethod) { SetLateUpdateMethodEnabled(feature.FeatureInternal.LateUpdateDelegate, enable); } } private void SetLateUpdateMethodEnabled(FeatureInternal.LateUpdate update, bool enable) { if (update != null) { if (InLateUpdateCycle) { _lateUpdateModification.Push(new UpdateModification(enable, update)); } else if (enable) { _activeLateUpdateMethods.Add(update); } else { _activeLateUpdateMethods.Remove(update); } } } internal void CheckSpecialFeatures() { Feature[] array = RegisteredFeatures.Where((Feature f) => f.Enabled && f.RequiresUnityAudioListener).ToArray(); if (array.Length != 0) { if (UnityAudioListenerHelper != null && !UnityAudioListenerHelper.Enabled) { _logger.Notice("Some Features require a UnityEngine AudioListener: [" + string.Join("], [", array.Select((Feature f) => f.Identifier)) + "]"); EnableFeature(UnityAudioListenerHelper); } } else if (UnityAudioListenerHelper != null && UnityAudioListenerHelper.Enabled) { DisableFeature(UnityAudioListenerHelper); } } public static void EnableAutomatedFeature(Type type) { Feature feature = InitSingletonBase.Instance.RegisteredFeatures.FirstOrDefault((Feature x) => x.GetType() == type); if (feature != null && feature.IsAutomated) { InitSingletonBase.Instance.EnableFeature(feature, setConfig: false); } } public static void DisableAutomatedFeature(Type type) { Feature feature = InitSingletonBase.Instance.RegisteredFeatures.FirstOrDefault((Feature x) => x.GetType() == type); if (feature != null && feature.IsAutomated) { InitSingletonBase.Instance.DisableFeature(feature, setConfig: false); } } internal static void RequestRestart(Feature feature) { if (InitSingletonBase.Instance.FeaturesRequestingRestart.Add(feature)) { InitSingletonBase.Instance.OnFeatureRestartRequestChanged?.Invoke(feature, arg2: true); } } internal static void RevokeRestartRequest(Feature feature) { if (InitSingletonBase.Instance.FeaturesRequestingRestart.Remove(feature)) { InitSingletonBase.Instance.OnFeatureRestartRequestChanged?.Invoke(feature, arg2: false); } } internal static Feature GetById(string featureIdentifier) { return InitSingletonBase.Instance.RegisteredFeatures.FirstOrDefault((Feature f) => f.Identifier == featureIdentifier); } internal static Feature GetByGuid(string featureGuid) { return InitSingletonBase.Instance.RegisteredFeatures.FirstOrDefault((Feature f) => f.GUID == featureGuid); } internal static Feature GetByType() where T : Feature { return GetByType(typeof(T)); } internal static Feature GetByType(Type type) { return InitSingletonBase.Instance.RegisteredFeatures.FirstOrDefault((Feature f) => f.GetType() == type); } public static bool IsFeatureEnabled() where T : Feature { return GetByType()?.Enabled ?? false; } private static void SaveFeatureConfig(Feature feature) { feature.FeatureInternal.SaveFeatureSettings(); } internal static void Internal_Init() { InitSingletonBase.Instance = new FeatureManager(); } public static void ToggleFeature(Feature feature) { InitSingletonBase.Instance.ToggleFeatureInstance(feature); } private void ToggleFeatureInstance(Feature feature) { bool flag = ((feature.IsLoadedAndNotDisabledInternally && !feature.RequiresRestart) ? feature.Enabled : IsEnabledInConfig(feature)); if (feature.RequiresRestart) { if (feature.FeatureInternal.InitialEnabledState == flag) { feature.RequestRestart(); } else { feature.RevokeRestartRequest(); } } if (flag) { DisableFeature(feature); } else { EnableFeature(feature); } } private void SaveEnabledFeaturesConfig() { LocalFiles.SaveConfig(_enabledFeatures); } internal static void SetEnabledInConfig(Feature feature, bool value) { InitSingletonBase.Instance.SetFeatureEnabledInConfig(feature, value); } private void SetFeatureEnabledInConfig(Feature feature, bool value) { if (feature.FeatureInternal.DoNotSaveToConfig) { return; } if (_enabledFeatures.Features.TryGetValue(feature.GUID, out var value2)) { if (value2 == value) { return; } _enabledFeatures.Features.Remove(feature.GUID); } _enabledFeatures.Features.Add(feature.GUID, value); } public static bool IsEnabledInConfig(Feature feature) { return InitSingletonBase.Instance.IsFeatureEnabledInConfig(feature); } private bool IsFeatureEnabledInConfig(Feature feature) { if (feature.FeatureInternal.DoNotSaveToConfig) { if (feature.FeatureInternal.AutomatedFeature) { return false; } return true; } if (_enabledFeatures.Features.TryGetValue(feature.GUID, out var value)) { return value; } bool flag = feature.GetType().GetCustomAttribute() != null; SetFeatureEnabledInConfig(feature, flag); return flag; } internal static void AddGroupedFeature(Feature feature) { InitSingletonBase.Instance.AddGroupedFeatureI(feature); } private void AddGroupedFeatureI(Feature feature) { if (feature != null && feature.BelongsToGroup) { if (!GroupedFeatures.TryGetValue(feature.Group, out var value)) { GroupedFeatures.Add(feature.Group, new HashSet { feature }); } else { value.Add(feature); } } } internal static void InvokeButtonPressed(Feature feature, ButtonSetting setting) { if (feature != null && setting != null) { try { setting.Callback?.Invoke(); } catch (Exception ex) { feature.FeatureLogger.Error($"Button {setting.ButtonID} callback threw an exception! {ex}: {ex.Message}"); feature.FeatureLogger.Exception(ex); } feature.FeatureInternal.OnButtonPressed(setting); } } } public class FeatureSettingsHelper { private bool _isDirty; private static readonly IArchiveLogger _logger = LoaderWrapper.CreateArSubLoggerInstance("FeatureSettingsHelper", ConsoleColor.DarkYellow); private PropertyInfo Property { get; } public string DisplayName { get; private set; } internal string TypeName => SettingType?.Name; internal string PropertyName => Property?.Name; internal Type SettingType { get; } internal object Instance { get; private set; } public Feature Feature { get; protected init; } internal FeatureSettingsHelper ParentHelper { get; init; } public bool IsDirty { get { if (ParentHelper != null) { return ParentHelper.IsDirty; } return _isDirty; } internal set { if (ParentHelper != null) { ParentHelper.IsDirty = value; } _isDirty = value; } } internal static bool ForceEnableDebugLogging { get; set; } = false; public HashSet Settings { get; } = new HashSet(); internal FeatureLocalizationService Localization => Feature.FeatureInternal.Localization; internal FeatureSettingsHelper(Feature feature, PropertyInfo settingsProperty) { Feature = feature; Property = settingsProperty; SettingType = settingsProperty?.GetMethod?.ReturnType ?? throw new ArgumentNullException("settingsProperty", "Settings Property must implement a get method!"); SetDisplayName(settingsProperty); } protected FeatureSettingsHelper() { } protected void PopulateSettings(Type typeToCheck, object instance, string path = "") { if (typeToCheck.IsValueType) { return; } if (string.IsNullOrWhiteSpace(path)) { path = typeToCheck.FullName; } DebugLog("Populate: " + path); PropertyInfo[] properties = typeToCheck.GetProperties(); foreach (PropertyInfo propertyInfo in properties) { string text = path + "." + propertyInfo.Name; Type type = propertyInfo.GetMethod?.ReturnType; if (type == null || propertyInfo.GetCustomAttribute() != null || propertyInfo.SetMethod == null) { continue; } bool flag = propertyInfo.GetCustomAttribute() != null; if (!type.IsValueType && !typeof(IList).IsAssignableFrom(type) && !typeof(IDictionary).IsAssignableFrom(type) && type != typeof(string) && type.GenericTypeArguments.Length <= 1 && flag) { PopulateSettings(type, propertyInfo.GetValue(instance), text); continue; } FeatureSetting featureSetting; switch (type.Name) { case "FButton": featureSetting = new ButtonSetting(this, propertyInfo, instance, text); break; case "FLabel": featureSetting = new LabelSetting(this, propertyInfo, instance, text); break; case "SColor": featureSetting = new ColorSetting(this, propertyInfo, instance, text); break; case "Boolean": featureSetting = new BoolSetting(this, propertyInfo, instance, text); break; case "String": featureSetting = new StringSetting(this, propertyInfo, instance, text); break; case "UInt64": case "UInt32": case "UInt16": case "Single": case "Double": case "Int64": case "SByte": case "Int16": case "Int32": case "Byte": featureSetting = new NumberSetting(this, propertyInfo, instance, text); break; default: featureSetting = ((!(type == typeof(KeyCode))) ? ((typeof(IList).IsAssignableFrom(type) && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments[0].IsEnum) ? new EnumListSetting(this, propertyInfo, instance, text) : ((typeof(IList).IsAssignableFrom(type) && type.GenericTypeArguments.Length == 1) ? new GenericListSetting(this, propertyInfo, instance, text) : ((typeof(IDictionary).IsAssignableFrom(type) && type.GenericTypeArguments.Length == 2) ? new GenericDictionarySetting(this, propertyInfo, instance, text) : ((!type.IsEnum) ? (flag ? new FeatureSetting(this, propertyInfo, instance, text) : new SubmenuSetting(this, propertyInfo, instance, text)) : new EnumSetting(this, propertyInfo, instance, text))))) : new KeySetting(this, propertyInfo, instance, text)); break; } Settings.Add(featureSetting); DebugLog("Setting Added: " + text + " | " + featureSetting.GetType().Name); } } protected static void DebugLog(string msg) { if (ForceEnableDebugLogging || !Feature.GameDataInited) { _logger.Debug(msg); } } private void SetDisplayName(PropertyInfo settingsProperty) { FSDisplayName customAttribute = settingsProperty.GetCustomAttribute(inherit: true); DisplayName = customAttribute?.DisplayName ?? settingsProperty.Name; if (customAttribute != null) { string propID = settingsProperty.DeclaringType.FullName + "." + settingsProperty.Name; if (Localization.TryGetFSText(propID, FSType.FSDisplayName, out var text)) { DisplayName = text; } } } internal void RefreshDisplayName() { SetDisplayName(Property); } internal virtual void SetupViaFeatureInstance(object configInstance) { SetupViaInstanceOnHost(Feature, configInstance); } internal virtual void SetupViaInstanceOnHost(object host, object configInstance) { if (configInstance == null) { _logger.Warning("Config instance (" + SettingType.FullName + ") is null! This should not happen! Resetting values ... :("); configInstance = Activator.CreateInstance(SettingType); } Instance = configInstance; Property.SetValue(host, configInstance); Settings.Clear(); PopulateSettings(SettingType, Instance, string.Empty); } public virtual object GetFeatureInstance() { return GetInstanceOnHost(Feature); } public virtual object GetInstanceOnHost(object host) { return Instance ?? Property.GetValue(host); } } public static class GroupManager { internal static Dictionary TopLevelGroups { get; private set; } = new Dictionary(); internal static Dictionary ModuleGroups { get; private set; } = new Dictionary(); internal static Dictionary AllGroups { get; private set; } = new Dictionary(); public static TopLevelGroup Accessibility { get; private set; } = GetOrCreateTopLevelGroupInternal("Accessibility"); public static TopLevelGroup Audio { get; private set; } = GetOrCreateTopLevelGroupInternal("Audio"); public static TopLevelGroup Cosmetic { get; private set; } = GetOrCreateTopLevelGroupInternal("Cosmetic"); public static TopLevelGroup Dev { get; private set; } = GetOrCreateTopLevelGroupInternal("Developer", dev: true); public static TopLevelGroup Fixes { get; private set; } = GetOrCreateTopLevelGroupInternal("Fixes"); public static TopLevelGroup Hud { get; private set; } = GetOrCreateTopLevelGroupInternal("HUD"); internal static TopLevelGroup LocalProgression { get; private set; } = GetOrCreateTopLevelGroupInternal("LocalProgression"); public static TopLevelGroup Special { get; private set; } = GetOrCreateTopLevelGroupInternal("Misc"); public static TopLevelGroup Presence { get; private set; } = GetOrCreateTopLevelGroupInternal("Presence"); public static TopLevelGroup Security { get; private set; } = GetOrCreateTopLevelGroupInternal("Security"); public static TopLevelGroup QualityOfLife { get; private set; } = GetOrCreateTopLevelGroupInternal("QoL"); public static TopLevelGroup GetOrCreateTopLevelGroup(string name) { return GetOrCreateTopLevelGroupInternal(name); } private static TopLevelGroup GetOrCreateTopLevelGroupInternal(string name, bool dev = false, GroupLocalizationData localizationData = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Parameter 'name' can not be null or white space"); } if (TopLevelGroups.TryGetValue(name, out var value)) { value.IsNewlyCreated = false; } if (value == null) { value = new TopLevelGroup(name, dev); } if (localizationData != null) { value.AppendLocalization(localizationData); } TopLevelGroups[name] = value; return value; } internal static ModuleGroup GetOrCreateModuleGroup(IArchiveModule module, GroupLocalizationData localizationData = null) { string key = module.GetType().Assembly.GetName().Name + ".ModuleGroup"; if (ModuleGroups.TryGetValue(key, out var value)) { value.IsNewlyCreated = false; } if (value == null) { value = new ModuleGroup(module); } if (localizationData != null) { value.AppendLocalization(localizationData); } return value; } internal static bool TryGetGroup(string identifier, out GroupBase group) { if (!AllGroups.TryGetValue(identifier, out group)) { return false; } return true; } internal static ModuleGroup GetModuleGroup(string asmName) { if (!ModuleGroups.TryGetValue(asmName + ".ModuleGroup", out var value)) { return null; } return value; } } } namespace TheArchive.Core.FeaturesAPI.Settings { public class BoolSetting : FeatureSetting { public BoolSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { } } public class ButtonSetting : FComponentSetting { public string ButtonText => base.FComponent.ButtonText; public string ButtonID => base.FComponent.ButtonID; public Action Callback => base.FComponent.Callback; public bool RefreshSubMenu => base.FComponent.RefreshSubMenu; public ButtonSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { string text = prop.DeclaringType.FullName + "." + prop.Name; if (string.IsNullOrWhiteSpace(base.FComponent.ButtonID)) { base.FComponent.ButtonID = text; } if (featureSettingsHelper.Localization.TryGetFSText(text, FSType.FSButtonText, out var text2)) { base.FComponent.ButtonText = text2; } } } public class ColorSetting : FeatureSetting { public ColorSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { } } public class EnumListSetting : FeatureSetting { public string[] Options { get; } public Dictionary Map { get; } = new Dictionary(); public Dictionary ReversedMap { get; } = new Dictionary(); public Type EnumType { get; } public EnumListSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { EnumType = base.Type.GenericTypeArguments[0]; Options = Enum.GetNames(EnumType); string[] options = Options; foreach (string text in options) { object obj = Enum.Parse(EnumType, text); if (featureSettingsHelper.Localization.TryGetFSEnumText(EnumType, out var enumTexts) && enumTexts.TryGetValue(text, out var value)) { Map.Add(value, obj); ReversedMap.Add(obj, value); } else { Map.Add(text, obj); ReversedMap.Add(obj, text); } } } public object GetEnumValueFor(string option) { return Map.GetValueOrDefault(option); } public IList GetList() { return GetValue() as IList; } public bool ToggleInList(object value) { if (RemoveFromList(value)) { return false; } AddToList(value); return true; } public void AddToList(object value) { GetList().Add(value); InitSingletonBase.Instance.OnFeatureSettingChanged(this); base.Helper.IsDirty = true; } public bool RemoveFromList(object value) { IList list = GetList(); if (!list.Contains(value)) { return false; } list.Remove(value); InitSingletonBase.Instance.OnFeatureSettingChanged(this); base.Helper.IsDirty = true; return true; } public object[] CurrentSelectedValues() { IList list = GetList(); object[] array = new object[list.Count]; list.CopyTo(array, 0); return array; } public string[] CurrentSelectedValuesName() { IList list = GetList(); List list2 = new List(list.Count); foreach (object item in list) { list2.Add(ReversedMap[item]); } return list2.ToArray(); } } public class EnumSetting : FeatureSetting { public string[] Options { get; } public Dictionary Map { get; } = new Dictionary(); public EnumSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { Options = Enum.GetNames(base.Type); string[] options = Options; foreach (string text in options) { if (featureSettingsHelper.Localization.TryGetFSEnumText(base.Type, out var enumTexts) && enumTexts.TryGetValue(text, out var value)) { Map.Add(value, Enum.Parse(base.Type, text)); } else { Map.Add(text, Enum.Parse(base.Type, text)); } } } public object GetEnumValueFor(string option) { return Map.GetValueOrDefault(option); } public string GetCurrentEnumKey() { object value = GetValue(); return Map.FirstOrDefault((KeyValuePair kvp) => (int)kvp.Value == (int)value).Key; } } public class FComponentSetting : NotSavedFeatureSetting where T : class { public T FComponent { get; private set; } public FComponentSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { FComponent = (T)prop.GetValue(instance); } } public class FeatureSetting { public FeatureSettingsHelper Helper { get; } public PropertyInfo Prop { get; } public Utils.RundownFlags RundownHint { get; } public string DisplayName { get; } public bool UseTooltip { get; } public TooltipPositionType TooltipPositionType { get; } public string TooltipHeader { get; } public string TooltipText { get; } public string Description { get; } public string Identifier { get; } public bool Readonly { get; } public bool TopLevelReadonly { get; } public Type Type { get; } public string DEBUG_Path { get; } public object CM_SettingsItem { get; internal set; } public bool SeparatorAbove { get; private set; } public bool SpacerAbove { get; private set; } public FSHeader HeaderAbove { get; private set; } public bool HideInModSettings { get; private set; } public bool RequiresRestart { get; private set; } private object WrappedInstance { get; set; } public FeatureSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") { //IL_0118: 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) Helper = featureSettingsHelper; Prop = prop; Type = prop.GetMethod?.ReturnType; RequiresRestart = prop.GetCustomAttribute() != null; string propID = prop.DeclaringType.FullName + "." + prop.Name; if (featureSettingsHelper.Localization.TryGetFSText(propID, FSType.FSDisplayName, out var text)) { DisplayName = ">" + (RequiresRestart ? " [!]" : "") + " " + text; } else { DisplayName = ">" + (RequiresRestart ? " [!]" : "") + " " + (prop.GetCustomAttribute()?.DisplayName ?? prop.Name); } if (featureSettingsHelper.Localization.TryGetFSText(propID, FSType.FSDescription, out var text2)) { Description = text2; } else { Description = prop.GetCustomAttribute()?.Description; } FSTooltip customAttribute = prop.GetCustomAttribute(inherit: true); if (customAttribute != null) { TooltipPositionType = customAttribute.PositionType; TooltipHeader = customAttribute.TooltipHeader; TooltipText = customAttribute.TooltipText; if (featureSettingsHelper.Localization.TryGetFSText(propID, FSType.FSTooltipHeader, out var text3)) { TooltipHeader = text3; } if (featureSettingsHelper.Localization.TryGetFSText(propID, FSType.FSTooltipText, out var text4)) { TooltipText = text4; } UseTooltip = !string.IsNullOrWhiteSpace(TooltipHeader) || !string.IsNullOrWhiteSpace(TooltipText); } HeaderAbove = prop.GetCustomAttribute(); if (HeaderAbove != null && featureSettingsHelper.Localization.TryGetFSText(propID, FSType.FSHeader, out var text5)) { HeaderAbove = new FSHeader(text5, HeaderAbove.Color, HeaderAbove.Bold); } Identifier = prop.GetCustomAttribute()?.Identifier ?? (prop.PropertyType.FullName + "_" + prop.Name); RundownHint = prop.GetCustomAttribute()?.Rundowns ?? Utils.RundownFlags.None; SeparatorAbove = prop.GetCustomAttribute() != null; SpacerAbove = prop.GetCustomAttribute() != null; HideInModSettings = prop.GetCustomAttribute() != null; Readonly = prop.GetCustomAttribute()?.RecursiveReadOnly ?? false; TopLevelReadonly = !(prop.GetCustomAttribute()?.RecursiveReadOnly ?? true); WrappedInstance = instance; DEBUG_Path = debugPath; } public virtual object SetValue(object value) { Prop.SetValue(WrappedInstance, value); Helper.IsDirty = true; InitSingletonBase.Instance.OnFeatureSettingChanged(this); return value; } public virtual object GetValue() { return Prop.GetValue(WrappedInstance); } } public class GenericDictionarySetting : FeatureSetting { public class DictEntry { public GenericDictionarySetting Parent { get; } public Type KeyType { get; } public Type EntryType { get; } public string KeyName { get; } public object Key { get; } public object Value { get; } public DynamicFeatureSettingsHelper Helper { get; private set; } public DictEntry(GenericDictionarySetting gds, Type keyType, Type entryType, object key, object instance) { Parent = gds; KeyType = keyType; EntryType = entryType; Key = key; Value = instance; KeyName = gds.Localization.Get(KeyType, Key); Helper = new DynamicFeatureSettingsHelper(Parent.Helper.Feature, Parent.Helper).Initialize(entryType, instance); } public void RemoveFromList() { Parent.RemoveEntry(Key); } } public Type DictKeyType { get; } public Type DictValueType { get; } internal BaseLocalizationService Localization { get; } public GenericDictionarySetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { Localization = featureSettingsHelper.Localization; DictKeyType = base.Type.GenericTypeArguments[0]; DictValueType = base.Type.GenericTypeArguments[1]; } public IDictionary GetDict() { return GetValue() as IDictionary; } public void RemoveEntry(object key) { GetDict().Remove(key); base.Helper.IsDirty = true; } public void AddEntry(object key, object entry) { if (DictKeyType.IsAssignableFrom(key.GetType())) { GetDict().Add(key, entry); base.Helper.IsDirty = true; } } public IEnumerable GetEntries() { List list = new List(); foreach (DictionaryEntry item in GetDict()) { list.Add(new DictEntry(this, DictKeyType, DictValueType, item.Key, item.Value)); } return list; } } public class GenericListSetting : FeatureSetting { public class ListEntry { public GenericListSetting Parent { get; } public Type EntryType { get; private set; } public object Instance { get; } public DynamicFeatureSettingsHelper Helper { get; private set; } public ListEntry(GenericListSetting gls, Type entryType, object instance) { Parent = gls; EntryType = entryType; Instance = instance; Helper = new DynamicFeatureSettingsHelper(Parent.Helper.Feature, Parent.Helper).Initialize(entryType, instance); } public void RemoveFromList() { Parent.RemoveEntry(Instance); } } public Type ListType { get; } public GenericListSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { ListType = base.Type.GenericTypeArguments[0]; } public IList GetList() { return GetValue() as IList; } public void RemoveEntry(object entry) { GetList().Remove(entry); base.Helper.IsDirty = true; } public void AddEntry(object entry) { if (ListType.IsAssignableFrom(entry.GetType())) { GetList().Add(entry); base.Helper.IsDirty = true; } } public IEnumerable GetEntries() { List list = new List(); foreach (object item in GetList()) { list.Add(new ListEntry(this, ListType, item)); } return list; } } public class KeySetting : FeatureSetting { public Action KeyTextUpdated; public string Key { get; private set; } public KeySetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { Key = prop.GetValue(instance)?.ToString(); } public override object SetValue(object value) { UpdateKeyText(value.ToString()); return base.SetValue(value); } public KeyCode GetCurrent() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return (KeyCode)GetValue(); } public void UpdateKeyText(string keyText) { Key = keyText; KeyTextUpdated?.Invoke(Key); } } public class LabelSetting : FComponentSetting { public string LabelText => base.FComponent.LabelText; public string LabelID => base.FComponent.LabelID; public LabelSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { string text = prop.DeclaringType.FullName + "." + prop.Name; if (string.IsNullOrWhiteSpace(base.FComponent.LabelID)) { base.FComponent.LabelID = text; } if (featureSettingsHelper.Localization.TryGetFSText(text, FSType.FSLabelText, out var text2)) { base.FComponent.LabelText = text2; } } } public class NotSavedFeatureSetting : FeatureSetting { public NotSavedFeatureSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { } public override object SetValue(object value) { return value; } } public class NumberSetting : FeatureSetting { public enum NumberFormat { SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, UInt64, Single, Double } public bool HasSlider => Slider != null; public FSSlider Slider { get; } public FSTimestamp Timestamp { get; } public NumberFormat Format { get; } public NumberSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { Timestamp = prop.GetCustomAttribute(); Slider = prop.GetCustomAttribute(); switch (base.Type.Name) { case "Int64": Format = NumberFormat.Int64; break; case "Int32": Format = NumberFormat.Int32; break; case "Int16": Format = NumberFormat.Int16; break; case "UInt64": Format = NumberFormat.UInt64; break; case "UInt32": Format = NumberFormat.UInt32; break; case "UInt16": Format = NumberFormat.UInt16; break; case "Byte": Format = NumberFormat.Byte; break; case "SByte": Format = NumberFormat.SByte; break; case "Single": Format = NumberFormat.Single; break; case "Double": Format = NumberFormat.Double; break; default: throw new NotImplementedException(); } } public object ConvertNumber(object value) { return Format switch { NumberFormat.Single => Convert.ToSingle(value), NumberFormat.Double => Convert.ToDouble(value), NumberFormat.Byte => Convert.ToByte(value), NumberFormat.UInt16 => Convert.ToUInt16(value), NumberFormat.UInt32 => Convert.ToUInt32(value), NumberFormat.UInt64 => Convert.ToUInt64(value), NumberFormat.SByte => Convert.ToSByte(value), NumberFormat.Int16 => Convert.ToInt16(value), NumberFormat.Int32 => Convert.ToInt32(value), _ => Convert.ToInt64(value), }; } public override object GetValue() { object obj = base.GetValue(); if (Timestamp != null && long.TryParse(obj.ToString(), out var result)) { obj = new DateTime(result).ToString(Timestamp.Format); } return obj; } public override object SetValue(object value) { if (Timestamp != null) { ArchiveLogger.Warning($"Can't set backing value of {"NumberSetting"} \"{base.DEBUG_Path}\" with {"FSTimestamp"} attribute!"); return value; } string value2 = value.ToString(); if (string.IsNullOrWhiteSpace(value2)) { value2 = "0"; } try { return base.SetValue(ConvertNumber(value2)).ToString(); } catch (FormatException ex) { base.Helper.Feature.FeatureLogger.Warning($"[{"NumberSetting"}] {ex.GetType().FullName} was thrown! Invalid input data \"{value}\"."); return "0"; } } } public class StringSetting : FeatureSetting { public int MaxInputLength { get; } public StringSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object instance, string debugPath = "") : base(featureSettingsHelper, prop, instance, debugPath) { MaxInputLength = prop.GetCustomAttribute()?.MaxLength ?? 50; } } public class SubmenuSetting : FeatureSetting { public FeatureSettingsHelper SettingsHelper { get; private set; } public bool UseDynamicMenu { get; private set; } public SubmenuSetting(FeatureSettingsHelper featureSettingsHelper, PropertyInfo prop, object host, string debugPath = "") : base(featureSettingsHelper, prop, prop.GetValue(host), debugPath) { SettingsHelper = new FeatureSettingsHelper(featureSettingsHelper.Feature, prop) { ParentHelper = featureSettingsHelper }; SettingsHelper.SetupViaInstanceOnHost(host, prop.GetValue(host)); UseDynamicMenu = prop.GetCustomAttribute() != null; } } } namespace TheArchive.Core.FeaturesAPI.Groups { public sealed class FeatureGroup : GroupBase { internal FeatureGroup(string identifier, GroupBase parentGroup, bool isHidden = false) : base(parentGroup.Owner, identifier, parentGroup, isHidden) { } public override GroupBase GetOrCreateSubGroup(string name, bool isHidden = false) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Parameter 'name' can not be null or white space"); } string identifier = base.Identifier + "." + name; GroupBase groupBase = base.SubGroups.FirstOrDefault((GroupBase g) => g.Identifier == identifier); if (groupBase != null) { base.IsNewlyCreated = false; } if (groupBase == null) { groupBase = new FeatureGroup(identifier, this, isHidden); } return groupBase; } } public abstract class GroupBase { private GroupLocalizationData _localization = new GroupLocalizationData(); internal string DisplayName => _localization.GetLocalizedDisplayName(ArchiveLocalizationService.CurrentLanguage, Identifier) ?? Identifier; internal string Description => _localization.GetLocalizedDescription(ArchiveLocalizationService.CurrentLanguage, string.Empty) ?? string.Empty; public string Identifier { get; private set; } = string.Empty; public bool IsNewlyCreated { get; internal set; } = true; public bool IsHidden { get; private set; } public GroupBase ParentGroup { get; } public IArchiveModule Owner { get; } internal HashSet Features { get; } = new HashSet(); public HashSet SubGroups { get; } = new HashSet(); internal void AppendLocalization(GroupLocalizationData localizationData) { _localization.AppendData(localizationData); } protected GroupBase(IArchiveModule owner, string identifier, GroupBase parentGroup = null, bool isHidden = false) { if (string.IsNullOrWhiteSpace(identifier)) { throw new ArgumentException("Parameter 'identifier' can not be null or white space"); } Owner = owner; Identifier = identifier; IsHidden = isHidden; ParentGroup = parentGroup; parentGroup?.SubGroups.Add(this); GroupManager.AllGroups[Identifier] = this; } private GroupBase() { } public abstract GroupBase GetOrCreateSubGroup(string name, bool isHidden = false); public static implicit operator string(GroupBase group) { return group?.Identifier; } public static implicit operator GroupBase(string identifier) { return GroupManager.GetOrCreateTopLevelGroup(identifier); } } public sealed class ModuleGroup : GroupBase { internal ModuleGroup(IArchiveModule owner) : base(owner, owner.GetType().Assembly.GetName().Name + ".ModuleGroup") { GroupManager.ModuleGroups[owner.GetType().Assembly.GetName().Name + ".ModuleGroup"] = this; } public override GroupBase GetOrCreateSubGroup(string name, bool isHidden = false) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Parameter 'name' can not be null or white space"); } string identifier = base.Identifier + "." + name; GroupBase groupBase = base.SubGroups.FirstOrDefault((GroupBase g) => g.Identifier == identifier); if (groupBase != null) { base.IsNewlyCreated = false; } if (groupBase == null) { groupBase = new FeatureGroup(identifier, this, isHidden); } return groupBase; } } public sealed class TopLevelGroup : GroupBase { [CompilerGenerated] private sealed class <>c__DisplayClass1_0 { public string identifier; internal bool b__0(GroupBase g) { return g.Identifier == identifier; } } internal TopLevelGroup(string name, bool isHidden = false) : base(null, "Core." + name, null, isHidden) { GroupManager.TopLevelGroups["Core." + name] = this; } public override GroupBase GetOrCreateSubGroup(string name, bool isHidden = false) { <>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0(); throw new InvalidOperationException("Top-Level Group does not allow subgroups"); } public static implicit operator TopLevelGroup(string name) { return GroupManager.GetOrCreateTopLevelGroup(name); } } } namespace TheArchive.Core.FeaturesAPI.Components { public class FButton : ISettingsComponent { public string ButtonText { get; set; } internal string ButtonID { get; set; } public bool HasPrimaryText => (Object)(object)PrimaryText != (Object)null; public TextMeshPro PrimaryText { get; set; } public bool HasSecondaryText => (Object)(object)SecondaryText != (Object)null; public TextMeshPro SecondaryText { get; set; } public bool HasCallback => Callback != null; internal Action Callback { get; } public bool RefreshSubMenu { get; } public FButton() { } public FButton(string buttonText, string buttonId = null, Action callback = null, bool refreshSubMenu = false) { ButtonText = buttonText; ButtonID = buttonId; Callback = callback; RefreshSubMenu = refreshSubMenu; } } public class FLabel : ISettingsComponent { public string LabelText { get; set; } internal string LabelID { get; set; } public bool HasPrimaryText => (Object)(object)PrimaryText != (Object)null; public TextMeshPro PrimaryText { get; set; } public bool HasSecondaryText => false; public TextMeshPro SecondaryText { get; set; } public FLabel() { } public FLabel(string labelText, string labelId = null) { LabelText = labelText; LabelID = labelId; } } public interface ISettingsComponent { bool HasPrimaryText { get; } TextMeshPro PrimaryText { get; set; } bool HasSecondaryText { get; } TextMeshPro SecondaryText { get; set; } } } namespace TheArchive.Core.Exceptions { public class ArchiveFeatureDuplicateIDException : Exception { public ArchiveFeatureDuplicateIDException(string message) : base(message) { } } public class ArchivePatchMethodNotStaticException : Exception { public ArchivePatchMethodNotStaticException(string message) : base(message) { } } public class ArchivePatchNoTypeProvidedException : Exception { public ArchivePatchNoTypeProvidedException(string message) : base(message) { } } public class ArchivePatchNoOriginalMethodException : Exception { public ArchivePatchNoOriginalMethodException(string message) : base(message) { } } public class ArchivePatchNoPatchMethodException : Exception { public ArchivePatchNoPatchMethodException(string message) : base(message) { } } } namespace TheArchive.Core.Definitions { internal static class DefinitionManager { public enum DefinitionCategory { Group } private static readonly Dictionary> _loadedDefinitions = new Dictionary>(); private static readonly IArchiveLogger _logger = LoaderWrapper.CreateArSubLoggerInstance("DefinitionManager", ConsoleColor.DarkRed); public static void LoadModuleDefinitions(IArchiveModule module) { string name = module.GetType().Assembly.GetName().Name; string location = module.GetType().Assembly.Location; _loadedDefinitions.Add(name, new Dictionary()); if (LoadModuleDefinitionsFile(name, location, out var moduleDefinition) && ParseModuleGroupsDefinition(name, location, moduleDefinition, module)) { _loadedDefinitions[name][DefinitionCategory.Group] = moduleDefinition; } else { GroupManager.GetOrCreateModuleGroup(module); } } private static bool LoadModuleDefinitionsFile(string asmName, string asmLocation, out ModuleGroupsDefinition moduleDefinition) { moduleDefinition = null; string text = $"Module_{asmName}_{0}_Definition.json"; try { string text2 = Path.Combine(Path.GetDirectoryName(asmLocation), "Definitions"); if (!Directory.Exists(text2)) { return false; } string path = Path.Combine(text2, text); if (!File.Exists(path)) { return false; } moduleDefinition = JsonConvert.DeserializeObject(File.ReadAllText(path), ArchiveMod.JsonSerializerSettings); } catch (Exception ex) { _logger.Error("Failed to read module definition file: \"" + text + "\"."); _logger.Exception(ex); return false; } return true; } private static bool ParseModuleGroupsDefinition(string asmName, string asmLocation, ModuleGroupsDefinition moduleDefinition, IArchiveModule module) { try { BuildGroup(module, moduleDefinition.ModuleGroup); foreach (TopLevelGroupDefinition topLevelGroup in moduleDefinition.TopLevelGroups) { BuildGroup(module, topLevelGroup); } } catch (Exception ex) { _logger.Error("Failed to parse module definition for module: \"" + asmName + "\"."); _logger.Exception(ex); return false; } return true; } private static void BuildGroup(IArchiveModule owner, GroupDefinitionBase groupDefinition, GroupBase parentGroup = null) { if (groupDefinition == null) { return; } GroupBase groupBase = ((parentGroup != null) ? parentGroup.GetOrCreateSubGroup(groupDefinition.Name, groupDefinition.IsHidden) : (groupDefinition.Type switch { GroupType.TopLevel => GroupManager.GetOrCreateTopLevelGroup(groupDefinition.Name), GroupType.Module => GroupManager.GetOrCreateModuleGroup(owner), _ => throw new ArgumentNullException("parentGroup", $"{groupDefinition.Type}Group must have parent group."), })); groupBase.AppendLocalization(groupDefinition.LocalizationData); switch (groupDefinition.Type) { case GroupType.Module: { ModuleGroupDefinition moduleGroupDefinition = groupDefinition as ModuleGroupDefinition; if (moduleGroupDefinition.SubGroups == null || moduleGroupDefinition.SubGroups.Count <= 0) { break; } { foreach (FeatureGroupDefinition subGroup in moduleGroupDefinition.SubGroups) { BuildGroup(owner, subGroup, groupBase); } break; } } case GroupType.Feature: { FeatureGroupDefinition featureGroupDefinition = groupDefinition as FeatureGroupDefinition; if (featureGroupDefinition.SubGroups == null || featureGroupDefinition.SubGroups.Count <= 0) { break; } { foreach (FeatureGroupDefinition subGroup2 in featureGroupDefinition.SubGroups) { BuildGroup(owner, subGroup2, groupBase); } break; } } case GroupType.TopLevel: break; } } } } namespace TheArchive.Core.Definitions.Data { public enum GroupType { TopLevel, Module, Feature } public abstract class GroupDefinitionBase { public virtual string Name { get; set; } public virtual GroupType Type { get; set; } public virtual bool IsHidden { get; set; } public virtual GroupLocalizationData LocalizationData { get; set; } = new GroupLocalizationData(); } public class TopLevelGroupDefinition : GroupDefinitionBase { [JsonIgnore] public override GroupType Type => GroupType.TopLevel; [JsonIgnore] public override bool IsHidden => false; } public class FeatureGroupDefinition : GroupDefinitionBase { [JsonIgnore] public override GroupType Type => GroupType.Feature; public List SubGroups { get; set; } = new List(); } public class ModuleGroupDefinition : GroupDefinitionBase { [JsonIgnore] public override string Name => null; [JsonIgnore] public override GroupType Type => GroupType.Module; [JsonIgnore] public override bool IsHidden => false; public List SubGroups { get; set; } = new List(); } public class ModuleGroupsDefinition { public ModuleGroupDefinition ModuleGroup { get; set; } public List TopLevelGroups { get; set; } = new List(); } } namespace TheArchive.Core.Bootstrap { public class ArchiveModuleChainloader { protected static readonly string CurrentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name; protected static readonly Version CurrentAssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version; private static IArchiveLogger _logger; public static ArchiveModuleChainloader Instance { get; private set; } private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("ArchiveModuleChainloader", ConsoleColor.Magenta)); private static Regex allowedGuidRegex { get; } = new Regex("^[a-zA-Z0-9\\._\\-]+$"); public Dictionary Modules { get; } = new Dictionary(); public List DependencyErrors { get; } = new List(); public event Action ModuleLoaded; public event Action Finished; public static ModuleInfo ToModuleInfo(TypeDefinition type, string assemblyLocation) { //IL_004b: Expected O, but got Unknown if (type.IsInterface || type.IsAbstract) { return null; } try { if (((IEnumerable)type.Interfaces).All((InterfaceImplementation i) => ((MemberReference)i.InterfaceType).FullName != typeof(IArchiveModule).FullName)) { return null; } } catch (AssemblyResolutionException val) { AssemblyResolutionException ex = val; Logger.Exception((Exception)(object)ex); return null; } ArchiveModule archiveModule = ArchiveModule.FromCecilType(type); if (archiveModule == null) { Logger.Warning("Skipping over type [" + ((MemberReference)type).FullName + "] as no metadata attribute is specified."); return null; } if (string.IsNullOrEmpty(archiveModule.GUID) || !allowedGuidRegex.IsMatch(archiveModule.GUID)) { Logger.Warning($"Skipping type [{((MemberReference)type).FullName}] because its GUID [{archiveModule.GUID}] is of an illegal format."); return null; } if (archiveModule.Version == (Version)null) { Logger.Warning("Skipping type [" + ((MemberReference)type).FullName + "] because its version is invalid."); return null; } if (archiveModule.Name == null) { Logger.Warning("Skipping type [" + ((MemberReference)type).FullName + "] because its name is null."); return null; } IEnumerable dependencies = ArchiveDependency.FromCecilType(type); IEnumerable incompatibilities = ArchiveIncompatibility.FromCecilType(type); AssemblyNameReference? obj = ((IEnumerable)((MemberReference)type).Module.AssemblyReferences).FirstOrDefault((Func)((AssemblyNameReference reference) => reference.Name == CurrentAssemblyName)); Version targetedTheArchiveVersion = ((obj != null) ? obj.Version : null) ?? new Version(); return new ModuleInfo { Metadata = archiveModule, Dependencies = dependencies, Incompatibilities = incompatibilities, TypeName = ((MemberReference)type).FullName, TargetedTheArchiveVersion = targetedTheArchiveVersion, Location = assemblyLocation }; } protected static bool HasArchiveModule(AssemblyDefinition ass) { if (((IEnumerable)ass.MainModule.AssemblyReferences).All((AssemblyNameReference r) => r.Name != CurrentAssemblyName)) { return false; } return ass.MainModule.GetTypes().Any((TypeDefinition td) => ((IEnumerable)td.Interfaces).Any((InterfaceImplementation p) => ((MemberReference)p.InterfaceType).FullName == typeof(IArchiveModule).FullName)); } protected static bool ModuleTargetsWrongTheArchive(ModuleInfo ModuleInfo) { Version targetedTheArchiveVersion = ModuleInfo.TargetedTheArchiveVersion; if (targetedTheArchiveVersion.Major != CurrentAssemblyVersion.Major) { return true; } if (targetedTheArchiveVersion.Minor > CurrentAssemblyVersion.Minor) { return true; } if (targetedTheArchiveVersion.Minor < CurrentAssemblyVersion.Minor) { return false; } return targetedTheArchiveVersion.Build > CurrentAssemblyVersion.Build; } internal static void Initialize() { if (Instance != null) { throw new InvalidOperationException("Chainloader cannot be initialized multiple times"); } Instance = new ArchiveModuleChainloader(); Logger.Notice("Chainloader initialized"); Instance.Execute(); } protected IList DiscoverModulesFrom(string path, string cacheName = "TheArchive_ModuleChainloader") { return TypeLoader.FindModuleTypes(path, ToModuleInfo, HasArchiveModule, cacheName).SelectMany((KeyValuePair> p) => p.Value).ToList(); } protected IList DiscoverModules() { return DiscoverModulesFrom(Paths.PluginPath); } protected IList ModifyLoadOrder(IList modules) { SortedDictionary> dependencyDict = new SortedDictionary>(StringComparer.InvariantCultureIgnoreCase); Dictionary modulesByGuid = new Dictionary(); foreach (IGrouping item in from info in modules group info by info.Metadata.GUID) { if (Modules.TryGetValue(item.Key, out var value)) { Logger.Warning($"Skipping [{item.Key}] because a module with a similar GUID ([{value}]) has been already loaded."); continue; } ModuleInfo moduleInfo = null; foreach (ModuleInfo item2 in item.OrderByDescending((ModuleInfo x) => x.Metadata.Version)) { if (moduleInfo != null) { Logger.Warning($"Skip [{item2}] because a newer version exists ({moduleInfo})"); } else { moduleInfo = item2; dependencyDict[item2.Metadata.GUID] = item2.Dependencies.Select((ArchiveDependency d) => d.DependencyGUID); modulesByGuid[item2.Metadata.GUID] = item2; } } } foreach (ModuleInfo item3 in modulesByGuid.Values.ToList()) { if (item3.Incompatibilities.Any((ArchiveIncompatibility incompatibility) => modulesByGuid.ContainsKey(incompatibility.IncompatibilityGUID) || Modules.ContainsKey(incompatibility.IncompatibilityGUID) || ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey(incompatibility.IncompatibilityGUID))) { modulesByGuid.Remove(item3.Metadata.GUID); dependencyDict.Remove(item3.Metadata.GUID); IEnumerable first = from x in item3.Incompatibilities select x.IncompatibilityGUID into x where modulesByGuid.ContainsKey(x) select x; IEnumerable second = from x in item3.Incompatibilities select x.IncompatibilityGUID into x where Modules.ContainsKey(x) select x; string[] value2 = first.Concat(second).ToArray(); string text = $"Could not load [{item3}] because it is incompatible with: {string.Join(", ", value2)}"; DependencyErrors.Add(text); Logger.Error(text); } else if (ModuleTargetsWrongTheArchive(item3)) { string text2 = $"Module [{item3}] targets a wrong version of TheArchive ({item3.TargetedTheArchiveVersion}) and might not work until you update"; DependencyErrors.Add(text2); Logger.Warning(text2); } } string[] emptyDependencies = Array.Empty(); return (from x in Utils.TopologicalSort(dependencyDict.Keys, (string x) => dependencyDict.GetValueOrDefault(x, emptyDependencies)).ToList().Where(modulesByGuid.ContainsKey) select modulesByGuid[x]).ToList(); } public void Execute() { try { IList list = DiscoverModules(); Logger.Info($"{list.Count} module{((list.Count == 1) ? "" : "s")} to load"); LoadModules(list); this.Finished?.Invoke(); } catch (Exception ex) { Logger.Error("Error occurred loading modules: "); Logger.Exception(ex); } Logger.Notice("Chainloader startup complete"); } private IList LoadModules(IList modules) { IList list = ModifyLoadOrder(modules); HashSet hashSet = new HashSet(); Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); List list2 = new List(); foreach (ModuleInfo item in list) { bool flag = false; List list3 = new List(); foreach (ArchiveDependency dependency in item.Dependencies) { Version value; bool flag2 = dictionary.TryGetValue(dependency.DependencyGUID, out value); if (!flag2) { flag2 = Modules.TryGetValue(dependency.DependencyGUID, out var value2); value = value2?.Metadata.Version; if (!flag2 || value == (Version)null) { flag2 = ((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue(dependency.DependencyGUID, out var value3); value = ((value3 != null) ? value3.Metadata.Version : null); } } if (!flag2 || (dependency.VersionRange != (Range)null && !dependency.VersionRange.IsSatisfied(value, false))) { if (IsHardDependency(dependency)) { list3.Add(dependency); } } else if (hashSet.Contains(dependency.DependencyGUID) && IsHardDependency(dependency)) { flag = true; break; } } dictionary.Add(item.Metadata.GUID, item.Metadata.Version); if (flag) { string text = $"Skipping [{item}] because it has a dependency that was not loaded. See previous errors for details."; DependencyErrors.Add(text); Logger.Warning(text); continue; } if (list3.Count != 0) { string text2 = $"Could not load [{item}] because it has missing dependencies: {string.Join(", ", list3.Select((ArchiveDependency s) => (!(s.VersionRange == (Range)null)) ? $"{s.DependencyGUID} ({s.VersionRange})" : s.DependencyGUID).ToArray())}"; DependencyErrors.Add(text2); Logger.Error(text2); hashSet.Add(item.Metadata.GUID); continue; } try { Logger.Info($"Loading [{item}]"); if (!dictionary2.TryGetValue(item.Location, out var value4)) { value4 = (dictionary2[item.Location] = Assembly.LoadFrom(item.Location)); } Modules[item.Metadata.GUID] = item; TryRunModuleCtor(item, value4); item.Instance = LoadModule(item, value4); list2.Add(item); this.ModuleLoaded?.Invoke(item); } catch (Exception ex) { hashSet.Add(item.Metadata.GUID); Modules.Remove(item.Metadata.GUID); Logger.Error($"Error loading [{item}]:"); if (ex is ReflectionTypeLoadException ex2) { Logger.Error(TypeLoader.TypeLoadExceptionToString(ex2)); } else { Logger.Exception(ex); } } } return list2; } public IList LoadModule(params string[] modulesPaths) { List list = new List(); foreach (string path in modulesPaths) { list.AddRange(DiscoverModulesFrom(path)); } return LoadModules(list); } private static void TryRunModuleCtor(ModuleInfo module, Assembly assembly) { try { RuntimeHelpers.RunModuleConstructor(assembly.GetType(module.TypeName).Module.ModuleHandle); } catch (Exception ex) { Logger.Error($"Couldn't run Module constructor for {assembly.FullName}::{module.TypeName}:"); Logger.Exception(ex); } } private IArchiveModule LoadModule(ModuleInfo moduleInfo, Assembly moduleAssembly) { return ArchiveMod.CreateAndInitModule(moduleAssembly.GetType(moduleInfo.TypeName)); } private static bool IsHardDependency(ArchiveDependency dep) { return (dep.Flags & ArchiveDependency.DependencyFlags.HardDependency) > (ArchiveDependency.DependencyFlags)0; } } public class CachedAssembly where T : ICacheable { public List CacheItems { get; set; } public string Hash { get; set; } } public interface ICacheable { void Save(BinaryWriter bw); void Load(BinaryReader br); } public class ModuleInfo : ICacheable { public ArchiveModule Metadata { get; internal set; } public IEnumerable Dependencies { get; internal set; } public IEnumerable Incompatibilities { get; internal set; } public string Location { get; internal set; } public object Instance { get; internal set; } public string TypeName { get; internal set; } internal Version TargetedTheArchiveVersion { get; set; } public void Save(BinaryWriter bw) { bw.Write(TypeName); bw.Write(Location); bw.Write(Metadata.GUID); bw.Write(Metadata.Name); bw.Write(((object)Metadata.Version).ToString()); List list = Dependencies.ToList(); bw.Write(list.Count); foreach (ArchiveDependency item in list) { ((ICacheable)item).Save(bw); } List list2 = Incompatibilities.ToList(); bw.Write(list2.Count); foreach (ArchiveIncompatibility item2 in list2) { ((ICacheable)item2).Save(bw); } bw.Write(TargetedTheArchiveVersion.ToString(4)); } public void Load(BinaryReader br) { TypeName = br.ReadString(); Location = br.ReadString(); Metadata = new ArchiveModule(br.ReadString(), br.ReadString(), br.ReadString()); int num = br.ReadInt32(); List list = new List(num); for (int i = 0; i < num; i++) { ArchiveDependency archiveDependency = new ArchiveDependency(string.Empty); ((ICacheable)archiveDependency).Load(br); list.Add(archiveDependency); } Dependencies = list; int num2 = br.ReadInt32(); List list2 = new List(num2); for (int j = 0; j < num2; j++) { ArchiveIncompatibility archiveIncompatibility = new ArchiveIncompatibility(string.Empty); ((ICacheable)archiveIncompatibility).Load(br); list2.Add(archiveIncompatibility); } Incompatibilities = list2; TargetedTheArchiveVersion = new Version(br.ReadString()); } public override string ToString() { return $"{Metadata?.Name} {Metadata?.Version}"; } } public static class TypeLoader { private static IArchiveLogger _logger; public static readonly DefaultAssemblyResolver CecilResolver; public static readonly ReaderParameters ReaderParameters; public static HashSet SearchDirectories; private static readonly bool EnableAssemblyCache; [CompilerGenerated] private static AssemblyResolveEventHandler m_AssemblyResolve; private static IArchiveLogger Logger => _logger ?? (_logger = LoaderWrapper.CreateLoggerInstance("TypeLoader")); public static event AssemblyResolveEventHandler AssemblyResolve { [CompilerGenerated] add { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown AssemblyResolveEventHandler val = TypeLoader.m_AssemblyResolve; AssemblyResolveEventHandler val2; do { val2 = val; AssemblyResolveEventHandler value2 = (AssemblyResolveEventHandler)Delegate.Combine((Delegate?)(object)val2, (Delegate?)(object)value); val = Interlocked.CompareExchange(ref TypeLoader.m_AssemblyResolve, value2, val2); } while (val != val2); } [CompilerGenerated] remove { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown AssemblyResolveEventHandler val = TypeLoader.m_AssemblyResolve; AssemblyResolveEventHandler val2; do { val2 = val; AssemblyResolveEventHandler value2 = (AssemblyResolveEventHandler)Delegate.Remove((Delegate?)(object)val2, (Delegate?)(object)value); val = Interlocked.CompareExchange(ref TypeLoader.m_AssemblyResolve, value2, val2); } while (val != val2); } } static TypeLoader() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown SearchDirectories = new HashSet(); EnableAssemblyCache = true; CecilResolver = new DefaultAssemblyResolver(); ReaderParameters = new ReaderParameters { AssemblyResolver = (IAssemblyResolver)(object)CecilResolver }; ((BaseAssemblyResolver)CecilResolver).ResolveFailure += new AssemblyResolveEventHandler(CecilResolveOnFailure); } public static AssemblyDefinition CecilResolveOnFailure(object sender, AssemblyNameReference reference) { if (!Utils.TryParseAssemblyName(reference.FullName, out var assemblyName)) { return null; } foreach (string item in new string[4] { Paths.BepInExAssemblyDirectory, Paths.PluginPath, Paths.PatcherPluginPath, Paths.ManagedPath }.Concat(SearchDirectories)) { AssemblyDefinition assembly; if (!Directory.Exists(item)) { Logger.Debug("Unable to resolve cecil search directory '" + item + "'"); } else if (Utils.TryResolveDllAssembly(assemblyName, item, ReaderParameters, out assembly)) { return assembly; } } AssemblyResolveEventHandler assemblyResolve = TypeLoader.AssemblyResolve; if (assemblyResolve == null) { return null; } return assemblyResolve.Invoke(sender, reference); } public static Dictionary> FindModuleTypes(string directory, Func typeSelector, Func assemblyFilter = null, string cacheName = null) where T : ICacheable, new() { Dictionary> dictionary = new Dictionary>(); Dictionary dictionary2 = new Dictionary(); Dictionary> dictionary3 = null; if (cacheName != null) { dictionary3 = LoadAssemblyCache(cacheName); } string[] files = Directory.GetFiles(Path.GetFullPath(directory), "*.dll", SearchOption.AllDirectories); foreach (string dll in files) { try { using MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(dll)); string text = Utils.HashStream(memoryStream); dictionary2[dll] = text; memoryStream.Position = 0L; if (dictionary3 != null && dictionary3.TryGetValue(dll, out var value) && text == value.Hash) { dictionary[dll] = value.CacheItems; continue; } AssemblyDefinition val = AssemblyDefinition.ReadAssembly((Stream)memoryStream, ReaderParameters); try { if (assemblyFilter != null && !assemblyFilter(val)) { dictionary[dll] = new List(); continue; } List value2 = (from t in (IEnumerable)val.MainModule.Types select typeSelector(t, dll) into t where t != null select t).ToList(); dictionary[dll] = value2; } finally { ((IDisposable)val)?.Dispose(); } } catch (BadImageFormatException ex) { Logger.Debug("Skipping loading " + dll + " because it's not a valid .NET assembly. Full error: " + ex.Message); } catch (Exception ex2) { Logger.Exception(ex2); } } if (cacheName != null) { SaveAssemblyCache(cacheName, dictionary, dictionary2); } return dictionary; } public static Dictionary> LoadAssemblyCache(string cacheName) where T : ICacheable, new() { if (!EnableAssemblyCache) { return null; } Dictionary> dictionary = new Dictionary>(); try { string path = Path.Combine(Paths.CachePath, cacheName + "_typeloader.dat"); if (!File.Exists(path)) { return null; } using BinaryReader binaryReader = new BinaryReader(File.OpenRead(path)); int num = binaryReader.ReadInt32(); for (int i = 0; i < num; i++) { string key = binaryReader.ReadString(); string hash = binaryReader.ReadString(); int num2 = binaryReader.ReadInt32(); List list = new List(); for (int j = 0; j < num2; j++) { T item = new T(); item.Load(binaryReader); list.Add(item); } dictionary[key] = new CachedAssembly { Hash = hash, CacheItems = list }; } } catch (Exception ex) { Logger.Warning("Failed to load cache \"" + cacheName + "\": skipping loading cache. Reason: " + ex.Message); } return dictionary; } public static void SaveAssemblyCache(string cacheName, Dictionary> entries, Dictionary hashes) where T : ICacheable { if (!EnableAssemblyCache) { return; } try { if (!Directory.Exists(Paths.CachePath)) { Directory.CreateDirectory(Paths.CachePath); } using BinaryWriter binaryWriter = new BinaryWriter(File.OpenWrite(Path.Combine(Paths.CachePath, cacheName + "_typeloader.dat"))); binaryWriter.Write(entries.Count); foreach (KeyValuePair> entry in entries) { binaryWriter.Write(entry.Key); binaryWriter.Write(hashes.GetValueOrDefault(entry.Key, "")); binaryWriter.Write(entry.Value.Count); foreach (T item in entry.Value) { item.Save(binaryWriter); } } } catch (Exception ex) { Logger.Warning("Failed to save cache \"" + cacheName + "\"; skipping saving cache. Reason: " + ex.Message); } } public static string TypeLoadExceptionToString(ReflectionTypeLoadException ex) { StringBuilder stringBuilder = new StringBuilder(); Exception[] loaderExceptions = ex.LoaderExceptions; foreach (Exception ex2 in loaderExceptions) { stringBuilder.AppendLine(ex2.Message); if (ex2 is FileNotFoundException ex3) { if (!string.IsNullOrEmpty(ex3.FusionLog)) { stringBuilder.AppendLine("Fusion Log:"); stringBuilder.AppendLine(ex3.FusionLog); } } else if (ex2 is FileLoadException ex4 && !string.IsNullOrEmpty(ex4.FusionLog)) { stringBuilder.AppendLine("Fusion Log:"); stringBuilder.AppendLine(ex4.FusionLog); } stringBuilder.AppendLine(); } return stringBuilder.ToString(); } } } namespace TheArchive.Core.Attributes { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] public class BuildConstraint : Attribute { public enum MatchMode { Exact, Lower, LowerOrEqual, Greater, GreaterOrEqual, Exclude } private int BuildNumber { get; set; } private MatchMode Mode { get; set; } public BuildConstraint(int build, MatchMode mode = MatchMode.Exact) { BuildNumber = build; Mode = mode; } public bool Matches(int buildNumber) { return Mode switch { MatchMode.Greater => buildNumber > BuildNumber, MatchMode.GreaterOrEqual => buildNumber >= BuildNumber, MatchMode.Lower => buildNumber < BuildNumber, MatchMode.LowerOrEqual => buildNumber <= BuildNumber, MatchMode.Exclude => buildNumber != BuildNumber, _ => buildNumber == BuildNumber, }; } } [AttributeUsage(AttributeTargets.Assembly)] public class ModSettingsDisplayName : Attribute { public string DisplayName { get; private set; } public ModSettingsDisplayName(string displayName) { DisplayName = displayName; } } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class ArchiveDependency : Attribute, ICacheable { [Flags] public enum DependencyFlags { HardDependency = 1, SoftDependency = 2 } public string DependencyGUID { get; protected set; } public DependencyFlags Flags { get; protected set; } public Range VersionRange { get; protected set; } public ArchiveDependency(string DependencyGUID, DependencyFlags Flags = DependencyFlags.HardDependency) { this.DependencyGUID = DependencyGUID; this.Flags = Flags; VersionRange = null; } public ArchiveDependency(string guid, string version) : this(guid) { VersionRange = Range.Parse(version, false); } public void Save(BinaryWriter bw) { bw.Write(DependencyGUID); bw.Write((int)Flags); bw.Write(((object)VersionRange)?.ToString() ?? string.Empty); } public void Load(BinaryReader br) { DependencyGUID = br.ReadString(); Flags = (DependencyFlags)br.ReadInt32(); string text = br.ReadString(); VersionRange = ((text == string.Empty) ? null : Range.Parse(text, false)); } internal static IEnumerable FromCecilType(TypeDefinition td) { return MetadataHelper.GetCustomAttributes(td, inherit: true).Select(delegate(CustomAttribute customAttribute) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) CustomAttributeArgument val = customAttribute.ConstructorArguments[0]; string text = (string)((CustomAttributeArgument)(ref val)).Value; val = customAttribute.ConstructorArguments[1]; object value = ((CustomAttributeArgument)(ref val)).Value; return (value is string version) ? new ArchiveDependency(text, version) : new ArchiveDependency(text, (DependencyFlags)value); }).ToList(); } } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class ArchiveIncompatibility : Attribute, ICacheable { public string IncompatibilityGUID { get; protected set; } public ArchiveIncompatibility(string IncompatibilityGUID) { this.IncompatibilityGUID = IncompatibilityGUID; } internal static IEnumerable FromCecilType(TypeDefinition td) { return MetadataHelper.GetCustomAttributes(td, inherit: true).Select(delegate(CustomAttribute customAttribute) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) CustomAttributeArgument val = customAttribute.ConstructorArguments[0]; return new ArchiveIncompatibility((string)((CustomAttributeArgument)(ref val)).Value); }).ToList(); } public void Save(BinaryWriter bw) { bw.Write(IncompatibilityGUID); } public void Load(BinaryReader br) { IncompatibilityGUID = br.ReadString(); } } [AttributeUsage(AttributeTargets.Class)] [MeansImplicitUse(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers)] public class ArchiveModule : Attribute { public string GUID { get; protected set; } public string Name { get; protected set; } public Version Version { get; protected set; } public ArchiveModule(string GUID, string Name, string Version) { this.GUID = GUID; this.Name = Name; this.Version = TryParseLongVersion(Version); } private static Version TryParseLongVersion(string version) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown Version result = default(Version); if (Version.TryParse(version, ref result)) { return result; } try { Version version2 = new Version(version); return new Version(version2.Major, version2.Minor, (version2.Build != -1) ? version2.Build : 0, (string)null, (string)null); } catch { } return null; } internal static ArchiveModule FromCecilType(TypeDefinition td) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) CustomAttribute val = MetadataHelper.GetCustomAttributes(td, inherit: false).FirstOrDefault(); if (val == null) { return null; } CustomAttributeArgument val2 = val.ConstructorArguments[0]; string gUID = (string)((CustomAttributeArgument)(ref val2)).Value; val2 = val.ConstructorArguments[1]; string name = (string)((CustomAttributeArgument)(ref val2)).Value; val2 = val.ConstructorArguments[2]; return new ArchiveModule(gUID, name, (string)((CustomAttributeArgument)(ref val2)).Value); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] public class RundownConstraint : Attribute { public Utils.RundownFlags Rundowns { get; private set; } public RundownConstraint(Utils.RundownFlags flags) { Rundowns = flags; } public RundownConstraint(Utils.RundownFlags from, Utils.RundownFlags to) { Rundowns = from.To(to); } public bool Matches(Utils.RundownID value) { return value.IsIncludedIn(Rundowns); } } } namespace TheArchive.Core.Attributes.Feature { [AttributeUsage(AttributeTargets.Class)] public class AutomatedFeature : Attribute { } [AttributeUsage(AttributeTargets.Class)] public class DisallowInGameToggle : Attribute { } [AttributeUsage(AttributeTargets.Class)] public class DoNotSaveToConfig : Attribute { } [AttributeUsage(AttributeTargets.Class)] public class EnableFeatureByDefault : Attribute { } [AttributeUsage(AttributeTargets.Class)] public class FeatureIncompatiblity : Attribute { public string IncompatibilityGUID { get; } public FeatureIncompatiblity(string IncompatibilityGUID) { this.IncompatibilityGUID = IncompatibilityGUID; } } [AttributeUsage(AttributeTargets.Class)] public class ForceDisable : Attribute { public string Justification { get; set; } public ForceDisable(string justification) { Justification = justification; } } [AttributeUsage(AttributeTargets.Class)] public class HideInModSettings : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class RequiresRestart : Attribute { } } namespace TheArchive.Core.Attributes.Feature.Settings { [AttributeUsage(AttributeTargets.Property)] public class FSDescription : Localized { public string Description => base.UntranslatedText; public FSDescription(string description) : base(description) { } } [AttributeUsage(AttributeTargets.Property)] public class FSDisplayName : Localized { public string DisplayName => base.UntranslatedText; public FSDisplayName(string displayName) : base(displayName) { } } [AttributeUsage(AttributeTargets.Property)] public class FSHeader : Localized { internal string Title => base.UntranslatedText; internal SColor Color { get; } internal bool Bold { get; } public FSHeader(string title, bool bold = true) : base(title) { Color = SColor.DARK_ORANGE.WithAlpha(0.8f); Bold = bold; } public FSHeader(string title, SColor color, bool bold = true) : base(title) { Color = color; Bold = bold; } } [AttributeUsage(AttributeTargets.Property)] public class FSHide : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class FSIdentifier : Attribute { internal string Identifier { get; } public FSIdentifier(string identifier) { Identifier = identifier; } } [AttributeUsage(AttributeTargets.Property)] public class FSIgnore : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class FSInline : Attribute { } public class FSMaxLength : Attribute { public int MaxLength { get; } public FSMaxLength(int maxLength = 50) { MaxLength = maxLength; } } [AttributeUsage(AttributeTargets.Property)] public class FSReadOnly : Attribute { public bool RecursiveReadOnly { get; } public FSReadOnly(bool recursive = true) { RecursiveReadOnly = recursive; } } [AttributeUsage(AttributeTargets.Property)] public class FSRundownHint : Attribute { public Utils.RundownFlags Rundowns { get; private set; } public FSRundownHint(Utils.RundownFlags flags) { Rundowns = flags; } public FSRundownHint(Utils.RundownFlags from, Utils.RundownFlags to) { Rundowns = from.To(to); } } [AttributeUsage(AttributeTargets.Property)] public class FSSeparator : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class FSSlider : Attribute { [Localized] public enum SliderStyle { FloatPercent, FloatNoDecimal, FloatOneDecimal, FloatTwoDecimal, [Obsolete("Not yet implemented properly! Use a float value instead!")] IntMinMax } [Localized] public enum RoundTo { NoRounding = -1, NoDecimal, OneDecimal, TwoDecimal, ThreeDecimal } public float Min { get; set; } public float Max { get; set; } public SliderStyle Style { get; set; } public RoundTo Rounding { get; set; } public FSSlider(float min, float max, SliderStyle style = SliderStyle.FloatPercent, RoundTo rounding = RoundTo.TwoDecimal) { Min = min; Max = max; Style = style; Rounding = rounding; } } [AttributeUsage(AttributeTargets.Property)] public class FSSpacer : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class FSTimestamp : Attribute { public string Format { get; } = "U"; public FSTimestamp(string customFormat = "U") { try { Format = customFormat; DateTime.Now.ToString(Format); } catch (Exception) { ArchiveLogger.Warning($"A {"FSTimestamp"}s custom format threw an exception! Format String: \"{Format}\""); Format = "U"; } } } [AttributeUsage(AttributeTargets.Property)] public class FSTooltip : Localized { public TooltipPositionType PositionType { get; private set; } public string TooltipHeader { get; private set; } public string TooltipText { get; private set; } public FSTooltip(string text, string header = null, TooltipPositionType positionType = 0) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) TooltipHeader = header; TooltipText = text; PositionType = positionType; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] public class FSUseDynamicSubmenu : Attribute { } } namespace TheArchive.Core.Attributes.Feature.Patches { [AttributeUsage(AttributeTargets.Method)] public class ArchiveAfter : Attribute { public string[] After { get; internal set; } public ArchiveAfter(params string[] harmonyIds) { After = harmonyIds?.Distinct().ToArray(); } } public class ArchiveAfterFeature : ArchiveAfter { public ArchiveAfterFeature(params string[] guids) { base.After = guids?.Select((string s) => string.Format("TheArchive_FeaturesAPI_" + s)).Distinct().ToArray(); } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class ArchiveBefore : Attribute { public string[] Before { get; internal set; } public ArchiveBefore(params string[] harmonyIds) { Before = harmonyIds?.Distinct().ToArray(); } } public class ArchiveBeforeFeature : ArchiveBefore { public ArchiveBeforeFeature(params string[] guids) { base.Before = guids?.Select((string s) => string.Format("TheArchive_FeaturesAPI_" + s)).Distinct().ToArray(); } } [AttributeUsage(AttributeTargets.Class)] internal class ArchiveConstructorPatch : ArchivePatch { public ArchiveConstructorPatch(Type type, Type[] parameterTypes = null) : base(type, null, parameterTypes, PatchMethodType.Constructor) { } } [AttributeUsage(AttributeTargets.Class)] [MeansImplicitUse(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.WithMembers | ImplicitUseTargetFlags.WithInheritors)] public class ArchivePatch : Attribute { public enum PatchMethodType { Method, Getter, Setter, Constructor } public const bool SKIP_OG = false; public const bool RUN_OG = true; public bool HasType => Type != null; public Type Type { get; internal set; } public string MethodName { get; internal set; } public Type[] ParameterTypes { get; internal set; } public PatchMethodType MethodType { get; internal set; } public int Priority { get; set; } public ArchivePatch(string methodName, Type[] parameterTypes = null, PatchMethodType patchMethodType = PatchMethodType.Method, int priority = -1) : this(null, methodName, parameterTypes, patchMethodType, priority) { } public ArchivePatch(Type type, string methodName, Type[] parameterTypes = null, PatchMethodType patchMethodType = PatchMethodType.Method, int priority = -1) { Type = type; MethodName = methodName; ParameterTypes = parameterTypes; MethodType = patchMethodType; Priority = priority; if (patchMethodType == PatchMethodType.Constructor) { MethodName = ".ctor"; } } } [AttributeUsage(AttributeTargets.Method)] public class IsFinalizer : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsILManipulator : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsInitMethod : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsMethodNameProvider : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsOriginal : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsParameterTypesProvider : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsPostfix : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsPrefix : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsPriorityProvider : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsSnapshot : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsTranspiler : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsTypeProvider : Attribute { } } namespace TheArchive.Core.Attributes.Feature.Members { [AttributeUsage(AttributeTargets.Property)] public class FeatureConfig : Attribute { } [AttributeUsage(AttributeTargets.Method)] [Obsolete("Feature not implemented.")] public class IsAreaCullUpdateMethod : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsGameStateChangedMethod : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsLateUpdate : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class IsUpdate : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class SetEnabledStatus : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class SetStaticInstance : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class SetStaticLocalizationService : Attribute { } [AttributeUsage(AttributeTargets.Property)] public class SetStaticLogger : Attribute { } }