Decompiled source of LC DOOM v1.1.2

LethalCompany.Doom.dll

Decompiled 11 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LethalCompany.Doom.Unity;
using ManagedDoom;
using ManagedDoom.Audio;
using ManagedDoom.UserInput;
using ManagedDoom.Video;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[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("Samuel Steele")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("2023 Samuel Steele")]
[assembly: AssemblyDescription("Play DOOM in the Ship's Terminal")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2+2e3fe63fa663bee40733b6ea1c95f8150d066ecf")]
[assembly: AssemblyProduct("LethalCompany.Doom")]
[assembly: AssemblyTitle("LethalCompany.Doom")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/cryptoc1/lc-doom.git")]
[assembly: AssemblyVersion("1.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace System.Runtime.Versioning
{
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresPreviewFeaturesAttribute : Attribute
	{
		public string? Message { get; }

		public string? Url { get; set; }

		public RequiresPreviewFeaturesAttribute()
		{
		}

		public RequiresPreviewFeaturesAttribute(string? message)
		{
			Message = message;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CallerArgumentExpressionAttribute : Attribute
	{
		public string ParameterName { get; }

		public CallerArgumentExpressionAttribute(string parameterName)
		{
			ParameterName = parameterName;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CollectionBuilderAttribute : Attribute
	{
		public Type BuilderType { get; }

		public string MethodName { get; }

		public CollectionBuilderAttribute(Type builderType, string methodName)
		{
			BuilderType = builderType;
			MethodName = methodName;
		}
	}
	[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CompilerFeatureRequiredAttribute : Attribute
	{
		public const string RefStructs = "RefStructs";

		public const string RequiredMembers = "RequiredMembers";

		public string FeatureName { get; }

		public bool IsOptional { get; set; }

		public CompilerFeatureRequiredAttribute(string featureName)
		{
			FeatureName = featureName;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
	{
		public string[] Arguments { get; }

		public InterpolatedStringHandlerArgumentAttribute(string argument)
		{
			Arguments = new string[1] { argument };
		}

		public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
		{
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerAttribute : Attribute
	{
	}
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal static class IsExternalInit
	{
	}
	[AttributeUsage(AttributeTargets.Method, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ModuleInitializerAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiredMemberAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresLocationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SkipLocalsInitAttribute : Attribute
	{
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ExperimentalAttribute : Attribute
	{
		public string DiagnosticId { get; }

		public string? UrlFormat { get; set; }

		public ExperimentalAttribute(string diagnosticId)
		{
			DiagnosticId = diagnosticId;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullAttribute : Attribute
	{
		public string[] Members { get; }

		public MemberNotNullAttribute(string member)
		{
			Members = new string[1] { member };
		}

		public MemberNotNullAttribute(params string[] members)
		{
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public string[] Members { get; }

		public MemberNotNullWhenAttribute(bool returnValue, string member)
		{
			ReturnValue = returnValue;
			Members = new string[1] { member };
		}

		public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
		{
			ReturnValue = returnValue;
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SetsRequiredMembersAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class StringSyntaxAttribute : Attribute
	{
		public const string CompositeFormat = "CompositeFormat";

		public const string DateOnlyFormat = "DateOnlyFormat";

		public const string DateTimeFormat = "DateTimeFormat";

		public const string EnumFormat = "EnumFormat";

		public const string GuidFormat = "GuidFormat";

		public const string Json = "Json";

		public const string NumericFormat = "NumericFormat";

		public const string Regex = "Regex";

		public const string TimeOnlyFormat = "TimeOnlyFormat";

		public const string TimeSpanFormat = "TimeSpanFormat";

		public const string Uri = "Uri";

		public const string Xml = "Xml";

		public string Syntax { get; }

		public object?[] Arguments { get; }

		public StringSyntaxAttribute(string syntax)
		{
			Syntax = syntax;
			Arguments = new object[0];
		}

		public StringSyntaxAttribute(string syntax, params object?[] arguments)
		{
			Syntax = syntax;
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class UnscopedRefAttribute : Attribute
	{
	}
}
namespace LethalCompany.Doom
{
	[BepInPlugin("cryptoc1.lethalcompany.doom", "LC-DOOM", "1.1.2")]
	public sealed class DoomPlugin : BaseUnityPlugin
	{
		public static DoomPlugin Value { get; private set; }

		public ManualLogSource Logger => ((BaseUnityPlugin)this).Logger;

		public void Awake()
		{
			Value = this;
			Harmony.CreateAndPatchAll(typeof(DoomPlugin).Assembly, "cryptoc1.lethalcompany.doom");
		}
	}
	internal static class GeneratedPluginInfo
	{
		public const string Identifier = "cryptoc1.lethalcompany.doom";

		public const string Name = "LC-DOOM";

		public const string Version = "1.1.2";
	}
}
namespace LethalCompany.Doom.Unity
{
	internal static class DoomAudioSource
	{
		public static AudioSource Create(string name)
		{
			//IL_000b: 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)
			AudioSource obj = new GameObject("LCDoomAudio_" + name)
			{
				hideFlags = (HideFlags)61
			}.AddComponent<AudioSource>();
			obj.playOnAwake = false;
			obj.spatialBlend = 0f;
			return obj;
		}
	}
	internal sealed class UnityDoom : IDisposable
	{
		private readonly DoomConfigBinder binder;

		private readonly Config config;

		private readonly GameContent content;

		private readonly Doom? doom;

		private readonly int fpsScale;

		private int frameCount;

		private UnitySound? sound;

		private UnityUserInput? userInput;

		private UnityVideo? video;

		public Texture2D? Texture => video?.Texture;

		public UnityDoom(DoomConfigBinder binder)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			try
			{
				CommandLineArgs val = new CommandLineArgs(new string[2]
				{
					"iwad",
					binder.Wad.Value
				});
				this.binder = binder;
				config = binder.GetConfig();
				content = new GameContent(val);
				doom = new Doom(val, config, content, (IVideo)(object)(video = new UnityVideo(config, content)), (ISound)(object)(sound = ((!val.nosound.Present && !val.nosfx.Present) ? new UnitySound(config, content) : null)), (IMusic)null, (IUserInput)(object)(userInput = new UnityUserInput(config)));
				frameCount = -1;
				fpsScale = (val.timedemo.Present ? 1 : config.video_fpsscale);
			}
			catch (Exception source)
			{
				Dispose();
				ExceptionDispatchInfo.Throw(source);
			}
		}

		public void Dispose()
		{
			if (sound != null)
			{
				sound.Dispose();
				sound = null;
			}
			if (video != null)
			{
				video.Dispose();
				video = null;
			}
			if (userInput != null)
			{
				userInput.Dispose();
				userInput = null;
			}
			binder.Save(config);
		}

		public unsafe void Input(Mouse mouse, Keyboard keyboard)
		{
			//IL_0011: 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)
			//IL_003f: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			userInput.MoveMouse(Unsafe.Read<Vector2>((void*)((InputControl<Vector2>)(object)((Pointer)mouse).delta).value));
			foreach (var (val, button2) in UnityUserInput.MouseButtons(Mouse.current))
			{
				if (val.wasPressedThisFrame)
				{
					PressButton(button2);
				}
				if (val.wasReleasedThisFrame)
				{
					ReleaseButton(button2);
				}
			}
			foreach (var (val2, key2) in UnityUserInput.Keys)
			{
				if (((ButtonControl)keyboard[val2]).wasPressedThisFrame)
				{
					PressKey(key2);
				}
				if (((ButtonControl)keyboard[val2]).wasReleasedThisFrame)
				{
					ReleaseKey(key2);
				}
			}
			void PressButton(DoomMouseButton button)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Invalid comparison between Unknown and I4
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Expected O, but got Unknown
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				if ((int)button != -1 && !userInput.PressedButtons.Contains(button))
				{
					userInput.PressedButtons.Add(button);
				}
				doom.PostEvent(new DoomEvent((EventType)2, (DoomKey)(-1)));
			}
			void PressKey(DoomKey key)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Invalid comparison between Unknown and I4
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Expected O, but got Unknown
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				if ((int)key != -1 && !userInput.PressedKeys.Contains(key))
				{
					userInput.PressedKeys.Add(key);
				}
				doom.PostEvent(new DoomEvent((EventType)0, key));
			}
			void ReleaseButton(DoomMouseButton button)
			{
				//IL_000b: 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_0024: Expected O, but got Unknown
				userInput.PressedButtons.Remove(button);
				doom.PostEvent(new DoomEvent((EventType)2, (DoomKey)(-1)));
			}
			void ReleaseKey(DoomKey key)
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: 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_0024: Expected O, but got Unknown
				userInput.PressedKeys.Remove(key);
				doom.PostEvent(new DoomEvent((EventType)1, key));
			}
		}

		public bool Render()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0045: 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)
			if (++frameCount % fpsScale == 0)
			{
				if ((int)doom.Update() == 1)
				{
					return false;
				}
				video.Render(doom, Fixed.FromInt(frameCount % fpsScale + 1) / fpsScale);
			}
			return true;
		}
	}
	public sealed class DoomConfigBinder
	{
		[CompilerGenerated]
		private ConfigFile <file>P;

		[CompilerGenerated]
		private (int width, int height) <size>P;

		private static readonly Lazy<string> DefaultWadPath = new Lazy<string>(GetWadPath);

		private static readonly Lazy<string> DefaultSoundFontPath = new Lazy<string>(GetSoundFontPath);

		public ConfigEntry<bool> AlwaysRun { get; }

		public ConfigEntry<string?> Wad { get; }

		public ConfigEntry<string> KeyBackward { get; }

		public ConfigEntry<string> KeyForward { get; }

		public ConfigEntry<string> KeyFire { get; }

		public ConfigEntry<string> KeyRun { get; }

		public ConfigEntry<string> KeyStrafe { get; }

		public ConfigEntry<string> KeyStrafeLeft { get; }

		public ConfigEntry<string> KeyStrafeRight { get; }

		public ConfigEntry<string> KeyTurnLeft { get; }

		public ConfigEntry<string> KeyTurnRight { get; }

		public ConfigEntry<string> KeyUse { get; }

		public ConfigEntry<bool> MouseDisableYAxis { get; }

		public ConfigEntry<int> MouseSensitivity { get; }

		public ConfigEntry<bool> SfxRandomPitch { get; }

		public ConfigEntry<int> SfxVolume { get; }

		public ConfigEntry<bool> VideoDisplayMessage { get; }

		public ConfigEntry<int> VideoFpsScale { get; }

		public ConfigEntry<int> VideoGammaCorrection { get; }

		public ConfigEntry<bool> VideoHighResolution { get; }

		public ConfigEntry<int> VideoScreenSize { get; }

		public DoomConfigBinder(ConfigFile file, (int width, int height) size)
		{
			<file>P = file;
			<size>P = size;
			AlwaysRun = <file>P.Bind<bool>("General", "Always Run", true, (ConfigDescription)null);
			Wad = <file>P.Bind<string>("General", "WAD", DefaultWadPath.Value, "An absolute path to the WAD file to be loaded.");
			KeyBackward = <file>P.Bind<string>("Keybinds", "Backward", "s,down", (ConfigDescription)null);
			KeyForward = <file>P.Bind<string>("Keybinds", "Forward", "w,up", (ConfigDescription)null);
			KeyFire = <file>P.Bind<string>("Keybinds", "Fire", "mouse1,f,lcontrol,rcontrol", (ConfigDescription)null);
			KeyRun = <file>P.Bind<string>("Keybinds", "Run", "lshift,rshift", (ConfigDescription)null);
			KeyStrafe = <file>P.Bind<string>("Keybinds", "Strafe", "lalt,ralt", (ConfigDescription)null);
			KeyStrafeLeft = <file>P.Bind<string>("Keybinds", "Strafe Left", "a", (ConfigDescription)null);
			KeyStrafeRight = <file>P.Bind<string>("Keybinds", "Strafe Right", "d", (ConfigDescription)null);
			KeyTurnLeft = <file>P.Bind<string>("Keybinds", "Turn Left", "left", (ConfigDescription)null);
			KeyTurnRight = <file>P.Bind<string>("Keybinds", "Turn Right", "right", (ConfigDescription)null);
			KeyUse = <file>P.Bind<string>("Keybinds", "Use", "space,mouse2", (ConfigDescription)null);
			MouseDisableYAxis = <file>P.Bind<bool>("Mouse", "Disable Y-Axis", false, (ConfigDescription)null);
			MouseSensitivity = <file>P.Bind<int>("Mouse", "Sensitivity", 8, (ConfigDescription)null);
			SfxRandomPitch = <file>P.Bind<bool>("Sfx", "Random Pitch", true, (ConfigDescription)null);
			SfxVolume = <file>P.Bind<int>("Sfx", "Volume", 8, "The volume of sound effects.");
			VideoDisplayMessage = <file>P.Bind<bool>("Video", "Display Message", true, (ConfigDescription)null);
			VideoFpsScale = <file>P.Bind<int>("Video", "FPS Scale", 2, (ConfigDescription)null);
			VideoGammaCorrection = <file>P.Bind<int>("Video", "Gamma Correction", 2, (ConfigDescription)null);
			VideoHighResolution = <file>P.Bind<bool>("Video", "High Resolution", true, (ConfigDescription)null);
			VideoScreenSize = <file>P.Bind<int>("Video", "Screen Size", 7, (ConfigDescription)null);
			base..ctor();
		}

		public Config GetConfig()
		{
			//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_0016: 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)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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)
			//IL_0090: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			return new Config
			{
				audio_randompitch = SfxRandomPitch.Value,
				audio_soundvolume = SfxVolume.Value,
				game_alwaysrun = AlwaysRun.Value,
				key_backward = KeyBinding.Parse(KeyBackward.Value),
				key_fire = KeyBinding.Parse(KeyFire.Value),
				key_forward = KeyBinding.Parse(KeyForward.Value),
				key_run = KeyBinding.Parse(KeyRun.Value),
				key_strafe = KeyBinding.Parse(KeyStrafe.Value),
				key_strafeleft = KeyBinding.Parse(KeyStrafeLeft.Value),
				key_straferight = KeyBinding.Parse(KeyStrafeRight.Value),
				key_turnleft = KeyBinding.Parse(KeyTurnLeft.Value),
				key_turnright = KeyBinding.Parse(KeyTurnRight.Value),
				key_use = KeyBinding.Parse(KeyUse.Value),
				mouse_disableyaxis = MouseDisableYAxis.Value,
				mouse_sensitivity = MouseSensitivity.Value,
				video_displaymessage = VideoDisplayMessage.Value,
				video_fpsscale = VideoFpsScale.Value,
				video_fullscreen = false,
				video_gamescreensize = VideoScreenSize.Value,
				video_gammacorrection = VideoGammaCorrection.Value,
				video_highresolution = VideoHighResolution.Value,
				video_screenheight = <size>P.height,
				video_screenwidth = <size>P.width
			};
		}

		private static string GetSoundFontPath()
		{
			return Path.Combine(Path.GetDirectoryName(typeof(DoomPlugin).Assembly.Location), "RLNDGM.SF2");
		}

		private static string GetWadPath()
		{
			return Path.Combine(Path.GetDirectoryName(typeof(DoomPlugin).Assembly.Location), "DOOM1.WAD");
		}

		public void Save(Config config)
		{
			AlwaysRun.Value = config.game_alwaysrun;
			KeyBackward.Value = ((object)config.key_backward).ToString();
			KeyForward.Value = ((object)config.key_forward).ToString();
			KeyFire.Value = ((object)config.key_fire).ToString();
			KeyRun.Value = ((object)config.key_run).ToString();
			KeyStrafe.Value = ((object)config.key_strafe).ToString();
			KeyStrafeLeft.Value = ((object)config.key_strafeleft).ToString();
			KeyStrafeRight.Value = ((object)config.key_straferight).ToString();
			KeyTurnLeft.Value = ((object)config.key_turnleft).ToString();
			KeyTurnRight.Value = ((object)config.key_turnright).ToString();
			KeyUse.Value = ((object)config.key_use).ToString();
			MouseDisableYAxis.Value = config.mouse_disableyaxis;
			MouseSensitivity.Value = config.mouse_sensitivity;
			SfxRandomPitch.Value = config.audio_randompitch;
			SfxVolume.Value = config.audio_soundvolume;
			VideoDisplayMessage.Value = config.video_displaymessage;
			VideoFpsScale.Value = config.video_fpsscale;
			VideoGammaCorrection.Value = config.video_gammacorrection;
			VideoHighResolution.Value = config.video_highresolution;
			VideoScreenSize.Value = config.video_gamescreensize;
			<file>P.Save();
		}
	}
	internal sealed class UnitySound : ISound, IDisposable
	{
		private sealed class ChannelInfo
		{
			public Sfx Reserved;

			public Sfx Playing;

			public float Priority;

			public Mobj? Source;

			public SfxType Type;

			public int Volume;

			public Fixed LastX;

			public Fixed LastY;

			public void Clear()
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: 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_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				Reserved = (Sfx)0;
				Playing = (Sfx)0;
				Priority = 0f;
				Source = null;
				Type = (SfxType)0;
				Volume = 0;
				LastX = Fixed.Zero;
				LastY = Fixed.Zero;
			}
		}

		private static readonly int ChannelCount = 8;

		private static readonly float FastDecay = (float)Math.Pow(0.5, 1.0 / 7.0);

		private static readonly float SlowDecay = (float)Math.Pow(0.5, 1.0 / 35.0);

		private static readonly float ClipDist = 1200f;

		private static readonly float CloseDist = 160f;

		private static readonly float Attenuator = ClipDist - CloseDist;

		private readonly float[] amplitudes;

		private readonly Config config;

		private readonly ChannelInfo[] infos;

		private readonly DoomRandom? random;

		private AudioClip[] buffers;

		private AudioSource[] channels;

		private AudioSource uiChannel;

		private Sfx uiReserved;

		private DateTime lastUpdate;

		private Mobj listener;

		private float masterVolumeDecay;

		public int MaxVolume => 15;

		public int Volume
		{
			get
			{
				return config.audio_soundvolume;
			}
			set
			{
				config.audio_soundvolume = value;
				masterVolumeDecay = (float)config.audio_soundvolume / (float)MaxVolume;
			}
		}

		public UnitySound(Config config, GameContent content)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				this.config = config;
				config.audio_soundvolume = Math.Clamp(config.audio_soundvolume, 0, MaxVolume);
				buffers = (AudioClip[])(object)new AudioClip[DoomInfo.SfxNames.Length];
				amplitudes = new float[DoomInfo.SfxNames.Length];
				if (config.audio_randompitch)
				{
					random = new DoomRandom();
				}
				for (int i = 0; i < DoomInfo.SfxNames.Length; i++)
				{
					string text = $"DS{DoomInfo.SfxNames[i]}".ToUpperInvariant();
					if (content.Wad.GetLumpNumber(text) != -1)
					{
						int sampleRate;
						int sampleCount;
						float[] samples = GetSamples(content.Wad, text, out sampleRate, out sampleCount);
						if ((samples?.Length).HasValue)
						{
							AudioClip val = AudioClip.Create(text, samples.Length, 1, sampleRate, false);
							val.SetData(samples, 0);
							buffers[i] = val;
							amplitudes[i] = GetAmplitude(samples, sampleRate, samples.Length);
						}
					}
				}
				channels = CreateChannels(ChannelCount);
				infos = new ChannelInfo[ChannelCount];
				for (int j = 0; j < ChannelCount; j++)
				{
					infos[j] = new ChannelInfo();
				}
				lastUpdate = DateTime.MinValue;
				masterVolumeDecay = (float)config.audio_soundvolume / (float)MaxVolume;
				uiChannel = DoomAudioSource.Create("UI");
				uiReserved = (Sfx)0;
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		private AudioSource[] CreateChannels(int channelCount)
		{
			AudioSource[] array = (AudioSource[])(object)new AudioSource[channelCount];
			for (int i = 0; i < channelCount; i++)
			{
				AudioSource val = DoomAudioSource.Create($"SFX_{i}");
				val.spatialBlend = 0f;
				val.playOnAwake = false;
				array[i] = val;
			}
			return array;
		}

		public void Dispose()
		{
			if (channels != null)
			{
				for (int i = 0; i < channels.Length; i++)
				{
					if (channels[i] != null)
					{
						channels[i].Stop();
						Object.DestroyImmediate((Object)(object)((Component)channels[i]).gameObject, true);
						channels[i] = null;
					}
				}
				channels = null;
			}
			if (buffers != null)
			{
				for (int j = 0; j < buffers.Length; j++)
				{
					if (buffers[j] != null)
					{
						Object.DestroyImmediate((Object)(object)buffers[j], true);
						buffers[j] = null;
					}
				}
				buffers = null;
			}
			if (uiChannel != null)
			{
				Object.DestroyImmediate((Object)(object)((Component)uiChannel).gameObject, true);
				uiChannel = null;
			}
		}

		private static float[]? GetSamples(Wad wad, string name, out int sampleRate, out int sampleCount)
		{
			byte[] array = wad.ReadLump(name);
			if (array.Length < 8)
			{
				sampleRate = -1;
				sampleCount = -1;
				return null;
			}
			sampleRate = BitConverter.ToUInt16(array, 2);
			sampleCount = BitConverter.ToInt32(array, 4);
			int num = 8;
			if (ContainsDmxPadding(array))
			{
				num += 16;
				sampleCount -= 32;
			}
			if (sampleCount > 0)
			{
				float[] array2 = new float[sampleCount];
				for (int i = num; i < sampleCount; i++)
				{
					array2[i] = (float)(int)array[i] / 127f - 1f;
				}
				return array2;
			}
			return Array.Empty<float>();
		}

		private static bool ContainsDmxPadding(byte[] data)
		{
			int num = BitConverter.ToInt32(data, 4);
			if (num < 32)
			{
				return false;
			}
			byte b = data[8];
			for (int i = 1; i < 16; i++)
			{
				if (data[8 + i] != b)
				{
					return false;
				}
			}
			byte b2 = data[8 + num - 1];
			for (int j = 1; j < 16; j++)
			{
				if (data[8 + num - j - 1] != b2)
				{
					return false;
				}
			}
			return true;
		}

		private static float GetAmplitude(float[] samples, int sampleRate, int sampleCount)
		{
			float num = 0f;
			if (sampleCount > 0)
			{
				int num2 = Math.Min(sampleRate / 5, sampleCount);
				for (int i = 0; i < num2; i++)
				{
					float num3 = samples[i] - 0.5f;
					if (num3 < 0f)
					{
						num3 = 0f - num3;
					}
					if (num3 > num)
					{
						num = num3;
					}
				}
			}
			return num;
		}

		public void SetListener(Mobj listener)
		{
			this.listener = listener;
		}

		public void Update()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			DateTime now = DateTime.Now;
			if ((now - lastUpdate).TotalSeconds < 0.01)
			{
				return;
			}
			for (int i = 0; i < infos.Length; i++)
			{
				ChannelInfo channelInfo = infos[i];
				AudioSource val = channels[i];
				if ((int)channelInfo.Playing != 0)
				{
					channelInfo.Priority *= (((int)channelInfo.Type == 0) ? SlowDecay : FastDecay);
					UpdateAudioSource(val, channelInfo);
				}
				if ((int)channelInfo.Reserved != 0)
				{
					if ((int)channelInfo.Playing != 0)
					{
						val.Stop();
					}
					val.clip = buffers[channelInfo.Reserved];
					UpdateAudioSource(val, channelInfo);
					val.pitch = GetPitch(channelInfo.Type, channelInfo.Reserved);
					val.PlayOneShot(val.clip);
					channelInfo.Playing = channelInfo.Reserved;
					channelInfo.Reserved = (Sfx)0;
				}
			}
			if ((int)uiReserved != 0)
			{
				if (uiChannel.isPlaying)
				{
					uiChannel.Stop();
				}
				uiChannel.volume = masterVolumeDecay;
				uiChannel.clip = buffers[uiReserved];
				uiChannel.Play();
				uiReserved = (Sfx)0;
			}
			lastUpdate = now;
		}

		public void StartSound(Sfx sfx)
		{
			//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_000d: Unknown result type (might be due to invalid IL or missing references)
			if (buffers[sfx] != null)
			{
				uiReserved = sfx;
			}
		}

		public void StartSound(Mobj mobj, Sfx sfx, SfxType type)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			StartSound(mobj, sfx, type, 100);
		}

		public void StartSound(Mobj mobj, Sfx sfx, SfxType type, int volume)
		{
			//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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00e7: 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_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			if (buffers[sfx] == null)
			{
				return;
			}
			Fixed val = mobj.X - listener.X;
			float num = ((Fixed)(ref val)).ToFloat();
			val = mobj.Y - listener.Y;
			float num2 = ((Fixed)(ref val)).ToFloat();
			float dist = MathF.Sqrt(num * num + num2 * num2);
			float num3 = (((int)type == 0) ? ((float)volume) : (amplitudes[sfx] * GetDistanceDecay(dist) * (float)volume));
			for (int i = 0; i < infos.Length; i++)
			{
				ChannelInfo channelInfo = infos[i];
				if (channelInfo.Source == mobj && channelInfo.Type == type)
				{
					channelInfo.Reserved = sfx;
					channelInfo.Priority = num3;
					channelInfo.Volume = volume;
					return;
				}
			}
			for (int j = 0; j < infos.Length; j++)
			{
				ChannelInfo channelInfo2 = infos[j];
				if ((int)channelInfo2.Reserved == 0 && (int)channelInfo2.Playing == 0)
				{
					channelInfo2.Reserved = sfx;
					channelInfo2.Priority = num3;
					channelInfo2.Source = mobj;
					channelInfo2.Type = type;
					channelInfo2.Volume = volume;
					return;
				}
			}
			float num4 = float.MaxValue;
			int num5 = -1;
			for (int k = 0; k < infos.Length; k++)
			{
				ChannelInfo channelInfo3 = infos[k];
				if (channelInfo3.Priority < num4)
				{
					num4 = channelInfo3.Priority;
					num5 = k;
				}
			}
			if (num3 >= num4)
			{
				ChannelInfo obj = infos[num5];
				obj.Reserved = sfx;
				obj.Priority = num3;
				obj.Source = mobj;
				obj.Type = type;
				obj.Volume = volume;
			}
		}

		public void StopSound(Mobj mobj)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			for (int i = 0; i < infos.Length; i++)
			{
				ChannelInfo channelInfo = infos[i];
				if (channelInfo.Source == mobj)
				{
					channelInfo.LastX = channelInfo.Source.X;
					channelInfo.LastY = channelInfo.Source.Y;
					channelInfo.Source = null;
					channelInfo.Volume /= 5;
				}
			}
		}

		public void Reset()
		{
			DoomRandom? obj = random;
			if (obj != null)
			{
				obj.Clear();
			}
			for (int i = 0; i < infos.Length; i++)
			{
				channels[i].Stop();
				infos[i].Clear();
			}
			listener = null;
		}

		public void Pause()
		{
		}

		public void Resume()
		{
		}

		private void UpdateAudioSource(AudioSource sound, ChannelInfo info)
		{
			//IL_0001: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if ((int)info.Type == 0)
			{
				sound.panStereo = 0f;
				sound.volume = 0.01f * masterVolumeDecay * (float)info.Volume;
				return;
			}
			Fixed val;
			Fixed val2;
			if (info.Source == null)
			{
				val = info.LastX;
				val2 = info.LastY;
			}
			else
			{
				val = info.Source.X;
				val2 = info.Source.Y;
			}
			Fixed val3 = val - listener.X;
			float num = ((Fixed)(ref val3)).ToFloat();
			val3 = val2 - listener.Y;
			float num2 = ((Fixed)(ref val3)).ToFloat();
			if (Math.Abs(num) < 16f && Math.Abs(num2) < 16f)
			{
				sound.panStereo = 0f;
				sound.volume = 0.01f * masterVolumeDecay * (float)info.Volume;
				return;
			}
			float dist = MathF.Sqrt(num * num + num2 * num2);
			float num3 = MathF.Atan2(num2, num);
			Angle angle = listener.Angle;
			float num4 = num3 - (float)((Angle)(ref angle)).ToRadian();
			num4 = num4 * 57.29578f / 180f;
			num4 = Mathf.Clamp(num4, -0.2f, 0.2f);
			sound.panStereo = num4;
			sound.volume = 0.01f * masterVolumeDecay * GetDistanceDecay(dist) * (float)info.Volume;
		}

		private float GetDistanceDecay(float dist)
		{
			if (dist < CloseDist)
			{
				return 1f;
			}
			return Math.Max((ClipDist - dist) / Attenuator, 0f);
		}

		private float GetPitch(SfxType type, Sfx sfx)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			if (random != null)
			{
				if ((int)type == 2)
				{
					return 1f + 0.075f * (float)(random.Next() - 128) / 128f;
				}
				if ((int)sfx == 32 || (int)sfx == 87 || (int)sfx == 108)
				{
					return 1f;
				}
				return 1f + 0.025f * (float)(random.Next() - 128) / 128f;
			}
			return 1f;
		}
	}
	internal sealed class UnityUserInput : IUserInput, IDisposable
	{
		private readonly Config config;

		private readonly bool[] weaponKeys;

		private Vector2 mouseDelta = new Vector2(0f, 0f);

		private Vector2 mousePosition = new Vector2(0f, 0f);

		private Vector2 mousePreviousPosition = new Vector2(0f, 0f);

		private int turnHeld;

		public static readonly IEnumerable<(Key InputKey, DoomKey Key)> Keys = new <>z__ReadOnlyArray<(Key, DoomKey)>(new(Key, DoomKey)[95]
		{
			((Key)1, (DoomKey)57),
			((Key)7, (DoomKey)49),
			((Key)13, (DoomKey)68),
			((Key)8, (DoomKey)50),
			((Key)9, (DoomKey)52),
			((Key)50, (DoomKey)26),
			((Key)41, (DoomKey)27),
			((Key)42, (DoomKey)28),
			((Key)43, (DoomKey)29),
			((Key)44, (DoomKey)30),
			((Key)45, (DoomKey)31),
			((Key)46, (DoomKey)32),
			((Key)47, (DoomKey)33),
			((Key)48, (DoomKey)34),
			((Key)49, (DoomKey)35),
			((Key)6, (DoomKey)48),
			((Key)83, (DoomKey)55),
			((Key)15, (DoomKey)0),
			((Key)16, (DoomKey)1),
			((Key)17, (DoomKey)2),
			((Key)18, (DoomKey)3),
			((Key)19, (DoomKey)4),
			((Key)20, (DoomKey)5),
			((Key)21, (DoomKey)6),
			((Key)22, (DoomKey)7),
			((Key)23, (DoomKey)8),
			((Key)24, (DoomKey)9),
			((Key)25, (DoomKey)10),
			((Key)26, (DoomKey)11),
			((Key)27, (DoomKey)12),
			((Key)28, (DoomKey)13),
			((Key)29, (DoomKey)14),
			((Key)30, (DoomKey)15),
			((Key)31, (DoomKey)16),
			((Key)32, (DoomKey)17),
			((Key)33, (DoomKey)18),
			((Key)34, (DoomKey)19),
			((Key)35, (DoomKey)20),
			((Key)36, (DoomKey)21),
			((Key)37, (DoomKey)22),
			((Key)38, (DoomKey)23),
			((Key)39, (DoomKey)24),
			((Key)40, (DoomKey)25),
			((Key)11, (DoomKey)46),
			((Key)10, (DoomKey)53),
			((Key)12, (DoomKey)47),
			((Key)60, (DoomKey)36),
			((Key)2, (DoomKey)58),
			((Key)3, (DoomKey)60),
			((Key)65, (DoomKey)59),
			((Key)70, (DoomKey)65),
			((Key)71, (DoomKey)66),
			((Key)62, (DoomKey)72),
			((Key)61, (DoomKey)71),
			((Key)64, (DoomKey)74),
			((Key)63, (DoomKey)73),
			((Key)67, (DoomKey)61),
			((Key)66, (DoomKey)62),
			((Key)68, (DoomKey)64),
			((Key)69, (DoomKey)63),
			((Key)76, (DoomKey)100),
			((Key)94, (DoomKey)85),
			((Key)95, (DoomKey)86),
			((Key)96, (DoomKey)87),
			((Key)97, (DoomKey)88),
			((Key)98, (DoomKey)89),
			((Key)99, (DoomKey)90),
			((Key)100, (DoomKey)91),
			((Key)101, (DoomKey)92),
			((Key)102, (DoomKey)93),
			((Key)103, (DoomKey)94),
			((Key)104, (DoomKey)95),
			((Key)105, (DoomKey)96),
			((Key)84, (DoomKey)75),
			((Key)85, (DoomKey)76),
			((Key)86, (DoomKey)77),
			((Key)87, (DoomKey)78),
			((Key)88, (DoomKey)79),
			((Key)89, (DoomKey)80),
			((Key)90, (DoomKey)81),
			((Key)91, (DoomKey)82),
			((Key)92, (DoomKey)83),
			((Key)93, (DoomKey)84),
			((Key)78, (DoomKey)70),
			((Key)79, (DoomKey)69),
			((Key)81, (DoomKey)68),
			((Key)80, (DoomKey)67),
			((Key)77, (DoomKey)58),
			((Key)51, (DoomKey)38),
			((Key)55, (DoomKey)37),
			((Key)53, (DoomKey)39),
			((Key)52, (DoomKey)42),
			((Key)56, (DoomKey)41),
			((Key)54, (DoomKey)43),
			((Key)59, (DoomKey)45)
		});

		public int MaxMouseSensitivity => 15;

		public int MouseSensitivity
		{
			get
			{
				return config.mouse_sensitivity;
			}
			set
			{
				config.mouse_sensitivity = value;
			}
		}

		public List<DoomMouseButton> PressedButtons { get; } = new List<DoomMouseButton>();


		public List<DoomKey> PressedKeys { get; } = new List<DoomKey>();


		public UnityUserInput(Config config)
		{
			//IL_000b: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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)
			try
			{
				this.config = config;
				weaponKeys = new bool[7];
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		public void BuildTicCmd(TicCmd cmd)
		{
			bool num = IsPressed(config.key_forward);
			bool flag = IsPressed(config.key_backward);
			bool flag2 = IsPressed(config.key_strafeleft);
			bool flag3 = IsPressed(config.key_straferight);
			bool flag4 = IsPressed(config.key_turnleft);
			bool flag5 = IsPressed(config.key_turnright);
			bool flag6 = IsPressed(config.key_fire);
			bool flag7 = IsPressed(config.key_use);
			bool flag8 = IsPressed(config.key_run);
			bool num2 = IsPressed(config.key_strafe);
			weaponKeys[0] = PressedKeys.Contains((DoomKey)27);
			weaponKeys[1] = PressedKeys.Contains((DoomKey)28);
			weaponKeys[2] = PressedKeys.Contains((DoomKey)29);
			weaponKeys[3] = PressedKeys.Contains((DoomKey)30);
			weaponKeys[4] = PressedKeys.Contains((DoomKey)31);
			weaponKeys[5] = PressedKeys.Contains((DoomKey)32);
			weaponKeys[6] = PressedKeys.Contains((DoomKey)33);
			cmd.Clear();
			bool flag9 = num2;
			int num3 = (flag8 ? 1 : 0);
			int num4 = 0;
			int num5 = 0;
			if (config.game_alwaysrun)
			{
				num3 = 1 - num3;
			}
			turnHeld = ((flag4 || flag5) ? (turnHeld + 1) : (turnHeld = 0));
			int num6 = ((turnHeld < PlayerBehavior.SlowTurnTics) ? 2 : num3);
			if (flag9)
			{
				if (flag5)
				{
					num5 += PlayerBehavior.SideMove[num3];
				}
				if (flag4)
				{
					num5 -= PlayerBehavior.SideMove[num3];
				}
			}
			else
			{
				if (flag5)
				{
					cmd.AngleTurn -= (short)PlayerBehavior.AngleTurn[num6];
				}
				if (flag4)
				{
					cmd.AngleTurn += (short)PlayerBehavior.AngleTurn[num6];
				}
			}
			if (num)
			{
				num4 += PlayerBehavior.ForwardMove[num3];
			}
			if (flag)
			{
				num4 -= PlayerBehavior.ForwardMove[num3];
			}
			if (flag2)
			{
				num5 -= PlayerBehavior.SideMove[num3];
			}
			if (flag3)
			{
				num5 += PlayerBehavior.SideMove[num3];
			}
			if (flag6)
			{
				cmd.Buttons |= TicCmdButtons.Attack;
			}
			if (flag7)
			{
				cmd.Buttons |= TicCmdButtons.Use;
			}
			for (int i = 0; i < weaponKeys.Length; i++)
			{
				if (weaponKeys[i])
				{
					cmd.Buttons |= TicCmdButtons.Change;
					cmd.Buttons |= (byte)(i << (int)TicCmdButtons.WeaponShift);
					break;
				}
			}
			float num7 = 0.5f * (float)config.mouse_sensitivity;
			int num8 = (int)MathF.Round(num7 * mouseDelta.x);
			int num9 = (int)MathF.Round(num7 * (0f - mouseDelta.y));
			num4 += num9;
			if (flag9)
			{
				num5 += num8 * 2;
			}
			else
			{
				cmd.AngleTurn -= (short)(num8 * 22);
			}
			if (num4 > PlayerBehavior.MaxMove)
			{
				num4 = PlayerBehavior.MaxMove;
			}
			else if (num4 < -PlayerBehavior.MaxMove)
			{
				num4 = -PlayerBehavior.MaxMove;
			}
			if (num5 > PlayerBehavior.MaxMove)
			{
				num5 = PlayerBehavior.MaxMove;
			}
			else if (num5 < -PlayerBehavior.MaxMove)
			{
				num5 = -PlayerBehavior.MaxMove;
			}
			cmd.ForwardMove += (sbyte)num4;
			cmd.SideMove += (sbyte)num5;
		}

		public void Dispose()
		{
			PressedButtons.Clear();
			PressedKeys.Clear();
		}

		public void GrabMouse()
		{
		}

		private bool IsPressed(KeyBinding binding)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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)
			foreach (DoomKey key in binding.Keys)
			{
				if (PressedKeys.Contains(key))
				{
					return true;
				}
			}
			foreach (DoomMouseButton mouseButton in binding.MouseButtons)
			{
				if (PressedButtons.Contains(mouseButton))
				{
					return true;
				}
			}
			return false;
		}

		public void MoveMouse(Vector2 delta)
		{
			//IL_002e: 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_0034: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			mousePreviousPosition.x = mousePosition.x;
			mousePreviousPosition.y = mousePosition.y;
			mousePosition += delta;
			mouseDelta = mousePosition - mousePreviousPosition;
			if (config.mouse_disableyaxis)
			{
				mouseDelta.y = 0f;
			}
		}

		public void ReleaseMouse()
		{
		}

		public void Reset()
		{
		}

		public static IEnumerable<(ButtonControl Control, DoomMouseButton Button)> MouseButtons(Mouse mouse)
		{
			return new <>z__ReadOnlyArray<(ButtonControl, DoomMouseButton)>(new(ButtonControl, DoomMouseButton)[5]
			{
				(mouse.leftButton, (DoomMouseButton)0),
				(mouse.rightButton, (DoomMouseButton)1),
				(mouse.middleButton, (DoomMouseButton)2),
				(mouse.backButton, (DoomMouseButton)3),
				(mouse.forwardButton, (DoomMouseButton)4)
			});
		}
	}
	internal sealed class UnityVideo : IVideo, IDisposable
	{
		private byte[] buffer;

		private readonly Renderer renderer;

		public Texture2D Texture { get; private set; }

		public bool DisplayMessage
		{
			get
			{
				return renderer.DisplayMessage;
			}
			set
			{
				renderer.DisplayMessage = value;
			}
		}

		public int GammaCorrectionLevel
		{
			get
			{
				return renderer.GammaCorrectionLevel;
			}
			set
			{
				renderer.GammaCorrectionLevel = value;
			}
		}

		public int MaxGammaCorrectionLevel => renderer.MaxGammaCorrectionLevel;

		public int MaxWindowSize => renderer.MaxWindowSize;

		public int WindowSize
		{
			get
			{
				return renderer.WindowSize;
			}
			set
			{
				renderer.WindowSize = value;
			}
		}

		public int WipeBandCount => renderer.WipeBandCount;

		public int WipeHeight => renderer.WipeHeight;

		public UnityVideo(Config config, GameContent content)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//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)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			try
			{
				int num;
				int num2;
				if (!config.video_highresolution)
				{
					num = 200;
					num2 = 320;
				}
				else
				{
					num = 400;
					num2 = 640;
				}
				renderer = new Renderer(config, content);
				buffer = new byte[4 * num * num2];
				Texture = new Texture2D(num, num2, (TextureFormat)4, false, false)
				{
					filterMode = (FilterMode)0,
					name = "LCDoomScreen"
				};
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		public void Dispose()
		{
			buffer = null;
			if (Texture != null)
			{
				Object.DestroyImmediate((Object)(object)Texture, true);
				Texture = null;
			}
		}

		public bool HasFocus()
		{
			return true;
		}

		public void InitializeWipe()
		{
			renderer.InitializeWipe();
		}

		public void Render(Doom doom, Fixed frame)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			renderer.Render(doom, buffer, frame);
			Texture.SetPixelData<byte>(buffer, 0, 0);
			Texture.Apply(false, false);
		}
	}
}
namespace LethalCompany.Doom.Patches
{
	[HarmonyPatch(typeof(Terminal))]
	internal static class TerminalPatches
	{
		private static readonly Vector2 TerminalImageSize = new Vector2(428f, 500f);

		private static readonly Vector3 TerminalImageRotation = new Vector3(0f, 0f, 90f);

		private static readonly Vector2 TerminalImageTranslation = new Vector2(11.65f, -48f);

		private static Coroutine? cpu;

		private static UnityDoom? doom;

		private static Vector2? terminalImageSize;

		[MemberNotNullWhen(true, new string[] { "cpu", "doom", "terminalImageSize" })]
		private static bool isRunning
		{
			[MemberNotNullWhen(true, new string[] { "cpu", "doom", "terminalImageSize" })]
			get
			{
				if (cpu != null && doom != null)
				{
					return terminalImageSize.HasValue;
				}
				return false;
			}
		}

		private static void DestroyDoom(Terminal terminal)
		{
			//IL_0035: 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)
			//IL_003f: 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)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if (isRunning)
			{
				((MonoBehaviour)terminal).StopCoroutine(cpu);
				cpu = null;
				doom.Dispose();
				doom = null;
				RectTransform rectTransform = ((Graphic)terminal.terminalImage).rectTransform;
				rectTransform.anchoredPosition -= TerminalImageTranslation;
				((Transform)((Graphic)terminal.terminalImage).rectTransform).Rotate(TerminalImageRotation);
				((Graphic)terminal.terminalImage).rectTransform.sizeDelta = terminalImageSize.Value;
				terminalImageSize = null;
				((Behaviour)terminal.screenText).enabled = true;
				TMP_InputField screenText = terminal.screenText;
				screenText.text += "EXIT (0)\n\n";
				((Behaviour)terminal.terminalImageMask).enabled = true;
				((Behaviour)terminal.topRightText).enabled = true;
				GC.Collect();
				DoomPlugin.Value.Logger.LogInfo((object)"TerminalPatches: destroyed doom instance");
			}
		}

		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		public static void OnPostAwake(Terminal __instance)
		{
			TerminalNodesList terminalNodes = __instance.terminalNodes;
			TerminalKeyword[] allKeywords = __instance.terminalNodes.allKeywords;
			int num = 0;
			TerminalKeyword[] array = (TerminalKeyword[])(object)new TerminalKeyword[1 + allKeywords.Length];
			TerminalKeyword[] array2 = allKeywords;
			foreach (TerminalKeyword val in array2)
			{
				array[num] = val;
				num++;
			}
			array[num] = DoomKeyword();
			num++;
			terminalNodes.allKeywords = array;
			TerminalKeyword val2 = __instance.terminalNodes.allKeywords.First((TerminalKeyword keyword) => keyword.word == "other");
			val2.specialKeywordResult.displayText = val2.specialKeywordResult.displayText.TrimEnd() + "\n\n>DOOM\nBoss makes a dollar, I make a dime. That's why I play DOOM on company time.\n\n";
			static TerminalKeyword DoomKeyword()
			{
				TerminalNode val3 = ScriptableObject.CreateInstance<TerminalNode>();
				val3.clearPreviousText = true;
				val3.displayText = "LC-DOOM v1.1.2\nhttps://github.com/cryptoc1/lc-doom\n \nCREDITS:\n• id Software\n  https://www.idsoftware.com\n\n• " + ApplicationInfo.Title + "\n  https://github.com/sinshu/managed-doom\n\n• DoomInUnityInspector\n  https://github.com/xabblll/DoomInUnityInspector\n\nLOADING .... ";
				val3.persistentImage = true;
				val3.terminalEvent = "doom";
				TerminalKeyword obj = ScriptableObject.CreateInstance<TerminalKeyword>();
				obj.isVerb = false;
				obj.specialKeywordResult = val3;
				obj.word = "doom";
				return obj;
			}
		}

		[HarmonyPatch("QuitTerminal")]
		[HarmonyPostfix]
		public static void OnPostQuitTerminal(Terminal __instance)
		{
			DestroyDoom(__instance);
		}

		[HarmonyPatch("LoadTerminalImage")]
		[HarmonyPostfix]
		public static void OnPostLoadTerminalImage(Terminal __instance, TerminalNode node)
		{
			if (node.terminalEvent == "doom" && doom != null)
			{
				TMP_InputField screenText = __instance.screenText;
				screenText.text += "LOADED!\n\n";
				__instance.terminalImage.texture = (Texture)(object)doom.Texture;
				if (StartOfRound.Instance.inShipPhase)
				{
					__instance.displayingPersistentImage = (Texture)(object)doom.Texture;
				}
				cpu = ((MonoBehaviour)__instance).StartCoroutine(RenderDoom(__instance));
				((Behaviour)__instance.terminalImage).enabled = true;
				((Behaviour)__instance.topRightText).enabled = false;
				((Behaviour)__instance.screenText).enabled = false;
			}
		}

		[HarmonyPatch("LoadNewNode")]
		[HarmonyPrefix]
		public static void OnPreLoadNewNode(Terminal __instance, TerminalNode node)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (node.terminalEvent == "doom" && doom == null)
			{
				terminalImageSize = ((Graphic)__instance.terminalImage).rectTransform.sizeDelta;
				((Graphic)__instance.terminalImage).rectTransform.sizeDelta = TerminalImageSize;
				((Transform)((Graphic)__instance.terminalImage).rectTransform).Rotate(-TerminalImageRotation);
				RectTransform rectTransform = ((Graphic)__instance.terminalImage).rectTransform;
				rectTransform.anchoredPosition += TerminalImageTranslation;
				((Behaviour)__instance.terminalImageMask).enabled = false;
				doom = new UnityDoom(new DoomConfigBinder(((BaseUnityPlugin)DoomPlugin.Value).Config, (Mathf.RoundToInt(TerminalImageSize.x), Mathf.RoundToInt(TerminalImageSize.y))));
			}
		}

		[HarmonyPatch("PressESC")]
		[HarmonyPrefix]
		public static bool OnPrePressESC()
		{
			return !isRunning;
		}

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		public static void OnPreUpdate()
		{
			if (isRunning)
			{
				doom.Input(Mouse.current, Keyboard.current);
			}
		}

		private static IEnumerator RenderDoom(Terminal terminal)
		{
			while (doom != null)
			{
				if (!doom.Render())
				{
					DestroyDoom(terminal);
					break;
				}
				yield return (object)new WaitForEndOfFrame();
			}
		}
	}
}
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int IReadOnlyCollection<T>.Count => _items.Length;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Length;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyArray(T[] items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return ((ICollection<T>)_items).Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		((ICollection<T>)_items).CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return ((IList<T>)_items).IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}

LethalCompany.Doom.ManagedDoom.dll

Decompiled 11 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using ManagedDoom.Audio;
using ManagedDoom.UserInput;
using ManagedDoom.Video;

[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("Samuel Steele")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("2023 Samuel Steele")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2e3fe63fa663bee40733b6ea1c95f8150d066ecf")]
[assembly: AssemblyProduct("LethalCompany.Doom.ManagedDoom")]
[assembly: AssemblyTitle("LethalCompany.Doom.ManagedDoom")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/cryptoc1/lc-doom.git")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ManagedDoom
{
	public static class ApplicationInfo
	{
		public static readonly string Title = "Managed Doom v2.1a";
	}
	public enum Bgm
	{
		NONE,
		E1M1,
		E1M2,
		E1M3,
		E1M4,
		E1M5,
		E1M6,
		E1M7,
		E1M8,
		E1M9,
		E2M1,
		E2M2,
		E2M3,
		E2M4,
		E2M5,
		E2M6,
		E2M7,
		E2M8,
		E2M9,
		E3M1,
		E3M2,
		E3M3,
		E3M4,
		E3M5,
		E3M6,
		E3M7,
		E3M8,
		E3M9,
		INTER,
		INTRO,
		BUNNY,
		VICTOR,
		INTROA,
		RUNNIN,
		STALKS,
		COUNTD,
		BETWEE,
		DOOM,
		THE_DA,
		SHAWN,
		DDTBLU,
		IN_CIT,
		DEAD,
		STLKS2,
		THEDA2,
		DOOM2,
		DDTBL2,
		RUNNI2,
		DEAD2,
		STLKS3,
		ROMERO,
		SHAWN2,
		MESSAG,
		COUNT2,
		DDTBL3,
		AMPIE,
		THEDA3,
		ADRIAN,
		MESSG2,
		ROMER2,
		TENSE,
		SHAWN3,
		OPENIN,
		EVIL,
		ULTIMA,
		READ_M,
		DM2TTL,
		DM2INT
	}
	public enum Sfx
	{
		NONE,
		PISTOL,
		SHOTGN,
		SGCOCK,
		DSHTGN,
		DBOPN,
		DBCLS,
		DBLOAD,
		PLASMA,
		BFG,
		SAWUP,
		SAWIDL,
		SAWFUL,
		SAWHIT,
		RLAUNC,
		RXPLOD,
		FIRSHT,
		FIRXPL,
		PSTART,
		PSTOP,
		DOROPN,
		DORCLS,
		STNMOV,
		SWTCHN,
		SWTCHX,
		PLPAIN,
		DMPAIN,
		POPAIN,
		VIPAIN,
		MNPAIN,
		PEPAIN,
		SLOP,
		ITEMUP,
		WPNUP,
		OOF,
		TELEPT,
		POSIT1,
		POSIT2,
		POSIT3,
		BGSIT1,
		BGSIT2,
		SGTSIT,
		CACSIT,
		BRSSIT,
		CYBSIT,
		SPISIT,
		BSPSIT,
		KNTSIT,
		VILSIT,
		MANSIT,
		PESIT,
		SKLATK,
		SGTATK,
		SKEPCH,
		VILATK,
		CLAW,
		SKESWG,
		PLDETH,
		PDIEHI,
		PODTH1,
		PODTH2,
		PODTH3,
		BGDTH1,
		BGDTH2,
		SGTDTH,
		CACDTH,
		SKLDTH,
		BRSDTH,
		CYBDTH,
		SPIDTH,
		BSPDTH,
		VILDTH,
		KNTDTH,
		PEDTH,
		SKEDTH,
		POSACT,
		BGACT,
		DMACT,
		BSPACT,
		BSPWLK,
		VILACT,
		NOWAY,
		BAREXP,
		PUNCH,
		HOOF,
		METAL,
		CHGUN,
		TINK,
		BDOPN,
		BDCLS,
		ITMBK,
		FLAME,
		FLAMST,
		GETPOW,
		BOSPIT,
		BOSCUB,
		BOSSIT,
		BOSPN,
		BOSDTH,
		MANATK,
		MANDTH,
		SSSIT,
		SSDTH,
		KEENPN,
		KEENDT,
		SKEACT,
		SKESIT,
		SKEATK,
		RADIO
	}
	public enum SfxType
	{
		Diffuse,
		Weapon,
		Voice,
		Footstep,
		Misc
	}
	public sealed class CommandLineArgs
	{
		public class Arg
		{
			private bool present;

			public bool Present => present;

			public Arg()
			{
				present = false;
			}

			public Arg(bool present)
			{
				this.present = present;
			}
		}

		public class Arg<T>
		{
			private bool present;

			private T value;

			public bool Present => present;

			public T Value => value;

			public Arg()
			{
				present = false;
				value = default(T);
			}

			public Arg(T value)
			{
				present = true;
				this.value = value;
			}
		}

		public readonly Arg<string> iwad;

		public readonly Arg<string[]> file;

		public readonly Arg<string[]> deh;

		public readonly Arg<Tuple<int, int>> warp;

		public readonly Arg<int> episode;

		public readonly Arg<int> skill;

		public readonly Arg deathmatch;

		public readonly Arg altdeath;

		public readonly Arg fast;

		public readonly Arg respawn;

		public readonly Arg nomonsters;

		public readonly Arg solonet;

		public readonly Arg<string> playdemo;

		public readonly Arg<string> timedemo;

		public readonly Arg<int> loadgame;

		public readonly Arg nomouse;

		public readonly Arg nosound;

		public readonly Arg nosfx;

		public readonly Arg nomusic;

		public readonly Arg nodeh;

		public CommandLineArgs(string[] args)
		{
			iwad = GetString(args, "-iwad");
			file = Check_file(args);
			deh = Check_deh(args);
			warp = Check_warp(args);
			episode = GetInt(args, "-episode");
			skill = GetInt(args, "-skill");
			deathmatch = new Arg(args.Contains("-deathmatch"));
			altdeath = new Arg(args.Contains("-altdeath"));
			fast = new Arg(args.Contains("-fast"));
			respawn = new Arg(args.Contains("-respawn"));
			nomonsters = new Arg(args.Contains("-nomonsters"));
			solonet = new Arg(args.Contains("-solo-net"));
			playdemo = GetString(args, "-playdemo");
			timedemo = GetString(args, "-timedemo");
			loadgame = GetInt(args, "-loadgame");
			nomouse = new Arg(args.Contains("-nomouse"));
			nosound = new Arg(args.Contains("-nosound"));
			nosfx = new Arg(args.Contains("-nosfx"));
			nomusic = new Arg(args.Contains("-nomusic"));
			nodeh = new Arg(args.Contains("-nodeh"));
			if (args.Length == 0 || !args.All((string arg) => arg.FirstOrDefault() != '-'))
			{
				return;
			}
			string text = null;
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			foreach (string text2 in args)
			{
				string text3 = Path.GetExtension(text2).ToLower();
				if (text3 == ".wad")
				{
					if (ConfigUtilities.IsIwad(text2))
					{
						text = text2;
					}
					else
					{
						list.Add(text2);
					}
				}
				else if (text3 == ".deh")
				{
					list2.Add(text2);
				}
			}
			if (text != null)
			{
				iwad = new Arg<string>(text);
			}
			if (list.Count > 0)
			{
				file = new Arg<string[]>(list.ToArray());
			}
			if (list2.Count > 0)
			{
				deh = new Arg<string[]>(list2.ToArray());
			}
		}

		private static Arg<string[]> Check_file(string[] args)
		{
			string[] values = GetValues(args, "-file");
			if (values.Length >= 1)
			{
				return new Arg<string[]>(values);
			}
			return new Arg<string[]>();
		}

		private static Arg<string[]> Check_deh(string[] args)
		{
			string[] values = GetValues(args, "-deh");
			if (values.Length >= 1)
			{
				return new Arg<string[]>(values);
			}
			return new Arg<string[]>();
		}

		private static Arg<Tuple<int, int>> Check_warp(string[] args)
		{
			string[] values = GetValues(args, "-warp");
			int result2;
			int result3;
			if (values.Length == 1)
			{
				if (int.TryParse(values[0], out var result))
				{
					return new Arg<Tuple<int, int>>(Tuple.Create(1, result));
				}
			}
			else if (values.Length == 2 && int.TryParse(values[0], out result2) && int.TryParse(values[1], out result3))
			{
				return new Arg<Tuple<int, int>>(Tuple.Create(result2, result3));
			}
			return new Arg<Tuple<int, int>>();
		}

		private static Arg<string> GetString(string[] args, string name)
		{
			string[] values = GetValues(args, name);
			if (values.Length == 1)
			{
				return new Arg<string>(values[0]);
			}
			return new Arg<string>();
		}

		private static Arg<int> GetInt(string[] args, string name)
		{
			string[] values = GetValues(args, name);
			if (values.Length == 1 && int.TryParse(values[0], out var result))
			{
				return new Arg<int>(result);
			}
			return new Arg<int>();
		}

		private static string[] GetValues(string[] args, string name)
		{
			return args.SkipWhile((string arg) => arg != name).Skip(1).TakeWhile((string arg) => arg[0] != '-')
				.ToArray();
		}
	}
	public sealed class Config
	{
		public KeyBinding key_forward;

		public KeyBinding key_backward;

		public KeyBinding key_strafeleft;

		public KeyBinding key_straferight;

		public KeyBinding key_turnleft;

		public KeyBinding key_turnright;

		public KeyBinding key_fire;

		public KeyBinding key_use;

		public KeyBinding key_run;

		public KeyBinding key_strafe;

		public int mouse_sensitivity;

		public bool mouse_disableyaxis;

		public bool game_alwaysrun;

		public int video_screenwidth;

		public int video_screenheight;

		public bool video_fullscreen;

		public bool video_highresolution;

		public bool video_displaymessage;

		public int video_gamescreensize;

		public int video_gammacorrection;

		public int video_fpsscale;

		public int audio_soundvolume;

		public int audio_musicvolume;

		public bool audio_randompitch;

		public string audio_soundfont;

		public bool audio_musiceffect;

		private bool isRestoredFromFile;

		public bool IsRestoredFromFile => isRestoredFromFile;

		public Config()
		{
			key_forward = new KeyBinding(new DoomKey[2]
			{
				DoomKey.Up,
				DoomKey.W
			});
			key_backward = new KeyBinding(new DoomKey[2]
			{
				DoomKey.Down,
				DoomKey.S
			});
			key_strafeleft = new KeyBinding(new DoomKey[1]);
			key_straferight = new KeyBinding(new DoomKey[1] { DoomKey.D });
			key_turnleft = new KeyBinding(new DoomKey[1] { DoomKey.Left });
			key_turnright = new KeyBinding(new DoomKey[1] { DoomKey.Right });
			key_fire = new KeyBinding(new DoomKey[2]
			{
				DoomKey.LControl,
				DoomKey.RControl
			}, new DoomMouseButton[1]);
			key_use = new KeyBinding(new DoomKey[1] { DoomKey.Space }, new DoomMouseButton[1] { DoomMouseButton.Mouse2 });
			key_run = new KeyBinding(new DoomKey[2]
			{
				DoomKey.LShift,
				DoomKey.RShift
			});
			key_strafe = new KeyBinding(new DoomKey[2]
			{
				DoomKey.LAlt,
				DoomKey.RAlt
			});
			mouse_sensitivity = 8;
			mouse_disableyaxis = false;
			game_alwaysrun = true;
			video_screenwidth = 640;
			video_screenheight = 400;
			video_fullscreen = false;
			video_highresolution = true;
			video_gamescreensize = 7;
			video_displaymessage = true;
			video_gammacorrection = 2;
			video_fpsscale = 2;
			audio_soundvolume = 8;
			audio_musicvolume = 8;
			audio_randompitch = true;
			audio_soundfont = "TimGM6mb.sf2";
			audio_musiceffect = true;
			isRestoredFromFile = false;
		}

		public Config(string path)
			: this()
		{
			try
			{
				Console.Write("Restore settings: ");
				Dictionary<string, string> dictionary = new Dictionary<string, string>();
				foreach (string item in File.ReadLines(path))
				{
					string[] array = item.Split('=', StringSplitOptions.RemoveEmptyEntries);
					if (array.Length == 2)
					{
						dictionary[array[0].Trim()] = array[1].Trim();
					}
				}
				key_forward = GetKeyBinding(dictionary, "key_forward", key_forward);
				key_backward = GetKeyBinding(dictionary, "key_backward", key_backward);
				key_strafeleft = GetKeyBinding(dictionary, "key_strafeleft", key_strafeleft);
				key_straferight = GetKeyBinding(dictionary, "key_straferight", key_straferight);
				key_turnleft = GetKeyBinding(dictionary, "key_turnleft", key_turnleft);
				key_turnright = GetKeyBinding(dictionary, "key_turnright", key_turnright);
				key_fire = GetKeyBinding(dictionary, "key_fire", key_fire);
				key_use = GetKeyBinding(dictionary, "key_use", key_use);
				key_run = GetKeyBinding(dictionary, "key_run", key_run);
				key_strafe = GetKeyBinding(dictionary, "key_strafe", key_strafe);
				mouse_sensitivity = GetInt(dictionary, "mouse_sensitivity", mouse_sensitivity);
				mouse_disableyaxis = GetBool(dictionary, "mouse_disableyaxis", mouse_disableyaxis);
				game_alwaysrun = GetBool(dictionary, "game_alwaysrun", game_alwaysrun);
				video_screenwidth = GetInt(dictionary, "video_screenwidth", video_screenwidth);
				video_screenheight = GetInt(dictionary, "video_screenheight", video_screenheight);
				video_fullscreen = GetBool(dictionary, "video_fullscreen", video_fullscreen);
				video_highresolution = GetBool(dictionary, "video_highresolution", video_highresolution);
				video_displaymessage = GetBool(dictionary, "video_displaymessage", video_displaymessage);
				video_gamescreensize = GetInt(dictionary, "video_gamescreensize", video_gamescreensize);
				video_gammacorrection = GetInt(dictionary, "video_gammacorrection", video_gammacorrection);
				video_fpsscale = GetInt(dictionary, "video_fpsscale", video_fpsscale);
				audio_soundvolume = GetInt(dictionary, "audio_soundvolume", audio_soundvolume);
				audio_musicvolume = GetInt(dictionary, "audio_musicvolume", audio_musicvolume);
				audio_randompitch = GetBool(dictionary, "audio_randompitch", audio_randompitch);
				audio_soundfont = GetString(dictionary, "audio_soundfont", audio_soundfont);
				audio_musiceffect = GetBool(dictionary, "audio_musiceffect", audio_musiceffect);
				isRestoredFromFile = true;
				Console.WriteLine("OK");
			}
			catch
			{
				Console.WriteLine("Failed");
			}
		}

		public void Save(string path)
		{
			try
			{
				using StreamWriter streamWriter = new StreamWriter(path);
				streamWriter.WriteLine("key_forward = " + key_forward);
				streamWriter.WriteLine("key_backward = " + key_backward);
				streamWriter.WriteLine("key_strafeleft = " + key_strafeleft);
				streamWriter.WriteLine("key_straferight = " + key_straferight);
				streamWriter.WriteLine("key_turnleft = " + key_turnleft);
				streamWriter.WriteLine("key_turnright = " + key_turnright);
				streamWriter.WriteLine("key_fire = " + key_fire);
				streamWriter.WriteLine("key_use = " + key_use);
				streamWriter.WriteLine("key_run = " + key_run);
				streamWriter.WriteLine("key_strafe = " + key_strafe);
				streamWriter.WriteLine("mouse_sensitivity = " + mouse_sensitivity);
				streamWriter.WriteLine("mouse_disableyaxis = " + BoolToString(mouse_disableyaxis));
				streamWriter.WriteLine("game_alwaysrun = " + BoolToString(game_alwaysrun));
				streamWriter.WriteLine("video_screenwidth = " + video_screenwidth);
				streamWriter.WriteLine("video_screenheight = " + video_screenheight);
				streamWriter.WriteLine("video_fullscreen = " + BoolToString(video_fullscreen));
				streamWriter.WriteLine("video_highresolution = " + BoolToString(video_highresolution));
				streamWriter.WriteLine("video_displaymessage = " + BoolToString(video_displaymessage));
				streamWriter.WriteLine("video_gamescreensize = " + video_gamescreensize);
				streamWriter.WriteLine("video_gammacorrection = " + video_gammacorrection);
				streamWriter.WriteLine("video_fpsscale = " + video_fpsscale);
				streamWriter.WriteLine("audio_soundvolume = " + audio_soundvolume);
				streamWriter.WriteLine("audio_musicvolume = " + audio_musicvolume);
				streamWriter.WriteLine("audio_randompitch = " + BoolToString(audio_randompitch));
				streamWriter.WriteLine("audio_soundfont = " + audio_soundfont);
				streamWriter.WriteLine("audio_musiceffect = " + BoolToString(audio_musiceffect));
			}
			catch
			{
			}
		}

		private static int GetInt(Dictionary<string, string> dic, string name, int defaultValue)
		{
			if (dic.TryGetValue(name, out var value) && int.TryParse(value, out var result))
			{
				return result;
			}
			return defaultValue;
		}

		private static string GetString(Dictionary<string, string> dic, string name, string defaultValue)
		{
			if (dic.TryGetValue(name, out var value))
			{
				return value;
			}
			return defaultValue;
		}

		private static bool GetBool(Dictionary<string, string> dic, string name, bool defaultValue)
		{
			if (dic.TryGetValue(name, out var value))
			{
				if (value == "true")
				{
					return true;
				}
				if (value == "false")
				{
					return false;
				}
			}
			return defaultValue;
		}

		private static KeyBinding GetKeyBinding(Dictionary<string, string> dic, string name, KeyBinding defaultValue)
		{
			if (dic.TryGetValue(name, out var value))
			{
				return KeyBinding.Parse(value);
			}
			return defaultValue;
		}

		private static string BoolToString(bool value)
		{
			if (!value)
			{
				return "false";
			}
			return "true";
		}
	}
	public static class ConfigUtilities
	{
		private static readonly string[] iwadNames = new string[7] { "DOOM2.WAD", "PLUTONIA.WAD", "TNT.WAD", "DOOM.WAD", "DOOM1.WAD", "FREEDOOM2.WAD", "FREEDOOM1.WAD" };

		public static string GetExeDirectory()
		{
			return Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
		}

		public static string GetConfigPath()
		{
			return Path.Combine(GetExeDirectory(), "managed-doom.cfg");
		}

		public static string GetDefaultIwadPath()
		{
			string exeDirectory = GetExeDirectory();
			string[] array = iwadNames;
			foreach (string path in array)
			{
				string text = Path.Combine(exeDirectory, path);
				if (File.Exists(text))
				{
					return text;
				}
			}
			string currentDirectory = Directory.GetCurrentDirectory();
			array = iwadNames;
			foreach (string path2 in array)
			{
				string text2 = Path.Combine(currentDirectory, path2);
				if (File.Exists(text2))
				{
					return text2;
				}
			}
			throw new Exception("No IWAD was found!");
		}

		public static bool IsIwad(string path)
		{
			string value = Path.GetFileName(path).ToUpper();
			return iwadNames.Contains(value);
		}

		public static string[] GetWadPaths(CommandLineArgs args)
		{
			List<string> list = new List<string>();
			if (args.iwad.Present)
			{
				list.Add(args.iwad.Value);
			}
			else
			{
				list.Add(GetDefaultIwadPath());
			}
			if (args.file.Present)
			{
				string[] value = args.file.Value;
				foreach (string item in value)
				{
					list.Add(item);
				}
			}
			return list.ToArray();
		}
	}
	public static class DoomDebug
	{
		public static int CombineHash(int a, int b)
		{
			return (3 * a) ^ b;
		}

		public static int GetMobjHash(Mobj mobj)
		{
			return CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(CombineHash(0, mobj.X.Data), mobj.Y.Data), mobj.Z.Data), (int)mobj.Angle.Data), (int)mobj.Sprite), mobj.Frame), mobj.FloorZ.Data), mobj.CeilingZ.Data), mobj.Radius.Data), mobj.Height.Data), mobj.MomX.Data), mobj.MomY.Data), mobj.MomZ.Data), mobj.Tics), (int)mobj.Flags), mobj.Health), (int)mobj.MoveDir), mobj.MoveCount), mobj.ReactionTime), mobj.Threshold);
		}

		public static int GetMobjHash(World world)
		{
			int num = 0;
			foreach (Thinker thinker in world.Thinkers)
			{
				if (thinker is Mobj mobj)
				{
					num = CombineHash(num, GetMobjHash(mobj));
				}
			}
			return num;
		}

		private static string GetMobjCsv(Mobj mobj)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(mobj.X.Data).Append(",");
			stringBuilder.Append(mobj.Y.Data).Append(",");
			stringBuilder.Append(mobj.Z.Data).Append(",");
			stringBuilder.Append((int)mobj.Angle.Data).Append(",");
			stringBuilder.Append((int)mobj.Sprite).Append(",");
			stringBuilder.Append(mobj.Frame).Append(",");
			stringBuilder.Append(mobj.FloorZ.Data).Append(",");
			stringBuilder.Append(mobj.CeilingZ.Data).Append(",");
			stringBuilder.Append(mobj.Radius.Data).Append(",");
			stringBuilder.Append(mobj.Height.Data).Append(",");
			stringBuilder.Append(mobj.MomX.Data).Append(",");
			stringBuilder.Append(mobj.MomY.Data).Append(",");
			stringBuilder.Append(mobj.MomZ.Data).Append(",");
			stringBuilder.Append(mobj.Tics).Append(",");
			stringBuilder.Append((int)mobj.Flags).Append(",");
			stringBuilder.Append(mobj.Health).Append(",");
			stringBuilder.Append((int)mobj.MoveDir).Append(",");
			stringBuilder.Append(mobj.MoveCount).Append(",");
			stringBuilder.Append(mobj.ReactionTime).Append(",");
			stringBuilder.Append(mobj.Threshold);
			return stringBuilder.ToString();
		}

		public static void DumpMobjCsv(string path, World world)
		{
			using StreamWriter streamWriter = new StreamWriter(path);
			foreach (Thinker thinker in world.Thinkers)
			{
				if (thinker is Mobj mobj)
				{
					streamWriter.WriteLine(GetMobjCsv(mobj));
				}
			}
		}

		public static int GetSectorHash(Sector sector)
		{
			return CombineHash(CombineHash(CombineHash(0, sector.FloorHeight.Data), sector.CeilingHeight.Data), sector.LightLevel);
		}

		public static int GetSectorHash(World world)
		{
			int num = 0;
			Sector[] sectors = world.Map.Sectors;
			foreach (Sector sector in sectors)
			{
				num = CombineHash(num, GetSectorHash(sector));
			}
			return num;
		}
	}
	public static class DoomInterop
	{
		public static string ToString(byte[] data, int offset, int maxLength)
		{
			int num = 0;
			for (int i = 0; i < maxLength && data[offset + i] != 0; i++)
			{
				num++;
			}
			char[] array = new char[num];
			for (int j = 0; j < array.Length; j++)
			{
				byte b = data[offset + j];
				if (97 <= b && b <= 122)
				{
					b -= 32;
				}
				array[j] = (char)b;
			}
			return new string(array);
		}
	}
	public sealed class DoomRandom
	{
		private static readonly int[] table = new int[256]
		{
			0, 8, 109, 220, 222, 241, 149, 107, 75, 248,
			254, 140, 16, 66, 74, 21, 211, 47, 80, 242,
			154, 27, 205, 128, 161, 89, 77, 36, 95, 110,
			85, 48, 212, 140, 211, 249, 22, 79, 200, 50,
			28, 188, 52, 140, 202, 120, 68, 145, 62, 70,
			184, 190, 91, 197, 152, 224, 149, 104, 25, 178,
			252, 182, 202, 182, 141, 197, 4, 81, 181, 242,
			145, 42, 39, 227, 156, 198, 225, 193, 219, 93,
			122, 175, 249, 0, 175, 143, 70, 239, 46, 246,
			163, 53, 163, 109, 168, 135, 2, 235, 25, 92,
			20, 145, 138, 77, 69, 166, 78, 176, 173, 212,
			166, 113, 94, 161, 41, 50, 239, 49, 111, 164,
			70, 60, 2, 37, 171, 75, 136, 156, 11, 56,
			42, 146, 138, 229, 73, 146, 77, 61, 98, 196,
			135, 106, 63, 197, 195, 86, 96, 203, 113, 101,
			170, 247, 181, 113, 80, 250, 108, 7, 255, 237,
			129, 226, 79, 107, 112, 166, 103, 241, 24, 223,
			239, 120, 198, 58, 60, 82, 128, 3, 184, 66,
			143, 224, 145, 224, 81, 206, 163, 45, 63, 90,
			168, 114, 59, 33, 159, 95, 28, 139, 123, 98,
			125, 196, 15, 70, 194, 253, 54, 14, 109, 226,
			71, 17, 161, 93, 186, 87, 244, 138, 20, 52,
			123, 251, 26, 36, 17, 46, 52, 231, 232, 76,
			31, 221, 84, 37, 216, 165, 212, 106, 197, 242,
			98, 43, 39, 175, 254, 145, 190, 84, 118, 222,
			187, 136, 120, 163, 236, 249
		};

		private int index;

		public DoomRandom()
		{
			index = 0;
		}

		public DoomRandom(int seed)
		{
			index = seed & 0xFF;
		}

		public int Next()
		{
			index = (index + 1) & 0xFF;
			return table[index];
		}

		public void Clear()
		{
			index = 0;
		}
	}
	public sealed class DoomString
	{
		private static Dictionary<string, DoomString> valueTable = new Dictionary<string, DoomString>();

		private static Dictionary<string, DoomString> nameTable = new Dictionary<string, DoomString>();

		private string original;

		private string replaced;

		public char this[int index] => replaced[index];

		public DoomString(string original)
		{
			this.original = original;
			replaced = original;
			if (!valueTable.ContainsKey(original))
			{
				valueTable.Add(original, this);
			}
		}

		public DoomString(string name, string original)
			: this(original)
		{
			nameTable.Add(name, this);
		}

		public override string ToString()
		{
			return replaced;
		}

		public static implicit operator string(DoomString ds)
		{
			return ds.replaced;
		}

		public static void ReplaceByValue(string original, string replaced)
		{
			if (valueTable.TryGetValue(original, out var value))
			{
				value.replaced = replaced;
			}
		}

		public static void ReplaceByName(string name, string value)
		{
			if (nameTable.TryGetValue(name, out var value2))
			{
				value2.replaced = value;
			}
		}
	}
	public static class DeHackEd
	{
		private enum Block
		{
			None,
			Thing,
			Frame,
			Pointer,
			Sound,
			Ammo,
			Weapon,
			Cheat,
			Misc,
			Text,
			Sprite,
			BexStrings,
			BexPars
		}

		private static Tuple<Action<World, Player, PlayerSpriteDef>, Action<World, Mobj>>[] sourcePointerTable;

		public static void Initialize(CommandLineArgs args, Wad wad)
		{
			if (args.deh.Present)
			{
				ReadFiles(args.deh.Value);
			}
			if (!args.nodeh.Present)
			{
				ReadDeHackEdLump(wad);
			}
		}

		private static void ReadFiles(params string[] fileNames)
		{
			string text = null;
			try
			{
				DoomInfo.Strings.PRESSKEY.GetHashCode();
				Console.Write("Load DeHackEd patches: ");
				for (int i = 0; i < fileNames.Length; i++)
				{
					ProcessLines(File.ReadLines(text = fileNames[i]));
				}
				Console.WriteLine("OK (" + string.Join(", ", fileNames.Select((string x) => Path.GetFileName(x))) + ")");
			}
			catch (Exception innerException)
			{
				Console.WriteLine("Failed");
				throw new Exception("Failed to apply DeHackEd patch: " + text, innerException);
			}
		}

		private static void ReadDeHackEdLump(Wad wad)
		{
			int lumpNumber = wad.GetLumpNumber("DEHACKED");
			if (lumpNumber != -1)
			{
				DoomInfo.Strings.PRESSKEY.GetHashCode();
				try
				{
					Console.Write("Load DeHackEd patch from WAD: ");
					ProcessLines(ReadLines(wad.ReadLump(lumpNumber)));
					Console.WriteLine("OK");
				}
				catch (Exception innerException)
				{
					Console.WriteLine("Failed");
					throw new Exception("Failed to apply DeHackEd patch!", innerException);
				}
			}
		}

		private static IEnumerable<string> ReadLines(byte[] data)
		{
			using MemoryStream ms = new MemoryStream(data);
			using StreamReader sr = new StreamReader(ms);
			for (string text = sr.ReadLine(); text != null; text = sr.ReadLine())
			{
				yield return text;
			}
		}

		private static void ProcessLines(IEnumerable<string> lines)
		{
			if (sourcePointerTable == null)
			{
				sourcePointerTable = new Tuple<Action<World, Player, PlayerSpriteDef>, Action<World, Mobj>>[DoomInfo.States.Length];
				for (int i = 0; i < sourcePointerTable.Length; i++)
				{
					Action<World, Player, PlayerSpriteDef> playerAction = DoomInfo.States[i].PlayerAction;
					Action<World, Mobj> mobjAction = DoomInfo.States[i].MobjAction;
					sourcePointerTable[i] = Tuple.Create(playerAction, mobjAction);
				}
			}
			int num = 0;
			List<string> list = new List<string>();
			Block type = Block.None;
			int lineNumber = 0;
			foreach (string line in lines)
			{
				num++;
				if (line.Length <= 0 || line[0] != '#')
				{
					Block blockType = GetBlockType(line.Split(' '));
					if (blockType == Block.None)
					{
						list.Add(line);
						continue;
					}
					ProcessBlock(type, list, lineNumber);
					list.Clear();
					list.Add(line);
					type = blockType;
					lineNumber = num;
				}
			}
			ProcessBlock(type, list, lineNumber);
		}

		private static void ProcessBlock(Block type, List<string> data, int lineNumber)
		{
			try
			{
				switch (type)
				{
				case Block.Thing:
					ProcessThingBlock(data);
					break;
				case Block.Frame:
					ProcessFrameBlock(data);
					break;
				case Block.Pointer:
					ProcessPointerBlock(data);
					break;
				case Block.Sound:
					ProcessSoundBlock(data);
					break;
				case Block.Ammo:
					ProcessAmmoBlock(data);
					break;
				case Block.Weapon:
					ProcessWeaponBlock(data);
					break;
				case Block.Cheat:
					ProcessCheatBlock(data);
					break;
				case Block.Misc:
					ProcessMiscBlock(data);
					break;
				case Block.Text:
					ProcessTextBlock(data);
					break;
				case Block.Sprite:
					ProcessSpriteBlock(data);
					break;
				case Block.BexStrings:
					ProcessBexStringsBlock(data);
					break;
				case Block.BexPars:
					ProcessBexParsBlock(data);
					break;
				}
			}
			catch (Exception innerException)
			{
				throw new Exception("Failed to process block: " + type.ToString() + " (line " + lineNumber + ")", innerException);
			}
		}

		private static void ProcessThingBlock(List<string> data)
		{
			int num = int.Parse(data[0].Split(' ')[1]) - 1;
			MobjInfo mobjInfo = DoomInfo.MobjInfos[num];
			Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data);
			mobjInfo.DoomEdNum = GetInt(keyValuePairs, "ID #", mobjInfo.DoomEdNum);
			mobjInfo.SpawnState = (MobjState)GetInt(keyValuePairs, "Initial frame", (int)mobjInfo.SpawnState);
			mobjInfo.SpawnHealth = GetInt(keyValuePairs, "Hit points", mobjInfo.SpawnHealth);
			mobjInfo.SeeState = (MobjState)GetInt(keyValuePairs, "First moving frame", (int)mobjInfo.SeeState);
			mobjInfo.SeeSound = (Sfx)GetInt(keyValuePairs, "Alert sound", (int)mobjInfo.SeeSound);
			mobjInfo.ReactionTime = GetInt(keyValuePairs, "Reaction time", mobjInfo.ReactionTime);
			mobjInfo.AttackSound = (Sfx)GetInt(keyValuePairs, "Attack sound", (int)mobjInfo.AttackSound);
			mobjInfo.PainState = (MobjState)GetInt(keyValuePairs, "Injury frame", (int)mobjInfo.PainState);
			mobjInfo.PainChance = GetInt(keyValuePairs, "Pain chance", mobjInfo.PainChance);
			mobjInfo.PainSound = (Sfx)GetInt(keyValuePairs, "Pain sound", (int)mobjInfo.PainSound);
			mobjInfo.MeleeState = (MobjState)GetInt(keyValuePairs, "Close attack frame", (int)mobjInfo.MeleeState);
			mobjInfo.MissileState = (MobjState)GetInt(keyValuePairs, "Far attack frame", (int)mobjInfo.MissileState);
			mobjInfo.DeathState = (MobjState)GetInt(keyValuePairs, "Death frame", (int)mobjInfo.DeathState);
			mobjInfo.XdeathState = (MobjState)GetInt(keyValuePairs, "Exploding frame", (int)mobjInfo.XdeathState);
			mobjInfo.DeathSound = (Sfx)GetInt(keyValuePairs, "Death sound", (int)mobjInfo.DeathSound);
			mobjInfo.Speed = GetInt(keyValuePairs, "Speed", mobjInfo.Speed);
			mobjInfo.Radius = new Fixed(GetInt(keyValuePairs, "Width", mobjInfo.Radius.Data));
			mobjInfo.Height = new Fixed(GetInt(keyValuePairs, "Height", mobjInfo.Height.Data));
			mobjInfo.Mass = GetInt(keyValuePairs, "Mass", mobjInfo.Mass);
			mobjInfo.Damage = GetInt(keyValuePairs, "Missile damage", mobjInfo.Damage);
			mobjInfo.ActiveSound = (Sfx)GetInt(keyValuePairs, "Action sound", (int)mobjInfo.ActiveSound);
			mobjInfo.Flags = (MobjFlags)GetInt(keyValuePairs, "Bits", (int)mobjInfo.Flags);
			mobjInfo.Raisestate = (MobjState)GetInt(keyValuePairs, "Respawn frame", (int)mobjInfo.Raisestate);
		}

		private static void ProcessFrameBlock(List<string> data)
		{
			int num = int.Parse(data[0].Split(' ')[1]);
			MobjStateDef mobjStateDef = DoomInfo.States[num];
			Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data);
			mobjStateDef.Sprite = (Sprite)GetInt(keyValuePairs, "Sprite number", (int)mobjStateDef.Sprite);
			mobjStateDef.Frame = GetInt(keyValuePairs, "Sprite subnumber", mobjStateDef.Frame);
			mobjStateDef.Tics = GetInt(keyValuePairs, "Duration", mobjStateDef.Tics);
			mobjStateDef.Next = (MobjState)GetInt(keyValuePairs, "Next frame", (int)mobjStateDef.Next);
			mobjStateDef.Misc1 = GetInt(keyValuePairs, "Unknown 1", mobjStateDef.Misc1);
			mobjStateDef.Misc2 = GetInt(keyValuePairs, "Unknown 2", mobjStateDef.Misc2);
		}

		private static void ProcessPointerBlock(List<string> data)
		{
			Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data);
			int num = data[0].IndexOf('(') + 1;
			int length = data[0].IndexOf(')') - num;
			int num2 = int.Parse(data[0].Substring(num, length).Split(' ')[1]);
			int @int = GetInt(keyValuePairs, "Codep Frame", -1);
			if (@int != -1)
			{
				MobjStateDef obj = DoomInfo.States[num2];
				obj.PlayerAction = sourcePointerTable[@int].Item1;
				obj.MobjAction = sourcePointerTable[@int].Item2;
			}
		}

		private static void ProcessSoundBlock(List<string> data)
		{
		}

		private static void ProcessAmmoBlock(List<string> data)
		{
			int num = int.Parse(data[0].Split(' ')[1]);
			Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data);
			int[] max = DoomInfo.AmmoInfos.Max;
			int[] clip = DoomInfo.AmmoInfos.Clip;
			max[num] = GetInt(keyValuePairs, "Max ammo", max[num]);
			clip[num] = GetInt(keyValuePairs, "Per ammo", clip[num]);
		}

		private static void ProcessWeaponBlock(List<string> data)
		{
			int num = int.Parse(data[0].Split(' ')[1]);
			WeaponInfo weaponInfo = DoomInfo.WeaponInfos[num];
			Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data);
			weaponInfo.Ammo = (AmmoType)GetInt(keyValuePairs, "Ammo type", (int)weaponInfo.Ammo);
			weaponInfo.UpState = (MobjState)GetInt(keyValuePairs, "Deselect frame", (int)weaponInfo.UpState);
			weaponInfo.DownState = (MobjState)GetInt(keyValuePairs, "Select frame", (int)weaponInfo.DownState);
			weaponInfo.ReadyState = (MobjState)GetInt(keyValuePairs, "Bobbing frame", (int)weaponInfo.ReadyState);
			weaponInfo.AttackState = (MobjState)GetInt(keyValuePairs, "Shooting frame", (int)weaponInfo.AttackState);
			weaponInfo.FlashState = (MobjState)GetInt(keyValuePairs, "Firing frame", (int)weaponInfo.FlashState);
		}

		private static void ProcessCheatBlock(List<string> data)
		{
		}

		private static void ProcessMiscBlock(List<string> data)
		{
			Dictionary<string, string> keyValuePairs = GetKeyValuePairs(data);
			DoomInfo.DeHackEdConst.InitialHealth = GetInt(keyValuePairs, "Initial Health", DoomInfo.DeHackEdConst.InitialHealth);
			DoomInfo.DeHackEdConst.InitialBullets = GetInt(keyValuePairs, "Initial Bullets", DoomInfo.DeHackEdConst.InitialBullets);
			DoomInfo.DeHackEdConst.MaxHealth = GetInt(keyValuePairs, "Max Health", DoomInfo.DeHackEdConst.MaxHealth);
			DoomInfo.DeHackEdConst.MaxArmor = GetInt(keyValuePairs, "Max Armor", DoomInfo.DeHackEdConst.MaxArmor);
			DoomInfo.DeHackEdConst.GreenArmorClass = GetInt(keyValuePairs, "Green Armor Class", DoomInfo.DeHackEdConst.GreenArmorClass);
			DoomInfo.DeHackEdConst.BlueArmorClass = GetInt(keyValuePairs, "Blue Armor Class", DoomInfo.DeHackEdConst.BlueArmorClass);
			DoomInfo.DeHackEdConst.MaxSoulsphere = GetInt(keyValuePairs, "Max Soulsphere", DoomInfo.DeHackEdConst.MaxSoulsphere);
			DoomInfo.DeHackEdConst.SoulsphereHealth = GetInt(keyValuePairs, "Soulsphere Health", DoomInfo.DeHackEdConst.SoulsphereHealth);
			DoomInfo.DeHackEdConst.MegasphereHealth = GetInt(keyValuePairs, "Megasphere Health", DoomInfo.DeHackEdConst.MegasphereHealth);
			DoomInfo.DeHackEdConst.GodModeHealth = GetInt(keyValuePairs, "God Mode Health", DoomInfo.DeHackEdConst.GodModeHealth);
			DoomInfo.DeHackEdConst.IdfaArmor = GetInt(keyValuePairs, "IDFA Armor", DoomInfo.DeHackEdConst.IdfaArmor);
			DoomInfo.DeHackEdConst.IdfaArmorClass = GetInt(keyValuePairs, "IDFA Armor Class", DoomInfo.DeHackEdConst.IdfaArmorClass);
			DoomInfo.DeHackEdConst.IdkfaArmor = GetInt(keyValuePairs, "IDKFA Armor", DoomInfo.DeHackEdConst.IdkfaArmor);
			DoomInfo.DeHackEdConst.IdkfaArmorClass = GetInt(keyValuePairs, "IDKFA Armor Class", DoomInfo.DeHackEdConst.IdkfaArmorClass);
			DoomInfo.DeHackEdConst.BfgCellsPerShot = GetInt(keyValuePairs, "BFG Cells/Shot", DoomInfo.DeHackEdConst.BfgCellsPerShot);
			DoomInfo.DeHackEdConst.MonstersInfight = GetInt(keyValuePairs, "Monsters Infight", 0) == 221;
		}

		private static void ProcessTextBlock(List<string> data)
		{
			string[] array = data[0].Split(' ');
			int num = int.Parse(array[1]);
			int num2 = int.Parse(array[2]);
			int num3 = 1;
			int num4 = 0;
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < num; i++)
			{
				if (num4 == data[num3].Length)
				{
					stringBuilder.Append('\n');
					num3++;
					num4 = 0;
				}
				else
				{
					stringBuilder.Append(data[num3][num4]);
					num4++;
				}
			}
			StringBuilder stringBuilder2 = new StringBuilder();
			for (int j = 0; j < num2; j++)
			{
				if (num4 == data[num3].Length)
				{
					stringBuilder2.Append('\n');
					num3++;
					num4 = 0;
				}
				else
				{
					stringBuilder2.Append(data[num3][num4]);
					num4++;
				}
			}
			DoomString.ReplaceByValue(stringBuilder.ToString(), stringBuilder2.ToString());
		}

		private static void ProcessSpriteBlock(List<string> data)
		{
		}

		private static void ProcessBexStringsBlock(List<string> data)
		{
			string text = null;
			StringBuilder stringBuilder = null;
			foreach (string item in data.Skip(1))
			{
				if (text == null)
				{
					int num = item.IndexOf('=');
					if (num != -1)
					{
						string text2 = item.Substring(0, num).Trim();
						string text3 = item.Substring(num + 1).Trim().Replace("\\n", "\n");
						if (text3.Last() != '\\')
						{
							DoomString.ReplaceByName(text2, text3);
							continue;
						}
						text = text2;
						stringBuilder = new StringBuilder();
						stringBuilder.Append(text3, 0, text3.Length - 1);
					}
				}
				else
				{
					string text4 = item.Trim().Replace("\\n", "\n");
					if (text4.Last() != '\\')
					{
						stringBuilder.Append(text4);
						DoomString.ReplaceByName(text, stringBuilder.ToString());
						text = null;
						stringBuilder = null;
					}
					else
					{
						stringBuilder.Append(text4, 0, text4.Length - 1);
					}
				}
			}
		}

		private static void ProcessBexParsBlock(List<string> data)
		{
			foreach (string item in data.Skip(1))
			{
				string[] array = item.Split(' ', StringSplitOptions.RemoveEmptyEntries);
				if (array.Length < 3 || !(array[0] == "par"))
				{
					continue;
				}
				List<int> list = new List<int>();
				using (IEnumerator<string> enumerator2 = array.Skip(1).GetEnumerator())
				{
					int result;
					while (enumerator2.MoveNext() && int.TryParse(enumerator2.Current, out result))
					{
						list.Add(result);
					}
				}
				if (list.Count == 2 && list[0] <= DoomInfo.ParTimes.Doom2.Count)
				{
					DoomInfo.ParTimes.Doom2[list[0] - 1] = list[1];
				}
				if (list.Count >= 3 && list[0] <= DoomInfo.ParTimes.Doom1.Count && list[1] <= DoomInfo.ParTimes.Doom1[list[0] - 1].Count)
				{
					DoomInfo.ParTimes.Doom1[list[0] - 1][list[1] - 1] = list[2];
				}
			}
		}

		private static Block GetBlockType(string[] split)
		{
			if (IsThingBlockStart(split))
			{
				return Block.Thing;
			}
			if (IsFrameBlockStart(split))
			{
				return Block.Frame;
			}
			if (IsPointerBlockStart(split))
			{
				return Block.Pointer;
			}
			if (IsSoundBlockStart(split))
			{
				return Block.Sound;
			}
			if (IsAmmoBlockStart(split))
			{
				return Block.Ammo;
			}
			if (IsWeaponBlockStart(split))
			{
				return Block.Weapon;
			}
			if (IsCheatBlockStart(split))
			{
				return Block.Cheat;
			}
			if (IsMiscBlockStart(split))
			{
				return Block.Misc;
			}
			if (IsTextBlockStart(split))
			{
				return Block.Text;
			}
			if (IsSpriteBlockStart(split))
			{
				return Block.Sprite;
			}
			if (IsBexStringsBlockStart(split))
			{
				return Block.BexStrings;
			}
			if (IsBexParsBlockStart(split))
			{
				return Block.BexPars;
			}
			return Block.None;
		}

		private static bool IsThingBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Thing")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			return true;
		}

		private static bool IsFrameBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Frame")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			return true;
		}

		private static bool IsPointerBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Pointer")
			{
				return false;
			}
			return true;
		}

		private static bool IsSoundBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Sound")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			return true;
		}

		private static bool IsAmmoBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Ammo")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			return true;
		}

		private static bool IsWeaponBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Weapon")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			return true;
		}

		private static bool IsCheatBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Cheat")
			{
				return false;
			}
			if (split[1] != "0")
			{
				return false;
			}
			return true;
		}

		private static bool IsMiscBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Misc")
			{
				return false;
			}
			if (split[1] != "0")
			{
				return false;
			}
			return true;
		}

		private static bool IsTextBlockStart(string[] split)
		{
			if (split.Length < 3)
			{
				return false;
			}
			if (split[0] != "Text")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			if (!IsNumber(split[2]))
			{
				return false;
			}
			return true;
		}

		private static bool IsSpriteBlockStart(string[] split)
		{
			if (split.Length < 2)
			{
				return false;
			}
			if (split[0] != "Sprite")
			{
				return false;
			}
			if (!IsNumber(split[1]))
			{
				return false;
			}
			return true;
		}

		private static bool IsBexStringsBlockStart(string[] split)
		{
			if (split[0] == "[STRINGS]")
			{
				return true;
			}
			return false;
		}

		private static bool IsBexParsBlockStart(string[] split)
		{
			if (split[0] == "[PARS]")
			{
				return true;
			}
			return false;
		}

		private static bool IsNumber(string value)
		{
			foreach (char c in value)
			{
				if ('0' > c || c > '9')
				{
					return false;
				}
			}
			return true;
		}

		private static Dictionary<string, string> GetKeyValuePairs(List<string> data)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (string datum in data)
			{
				string[] array = datum.Split('=');
				if (array.Length == 2)
				{
					dictionary[array[0].Trim()] = array[1].Trim();
				}
			}
			return dictionary;
		}

		private static int GetInt(Dictionary<string, string> dic, string key, int defaultValue)
		{
			if (dic.TryGetValue(key, out var value) && int.TryParse(value, out var result))
			{
				return result;
			}
			return defaultValue;
		}
	}
	public class Doom
	{
		private CommandLineArgs args;

		private Config config;

		private GameContent content;

		private IVideo video;

		private ISound sound;

		private IMusic music;

		private IUserInput userInput;

		private List<DoomEvent> events;

		private GameOptions options;

		private DoomMenu menu;

		private OpeningSequence opening;

		private DemoPlayback demoPlayback;

		private TicCmd[] cmds;

		private DoomGame game;

		private WipeEffect wipeEffect;

		private bool wiping;

		private DoomState currentState;

		private DoomState nextState;

		private bool needWipe;

		private bool sendPause;

		private bool quit;

		private string quitMessage;

		private bool mouseGrabbed;

		public DoomState State => currentState;

		public OpeningSequence Opening => opening;

		public DemoPlayback DemoPlayback => demoPlayback;

		public GameOptions Options => options;

		public DoomGame Game => game;

		public DoomMenu Menu => menu;

		public WipeEffect WipeEffect => wipeEffect;

		public bool Wiping => wiping;

		public string QuitMessage => quitMessage;

		public Doom(CommandLineArgs args, Config config, GameContent content, IVideo video, ISound sound, IMusic music, IUserInput userInput)
		{
			video = video ?? NullVideo.GetInstance();
			sound = sound ?? NullSound.GetInstance();
			music = music ?? NullMusic.GetInstance();
			userInput = userInput ?? NullUserInput.GetInstance();
			this.args = args;
			this.config = config;
			this.content = content;
			this.video = video;
			this.sound = sound;
			this.music = music;
			this.userInput = userInput;
			events = new List<DoomEvent>();
			options = new GameOptions(args, content);
			options.Video = video;
			options.Sound = sound;
			options.Music = music;
			options.UserInput = userInput;
			menu = new DoomMenu(this);
			opening = new OpeningSequence(content, options);
			cmds = new TicCmd[Player.MaxPlayerCount];
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				cmds[i] = new TicCmd();
			}
			game = new DoomGame(content, options);
			wipeEffect = new WipeEffect(video.WipeBandCount, video.WipeHeight);
			wiping = false;
			currentState = DoomState.None;
			nextState = DoomState.Opening;
			needWipe = false;
			sendPause = false;
			quit = false;
			quitMessage = null;
			mouseGrabbed = false;
			CheckGameArgs();
		}

		private void CheckGameArgs()
		{
			if (args.warp.Present)
			{
				nextState = DoomState.Game;
				options.Episode = args.warp.Value.Item1;
				options.Map = args.warp.Value.Item2;
				game.DeferedInitNew();
			}
			else if (args.episode.Present)
			{
				nextState = DoomState.Game;
				options.Episode = args.episode.Value;
				options.Map = 1;
				game.DeferedInitNew();
			}
			if (args.skill.Present)
			{
				options.Skill = (GameSkill)(args.skill.Value - 1);
			}
			if (args.deathmatch.Present)
			{
				options.Deathmatch = 1;
			}
			if (args.altdeath.Present)
			{
				options.Deathmatch = 2;
			}
			if (args.fast.Present)
			{
				options.FastMonsters = true;
			}
			if (args.respawn.Present)
			{
				options.RespawnMonsters = true;
			}
			if (args.nomonsters.Present)
			{
				options.NoMonsters = true;
			}
			if (args.loadgame.Present)
			{
				nextState = DoomState.Game;
				game.LoadGame(args.loadgame.Value);
			}
			if (args.playdemo.Present)
			{
				nextState = DoomState.DemoPlayback;
				demoPlayback = new DemoPlayback(args, content, options, args.playdemo.Value);
			}
			if (args.timedemo.Present)
			{
				nextState = DoomState.DemoPlayback;
				demoPlayback = new DemoPlayback(args, content, options, args.timedemo.Value);
			}
		}

		public void NewGame(GameSkill skill, int episode, int map)
		{
			game.DeferedInitNew(skill, episode, map);
			nextState = DoomState.Game;
		}

		public void EndGame()
		{
			nextState = DoomState.Opening;
		}

		private void DoEvents()
		{
			if (wiping)
			{
				return;
			}
			foreach (DoomEvent @event in events)
			{
				if (menu.DoEvent(@event) || (@event.Type == EventType.KeyDown && CheckFunctionKey(@event.Key)))
				{
					continue;
				}
				if (currentState == DoomState.Game)
				{
					if (@event.Key == DoomKey.Pause && @event.Type == EventType.KeyDown)
					{
						sendPause = true;
					}
					else if (!game.DoEvent(@event))
					{
					}
				}
				else if (currentState == DoomState.DemoPlayback)
				{
					demoPlayback.DoEvent(@event);
				}
			}
			events.Clear();
		}

		private bool CheckFunctionKey(DoomKey key)
		{
			switch (key)
			{
			case DoomKey.F1:
				menu.ShowHelpScreen();
				return true;
			case DoomKey.F2:
				menu.ShowSaveScreen();
				return true;
			case DoomKey.F3:
				menu.ShowLoadScreen();
				return true;
			case DoomKey.F4:
				menu.ShowVolumeControl();
				return true;
			case DoomKey.F6:
				menu.QuickSave();
				return true;
			case DoomKey.F7:
				if (currentState == DoomState.Game)
				{
					menu.EndGame();
				}
				else
				{
					options.Sound.StartSound(Sfx.OOF);
				}
				return true;
			case DoomKey.F8:
				video.DisplayMessage = !video.DisplayMessage;
				if (currentState == DoomState.Game && game.State == GameState.Level)
				{
					string message2 = ((!video.DisplayMessage) ? ((string)DoomInfo.Strings.MSGOFF) : ((string)DoomInfo.Strings.MSGON));
					game.World.ConsolePlayer.SendMessage(message2);
				}
				menu.StartSound(Sfx.SWTCHN);
				return true;
			case DoomKey.F9:
				menu.QuickLoad();
				return true;
			case DoomKey.F10:
				menu.Quit();
				return true;
			case DoomKey.F11:
			{
				int gammaCorrectionLevel = video.GammaCorrectionLevel;
				gammaCorrectionLevel++;
				if (gammaCorrectionLevel > video.MaxGammaCorrectionLevel)
				{
					gammaCorrectionLevel = 0;
				}
				video.GammaCorrectionLevel = gammaCorrectionLevel;
				if (currentState == DoomState.Game && game.State == GameState.Level)
				{
					string message = ((gammaCorrectionLevel != 0) ? ("Gamma correction level " + gammaCorrectionLevel) : ((string)DoomInfo.Strings.GAMMALVL0));
					game.World.ConsolePlayer.SendMessage(message);
				}
				return true;
			}
			case DoomKey.Quote:
			case DoomKey.Equal:
			case DoomKey.Add:
				if (currentState == DoomState.Game && game.State == GameState.Level && game.World.AutoMap.Visible)
				{
					return false;
				}
				video.WindowSize = Math.Min(video.WindowSize + 1, video.MaxWindowSize);
				sound.StartSound(Sfx.STNMOV);
				return true;
			case DoomKey.Semicolon:
			case DoomKey.Hyphen:
			case DoomKey.Subtract:
				if (currentState == DoomState.Game && game.State == GameState.Level && game.World.AutoMap.Visible)
				{
					return false;
				}
				video.WindowSize = Math.Max(video.WindowSize - 1, 0);
				sound.StartSound(Sfx.STNMOV);
				return true;
			default:
				return false;
			}
		}

		public UpdateResult Update()
		{
			DoEvents();
			if (!wiping)
			{
				menu.Update();
				if (nextState != currentState)
				{
					if (nextState != DoomState.Opening)
					{
						opening.Reset();
					}
					if (nextState != DoomState.DemoPlayback)
					{
						demoPlayback = null;
					}
					currentState = nextState;
				}
				if (quit)
				{
					return UpdateResult.Completed;
				}
				if (needWipe)
				{
					needWipe = false;
					StartWipe();
				}
			}
			if (!wiping)
			{
				switch (currentState)
				{
				case DoomState.Opening:
					if (opening.Update() == UpdateResult.NeedWipe)
					{
						StartWipe();
					}
					break;
				case DoomState.DemoPlayback:
					switch (demoPlayback.Update())
					{
					case UpdateResult.NeedWipe:
						StartWipe();
						break;
					case UpdateResult.Completed:
						Quit("FPS: " + demoPlayback.Fps.ToString("0.0"));
						break;
					}
					break;
				case DoomState.Game:
					userInput.BuildTicCmd(cmds[options.ConsolePlayer]);
					if (sendPause)
					{
						sendPause = false;
						cmds[options.ConsolePlayer].Buttons |= (byte)(TicCmdButtons.Special | TicCmdButtons.Pause);
					}
					if (game.Update(cmds) == UpdateResult.NeedWipe)
					{
						StartWipe();
					}
					break;
				default:
					throw new Exception("Invalid application state!");
				}
			}
			if (wiping && wipeEffect.Update() == UpdateResult.Completed)
			{
				wiping = false;
			}
			sound.Update();
			CheckMouseState();
			return UpdateResult.None;
		}

		private void CheckMouseState()
		{
			bool flag = video.HasFocus() && (config.video_fullscreen || (currentState == DoomState.Game && !menu.Active));
			if (mouseGrabbed)
			{
				if (!flag)
				{
					userInput.ReleaseMouse();
					mouseGrabbed = false;
				}
			}
			else if (flag)
			{
				userInput.GrabMouse();
				mouseGrabbed = true;
			}
		}

		private void StartWipe()
		{
			wipeEffect.Start();
			video.InitializeWipe();
			wiping = true;
		}

		public void PauseGame()
		{
			if (currentState == DoomState.Game && game.State == GameState.Level && !game.Paused && !sendPause)
			{
				sendPause = true;
			}
		}

		public void ResumeGame()
		{
			if (currentState == DoomState.Game && game.State == GameState.Level && game.Paused && !sendPause)
			{
				sendPause = true;
			}
		}

		public bool SaveGame(int slotNumber, string description)
		{
			if (currentState == DoomState.Game && game.State == GameState.Level)
			{
				game.SaveGame(slotNumber, description);
				return true;
			}
			return false;
		}

		public void LoadGame(int slotNumber)
		{
			game.LoadGame(slotNumber);
			nextState = DoomState.Game;
		}

		public void Quit()
		{
			quit = true;
		}

		public void Quit(string message)
		{
			quit = true;
			quitMessage = message;
		}

		public void PostEvent(DoomEvent e)
		{
			if (events.Count < 64)
			{
				events.Add(e);
			}
		}
	}
	public enum DoomState
	{
		None,
		Opening,
		DemoPlayback,
		Game
	}
	public sealed class DoomEvent
	{
		private EventType type;

		private DoomKey key;

		public EventType Type => type;

		public DoomKey Key => key;

		public DoomEvent(EventType type, DoomKey key)
		{
			this.type = type;
			this.key = key;
		}
	}
	public enum EventType
	{
		KeyDown,
		KeyUp,
		Mouse,
		Joystick
	}
	public sealed class Demo
	{
		private int p;

		private byte[] data;

		private GameOptions options;

		private int playerCount;

		public GameOptions Options => options;

		public Demo(byte[] data)
		{
			p = 0;
			if (data[p++] != 109)
			{
				throw new Exception("Demo is from a different game version!");
			}
			this.data = data;
			options = new GameOptions();
			options.Skill = (GameSkill)data[p++];
			options.Episode = data[p++];
			options.Map = data[p++];
			options.Deathmatch = data[p++];
			options.RespawnMonsters = data[p++] != 0;
			options.FastMonsters = data[p++] != 0;
			options.NoMonsters = data[p++] != 0;
			options.ConsolePlayer = data[p++];
			options.Players[0].InGame = data[p++] != 0;
			options.Players[1].InGame = data[p++] != 0;
			options.Players[2].InGame = data[p++] != 0;
			options.Players[3].InGame = data[p++] != 0;
			options.DemoPlayback = true;
			playerCount = 0;
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				if (options.Players[i].InGame)
				{
					playerCount++;
				}
			}
			if (playerCount >= 2)
			{
				options.NetGame = true;
			}
		}

		public Demo(string fileName)
			: this(File.ReadAllBytes(fileName))
		{
		}

		public bool ReadCmd(TicCmd[] cmds)
		{
			if (p == data.Length)
			{
				return false;
			}
			if (data[p] == 128)
			{
				return false;
			}
			if (p + 4 * playerCount > data.Length)
			{
				return false;
			}
			Player[] players = options.Players;
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				if (players[i].InGame)
				{
					TicCmd obj = cmds[i];
					obj.ForwardMove = (sbyte)data[p++];
					obj.SideMove = (sbyte)data[p++];
					obj.AngleTurn = (short)(data[p++] << 8);
					obj.Buttons = data[p++];
				}
			}
			return true;
		}
	}
	public sealed class DoomGame
	{
		private enum GameAction
		{
			Nothing,
			LoadLevel,
			NewGame,
			LoadGame,
			SaveGame,
			Completed,
			Victory,
			WorldDone
		}

		private GameContent content;

		private GameOptions options;

		private GameAction gameAction;

		private GameState gameState;

		private int gameTic;

		private World world;

		private Intermission intermission;

		private Finale finale;

		private bool paused;

		private int loadGameSlotNumber;

		private int saveGameSlotNumber;

		private string saveGameDescription;

		public GameOptions Options => options;

		public GameState State => gameState;

		public int GameTic => gameTic;

		public World World => world;

		public Intermission Intermission => intermission;

		public Finale Finale => finale;

		public bool Paused => paused;

		public DoomGame(GameContent content, GameOptions options)
		{
			this.content = content;
			this.options = options;
			gameAction = GameAction.Nothing;
			gameTic = 0;
		}

		public void DeferedInitNew()
		{
			gameAction = GameAction.NewGame;
		}

		public void DeferedInitNew(GameSkill skill, int episode, int map)
		{
			options.Skill = skill;
			options.Episode = episode;
			options.Map = map;
			gameAction = GameAction.NewGame;
		}

		public void LoadGame(int slotNumber)
		{
			loadGameSlotNumber = slotNumber;
			gameAction = GameAction.LoadGame;
		}

		public void SaveGame(int slotNumber, string description)
		{
			saveGameSlotNumber = slotNumber;
			saveGameDescription = description;
			gameAction = GameAction.SaveGame;
		}

		public UpdateResult Update(TicCmd[] cmds)
		{
			Player[] players = options.Players;
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				if (players[i].InGame && players[i].PlayerState == PlayerState.Reborn)
				{
					DoReborn(i);
				}
			}
			while (gameAction != 0)
			{
				switch (gameAction)
				{
				case GameAction.LoadLevel:
					DoLoadLevel();
					break;
				case GameAction.NewGame:
					DoNewGame();
					break;
				case GameAction.LoadGame:
					DoLoadGame();
					break;
				case GameAction.SaveGame:
					DoSaveGame();
					break;
				case GameAction.Completed:
					DoCompleted();
					break;
				case GameAction.Victory:
					DoFinale();
					break;
				case GameAction.WorldDone:
					DoWorldDone();
					break;
				}
			}
			for (int j = 0; j < Player.MaxPlayerCount; j++)
			{
				if (players[j].InGame)
				{
					TicCmd cmd = players[j].Cmd;
					cmd.CopyFrom(cmds[j]);
					if (cmd.ForwardMove > GameConst.TurboThreshold && (world.LevelTime & 0x1F) == 0 && ((world.LevelTime >> 5) & 3) == j)
					{
						players[options.ConsolePlayer].SendMessage(players[j].Name + " is turbo!");
					}
				}
			}
			for (int k = 0; k < Player.MaxPlayerCount; k++)
			{
				if (players[k].InGame && (players[k].Cmd.Buttons & TicCmdButtons.Special) != 0 && (players[k].Cmd.Buttons & TicCmdButtons.SpecialMask) == TicCmdButtons.Pause)
				{
					paused = !paused;
					if (paused)
					{
						options.Sound.Pause();
					}
					else
					{
						options.Sound.Resume();
					}
				}
			}
			UpdateResult updateResult = UpdateResult.None;
			switch (gameState)
			{
			case GameState.Level:
				if (!paused || world.FirstTicIsNotYetDone)
				{
					updateResult = world.Update();
					if (updateResult == UpdateResult.Completed)
					{
						gameAction = GameAction.Completed;
					}
				}
				break;
			case GameState.Intermission:
				updateResult = intermission.Update();
				if (updateResult != UpdateResult.Completed)
				{
					break;
				}
				gameAction = GameAction.WorldDone;
				if (world.SecretExit)
				{
					players[options.ConsolePlayer].DidSecret = true;
				}
				if (options.GameMode != GameMode.Commercial)
				{
					break;
				}
				switch (options.Map)
				{
				case 6:
				case 11:
				case 20:
				case 30:
					DoFinale();
					updateResult = UpdateResult.NeedWipe;
					break;
				case 15:
				case 31:
					if (world.SecretExit)
					{
						DoFinale();
						updateResult = UpdateResult.NeedWipe;
					}
					break;
				}
				break;
			case GameState.Finale:
				updateResult = finale.Update();
				if (updateResult == UpdateResult.Completed)
				{
					gameAction = GameAction.WorldDone;
				}
				break;
			}
			gameTic++;
			if (updateResult == UpdateResult.NeedWipe)
			{
				return UpdateResult.NeedWipe;
			}
			return UpdateResult.None;
		}

		private void DoLoadLevel()
		{
			gameAction = GameAction.Nothing;
			gameState = GameState.Level;
			Player[] players = options.Players;
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				if (players[i].InGame && players[i].PlayerState == PlayerState.Dead)
				{
					players[i].PlayerState = PlayerState.Reborn;
				}
				Array.Clear(players[i].Frags, 0, players[i].Frags.Length);
			}
			intermission = null;
			options.Sound.Reset();
			world = new World(content, options, this);
			options.UserInput.Reset();
		}

		private void DoNewGame()
		{
			gameAction = GameAction.Nothing;
			InitNew(options.Skill, options.Episode, options.Map);
		}

		private void DoLoadGame()
		{
			gameAction = GameAction.Nothing;
			string path = Path.Combine(ConfigUtilities.GetExeDirectory(), "doomsav" + loadGameSlotNumber + ".dsg");
			SaveAndLoad.Load(this, path);
		}

		private void DoSaveGame()
		{
			gameAction = GameAction.Nothing;
			string path = Path.Combine(ConfigUtilities.GetExeDirectory(), "doomsav" + saveGameSlotNumber + ".dsg");
			SaveAndLoad.Save(this, saveGameDescription, path);
			world.ConsolePlayer.SendMessage(DoomInfo.Strings.GGSAVED);
		}

		private void DoCompleted()
		{
			gameAction = GameAction.Nothing;
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				if (options.Players[i].InGame)
				{
					options.Players[i].FinishLevel();
				}
			}
			if (options.GameMode != GameMode.Commercial)
			{
				switch (options.Map)
				{
				case 8:
					gameAction = GameAction.Victory;
					return;
				case 9:
				{
					for (int j = 0; j < Player.MaxPlayerCount; j++)
					{
						options.Players[j].DidSecret = true;
					}
					break;
				}
				}
			}
			if (options.Map == 8 && options.GameMode != GameMode.Commercial)
			{
				gameAction = GameAction.Victory;
				return;
			}
			if (options.Map == 9 && options.GameMode != GameMode.Commercial)
			{
				for (int k = 0; k < Player.MaxPlayerCount; k++)
				{
					options.Players[k].DidSecret = true;
				}
			}
			IntermissionInfo intermissionInfo = options.IntermissionInfo;
			intermissionInfo.DidSecret = options.Players[options.ConsolePlayer].DidSecret;
			intermissionInfo.Episode = options.Episode - 1;
			intermissionInfo.LastLevel = options.Map - 1;
			if (options.GameMode == GameMode.Commercial)
			{
				if (world.SecretExit)
				{
					switch (options.Map)
					{
					case 15:
						intermissionInfo.NextLevel = 30;
						break;
					case 31:
						intermissionInfo.NextLevel = 31;
						break;
					}
				}
				else
				{
					int map = options.Map;
					if ((uint)(map - 31) <= 1u)
					{
						intermissionInfo.NextLevel = 15;
					}
					else
					{
						intermissionInfo.NextLevel = options.Map;
					}
				}
			}
			else if (world.SecretExit)
			{
				intermissionInfo.NextLevel = 8;
			}
			else if (options.Map == 9)
			{
				switch (options.Episode)
				{
				case 1:
					intermissionInfo.NextLevel = 3;
					break;
				case 2:
					intermissionInfo.NextLevel = 5;
					break;
				case 3:
					intermissionInfo.NextLevel = 6;
					break;
				case 4:
					intermissionInfo.NextLevel = 2;
					break;
				}
			}
			else
			{
				intermissionInfo.NextLevel = options.Map;
			}
			intermissionInfo.MaxKillCount = world.TotalKills;
			intermissionInfo.MaxItemCount = world.TotalItems;
			intermissionInfo.MaxSecretCount = world.TotalSecrets;
			intermissionInfo.TotalFrags = 0;
			if (options.GameMode == GameMode.Commercial)
			{
				intermissionInfo.ParTime = 35 * DoomInfo.ParTimes.Doom2[options.Map - 1];
			}
			else
			{
				intermissionInfo.ParTime = 35 * DoomInfo.ParTimes.Doom1[options.Episode - 1][options.Map - 1];
			}
			Player[] players = options.Players;
			for (int l = 0; l < Player.MaxPlayerCount; l++)
			{
				intermissionInfo.Players[l].InGame = players[l].InGame;
				intermissionInfo.Players[l].KillCount = players[l].KillCount;
				intermissionInfo.Players[l].ItemCount = players[l].ItemCount;
				intermissionInfo.Players[l].SecretCount = players[l].SecretCount;
				intermissionInfo.Players[l].Time = world.LevelTime;
				Array.Copy(players[l].Frags, intermissionInfo.Players[l].Frags, Player.MaxPlayerCount);
			}
			gameState = GameState.Intermission;
			intermission = new Intermission(options, intermissionInfo);
		}

		private void DoWorldDone()
		{
			gameAction = GameAction.Nothing;
			gameState = GameState.Level;
			options.Map = options.IntermissionInfo.NextLevel + 1;
			DoLoadLevel();
		}

		private void DoFinale()
		{
			gameAction = GameAction.Nothing;
			gameState = GameState.Finale;
			finale = new Finale(options);
		}

		public void InitNew(GameSkill skill, int episode, int map)
		{
			options.Skill = (GameSkill)Math.Clamp((int)skill, 0, 4);
			if (options.GameMode == GameMode.Retail)
			{
				options.Episode = Math.Clamp(episode, 1, 4);
			}
			else if (options.GameMode == GameMode.Shareware)
			{
				options.Episode = 1;
			}
			else
			{
				options.Episode = Math.Clamp(episode, 1, 4);
			}
			if (options.GameMode == GameMode.Commercial)
			{
				options.Map = Math.Clamp(map, 1, 32);
			}
			else
			{
				options.Map = Math.Clamp(map, 1, 9);
			}
			options.Random.Clear();
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				options.Players[i].PlayerState = PlayerState.Reborn;
			}
			DoLoadLevel();
		}

		public bool DoEvent(DoomEvent e)
		{
			if (gameState == GameState.Level)
			{
				return world.DoEvent(e);
			}
			if (gameState == GameState.Finale)
			{
				return finale.DoEvent(e);
			}
			return false;
		}

		private void DoReborn(int playerNumber)
		{
			if (!options.NetGame)
			{
				gameAction = GameAction.LoadLevel;
				return;
			}
			options.Players[playerNumber].Mobj.Player = null;
			ThingAllocation thingAllocation = world.ThingAllocation;
			if (options.Deathmatch != 0)
			{
				thingAllocation.DeathMatchSpawnPlayer(playerNumber);
				return;
			}
			if (thingAllocation.CheckSpot(playerNumber, thingAllocation.PlayerStarts[playerNumber]))
			{
				thingAllocation.SpawnPlayer(thingAllocation.PlayerStarts[playerNumber]);
				return;
			}
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				if (thingAllocation.CheckSpot(playerNumber, thingAllocation.PlayerStarts[i]))
				{
					thingAllocation.PlayerStarts[i].Type = playerNumber + 1;
					world.ThingAllocation.SpawnPlayer(thingAllocation.PlayerStarts[i]);
					thingAllocation.PlayerStarts[i].Type = i + 1;
					return;
				}
			}
			world.ThingAllocation.SpawnPlayer(thingAllocation.PlayerStarts[playerNumber]);
		}
	}
	public static class GameConst
	{
		public static readonly int TicRate = 35;

		public static readonly Fixed MaxThingRadius = Fixed.FromInt(32);

		public static readonly int TurboThreshold = 50;
	}
	public sealed class GameContent : IDisposable
	{
		private Wad wad;

		private Palette palette;

		private ColorMap colorMap;

		private ITextureLookup textures;

		private IFlatLookup flats;

		private ISpriteLookup sprites;

		private TextureAnimation animation;

		public Wad Wad => wad;

		public Palette Palette => palette;

		public ColorMap ColorMap => colorMap;

		public ITextureLookup Textures => textures;

		public IFlatLookup Flats => flats;

		public ISpriteLookup Sprites => sprites;

		public TextureAnimation Animation => animation;

		private GameContent()
		{
		}

		public GameContent(CommandLineArgs args)
		{
			wad = new Wad(ConfigUtilities.GetWadPaths(args));
			DeHackEd.Initialize(args, wad);
			palette = new Palette(wad);
			colorMap = new ColorMap(wad);
			textures = new TextureLookup(wad);
			flats = new FlatLookup(wad);
			sprites = new SpriteLookup(wad);
			animation = new TextureAnimation(textures, flats);
		}

		public static GameContent CreateDummy(params string[] wadPaths)
		{
			GameContent gameContent = new GameContent();
			gameContent.wad = new Wad(wadPaths);
			gameContent.palette = new Palette(gameContent.wad);
			gameContent.colorMap = new ColorMap(gameContent.wad);
			gameContent.textures = new DummyTextureLookup(gameContent.wad);
			gameContent.flats = new DummyFlatLookup(gameContent.wad);
			gameContent.sprites = new DummySpriteLookup(gameContent.wad);
			gameContent.animation = new TextureAnimation(gameContent.textures, gameContent.flats);
			return gameContent;
		}

		public void Dispose()
		{
			if (wad != null)
			{
				wad.Dispose();
				wad = null;
			}
		}
	}
	public enum GameMode
	{
		Shareware,
		Registered,
		Commercial,
		Retail,
		Indetermined
	}
	public sealed class GameOptions
	{
		private GameVersion gameVersion;

		private GameMode gameMode;

		private MissionPack missionPack;

		private Player[] players;

		private int consolePlayer;

		private int episode;

		private int map;

		private GameSkill skill;

		private bool demoPlayback;

		private bool netGame;

		private int deathmatch;

		private bool fastMonsters;

		private bool respawnMonsters;

		private bool noMonsters;

		private IntermissionInfo intermissionInfo;

		private DoomRandom random;

		private IVideo video;

		private ISound sound;

		private IMusic music;

		private IUserInput userInput;

		public GameVersion GameVersion
		{
			get
			{
				return gameVersion;
			}
			set
			{
				gameVersion = value;
			}
		}

		public GameMode GameMode
		{
			get
			{
				return gameMode;
			}
			set
			{
				gameMode = value;
			}
		}

		public MissionPack MissionPack
		{
			get
			{
				return missionPack;
			}
			set
			{
				missionPack = value;
			}
		}

		public Player[] Players => players;

		public int ConsolePlayer
		{
			get
			{
				return consolePlayer;
			}
			set
			{
				consolePlayer = value;
			}
		}

		public int Episode
		{
			get
			{
				return episode;
			}
			set
			{
				episode = value;
			}
		}

		public int Map
		{
			get
			{
				return map;
			}
			set
			{
				map = value;
			}
		}

		public GameSkill Skill
		{
			get
			{
				return skill;
			}
			set
			{
				skill = value;
			}
		}

		public bool DemoPlayback
		{
			get
			{
				return demoPlayback;
			}
			set
			{
				demoPlayback = value;
			}
		}

		public bool NetGame
		{
			get
			{
				return netGame;
			}
			set
			{
				netGame = value;
			}
		}

		public int Deathmatch
		{
			get
			{
				return deathmatch;
			}
			set
			{
				deathmatch = value;
			}
		}

		public bool FastMonsters
		{
			get
			{
				return fastMonsters;
			}
			set
			{
				fastMonsters = value;
			}
		}

		public bool RespawnMonsters
		{
			get
			{
				return respawnMonsters;
			}
			set
			{
				respawnMonsters = value;
			}
		}

		public bool NoMonsters
		{
			get
			{
				return noMonsters;
			}
			set
			{
				noMonsters = value;
			}
		}

		public IntermissionInfo IntermissionInfo => intermissionInfo;

		public DoomRandom Random => random;

		public IVideo Video
		{
			get
			{
				return video;
			}
			set
			{
				video = value;
			}
		}

		public ISound Sound
		{
			get
			{
				return sound;
			}
			set
			{
				sound = value;
			}
		}

		public IMusic Music
		{
			get
			{
				return music;
			}
			set
			{
				music = value;
			}
		}

		public IUserInput UserInput
		{
			get
			{
				return userInput;
			}
			set
			{
				userInput = value;
			}
		}

		public GameOptions()
		{
			gameVersion = GameVersion.Version109;
			gameMode = GameMode.Commercial;
			missionPack = MissionPack.Doom2;
			players = new Player[Player.MaxPlayerCount];
			for (int i = 0; i < Player.MaxPlayerCount; i++)
			{
				players[i] = new Player(i);
			}
			players[0].InGame = true;
			consolePlayer = 0;
			episode = 1;
			map = 1;
			skill = GameSkill.Medium;
			demoPlayback = false;
			netGame = false;
			deathmatch = 0;
			fastMonsters = false;
			respawnMonsters = false;
			noMonsters = false;
			intermissionInfo = new IntermissionInfo();
			random = new DoomRandom();
			video = NullVideo.GetInstance();
			sound = NullSound.GetInstance();
			music = NullMusic.GetInstance();
			userInput = NullUserInput.GetInstance();
		}

		public GameOptions(CommandLineArgs args, GameContent content)
			: this()
		{
			if (args.solonet.Present)
			{
				netGame = true;
			}
			gameVersion = content.Wad.GameVersion;
			gameMode = content.Wad.GameMode;
			missionPack = content.Wad.MissionPack;
		}
	}
	public enum GameSkill
	{
		Baby,
		Easy,
		Medium,
		Hard,
		Nightmare
	}
	public enum GameState
	{
		Level,
		Intermission,
		Finale
	}
	public enum GameVersion
	{
		Version109,
		Ultimate,
		Final,
		Final2
	}
	public enum MissionPack
	{
		Doom2,
		Plutonia,
		Tnt
	}
	public sealed class Player
	{
		public static readonly int MaxPlayerCount = 4;

		public static readonly Fixed NormalViewHeight = Fixed.FromInt(41);

		private static readonly string[] defaultPlayerNames = new string[4] { "Green", "Indigo", "Brown", "Red" };

		private int number;

		private string name;

		private bool inGame;

		private Mobj mobj;

		private PlayerState playerState;

		private TicCmd cmd;

		private Fixed viewZ;

		private Fixed viewHeight;

		private Fixed deltaViewHeight;

		private Fixed bob;

		private int health;

		private int armorPoints;

		private int armorType;

		private int[] powers;

		private bool[] cards;

		private bool backpack;

		private int[] frags;

		private WeaponType readyWeapon;

		private WeaponType pendingWeapon;

		private bool[] weaponOwned;

		private int[] ammo;

		private int[] maxAmmo;

		private bool attackDown;

		private bool useDown;

		private CheatFlags cheats;

		private int refire;

		private int killCount;

		private int itemCount;

		private int secretCount;

		private string message;

		private int messageTime;

		private int damageCount;

		private int bonusCount;

		private Mobj attacker;

		private int extraLight;

		private int fixedColorMap;

		private int colorMap;

		private PlayerSpriteDef[] playerSprites;

		private bool didSecret;

		private bool interpolate;

		private Fixed oldViewZ;

		private Angle oldAngle;

		public int Number => number;

		public string Name => name;

		public bool InGame
		{
			get
			{
				return inGame;
			}
			set
			{
				inGame = value;
			}
		}

		public Mobj Mobj
		{
			get
			{
				return mobj;
			}
			set
			{
				mobj = value;
			}
		}

		public PlayerState PlayerState
		{
			get
			{
				return playerState;
			}
			set
			{
				playerState = value;
			}
		}

		public TicCmd Cmd => cmd;

		public Fixed ViewZ
		{
			get
			{
				return viewZ;
			}
			set
			{
				viewZ = value;
			}
		}

		public Fixed ViewHeight
		{
			get
			{
				return viewHeight;
			}
			set
			{
				viewHeight = value;
			}
		}

		public Fixed DeltaViewHeight
		{
			get
			{
				return deltaViewHeight;
			}
			set
			{
				deltaViewHeight = value;
			}
		}

		public Fixed Bob
		{
			get
			{
				return bob;
			}
			set
			{
				bob = value;
			}
		}

		public int Health
		{
			get
			{
				return health;
			}
			set
			{
				health = value;
			}
		}

		public int ArmorPoints
		{
			get
			{
				return armorPoints;
			}
			set
			{
				armorPoints = value;
			}
		}

		public int ArmorType
		{
			get
			{
				return armorType;
			}
			set
			{
				armorType = value;
			}
		}

		public int[] Powers => powers;

		public bool[] Cards => cards;

		public bool Backpack
		{
			get
			{
				return backpack;
			}
			set
			{
				backpack = value;
			}
		}

		public int[] Frags => frags;

		public WeaponType ReadyWeapon
		{
			get
			{
				return readyWeapon;
			}
			set
			{
				readyWeapon = value;
			}
		}

		public WeaponType PendingWeapon
		{
			get
			{
				return pendingWeapon;
			}
			set
			{
				pendingWeapon = value;
			}
		}

		public bool[] WeaponOwned => weaponOwned;

		public int[] Ammo => ammo;

		public int[] MaxAmmo => maxAmmo;

		public bool AttackDown
		{
			get
			{
				return attackDown;
			}
			set
			{
				attackDown = value;
			}
		}

		public bool UseDown
		{
			get
			{
				return useDown;
			}
			set
			{
				useDown = value;
			}
		}

		public CheatFlags Cheats
		{
			get
			{
				return cheats;
			}
			set
			{
				cheats = value;
			}
		}

		public int Refire
		{
			get
			{
				return refire;
			}
			set
			{
				refire = value;
			}
		}

		public int KillCount
		{
			get
			{
				return killCount;
			}
			set
			{
				killCount = value;
			}
		}

		public int ItemCount
		{
			get
			{
				return itemCount;
			}
			set
			{
				itemCount = value;
			}
		}

		public int SecretCount
		{
			get
			{
				return secretCount;
			}
			set
			{
				secretCount = value;
			}
		}

		public string Message
		{
			get
			{
				return message;
			}
			set
			{
				message = value;
			}
		}

		public int MessageTime
		{
			get
			{
				return messageTime;
			}
			set
			{
				messageTime = value;
			}
		}

		public int DamageCount
		{
			get
			{
				return damageCount;
			}
			set
			{
				damageCount = value;
			}
		}

		public int BonusCount
		{
			get
			{
				return bonusCount;
			}
			set
			{
				bonusCount = value;
			}
		}

		public Mobj Attacker
		{
			get
			{
				return attacker;
			}
			set
			{
				attacker = value;
			}
		}

		public int ExtraLight
		{
			get
			{
				return extraLight;
			}
			set
			{
				extraLight = value;
			}
		}

		public int FixedColorMap
		{
			get
			{
				return fixedColorMap;
			}
			set
			{
				fixedColorMap = value;
			}
		}

		public int ColorMap
		{
			get
			{
				return colorMap;
			}
			set
			{
				colorMap = value;
			}
		}

		public PlayerSpriteDef[] PlayerSprites => playerSprites;

		public bool DidSecret
		{
			get
			{
				return didSecret;
			}
			set
			{
				didSecret = value;
			}
		}

		public Player(int number)
		{
			this.number = number;
			name = defaultPlayerNames[number];
			cmd = new TicCmd();
			powers = new int[6];
			cards = new bool[6];
			frags = new int[MaxPlayerCount];
			weaponOwned = new bool[9];
			ammo = new int[4];
			maxAmmo = new int[4];
			playerSprites = new PlayerSpriteDef[2];
			for (int i = 0; i < playerSprites.Length; i++)
			{
				playerSprites[i] = new PlayerSpriteDef();
			}
		}

		public void Clear()
		{
			mobj = null;
			playerState = PlayerState.Live;
			cmd.Clear();
			viewZ = Fixed.Zero;
			viewHeight = Fixed.Zero;
			deltaViewHeight = Fixed.Zero;
			bob = Fixed.Zero;
			health = 0;
			armorPoints = 0;
			armorType = 0;
			Array.Clear(powers, 0, powers.Length);
			Array.Clear(cards, 0, cards.Length);
			backpack = false;
			Array.Clear(frags, 0, frags.Length);
			readyWeapon = WeaponType.Fist;
			pendingWeapon = WeaponType.Fist;
			Array.Clear(weaponOwned, 0, weaponOwned.Length);
			Array.Clear(ammo, 0, ammo.Length);
			Array.Clear(maxAmmo, 0, maxAmmo.Length);
			useDown = false;
			attackDown = false;
			cheats = (CheatFlags)0;
			refire = 0;
			killCount = 0;
			itemCount = 0;
			secretCount = 0;
			message = null;
			messageTime = 0;
			damageCount = 0;
			bonusCount = 0;
			attacker = null;
			extraLight = 0;
			fixedColorMap = 0;
			colorMap = 0;
			PlayerSpriteDef[] array = playerSprites;
			for (int i = 0; i < array.Length; i++)
			{
				array[i].Clear();
			}
			didSecret = false;
			interpolate = false;
			oldViewZ = Fixed.Zero;
			oldAngle = Angle.Ang0;
		}

		public void Reborn()
		{
			mobj = null;
			playerState = PlayerState.Live;
			cmd.Clear();
			viewZ = Fixed.Zero;
			viewHeight = Fixed.Zero;
			deltaViewHeight = Fixed.Zero;
			bob = Fixed.Zero;
			health = DoomInfo.DeHackEdConst.InitialHealth;
			armorPoints = 0;
			armorType = 0;
			Array.Clear(powers, 0, powers.Length);
			Array.Clear(cards, 0, cards.Length);
			backpack = false;
			readyWeapon = WeaponType.Pistol;
			pendingWeapon = WeaponType.Pistol;
			Array.Clear(weaponOwned, 0, weaponOwned.Length);
			Array.Clear(ammo, 0, ammo.Length);
			Array.Clear(maxAmmo, 0, maxAmmo.Length);
			weaponOwned[0] = true;
			weaponOwned[1] = true;
			ammo[0] = DoomInfo.DeHackEdConst.InitialBullets;
			for (int i = 0; i < 4; i++)
			{
				maxAmmo[i] = DoomInfo.AmmoInfos.Max[i];
			}
			useDown = true;
			attackDown = true;
			cheats = (CheatFlags)0;
			refire = 0;
			message = null;
			messageTime = 0;
			damageCount = 0;
			bonusCount = 0;
			attacker = null;
			extraLight = 0;
			fixedColorMap = 0;
			colorMap = 0;
			PlayerSpriteDef[] array = playerSprites;
			for (int j = 0; j < array.Length; j++)
			{
				array[j].Clear();
			}
			didSecret = false;
			interpolate = false;
			oldViewZ = Fixed.Zero;
			oldAngle = Angle.Ang0;
		}

		public void FinishLevel()
		{
			Array.Clear(powers, 0, powers.Length);
			Array.Clear(cards, 0, cards.Length);
			mobj.Flags &= ~MobjFlags.Shadow;
			extraLight = 0;
			fixedColorMap = 0;
			damageCount = 0;
			bonusCount = 0;
		}

		public void SendMessage(string message)
		{
			if ((object)this.message != (string)DoomInfo.Strings.MSGOFF || (object)message == (string)DoomInfo.Strings.MSGON)
			{
				this.message = message;
				messageTime = 4 * GameConst.TicRate;
			}
		}

		public void UpdateFrameInterpolationInfo()
		{
			interpolate = true;
			oldViewZ = viewZ;
			oldAngle = mobj.Angle;
		}

		public void DisableFrameInterpolationForOneFrame()
		{
			interpolate = false;
		}

		public Fixed GetInterpolatedViewZ(Fixed frameFrac)
		{
			if (interpolate && mobj.World.LevelTime > 1)
			{
				return oldViewZ + frameFrac * (viewZ - oldViewZ);
			}
			return viewZ;
		}

		public Angle GetInterpolatedAngle(Fixed frameFrac)
		{
			if (interpolate)
			{
				Angle angle = mobj.Angle - oldAngle;
				if (angle < Angle.Ang180)
				{
					return oldAngle + Angle.FromDegree(frameFrac.ToDouble() * angle.ToDegree());
				}
				return oldAngle - Angle.FromDegree(frameFrac.ToDouble() * (360.0 - angle.ToDegree()));
			}
			return mobj.Angle;
		}
	}
	public enum PlayerState
	{
		Live,
		Dead,
		Reborn
	}
	public static class SaveAndLoad
	{
		private enum ThinkerClass
		{
			End,
			Mobj
		}

		private enum SpecialClass
		{
			Ceiling,
			Door,
			Floor,
			Plat,
			Flash,
			Strobe,
			Glow,
			EndSpecials
		}

		private class SaveGame
		{
			private byte[] data;

			private int ptr;

			public SaveGame(string description)
			{
				data = new byte[saveBufferSize];
				ptr = 0;
				WriteDescription(description);
				WriteVersion();
			}

			private void WriteDescription(string description)
			{
				for (int i = 0; i < description.Length; i++)
				{
					data[i] = (byte)description[i];
				}
				ptr += DescriptionSize;
			}

			private void WriteVersion()
			{
				string text = "version 109";
				for (int i = 0; i < text.Length; i++)
				{
					data[ptr + i] = (byte)text[i];
				}
				ptr += versionSize;
			}

			public void Save(DoomGame game, string path)
			{
				GameOptions options = game.World.Options;
				data[ptr++] = (byte)options.Skill;
				data[ptr++] = (byte)options.Episode;
				data[ptr++] = (byte)options.Map;
				for (int i = 0; i < Player.MaxPlayerCount; i++)
				{
					data[ptr++] = (options.Players[i].InGame ? ((byte)1) : ((byte)0));
				}
				data[ptr++] = (byte)(game.World.LevelTime >> 16);
				data[ptr++] = (byte)(game.World.LevelTime >> 8);
				data[ptr++] = (byte)game.World.LevelTime;
				ArchivePlayers(game.World);
				ArchiveWorld(game.World);
				ArchiveThinkers(game.World);
				ArchiveSpecials(game.World);
				data[ptr++] = 29;
				using FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
				fileStream.Write(data, 0, ptr);
			}

			private void PadPointer()
			{
				ptr += (4 - (ptr & 3)) & 3;
			}

			private void ArchivePlayers(World world)
			{
				Player[] players = world.Options.Players;
				for (int i = 0; i < Player.MaxPlayerCount; i++)
				{
					if (players[i].InGame)
					{
						PadPointer();
						ptr = ArchivePlayer(players[i], data, ptr);
					}
				}
			}

			private void ArchiveWorld(World world)
			{
				Sector[] sectors = world.Map.Sectors;
				for (int i = 0; i < sectors.Length; i++)
				{
					ptr = ArchiveSector(sectors[i], data, ptr);
				}
				LineDef[] lines = world.Map.Lines;
				for (int j = 0; j < lines.Length; j++)
				{
					ptr = ArchiveLine(lines[j], data, ptr);
				}
			}

			private void ArchiveThinkers(World world)
			{
				foreach (Thinker thinker in world.Thinkers)
				{
					if (thinker is Mobj mobj)
					{
						data[ptr++] = 1;
						PadPointer();
						WriteThinkerState(data, ptr + 8, mobj.ThinkerState);
						Write(data, ptr + 12, mobj.X.Data);
						Write(data, ptr + 16, mobj.Y.Data);
						Write(data, ptr + 20, mobj.Z.Data);
						Write(data, ptr + 32, mobj.Angle.Data);
						Write(data, ptr + 36, (int)mobj.Sprite);
						Write(data, ptr + 40, mobj.Frame);
						Write(data, ptr + 56, mobj.FloorZ.Data);
						Write(data, ptr + 60, mobj.CeilingZ.Data);
						Write(data, ptr + 64, mobj.Radius.Data);
						Write(data, ptr + 68, mobj.Height.Data);
						Write(data, ptr + 72, mobj.MomX.Data);
						Write(data, ptr + 76, mobj.MomY.Data);
						Write(data, ptr + 80, mobj.MomZ.Data);
						Write(data, ptr + 88, (int)mobj.Type);
						Write(data, ptr + 96, mobj.Tics);
						Write(data, ptr + 100, mobj.State.Number);
						Write(data, ptr + 104, (int)mobj.Flags);
						Write(data, ptr + 108, mobj.Health);
						Write(data, ptr + 112, (int)mobj.MoveDir);
						Write(data, ptr + 116, mobj.MoveCount);
						Write(data, ptr + 124, mobj.ReactionTime);
						Write(data, ptr + 128, mobj.Threshold);
						if (mobj.Player == null)
						{
							Write(data, ptr + 132, 0);
						}
						else
						{
							Write(data, ptr + 132, mobj.Player.Number + 1);
						}
						Write(data, ptr + 136, mobj.LastLook);
						if (mobj.SpawnPoint == null)
						{
							Write(data, ptr + 140, (short)0);
							Write(data, ptr + 142, (short)0);
							Write(data, ptr + 144, (short)0);
							Write(data, ptr + 146, (short)0);
							Write(data, ptr + 148, (short)0);
						}
						else
						{
							Write(data, ptr + 140, (short)mobj.SpawnPoint.X.ToIntFloor());
							Write(data, ptr + 142, (short)mobj.SpawnPoint.Y.ToIntFloor());
							Write(data, ptr + 144, (short)Math.Round(mobj.SpawnPoint.Angle.ToDegree()));
							Write(data, ptr + 146, (short)mobj.SpawnPoint.Type);
							Write(data, ptr + 148, (short)mobj.SpawnPoint.Flags);
						}
						ptr += 154;
					}
				}
				data[ptr++] = 0;
			}

			private void ArchiveSpecials(World world)
			{
				Thinkers thinkers = world.Thinkers;
				SectorAction sectorAction = world.SectorAction;
				foreach (Thinker item in thinkers)
				{
					if (item.ThinkerState == ThinkerState.InStasis)
					{
						CeilingMove ceilingMove = item as CeilingMove;
						if (sectorAction.CheckActiveCeiling(ceilingMove))
						{
							data[ptr++] = 0;
							PadPointer();
							WriteThinkerState(data, ptr + 8, ceilingMove.ThinkerState);
							Write(data, ptr + 12, (int)ceilingMove.Type);
							Write(data, ptr + 16, ceilingMove.Sector.Number);
							Write(data, ptr + 20, ceilingMove.BottomHeight.Data);
							Write(data, ptr + 24, ceilingMove.TopHeight.Data);
							Write(data, ptr + 28, ceilingMove.Speed.Data);
							Write(data, ptr + 32, ceilingMove.Crush ? 1 : 0);
							Write(data, ptr + 36, ceilingMove.Direction);
							Write(data, ptr + 40, ceilingMove.Tag);
							Write(data, ptr + 44, ceilingMove.OldDirection);
							ptr += 48;
						}
					}
					else if (item is CeilingMove ceilingMove2)
					{
						data[ptr++] = 0;
						PadPointer();
						WriteThinkerState(data, ptr + 8, ceilingMove2.ThinkerState);
						Write(data, ptr + 12, (int)ceilingMove2.Type);
						Write(data, ptr + 16, ceilingMove2.Sector.Number);
						Write(data, ptr + 20, ceilingMove2.BottomHeight.Data);
						Write(data, ptr + 24, ceilingMove2.TopHeight.Data);
						Write(data, ptr + 28, ceilingMove2.Speed.Data);
						Write(data, ptr + 32, ceilingMove2.Crush ? 1 : 0);
						Write(data, ptr + 36, ceilingMove2.Direction);
						Write(data, ptr + 40, ceilingMove2.Tag);
						Write(data, ptr + 44, ceilingMove2.OldDirection);
						ptr += 48;
					}
					else if (item is VerticalDoor verticalDoor)
					{
						data[ptr++] = 1;
						PadPointer();
						WriteThinkerState(data, ptr + 8, verticalDoor.ThinkerState);
						Write(data, ptr + 12, (int)verticalDoor.Type);
						Write(data, ptr + 16, verticalDoor.Sector.Number);
						Write(data, ptr + 20, verticalDoor.TopHeight.Data);
						Write(data, ptr + 24, verticalDoor.Speed.Data);
						Write(data, ptr + 28, verticalDoor.Direction);
						Write(data, ptr + 32, verticalDoor.TopWait);
						Write(data, ptr + 36, verticalDoor.TopCountDown);
						ptr += 40;
					}
					else if (item is FloorMove floorMove)
					{
						data[ptr++] = 2;
						PadPointer();
						WriteThinkerState(data, ptr + 8, floorMove.ThinkerState);
						Write(data, ptr + 12, (int)floorMove.Type);
						Write(data, ptr + 16, floorMove.Crush ? 1 : 0);
						Write(data, ptr + 20, floorMove.Sector.Number);
						Write(data, ptr + 24, floorMove.Direction);
						Write(data, ptr + 28, (int)floorMove.NewSpecial);
						Write(data, ptr + 32, floorMove.Texture);
						Write(data, ptr + 36, floorMove.FloorDestHeight.Data);
						Write(data, ptr + 40, floorMove.Speed.Data);
						ptr += 44;
					}
					else if (item is Platform platform)
					{
						data[ptr++] = 3;
						PadPointer();
						WriteThinkerState(data, ptr + 8, platform.ThinkerState);
						Write(data, ptr + 12, platform.Sector.Number);
						Write(data, ptr + 16, platform.Speed.Data);
						Write(data, ptr + 20, platform.Low.Data);
						Write(data, ptr + 24, platform.High.Data);
						Write(data, ptr + 28, platform.Wait);
						Write(data, ptr + 32, platform.Count);
						Write(data, ptr + 36, (int)platform.Status);
						Write(data, ptr + 40, (int)platform.OldStatus);
						Write(data, ptr + 44, platform.Crush ? 1 : 0);
						Write(data, ptr + 48, platform.Tag);
						Write(data, ptr + 52, (int)platform.Type);
						ptr += 56;
					}
					else if (item is LightFlash lightFlash)
					{
						data[ptr++] = 4;
						PadPointer();
						WriteThinkerState(data, ptr + 8, lightFlash.ThinkerState);
						Write(data, ptr + 12, lightFlash.Sector.Number);
						Write(data, ptr + 16, lightFlash.Count);
						Write(data, ptr + 20, lightFlash.MaxLight);
						Write(data, ptr + 24, lightFlash.MinLight);
						Write(data, ptr + 28, lightFlash.MaxTime);
						Write(data, ptr + 32, lightFlash.MinTime);
						ptr += 36;
					}
					else if (item is StrobeFlash strobeFlash)
					{
						data[ptr++] = 5;
						PadPointer();
						WriteThinkerState(data, ptr + 8, strobeFlash.ThinkerState);
						Write(data, ptr + 12, strobeFlash.Sector.Number);
						Write(data, ptr + 16, strobeFlash.Count);
						Write(data, ptr + 20, strobeFlash.MinLight);
						Write(data, ptr + 24, strobeFlash.MaxLight);
						Write(data, ptr + 28, strobeFlash.DarkTime);
						Write(data, ptr + 32, strobeFlash.BrightTime);
						ptr += 36;
					}
					else if (item is GlowingLight glowingLight)
					{
						data[ptr++] = 6;
						PadPointer();
						WriteThinkerState(data, ptr + 8, glowingLight.ThinkerState);
						Write(data, ptr + 12, glowingLight.Sector.Number);
						Write(data, ptr + 16, glowingLight.MinLight);
						Write(data, ptr + 20, glowingLight.MaxLight);
						Write(data, ptr + 24, glowingLight.Direction);
						ptr += 28;
					}
				}
				data[ptr++] = 7;
			}

			private static int ArchivePlayer(Player player, byte[] data, int p)
			{
				Write(data, p + 4, (int)player.PlayerState);
				Write(data, p + 16, player.ViewZ.Data);
				Write(data, p + 20, player.ViewHeight.Data);
				Write(data, p + 24, player.DeltaViewHeight.Data);
				Write(data, p + 28, player.Bob.Data);
				Write(data, p + 32, player.Health);
				Write(data, p + 36, player.ArmorPoints);
				Write(data, p + 40, player.ArmorType);
				for (int i = 0; i < 6; i++)
				{
					Write(data, p + 44 + 4 * i, player.Powers[i]);
				}
				for (int j = 0; j < 6; j++)
				{
					Write(data, p + 68 + 4 * j, player.Cards[j] ? 1 : 0);
				}
				Write(data, p + 92, player.Backpack ? 1 : 0);
				for (int k = 0; k < Player.MaxPlayerCount; k++)
				{
					Write(data, p + 96 + 4 * k, player.Frags[k]);
				}
				Write(data, p + 112, (int)player.ReadyWeapon);
				Write(data, p + 116, (int)player.PendingWeapon);
				for (int l = 0; l < 9; l++)
				{
					Write(data, p + 120 + 4 * l, player.WeaponOwned[l] ? 1 : 0);
				}
				for (int m = 0; m < 4; m++)
				{
					Write(data, p + 156 + 4 * m, player.Ammo[m]);
				}
				for (int n = 0; n < 4; n++)
				{
					Write(data, p + 172 + 4 * n, player.MaxAmmo[n]);
				}
				Write(data, p + 188, player.AttackDown ? 1 : 0);
				Write(data, p + 192, player.UseDown ? 1 : 0);
				Write(data, p + 196, (int)player.Cheats);
				Write(data, p + 200, player.Refire);
				Write(data, p + 204, player.KillCount);
				Write(data, p + 208, player.ItemCount);
				Write(data, p + 212, player.SecretCount);
				Write(data, p + 220, player.DamageCount);
				Write(data, p + 224, player.BonusCount);
				Write(data, p + 232, player.ExtraLight);
				Write(data, p + 236, player.FixedColorMap);
				Write(data, p + 240, player.ColorMap);
				for (int num = 0; num < 2; num++)
				{
					if (player.PlayerSprites[num].State == null)
					{
						Write(data, p + 244 + 16 * num, 0);
					}
					else
					{
						Write(data, p + 244 + 16 * num, player.PlayerSprites[num].State.Number);
					}
					Write(data, p + 244 + 16 * num + 4, player.PlayerSprites[num].Tics);
					Write(data, p + 244 + 16 * num + 8, player.PlayerSprites[num].Sx.Data);
					Write(data, p + 244 + 16 * num + 12, player.PlayerSprites[num].Sy.Data);
				}
				Write(data, p + 276, player.DidSecret ? 1 : 0);
				return p + 280;
			}

			private static int ArchiveSector(Sector sector, byte[] data, int p)
			{
				Write(data, p, (short)sector.FloorHeight.ToIntFloor());
				Write(data, p + 2, (short)sector.CeilingHeight.ToIntFloor());
				Write(data, p + 4, (short)sector.FloorFlat);
				Write(data, p + 6, (short)sector.CeilingFlat);
				Write(data, p + 8, (short)sector.LightLevel);
				Write(data, p + 10, (short)sector.Special);
				Write(data, p + 12, (short)sector.Tag);
				return p + 14;
			}

			private static int ArchiveLine(LineDef line, byte[] data, int p)
			{
				Write(data, p, (short)line.Flags);
				Write(data, p + 2, (short)line.Special);
				Write(data, p + 4, line.Tag);
				p += 6;
				if (line.FrontSide != null)
				{
					SideDef frontSide = line.FrontSide;
					Write(data, p, (short)frontSide.TextureOffset.ToIntFloor());
					Write(data, p + 2, (short)frontSide.RowOffset.ToIntFloor());
					Write(data, p + 4, (short)frontSide.TopTexture);
					Write(data, p + 6, (short)frontSide.BottomTexture);
					Write(data, p + 8, (short)frontSide.MiddleTexture);
					p += 10;
				}
				if (line.BackSide != null)
				{
					SideDef backSide = line.BackSide;
					Write(data, p, (short)backSide.TextureOffset.ToIntFloor());
					Write(data, p + 2, (short)backSide.RowOffset.ToIntFloor());
					Write(data, p + 4, (short)backSide.TopTexture);
					Write(data, p + 6, (short)backSide.BottomTexture);
					Write(data, p + 8, (short)backSide.MiddleTexture);
					p += 10;
				}
				return p;
			}

			private static void Write(byte[] data, int p, int value)
			{
				data[p] = (byte)value;
				data[p + 1] = (byte)(value >> 8);
				data[p + 2] = (byte)(value >> 16);
				data[p + 3] = (byte)(value >> 24);
			}

			private static void Write(byte[] data, int p, uint value)
			{
				data[p] = (byte)value;
				data[p + 1] = (byte)(value >> 8);
				data[p + 2] = (byte)(value >> 16);
				data[p + 3] = (byte)(value >> 24);
			}

			private static void Write(byte[] data, int p, short value)
			{
				data[p] = (byte)value;
				data[p + 1] = (byte)(value >> 8);
			}

			private static void WriteThinkerState(byte[] data, int p, ThinkerState state)
			{
				if (state == ThinkerState.InStasis)
				{
					Write(data, p, 0);
				}
				else
				{
					Write(data, p, 1);
				}
			}
		}

		private class LoadGame
		{
			private byte[] data;

			private int ptr;

			public LoadGame(byte[] data)
			{
				this.data = data;
				ptr = 0;
				ReadDescription();
				if (ReadVersion() != "VERSION 109")
				{
					throw new Exception("Unsupported version!");
				}
			}

			public void Load(DoomGame game)
			{
				GameOptions options = game.World.Options;
				options.Skill = (GameSkill)data[ptr++];
				options.Episode = data[ptr++];
				options.Map = data[ptr++];
				for (int i = 0; i < Player.MaxPlayerCount; i++)
				{
					options.Players[i].InGame = data[ptr++] != 0;
				}
				game.InitNew(options.Skill, options.Episode, options.Map);
				byte num = data[ptr++];
				byte b = data[ptr++];
				byte b2 = data[ptr++];
				int levelTime = (num << 16) + (b << 8) + b2;
				UnArchivePlayers(game.World);
				UnArchiveWorld(game.World);
				UnArchiveThinkers(game.World);
				UnArchiveSpecials(game.World);
				if (data[ptr] != 29)
				{
					throw new Exception("Bad savegame!");
				}
				game.World.LevelTime = levelTime;
				options.Sound.SetListener(game.World.ConsolePlayer.Mobj);
			}

			private void PadPointer()
			{
				ptr += (4 - (ptr & 3)) & 3;
			}

			private string ReadDescription()
			{
				string result = DoomInterop.ToString(data, ptr, DescriptionSize);
				ptr += DescriptionSize;
				return result;
			}

			private string ReadVersion()
			{
				string result = DoomInterop.ToString(data, ptr, versionSize);
				ptr += versionSize;
				return result;
			}

			private void UnArchivePlayers(World world)
			{
				Player[] players = world.Options.Players;
				for (int i = 0; i < Player.MaxPlayerCount; i++)
				{
					if (players[i].InGame)
					{
						PadPointer();
						ptr = UnArchivePlayer(players[i], data, ptr);
					}
				}
			}

			private void UnArchiveWorld(World world)
			{
				Sector[] sectors = world.Map.Sectors;
				for (int i = 0; i < sectors.Length; i++)
				{
					ptr = UnArchiveSector(sectors[i], data, ptr);
				}
				LineDef[] lines = world.Map.Lines;
				for (int j = 0; j < lines.Length; j++)
				{
					ptr = UnArchiveLine(lines[j], data, ptr);
				}
			}

			private void UnArchiveThinkers(World world)
			{
				Thinkers thinkers = world.Thinkers;
				ThingAllocation thingAllocation = world.ThingAllocation;
				foreach (Thinker item in thinkers)
				{
					if (item is Mobj mobj)
					{
						thingAllocation.RemoveMobj(mobj);
					}
				}
				thinkers.Reset();
				while (true)
				{
					Mobj mobj2;
					switch ((ThinkerClass)data[ptr++])
					{
					case ThinkerClass.End:
						return;
					case ThinkerClass.Mobj:
					{
						PadPointer();
						mobj2 = new Mobj(world);
						mobj2.ThinkerState = ReadThinkerState(data, ptr + 8);
						mobj2.X = new Fixed(BitConverter.ToInt32(data, ptr + 12));
						mobj2.Y = new Fixed(BitConverter.ToInt32(data, ptr + 16));
						mobj2.Z = new Fixed(BitConverter.ToInt32(data, ptr + 20));
						mobj2.Angle = new Angle(BitConverter.ToInt32(data, ptr + 32));
						mobj2.Sprite = (Sprite)BitConverter.ToInt32(data, ptr + 36);
						mobj2.Frame = BitConverter.ToInt32(data, ptr + 40);
						mobj2.FloorZ = new Fixed(BitConverter.ToInt32(data, ptr + 56));
						mobj2.CeilingZ = new Fixed(BitConverter.ToInt32(data, ptr + 60));
						mobj2.Radius = new Fixed(BitConverter.ToInt32(data, ptr + 64));
						mobj2.Height = new Fixed(BitConverter.ToInt32(data, ptr + 68));
						mobj2.MomX = new Fixed(BitConverter.ToInt32(data, ptr + 72));
						mobj2.MomY = new Fixed(BitConverter.ToInt32(data, ptr + 76));
						mobj2.MomZ = new Fixed(BitConverter.ToInt32(data, ptr + 80));
						mobj2.Type = (MobjType)BitConverter.ToInt32(data, ptr + 88);
						mobj2.Info = DoomInfo.MobjInfos[(int)mobj2.Type];
						mobj2.Tics = BitConverter.ToInt32(data, ptr + 96);
						mobj2.State = DoomInfo.States[BitConverter.ToInt32(data, ptr + 100)];
						mobj2.Flags = (MobjFlags)BitConverter.ToInt32(data, ptr + 104);
						mobj2.Health = BitConverter.ToInt32(data, ptr + 108);
						mobj2.MoveDir = (Direction)BitConverter.ToInt32(data, ptr + 112);
						mobj2.MoveCount = BitConverter.ToInt32(data, ptr + 116);
						mobj2.ReactionTime = BitConverter.ToInt32(data, ptr + 124);
						mobj2.Threshold = BitConverter.ToInt32(data, ptr + 128);
						int num = BitConverter.ToInt32(data, ptr + 132);
						if (num != 0)
						{
							mobj2.Player = world.Options.Players[num - 1];
							mobj2.Player.Mobj = mobj2;
						}
						break;
					}
					default:
						throw new Exception("Unknown thinker class in savegame!");
					}
					mobj2.LastLook = BitConverter.ToInt32(data, ptr + 136);
					mobj2.SpawnPoint = new MapThing(Fixed.FromInt(BitConverter.ToInt16(data, ptr + 140)), Fixed.FromInt(BitConverter.ToInt16(data, ptr + 142)), new Angle(Angle.Ang45.Data * (uint)(BitConverter.ToInt16(data, ptr + 144) / 45)), BitConverter.ToInt16(data, ptr + 146), (ThingFlags)BitConverter.ToInt16(data, ptr + 148));
					ptr += 154;
					world.ThingMovement.SetThingPosition(mobj2);
					thinkers.Add(mobj2);
				}
			}

			private void UnArchiveSpecials(World world)
			{
				Thinkers thinkers = world.Thinkers;
				SectorAction sectorAction = world.SectorAction;
				while (true)
				{
					switch ((SpecialClass)data[ptr++])
					{
					case SpecialClass.EndSpecials:
						return;
					case SpecialClass.Ceiling:
					{
						PadPointer();
						CeilingMove ceilingMove = new CeilingMove(world);
						ceilingMove.ThinkerState = ReadThinkerState(data, ptr + 8);
						ceilingMove.Type = (CeilingMoveType)BitConverter.ToInt32(data, ptr + 12);
						ceilingMove.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 16)];
						ceilingMove.Sector.SpecialData = ceilingMove;
						ceilingMove.BottomHeight = new Fixed(BitConverter.ToInt32(data, ptr + 20));
						ceilingMove.TopHeight = new Fixed(BitConverter.ToInt32(data, ptr + 24));
						ceilingMove.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 28));
						ceilingMove.Crush = BitConverter.ToInt32(data, ptr + 32) != 0;
						ceilingMove.Direction = BitConverter.ToInt32(data, ptr + 36);
						ceilingMove.Tag = BitConverter.ToInt32(data, ptr + 40);
						ceilingMove.OldDirection = BitConverter.ToInt32(data, ptr + 44);
						ptr += 48;
						thinkers.Add(ceilingMove);
						sectorAction.AddActiveCeiling(ceilingMove);
						break;
					}
					case SpecialClass.Door:
					{
						PadPointer();
						VerticalDoor verticalDoor = new VerticalDoor(world);
						verticalDoor.ThinkerState = ReadThinkerState(data, ptr + 8);
						verticalDoor.Type = (VerticalDoorType)BitConverter.ToInt32(data, ptr + 12);
						verticalDoor.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 16)];
						verticalDoor.Sector.SpecialData = verticalDoor;
						verticalDoor.TopHeight = new Fixed(BitConverter.ToInt32(data, ptr + 20));
						verticalDoor.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 24));
						verticalDoor.Direction = BitConverter.ToInt32(data, ptr + 28);
						verticalDoor.TopWait = BitConverter.ToInt32(data, ptr + 32);
						verticalDoor.TopCountDown = BitConverter.ToInt32(data, ptr + 36);
						ptr += 40;
						thinkers.Add(verticalDoor);
						break;
					}
					case SpecialClass.Floor:
					{
						PadPointer();
						FloorMove floorMove = new FloorMove(world);
						floorMove.ThinkerState = ReadThinkerState(data, ptr + 8);
						floorMove.Type = (FloorMoveType)BitConverter.ToInt32(data, ptr + 12);
						floorMove.Crush = BitConverter.ToInt32(data, ptr + 16) != 0;
						floorMove.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 20)];
						floorMove.Sector.SpecialData = floorMove;
						floorMove.Direction = BitConverter.ToInt32(data, ptr + 24);
						floorMove.NewSpecial = (SectorSpecial)BitConverter.ToInt32(data, ptr + 28);
						floorMove.Texture = BitConverter.ToInt32(data, ptr + 32);
						floorMove.FloorDestHeight = new Fixed(BitConverter.ToInt32(data, ptr + 36));
						floorMove.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 40));
						ptr += 44;
						thinkers.Add(floorMove);
						break;
					}
					case SpecialClass.Plat:
					{
						PadPointer();
						Platform platform = new Platform(world);
						platform.ThinkerState = ReadThinkerState(data, ptr + 8);
						platform.Sector = world.Map.Sectors[BitConverter.ToInt32(data, ptr + 12)];
						platform.Sector.SpecialData = platform;
						platform.Speed = new Fixed(BitConverter.ToInt32(data, ptr + 16));
						platform.Low = new Fixed(BitConverter.ToInt32(data, ptr + 20));
						platform.High = new Fixed(BitConverter.ToInt32(data, ptr + 24));
						platform.Wait = BitConverter.ToInt32(data, ptr + 28);
						platform.Count = BitConverter.ToInt32(data, ptr + 32);
						platform.Status = (PlatformState)BitConverter.ToInt32(data, ptr + 36);
						platform.OldStatus = (PlatformState)BitConverter.ToInt32(data, ptr + 40);
						platform.Crush = BitConverter.ToInt32(data, ptr + 44) != 0;
						platform.Tag = BitConverter.ToInt32(data, ptr + 48);
						platform.Type = (PlatformType)BitConverter.ToInt32(data, ptr + 52);
						ptr += 56;
						thinkers.Add(platform);
						sectorAction.AddAc