using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.FileProviders; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SimpleInjector; using TrainworksReloaded.Core.Configuration; using TrainworksReloaded.Core.Enum; using TrainworksReloaded.Core.Impl; using TrainworksReloaded.Core.Interfaces; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TrainworksReloaded.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.7.17.0")] [assembly: AssemblyInformationalVersion("0.7.17+70425a1dcb597e90fd3db40c36bb4d19c1a44045")] [assembly: AssemblyProduct("TrainworksReloaded.Core")] [assembly: AssemblyTitle("TrainworksReloaded.Core")] [assembly: AssemblyVersion("0.7.17.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class ParamCollectionAttribute : Attribute { } [CompilerGenerated] [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] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TrainworksReloaded.Core { public class Railend { private static readonly List> PreContainerActions = new List>(); private static readonly List> PostContainerActions = new List>(); private static readonly ManualLogSource logger = Logger.CreateLogSource("Railend"); private static readonly Lazy container = new Lazy((Func)delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown LazyBuilder builderForInit = Railhead.GetBuilderForInit(); Container val = new Container(); foreach (Action preContainerAction in PreContainerActions) { try { preContainerAction(val); } catch (Exception ex) { logger.LogError((object)"============================================================"); logger.LogError((object)$"[CATASTROPHIC] Mod at {preContainerAction.Method.DeclaringType.Assembly.GetName()} failed to load due to exception."); logger.LogError((object)"============================================================"); logger.LogError((object)ex.ToString()); } } builderForInit.Build(val); foreach (Action postContainerAction in PostContainerActions) { try { postContainerAction(val); } catch (Exception ex2) { logger.LogError((object)"============================================================"); logger.LogError((object)$"[CATASTROPHIC] Mod at {postContainerAction.Method.DeclaringType.Assembly.GetName()} failed to load due to exception."); logger.LogError((object)"============================================================"); logger.LogError((object)ex2.ToString()); } } val.Verify(); return val; }); public static void ConfigurePreAction(Action action) { PreContainerActions.Add(action); } public static void ConfigurePostAction(Action action) { PostContainerActions.Add(action); } public static Container GetContainer() { return container.Value; } } public class Railhead { private static readonly LazyBuilder lazyConfigure = new LazyBuilder(); public static ILazyBuilder GetBuilder() { return lazyConfigure; } internal static LazyBuilder GetBuilderForInit() { return lazyConfigure; } } } namespace TrainworksReloaded.Core.Interfaces { public interface ICache { void AddToCache(T item); IEnumerable GetCacheItems(); void Clear(); } public interface IDataFinalizer { void FinalizeData(); } public interface IDataPipeline where T : IRegister { List> Run(T service); } public interface IDefinition { string Key { get; } string Id { get; } T Data { get; } IConfiguration Configuration { get; } bool IsModded { get; } } public interface IFactory { string FactoryKey { get; } T? GetValue(); } public interface IGuidProvider { Guid GetGuidDeterministic(string key); } public interface IInstanceGenerator where T : new() { T CreateInstance(); } public interface ILazyBuilder { void Configure(string pluginId, Action action); void ConfigureLoaders(Action action); } [Flags] public enum LogLevel { None = 0, Fatal = 1, Error = 2, Warning = 4, Message = 8, Info = 0x10, Debug = 0x20, All = 0x3F } public interface IModLogger { void Log(LogLevel level, object data); } public interface IRegister : IRegisterableDictionary, IDictionary, ICollection>, IEnumerable>, IEnumerable { List GetAllIdentifiers(RegisterIdentifierType identifierType); bool TryLookupIdentifier(string identifier, RegisterIdentifierType identifierType, [NotNullWhen(true)] out T? lookup, [NotNullWhen(true)] out bool? IsModded); } public interface IRegisterableDictionary : IDictionary, ICollection>, IEnumerable>, IEnumerable { void Register(string key, T item); } } namespace TrainworksReloaded.Core.Impl { public class Cache : ICache { public List Values { get; set; } = new List(); public void AddToCache(T item) { Values.Add(item); } public void Clear() { Values.Clear(); } public IEnumerable GetCacheItems() { return Values.AsEnumerable(); } } public class CacheDataPipelineDecorator : IDataPipeline where T : IRegister { private readonly IDataPipeline decoratee; private readonly ICache> cache; public CacheDataPipelineDecorator(IDataPipeline decoratee, ICache> cache) { this.decoratee = decoratee; this.cache = cache; } public List> Run(T service) { List> list = decoratee.Run(service); foreach (IDefinition item in list) { cache.AddToCache(item); } return list; } } public class DeterministicGuidGenerator : IGuidProvider { private static readonly SHA256CryptoServiceProvider provider = new SHA256CryptoServiceProvider(); public Guid GetGuidDeterministic(string key) { byte[] bytes = Encoding.Default.GetBytes(key); byte[] array = provider.ComputeHash(bytes); array[7] = (byte)(0x40 | (array[7] & 0xF)); array[8] = (byte)(0x80 | (array[8] & 0x3F)); Array.Resize(ref array, 16); return new Guid(array); } } public class Finalizer { private readonly IEnumerable dataFinalizers; public Finalizer(IEnumerable dataFinalizers) { this.dataFinalizers = dataFinalizers; } public void FinalizeData() { foreach (IDataFinalizer dataFinalizer in dataFinalizers) { dataFinalizer.FinalizeData(); } } } public class InstanceGenerator : IInstanceGenerator where T : new() { public string Key { get; } = "InstanceGenerator"; public T CreateInstance() { return new T(); } } public class LazyBuilder : ILazyBuilder { public Dictionary>> configActions = new Dictionary>>(); public List> containerActions = new List>(); private ManualLogSource logger = Logger.CreateLogSource("LazyBuilder"); public void Configure(string pluginId, Action action) { if (configActions.TryGetValue(pluginId, out List> value)) { value.Add(action); return; } configActions.Add(pluginId, new List>(1) { action }); } public void ConfigureLoaders(Action action) { containerActions.Add(action); } public void Build(Container container) { PluginAtlas pluginAtlas = new PluginAtlas(); foreach (string key in configActions.Keys) { try { ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); HashSet hashSet = new HashSet(); Assembly assembly = null; foreach (Action item in configActions[key]) { string directoryName = Path.GetDirectoryName(item.Method.DeclaringType.Assembly.Location); hashSet.Add(directoryName); assembly = item.Method.DeclaringType.Assembly; configurationBuilder.SetBasePath(directoryName); item(configurationBuilder); } PluginDefinition pluginDefinition = new PluginDefinition(configurationBuilder.Build()); pluginDefinition.AssetDirectories.AddRange(hashSet); pluginDefinition.Assembly = assembly; pluginAtlas.PluginDefinitions.Add(key, pluginDefinition); } catch (Exception ex) { logger.LogError((object)"============================================================="); logger.LogError((object)("[CATASTROPHIC] Mod at " + key + " failed to build due to exception.")); logger.LogError((object)"============================================================="); logger.LogError((object)ex.ToString()); } } container.RegisterInstance(pluginAtlas); foreach (Action containerAction in containerActions) { try { containerAction(container); } catch (Exception ex2) { logger.LogError((object)"============================================================"); logger.LogError((object)$"[CATASTROPHIC] Mod at {containerAction.Method.DeclaringType.Assembly.GetName()} failed to load due to exception."); logger.LogError((object)"============================================================"); logger.LogError((object)ex2.ToString()); } } } } public class PluginAtlas { public Dictionary PluginDefinitions { get; set; } = new Dictionary(); } public class PluginDefinition { public IConfiguration Configuration { get; set; } public List AssetDirectories { get; } = new List(); public Assembly? Assembly { get; set; } public PluginDefinition(IConfiguration configuration) { Configuration = configuration; } } } namespace TrainworksReloaded.Core.Extensions { public static class ConfigurationExtensions { public static IConfigurationBuilder AddMergedJsonFile(this IConfigurationBuilder builder, [ParamCollection] List Paths) { return builder.AddMergedJsonFile(delegate(MergedJsonConfigurationSource xs) { xs.FileProvider = null; xs.Paths = Paths; xs.Optional = false; }); } public static IConfigurationBuilder AddMergedJsonFile(this IConfigurationBuilder builder, Action? configureSource) { return builder.Add(configureSource); } } public static class ParseExtensions { public static string? ParseString(this IConfigurationSection section) { return section.Value; } public static int? ParseInt(this IConfigurationSection section) { string value = section.Value; if (value == null) { return null; } if (int.TryParse(value, out var result)) { return result; } return null; } public static float? ParseFloat(this IConfigurationSection section) { string value = section.Value; if (value == null) { return null; } if (float.TryParse(value, out var result)) { return result; } return null; } public static bool? ParseBool(this IConfigurationSection section) { string value = section.Value; if (value == null) { return null; } if (bool.TryParse(value, out var result)) { return result; } return null; } } } namespace TrainworksReloaded.Core.Enum { public enum OverrideMode { New, Replace, Append } public static class OverrideModeExtensions { public static bool IsNewContent(this OverrideMode overrideMode) { return overrideMode == OverrideMode.New; } public static bool IsOverriding(this OverrideMode overrideMode) { if (overrideMode != OverrideMode.Replace) { return overrideMode == OverrideMode.Append; } return true; } } public enum RegisterIdentifierType { ReadableID, GUID } } namespace TrainworksReloaded.Core.Configuration { public class MergedJsonConfigurationSource : IConfigurationSource { public IFileProvider? FileProvider { get; set; } public List Paths { get; set; } public bool Optional { get; set; } public MergedJsonConfigurationSource() { Paths = new List(); Optional = true; FileProvider = null; } public MergedJsonConfigurationSource(IFileProvider fileProvider, bool optional = false, params string[] paths) { FileProvider = fileProvider; Optional = optional; Paths = paths.ToList(); } public IConfigurationProvider Build(IConfigurationBuilder builder) { EnsureDefaults(builder); return new MergedJsonConfigurationProvider(this); } public void EnsureDefaults(IConfigurationBuilder builder) { if (FileProvider == null) { IFileProvider fileProvider = (FileProvider = builder.GetFileProvider()); } } } public class MergedJsonConfigurationProvider : ConfigurationProvider { public MergedJsonConfigurationSource Source { get; } public MergedJsonConfigurationProvider(MergedJsonConfigurationSource source) { Source = source; } public override void Load() { //IL_00a0: Expected O, but got Unknown JObject val = null; foreach (string path in Source.Paths) { try { IFileInfo fileInfo = Source.FileProvider?.GetFileInfo(path) ?? null; if (fileInfo == null || !fileInfo.Exists) { continue; } using Stream stream = fileInfo.CreateReadStream(); using StreamReader streamReader = new StreamReader(stream); JObject val2 = JObject.Parse(streamReader.ReadToEnd()); if (val == null) { val = val2; } else if (val2 != null) { ((JContainer)val).Merge((object)val2); } } catch (JsonReaderException ex) { JsonReaderException innerException = ex; throw new Exception("Failed to parse file " + path + " due to exception", (Exception?)(object)innerException); } } Dictionary data = new Dictionary(StringComparer.OrdinalIgnoreCase); LoadNode((JToken?)(object)val, data, ""); base.Data = data; } private void LoadNode(JToken? node, IDictionary data, string path) { JObject val = (JObject)(object)((node is JObject) ? node : null); if (val != null) { foreach (JProperty item in val.Properties()) { LoadNode(item.Value, data, CombinePath(path, item.Name)); } return; } JArray val2 = (JArray)(object)((node is JArray) ? node : null); if (val2 != null) { for (int i = 0; i < ((JContainer)val2).Count; i++) { LoadNode(val2[i], data, $"{path}:{i}"); } return; } JValue val3 = (JValue)(object)((node is JValue) ? node : null); if (val3 != null) { data[path] = ((object)val3).ToString(); } } private string CombinePath(string path, string key) { if (!string.IsNullOrEmpty(path)) { return path + ":" + key; } return key; } } }