Decompiled source of Empress Gameboy Emulator v1.0.0

EmpressGameboy.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EmpressGameboy.Emulator;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[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("Empress")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EmpressGameboy")]
[assembly: AssemblyTitle("EmpressGameboy")]
[assembly: AssemblyVersion("1.0.0.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 EmpressGameboy
{
	internal sealed class EmpressGameboyConfig
	{
		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<string> RomFolderPath { get; }

		internal ConfigEntry<string> PreferredRomFile { get; }

		internal ConfigEntry<string> BootRomPath { get; }

		internal ConfigEntry<string> SaveFolderPath { get; }

		internal ConfigEntry<int> FrameSyncFps { get; }

		internal ConfigEntry<float> InteractionDistance { get; }

		internal ConfigEntry<float> ScreenAssemblyOffsetY { get; }

		internal ConfigEntry<bool> EnableRomAudio { get; }

		internal ConfigEntry<float> RomAudioVolume { get; }

		internal ConfigEntry<float> RomAudioMinDistance { get; }

		internal ConfigEntry<float> RomAudioMaxDistance { get; }

		internal ConfigEntry<string> UpBinding { get; }

		internal ConfigEntry<string> DownBinding { get; }

		internal ConfigEntry<string> LeftBinding { get; }

		internal ConfigEntry<string> RightBinding { get; }

		internal ConfigEntry<string> ABinding { get; }

		internal ConfigEntry<string> BBinding { get; }

		internal ConfigEntry<string> StartBinding { get; }

		internal ConfigEntry<string> SelectBinding { get; }

		internal EmpressGameboyConfig(BaseUnityPlugin plugin)
		{
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Expected O, but got Unknown
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Expected O, but got Unknown
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Expected O, but got Unknown
			string? path = Path.GetDirectoryName(plugin.Info.Location) ?? Directory.GetCurrentDirectory();
			string text = Path.Combine(path, "roms");
			string text2 = Path.Combine(path, "saves");
			Enabled = plugin.Config.Bind<bool>("General", "Enabled", true, "Enables the Empress Gameboy console.");
			RomFolderPath = plugin.Config.Bind<string>("Paths", "RomFolderPath", text, "Folder that contains user-supplied Game Boy ROM files.");
			PreferredRomFile = plugin.Config.Bind<string>("Paths", "PreferredRomFile", string.Empty, "Optional file name or absolute path for the ROM to load. Leave empty to auto-pick the first ROM found.");
			BootRomPath = plugin.Config.Bind<string>("Paths", "BootRomPath", string.Empty, "Optional file name or absolute path for a DMG boot ROM.");
			SaveFolderPath = plugin.Config.Bind<string>("Paths", "SaveFolderPath", text2, "Folder used for cartridge save files.");
			FrameSyncFps = plugin.Config.Bind<int>("Networking", "FrameSyncFps", 15, "How often the host sends screen updates to other players. Higher values are smoother but use more bandwidth.");
			InteractionDistance = plugin.Config.Bind<float>("Gameplay", "InteractionDistance", 2.4f, "Maximum distance to start using the Empress Gameboy.");
			ScreenAssemblyOffsetY = plugin.Config.Bind<float>("Appearance", "ScreenAssemblyOffsetY", 0.065f, "Raises or lowers the cabinet face and visible screen relative to the stand.");
			EnableRomAudio = plugin.Config.Bind<bool>("Audio", "EnableRomAudio", true, "Plays real ROM audio from the Empress Gameboy cabinet while someone is actively using it.");
			RomAudioVolume = plugin.Config.Bind<float>("Audio", "RomAudioVolume", 0.3f, new ConfigDescription("Volume multiplier for the Empress Gameboy speaker.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			RomAudioMinDistance = plugin.Config.Bind<float>("Audio", "RomAudioMinDistance", 1.8f, new ConfigDescription("Distance where the Empress Gameboy speaker stays at full volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>()));
			RomAudioMaxDistance = plugin.Config.Bind<float>("Audio", "RomAudioMaxDistance", 3.85f, new ConfigDescription("Distance where the Empress Gameboy speaker becomes inaudible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 40f), Array.Empty<object>()));
			UpBinding = plugin.Config.Bind<string>("Controls", "Up", "W,UpArrow", "Keys that act as Game Boy Up.");
			DownBinding = plugin.Config.Bind<string>("Controls", "Down", "S,DownArrow", "Keys that act as Game Boy Down.");
			LeftBinding = plugin.Config.Bind<string>("Controls", "Left", "A,LeftArrow", "Keys that act as Game Boy Left.");
			RightBinding = plugin.Config.Bind<string>("Controls", "Right", "D,RightArrow", "Keys that act as Game Boy Right.");
			ABinding = plugin.Config.Bind<string>("Controls", "AButton", "J,Z", "Keys that act as Game Boy A.");
			BBinding = plugin.Config.Bind<string>("Controls", "BButton", "K,X", "Keys that act as Game Boy B.");
			StartBinding = plugin.Config.Bind<string>("Controls", "Start", "Enter,NumpadEnter", "Keys that act as Game Boy Start.");
			SelectBinding = plugin.Config.Bind<string>("Controls", "Select", "RightShift,Space", "Keys that act as Game Boy Select.");
		}

		internal bool IsPressed(ConfigEntry<string> binding, Keyboard keyboard)
		{
			//IL_0014: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			foreach (Key item in ParseKeys(binding.Value))
			{
				KeyControl obj = keyboard[item];
				if (obj != null && ((ButtonControl)obj).isPressed)
				{
					return true;
				}
			}
			return false;
		}

		internal static string FormatBindingLabel(string configured)
		{
			string[] array = configured.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = array[i].Trim();
			}
			return string.Join("/", array);
		}

		private static IEnumerable<Key> ParseKeys(string configured)
		{
			string[] array = configured.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string text = array2[i].Trim();
				if (text.Length != 0 && Enum.TryParse<Key>(text, ignoreCase: true, out Key result))
				{
					yield return result;
				}
			}
		}
	}
	internal sealed class EmpressGameboyConsole : MonoBehaviour
	{
		private Material? _bodyMaterial;

		private Material? _screenMaterial;

		private Transform? _screenAssemblyRoot;

		internal Collider? InteractionCollider { get; private set; }

		internal Renderer? ScreenRenderer { get; private set; }

		internal void Initialize(Texture texture, float screenAssemblyOffsetY)
		{
			CreateGeometry();
			SetScreenAssemblyOffset(screenAssemblyOffsetY);
			ApplyTexture(texture);
		}

		internal void ApplyTexture(Texture texture)
		{
			if ((Object)(object)_screenMaterial != (Object)null)
			{
				_screenMaterial.mainTexture = texture;
			}
		}

		internal void SetScreenAssemblyOffset(float offsetY)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_screenAssemblyRoot == (Object)null))
			{
				Vector3 localPosition = _screenAssemblyRoot.localPosition;
				localPosition.y = offsetY;
				_screenAssemblyRoot.localPosition = localPosition;
			}
		}

		internal bool Contains(Collider collider)
		{
			if ((Object)(object)collider != (Object)null)
			{
				return ((Component)collider).transform.IsChildOf(((Component)this).transform);
			}
			return false;
		}

		private void CreateGeometry()
		{
			//IL_002b: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)InteractionCollider != (Object)null))
			{
				_bodyMaterial = new Material(FindShader("Standard", "Sprites/Default"))
				{
					color = new Color(0.13f, 0.14f, 0.16f, 1f)
				};
				_screenMaterial = new Material(FindShader("Unlit/Texture", "Sprites/Default"))
				{
					color = Color.white
				};
				GameObject val = new GameObject("ScreenAssembly");
				val.transform.SetParent(((Component)this).transform, false);
				_screenAssemblyRoot = val.transform;
				GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj).name = "Body";
				obj.transform.SetParent(_screenAssemblyRoot, false);
				obj.transform.localPosition = new Vector3(0f, 0.3f, 0f);
				obj.transform.localScale = new Vector3(0.7f, 0.48f, 0.16f);
				Renderer component = obj.GetComponent<Renderer>();
				if ((Object)(object)component != (Object)null)
				{
					component.material = _bodyMaterial;
				}
				BoxCollider component2 = obj.GetComponent<BoxCollider>();
				if ((Object)(object)component2 != (Object)null)
				{
					((Collider)component2).isTrigger = true;
					InteractionCollider = (Collider?)(object)component2;
				}
				GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj2).name = "Stand";
				obj2.transform.SetParent(((Component)this).transform, false);
				obj2.transform.localPosition = new Vector3(0f, 0.08f, 0f);
				obj2.transform.localScale = new Vector3(0.28f, 0.12f, 0.24f);
				Renderer component3 = obj2.GetComponent<Renderer>();
				if ((Object)(object)component3 != (Object)null)
				{
					component3.material = _bodyMaterial;
				}
				Collider component4 = obj2.GetComponent<Collider>();
				if ((Object)(object)component4 != (Object)null)
				{
					component4.enabled = false;
				}
				GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)5);
				((Object)val2).name = "Screen";
				val2.transform.SetParent(_screenAssemblyRoot, false);
				val2.transform.localPosition = new Vector3(0f, 0.34f, 0.085f);
				val2.transform.localScale = new Vector3(0.56f, 0.4f, 1f);
				ScreenRenderer = val2.GetComponent<Renderer>();
				if ((Object)(object)ScreenRenderer != (Object)null)
				{
					ScreenRenderer.material = _screenMaterial;
				}
				Collider component5 = val2.GetComponent<Collider>();
				if ((Object)(object)component5 != (Object)null)
				{
					component5.enabled = false;
				}
				CreateButton(_screenAssemblyRoot, new Vector3(-0.19f, 0.08f, 0.085f), new Vector3(0.07f, 0.03f, 0.03f), new Color(0.86f, 0.27f, 0.33f, 1f));
				CreateButton(_screenAssemblyRoot, new Vector3(0.19f, 0.08f, 0.085f), new Vector3(0.07f, 0.03f, 0.03f), new Color(0.27f, 0.76f, 0.61f, 1f));
				CreateButton(_screenAssemblyRoot, new Vector3(0f, -0.02f, 0.085f), new Vector3(0.2f, 0.025f, 0.03f), new Color(0.36f, 0.38f, 0.42f, 1f));
			}
		}

		private void CreateButton(Transform parent, Vector3 position, Vector3 scale, Color color)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
			val.transform.SetParent(parent, false);
			val.transform.localPosition = position;
			val.transform.localScale = scale;
			Renderer component = val.GetComponent<Renderer>();
			if ((Object)(object)component != (Object)null)
			{
				Material material = new Material(FindShader("Standard", "Sprites/Default"))
				{
					color = color
				};
				component.material = material;
			}
			Collider component2 = val.GetComponent<Collider>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.enabled = false;
			}
		}

		private static Shader FindShader(params string[] names)
		{
			for (int i = 0; i < names.Length; i++)
			{
				Shader val = Shader.Find(names[i]);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			throw new InvalidOperationException("Unable to find a shader for Empress Gameboy.");
		}

		private void OnDestroy()
		{
			if ((Object)(object)_bodyMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_bodyMaterial);
				_bodyMaterial = null;
			}
			if ((Object)(object)_screenMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_screenMaterial);
				_screenMaterial = null;
			}
		}
	}
	[BepInPlugin("com.empress.gameboy", "Empress Gameboy", "1.0.0")]
	public sealed class EmpressGameboyPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.empress.gameboy";

		public const string PluginName = "Empress Gameboy";

		public const string PluginVersion = "1.0.0";

		private EmpressGameboyRuntime? _runtime;

		private void Awake()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			HideBepInExManagerObject();
			EmpressGameboyConfig config = new EmpressGameboyConfig((BaseUnityPlugin)(object)this);
			GameObject val = new GameObject("EmpressGameboyRuntime");
			Object.DontDestroyOnLoad((Object)(object)val);
			_runtime = val.AddComponent<EmpressGameboyRuntime>();
			_runtime.Initialize(config, ((BaseUnityPlugin)this).Logger, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Directory.GetCurrentDirectory());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Empress Gameboy loaded.");
		}

		private void HideBepInExManagerObject()
		{
			if (!((Object)(object)Chainloader.ManagerObject == (Object)null))
			{
				((Object)Chainloader.ManagerObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)Chainloader.ManagerObject);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_runtime != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_runtime).gameObject);
				_runtime = null;
			}
		}
	}
	internal sealed class EmpressGameboyRuntime : MonoBehaviour
	{
		private const byte ControlRequestEventCode = 190;

		private const byte InputStateEventCode = 191;

		private const byte FrameSyncEventCode = 192;

		private const byte StatusEventCode = 193;

		private const byte AudioSyncEventCode = 194;

		private const int FrameWidth = 160;

		private const int FrameHeight = 144;

		private const int FramePixelCount = 23040;

		private const int PackedFrameLength = 5760;

		private const float ConsoleScaleMultiplier = 2f;

		private static readonly FieldInfo? PlayerControllerAvatarField = AccessTools.Field(typeof(PlayerController), "playerAvatarScript");

		private static readonly FieldInfo? PlayerAvatarIsDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled");

		private static readonly FieldInfo? GameDirectorCurrentStateField = AccessTools.Field(typeof(GameDirector), "currentState");

		private static readonly FieldInfo? LevelGeneratorGeneratedField = AccessTools.Field(typeof(LevelGenerator), "Generated");

		private static readonly FieldInfo? ShopManagerExtractionPointField = AccessTools.Field(typeof(ShopManager), "extractionPoint");

		private static readonly FieldInfo? ExtractionPointSafetySpawnField = AccessTools.Field(typeof(ExtractionPoint), "safetySpawn");

		private static readonly FieldInfo? ExtractionPointIsShopField = AccessTools.Field(typeof(ExtractionPoint), "isShop");

		private static readonly FieldInfo? ExtractionPointInStartRoomField = AccessTools.Field(typeof(ExtractionPoint), "inStartRoom");

		private static readonly Vector3 LevelConsoleHardcodedPosition = new Vector3(-2.031f, 1.738f, -19.573f);

		private static readonly Quaternion LevelConsoleHardcodedRotation = Quaternion.Euler(0f, 90f, 0f);

		private readonly Dictionary<int, string> _actorNameCache = new Dictionary<int, string>();

		private EmpressGameboyConfig _config;

		private ManualLogSource _log;

		private string _pluginDirectory = string.Empty;

		private EmpressGameboyConsole? _console;

		private EmpressGameboySpeaker? _speaker;

		private Texture2D? _displayTexture;

		private Color32[]? _displayPixels;

		private Color32[] _palette = EmpressGameboyPalette.DefaultPalette;

		private byte[]? _lastPackedFrame;

		private EmpressGameboyHostEmulator? _hostEmulator;

		private float[]? _hostAudioScratch;

		private float[]? _networkAudioPacketBuffer;

		private float[]? _remoteAudioDecodeScratch;

		private string? _loadedRomPath;

		private string _sceneSignature = string.Empty;

		private string _statusMessage = "Waiting for a ROM.";

		private string _romDisplayName = "No ROM";

		private bool _remoteMirrorDisplayInitialized;

		private bool _eventHookActive;

		private bool _hoveringConsole;

		private int _controllerActorNumber;

		private int _remoteInputMask;

		private int _localHeldInputMask;

		private int _lastSentInputMask = -1;

		private int _lastReceivedFrameId;

		private int _lastReceivedAudioPacketId;

		private int _broadcastFrameId;

		private int _broadcastAudioPacketId;

		private int _networkAudioPacketFill;

		private double _nextStatusSendTime;

		private double _nextFrameSendTime;

		private double _nextInputSendTime;

		private double _nextRomRetryTime;

		private double _nextSaveFlushTime;

		internal void Initialize(EmpressGameboyConfig config, ManualLogSource log, string pluginDirectory)
		{
			_config = config;
			_log = log;
			_pluginDirectory = pluginDirectory;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			((Component)this).transform.parent = null;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			EnsureDisplayTexture();
		}

		private void OnDestroy()
		{
			SetPhotonEventHook(active: false);
			CleanupConsole();
			DisposeHostEmulator();
			if ((Object)(object)_displayTexture != (Object)null)
			{
				Object.Destroy((Object)(object)_displayTexture);
				_displayTexture = null;
			}
		}

		private void Update()
		{
			if (!_config.Enabled.Value)
			{
				SetPhotonEventHook(active: false);
				CleanupConsole();
				return;
			}
			SetPhotonEventHook(active: true);
			TickSceneBinding();
			_speaker?.ApplySettings(_config.RomAudioVolume.Value, _config.RomAudioMinDistance.Value, _config.RomAudioMaxDistance.Value);
			RefreshSpeakerPlaybackState();
			TickControllerInteraction();
			TickHostEmulator();
			TickControlLifecycle();
		}

		private void OnGUI()
		{
			//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_0047: 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_0056: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (_config.Enabled.Value && !((Object)(object)_displayTexture == (Object)null))
			{
				string promptText = GetPromptText();
				if (!string.IsNullOrWhiteSpace(promptText))
				{
					GUIStyle val = new GUIStyle(GUI.skin.box)
					{
						fontSize = 16,
						alignment = (TextAnchor)4,
						wordWrap = true
					};
					float num = Mathf.Min((float)Screen.width - 40f, 980f);
					GUI.Box(new Rect((float)Screen.width * 0.5f - num * 0.5f, (float)Screen.height - 130f, num, 92f), promptText, val);
				}
			}
		}

		private void TickSceneBinding()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_00ed: 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)
			if (SemiFunc.RunIsLobby())
			{
				if (_sceneSignature != "lobby")
				{
					_sceneSignature = "lobby";
					if (TryGetLobbyConsolePose(out var position, out var rotation))
					{
						RebuildConsole(position, rotation);
					}
					else
					{
						CleanupConsole();
					}
				}
			}
			else if (SemiFunc.RunIsLevel() && IsLevelGenerated())
			{
				string text = $"level:{((Object)LevelGenerator.Instance).GetInstanceID()}";
				if (_sceneSignature != text)
				{
					_sceneSignature = text;
					if (TryGetLevelConsolePose(out var position2, out var rotation2))
					{
						RebuildConsole(position2, rotation2);
					}
					else
					{
						CleanupConsole();
					}
				}
			}
			else if (SemiFunc.RunIsShop() && (Object)(object)ShopManager.instance != (Object)null)
			{
				string text2 = $"shop:{((Object)ShopManager.instance).GetInstanceID()}";
				if (_sceneSignature != text2)
				{
					_sceneSignature = text2;
					if (TryGetShopConsolePose(out var position3, out var rotation3))
					{
						RebuildConsole(position3, rotation3);
					}
					else
					{
						CleanupConsole();
					}
				}
			}
			else if (_sceneSignature.Length > 0)
			{
				_sceneSignature = string.Empty;
				CleanupConsole();
				ReleaseControlLocalOrNetwork();
			}
		}

		private void TickControllerInteraction()
		{
			//IL_005a: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			_hoveringConsole = false;
			if ((Object)(object)_console == (Object)null || !CanUseConsolePrompt())
			{
				return;
			}
			Camera val = SemiFunc.MainCamera();
			Collider interactionCollider = _console.InteractionCollider;
			RaycastHit val2 = default(RaycastHit);
			if ((Object)(object)val == (Object)null || (Object)(object)interactionCollider == (Object)null || (Object)(object)PlayerController.instance == (Object)null || Vector3.Distance(((Component)PlayerController.instance).transform.position, ((Component)_console).transform.position) > _config.InteractionDistance.Value + 0.8f || !Physics.Raycast(((Component)val).transform.position, ((Component)val).transform.forward, ref val2, _config.InteractionDistance.Value + 1.2f, -5, (QueryTriggerInteraction)2) || !_console.Contains(((RaycastHit)(ref val2)).collider))
			{
				return;
			}
			_hoveringConsole = true;
			if (SemiFunc.InputDown((InputKey)2))
			{
				if (IsLocalController())
				{
					ReleaseControlLocalOrNetwork();
				}
				else
				{
					RequestControlLocalOrNetwork();
				}
			}
		}

		private void TickControlLifecycle()
		{
			if (!IsLocalController())
			{
				_localHeldInputMask = 0;
				return;
			}
			if ((Object)(object)_console == (Object)null || !CanMaintainControl())
			{
				ReleaseControlLocalOrNetwork();
				return;
			}
			SemiFunc.InputDisableMovement();
			SemiFunc.InputDisableAiming();
			PlayerController instance = PlayerController.instance;
			if (instance != null)
			{
				instance.InputDisable(0.12f);
			}
			if (SemiFunc.InputDown((InputKey)6) || SemiFunc.InputDown((InputKey)2))
			{
				ReleaseControlLocalOrNetwork();
				return;
			}
			_localHeldInputMask = ReadLocalInputMask();
			if (HasHostAuthority())
			{
				_hostEmulator?.SetInputMask(_localHeldInputMask);
			}
			else if (!(Time.unscaledTimeAsDouble < _nextInputSendTime) || _localHeldInputMask != _lastSentInputMask)
			{
				_nextInputSendTime = Time.unscaledTimeAsDouble + 0.05;
				_lastSentInputMask = _localHeldInputMask;
				SendInputState(_localHeldInputMask);
			}
		}

		private void TickHostEmulator()
		{
			if (!HasHostAuthority())
			{
				EnterRemoteMirrorMode();
				return;
			}
			_remoteMirrorDisplayInitialized = false;
			EnsureHostEmulator();
			if (_hostEmulator == null)
			{
				if (PhotonNetwork.InRoom && Time.unscaledTimeAsDouble >= _nextStatusSendTime)
				{
					BroadcastStatus(force: true);
				}
				return;
			}
			if (!HasVisibleConsoleScene())
			{
				if (PhotonNetwork.InRoom && Time.unscaledTimeAsDouble >= _nextStatusSendTime)
				{
					BroadcastStatus(force: true);
				}
				return;
			}
			int localActorNumber = GetLocalActorNumber();
			int inputMask = 0;
			if (_controllerActorNumber == localActorNumber)
			{
				inputMask = _localHeldInputMask;
			}
			else if (_controllerActorNumber != 0)
			{
				inputMask = _remoteInputMask;
			}
			_hostEmulator.SetInputMask(inputMask);
			_hostEmulator.Step(Time.unscaledDeltaTime);
			TickHostAudio();
			if (_hostEmulator.ConsumeFrameFlag())
			{
				UpdateDisplayTexture(_hostEmulator.FrameBuffer);
			}
			if (Time.unscaledTimeAsDouble >= _nextSaveFlushTime)
			{
				_nextSaveFlushTime = Time.unscaledTimeAsDouble + 5.0;
				_hostEmulator.FlushSave();
			}
			if (PhotonNetwork.InRoom && Time.unscaledTimeAsDouble >= _nextFrameSendTime)
			{
				_nextFrameSendTime = Time.unscaledTimeAsDouble + 1.0 / (double)Mathf.Clamp(_config.FrameSyncFps.Value, 4, 30);
				BroadcastFrame();
			}
			if (PhotonNetwork.InRoom && Time.unscaledTimeAsDouble >= _nextStatusSendTime)
			{
				BroadcastStatus(force: true);
			}
		}

		private void EnterRemoteMirrorMode()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (_hostEmulator != null)
			{
				DisposeHostEmulator();
			}
			_loadedRomPath = null;
			_networkAudioPacketFill = 0;
			if ((Object)(object)_speaker != (Object)null && _controllerActorNumber == 0)
			{
				_speaker.SetActive(active: false);
			}
			if (!_remoteMirrorDisplayInitialized)
			{
				_remoteMirrorDisplayInitialized = true;
				_romDisplayName = "Host Session";
				_statusMessage = "Waiting for host sync.";
				_lastReceivedFrameId = 0;
				_lastReceivedAudioPacketId = 0;
				ClearDisplayTexture(new Color32((byte)18, (byte)22, (byte)24, byte.MaxValue));
			}
		}

		private void TickHostAudio()
		{
			if (_hostEmulator == null)
			{
				return;
			}
			if (!_config.EnableRomAudio.Value || _controllerActorNumber == 0 || !((Object)(object)_speaker != (Object)null))
			{
				_speaker?.SetActive(active: false);
				_hostEmulator.DiscardAudioSamples();
				_networkAudioPacketFill = 0;
				return;
			}
			EnsureAudioScratchBuffers();
			_speaker.SetActive(active: true);
			while (true)
			{
				int num = _hostEmulator.DequeueAudioSamples(_hostAudioScratch, _hostAudioScratch.Length);
				if (num > 0)
				{
					_speaker.QueueSamples(_hostAudioScratch, num);
					if (PhotonNetwork.InRoom)
					{
						AppendAudioPacketData(_hostAudioScratch, num);
					}
					continue;
				}
				break;
			}
		}

		private void EnsureHostEmulator()
		{
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTimeAsDouble < _nextRomRetryTime)
			{
				return;
			}
			_nextRomRetryTime = Time.unscaledTimeAsDouble + 2.0;
			string text = ResolveRomPath();
			if (string.IsNullOrWhiteSpace(text))
			{
				if (_hostEmulator != null)
				{
					DisposeHostEmulator();
				}
				_loadedRomPath = null;
				_statusMessage = "No ROM found in the configured folder.";
				_romDisplayName = "No ROM";
				ClearDisplayTexture(new Color32((byte)18, (byte)22, (byte)24, byte.MaxValue));
				return;
			}
			if (_hostEmulator != null && string.Equals(_loadedRomPath, text, StringComparison.OrdinalIgnoreCase))
			{
				_statusMessage = "Ready";
				_romDisplayName = _hostEmulator.DisplayName;
				return;
			}
			try
			{
				DisposeHostEmulator();
				string text2 = ResolveConfiguredPath(_config.SaveFolderPath.Value, "saves");
				string bootRomPath = ResolveBootRomPath();
				Directory.CreateDirectory(Path.GetDirectoryName(text) ?? _pluginDirectory);
				Directory.CreateDirectory(text2);
				_hostEmulator = new EmpressGameboyHostEmulator(text, bootRomPath, text2);
				_loadedRomPath = text;
				_statusMessage = "Ready";
				_romDisplayName = _hostEmulator.DisplayName;
				UpdateDisplayTexture(_hostEmulator.FrameBuffer);
				_log.LogInfo((object)("Empress Gameboy loaded ROM: " + _romDisplayName));
			}
			catch (Exception ex)
			{
				DisposeHostEmulator();
				_loadedRomPath = null;
				_statusMessage = "Failed to load ROM: " + ex.Message;
				_romDisplayName = "Load Error";
				ClearDisplayTexture(new Color32((byte)54, (byte)18, (byte)18, byte.MaxValue));
				_log.LogError((object)$"Empress Gameboy failed to load ROM: {ex}");
			}
		}

		private void BroadcastFrame()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom && _hostEmulator != null)
			{
				byte[] array = PackFrame(_hostEmulator.FrameBuffer);
				if (_lastPackedFrame == null || !array.SequenceEqual(_lastPackedFrame))
				{
					_lastPackedFrame = array;
					_broadcastFrameId++;
					object[] array2 = new object[2] { _broadcastFrameId, array };
					RaiseEventOptions val = new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)0
					};
					PhotonNetwork.RaiseEvent((byte)192, (object)array2, val, SendOptions.SendUnreliable);
				}
			}
		}

		private void BroadcastStatus(bool force)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom && (force || !(Time.unscaledTimeAsDouble < _nextStatusSendTime)))
			{
				_nextStatusSendTime = Time.unscaledTimeAsDouble + 1.0;
				object[] array = new object[4]
				{
					_controllerActorNumber,
					_romDisplayName,
					_statusMessage,
					(_hostEmulator != null) ? 1 : 0
				};
				RaiseEventOptions val = new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				};
				PhotonNetwork.RaiseEvent((byte)193, (object)array, val, SendOptions.SendReliable);
			}
		}

		private void RequestControlLocalOrNetwork()
		{
			//IL_0045: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (CanClaimControl())
			{
				if (!PhotonNetwork.InRoom)
				{
					_controllerActorNumber = GetLocalActorNumber();
					_statusMessage = "Ready";
					_remoteInputMask = 0;
					RefreshSpeakerPlaybackState();
				}
				else
				{
					object[] array = new object[1] { 1 };
					RaiseEventOptions val = new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)2
					};
					PhotonNetwork.RaiseEvent((byte)190, (object)array, val, SendOptions.SendReliable);
				}
			}
		}

		private void ReleaseControlLocalOrNetwork()
		{
			//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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			_localHeldInputMask = 0;
			_lastSentInputMask = -1;
			_networkAudioPacketFill = 0;
			_speaker?.SetActive(active: false);
			if (!PhotonNetwork.InRoom)
			{
				if (_controllerActorNumber == GetLocalActorNumber())
				{
					_controllerActorNumber = 0;
				}
				_hostEmulator?.SetInputMask(0);
				_hostEmulator?.DiscardAudioSamples();
			}
			else if (_controllerActorNumber == GetLocalActorNumber())
			{
				object[] array = new object[1] { 0 };
				RaiseEventOptions val = new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)2
				};
				PhotonNetwork.RaiseEvent((byte)190, (object)array, val, SendOptions.SendReliable);
			}
		}

		private void SendInputState(int inputMask)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom)
			{
				object[] array = new object[1] { inputMask };
				RaiseEventOptions val = new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)2
				};
				PhotonNetwork.RaiseEvent((byte)191, (object)array, val, SendOptions.SendUnreliable);
			}
		}

		private void SetPhotonEventHook(bool active)
		{
			if (active)
			{
				if (!_eventHookActive)
				{
					PhotonNetwork.NetworkingClient.EventReceived += OnPhotonEvent;
					_eventHookActive = true;
				}
			}
			else if (_eventHookActive)
			{
				PhotonNetwork.NetworkingClient.EventReceived -= OnPhotonEvent;
				_eventHookActive = false;
			}
		}

		private void OnPhotonEvent(EventData eventData)
		{
			switch (eventData.Code)
			{
			case 190:
				if (HasHostAuthority())
				{
					HandleControlRequest(eventData.Sender, eventData.CustomData as object[]);
				}
				break;
			case 191:
				if (HasHostAuthority())
				{
					HandleRemoteInputState(eventData.Sender, eventData.CustomData as object[]);
				}
				break;
			case 192:
				if (!HasHostAuthority())
				{
					HandleFrameSync(eventData.CustomData as object[]);
				}
				break;
			case 193:
				if (!HasHostAuthority())
				{
					HandleStatusSync(eventData.CustomData as object[]);
				}
				break;
			case 194:
				if (!HasHostAuthority())
				{
					HandleAudioSync(eventData.CustomData as object[]);
				}
				break;
			}
		}

		private void HandleControlRequest(int senderActorNumber, object[]? payload)
		{
			if (payload == null || payload.Length == 0)
			{
				return;
			}
			if (Convert.ToInt32(payload[0]) == 1)
			{
				if (_hostEmulator == null)
				{
					_statusMessage = "Host has no ROM ready.";
					BroadcastStatus(force: true);
					return;
				}
				if (_controllerActorNumber == 0 || _controllerActorNumber == senderActorNumber)
				{
					_controllerActorNumber = senderActorNumber;
					_remoteInputMask = 0;
				}
			}
			else if (_controllerActorNumber == senderActorNumber)
			{
				_controllerActorNumber = 0;
				_remoteInputMask = 0;
			}
			RefreshSpeakerPlaybackState();
			BroadcastStatus(force: true);
		}

		private void HandleRemoteInputState(int senderActorNumber, object[]? payload)
		{
			if (payload != null && payload.Length != 0 && _controllerActorNumber == senderActorNumber)
			{
				_remoteInputMask = Convert.ToInt32(payload[0]);
			}
		}

		private void HandleFrameSync(object[]? payload)
		{
			if (payload != null && payload.Length >= 2)
			{
				int num = Convert.ToInt32(payload[0]);
				if (num > _lastReceivedFrameId && payload[1] is byte[] array && array.Length == 5760)
				{
					_lastReceivedFrameId = num;
					UpdateDisplayTextureFromPackedFrame(array);
				}
			}
		}

		private void HandleStatusSync(object[]? payload)
		{
			if (payload != null && payload.Length >= 4)
			{
				_controllerActorNumber = Convert.ToInt32(payload[0]);
				_romDisplayName = Convert.ToString(payload[1]) ?? "Unknown ROM";
				_statusMessage = Convert.ToString(payload[2]) ?? "Ready";
				RefreshSpeakerPlaybackState();
			}
		}

		private void HandleAudioSync(object[]? payload)
		{
			if (payload == null || payload.Length < 2)
			{
				return;
			}
			int num = Convert.ToInt32(payload[0]);
			if (num <= _lastReceivedAudioPacketId || !(payload[1] is byte[] array) || array.Length == 0 || ((uint)array.Length & (true ? 1u : 0u)) != 0)
			{
				return;
			}
			_lastReceivedAudioPacketId = num;
			if (!((Object)(object)_speaker == (Object)null) && _config.EnableRomAudio.Value && _controllerActorNumber != 0)
			{
				EnsureAudioScratchBuffers();
				int num2 = array.Length / 2;
				for (int i = 0; i < num2; i++)
				{
					short num3 = (short)(array[i * 2] | (array[i * 2 + 1] << 8));
					_remoteAudioDecodeScratch[i] = Mathf.Clamp((float)num3 / 32767f, -1f, 1f);
				}
				_speaker.SetActive(active: true);
				_speaker.QueueSamples(_remoteAudioDecodeScratch, num2);
			}
		}

		private void RebuildConsole(Vector3 position, Quaternion rotation)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_002f: 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_0047: 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)
			CleanupConsole();
			EnsureDisplayTexture();
			GameObject val = new GameObject("EmpressGameboyConsole");
			val.transform.SetParent(((Component)this).transform, false);
			val.transform.position = position;
			val.transform.rotation = rotation;
			val.transform.localScale = Vector3.one * 2f;
			_console = val.AddComponent<EmpressGameboyConsole>();
			_console.Initialize((Texture)(object)_displayTexture, _config.ScreenAssemblyOffsetY.Value);
			_speaker = val.AddComponent<EmpressGameboySpeaker>();
			_speaker.Initialize(_config.RomAudioVolume.Value, _config.RomAudioMinDistance.Value, _config.RomAudioMaxDistance.Value);
			RefreshSpeakerPlaybackState();
		}

		private void CleanupConsole()
		{
			if ((Object)(object)_console != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_console).gameObject);
				_console = null;
			}
			_speaker = null;
			_lastReceivedAudioPacketId = 0;
			_broadcastAudioPacketId = 0;
			_networkAudioPacketFill = 0;
			_remoteMirrorDisplayInitialized = false;
			_lastReceivedFrameId = 0;
		}

		private void EnsureAudioScratchBuffers()
		{
			if (_hostAudioScratch == null)
			{
				_hostAudioScratch = new float[512];
			}
			if (_networkAudioPacketBuffer == null)
			{
				_networkAudioPacketBuffer = new float[512];
			}
			if (_remoteAudioDecodeScratch == null)
			{
				_remoteAudioDecodeScratch = new float[512];
			}
		}

		private void AppendAudioPacketData(float[] samples, int sampleCount)
		{
			EnsureAudioScratchBuffers();
			int num = 0;
			while (num < sampleCount)
			{
				int num2 = Math.Min(sampleCount - num, _networkAudioPacketBuffer.Length - _networkAudioPacketFill);
				Array.Copy(samples, num, _networkAudioPacketBuffer, _networkAudioPacketFill, num2);
				_networkAudioPacketFill += num2;
				num += num2;
				if (_networkAudioPacketFill == _networkAudioPacketBuffer.Length)
				{
					BroadcastAudioSamples(_networkAudioPacketBuffer, _networkAudioPacketFill);
					_networkAudioPacketFill = 0;
				}
			}
		}

		private void BroadcastAudioSamples(float[] samples, int sampleCount)
		{
			//IL_0088: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom && sampleCount > 0)
			{
				byte[] array = new byte[sampleCount * 2];
				for (int i = 0; i < sampleCount; i++)
				{
					short num = (short)Mathf.RoundToInt(Mathf.Clamp(samples[i], -1f, 1f) * 32767f);
					array[i * 2] = (byte)((uint)num & 0xFFu);
					array[i * 2 + 1] = (byte)((uint)(num >> 8) & 0xFFu);
				}
				_broadcastAudioPacketId++;
				object[] array2 = new object[2] { _broadcastAudioPacketId, array };
				RaiseEventOptions val = new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				};
				PhotonNetwork.RaiseEvent((byte)194, (object)array2, val, SendOptions.SendUnreliable);
			}
		}

		private void RefreshSpeakerPlaybackState()
		{
			if (!((Object)(object)_speaker == (Object)null))
			{
				bool active = _config.EnableRomAudio.Value && _controllerActorNumber != 0;
				_speaker.SetActive(active);
			}
		}

		private void DisposeHostEmulator()
		{
			if (_hostEmulator != null)
			{
				_hostEmulator.Dispose();
				_hostEmulator = null;
			}
		}

		private void EnsureDisplayTexture()
		{
			//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_0029: 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_0040: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_displayTexture != (Object)null))
			{
				_displayTexture = new Texture2D(160, 144, (TextureFormat)4, false, false)
				{
					filterMode = (FilterMode)0,
					wrapMode = (TextureWrapMode)1,
					name = "EmpressGameboyDisplay"
				};
				_displayPixels = (Color32[]?)(object)new Color32[23040];
				ClearDisplayTexture(new Color32((byte)18, (byte)22, (byte)24, byte.MaxValue));
			}
		}

		private void ClearDisplayTexture(Color32 color)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			EnsureDisplayTexture();
			if (_displayPixels != null && !((Object)(object)_displayTexture == (Object)null))
			{
				for (int i = 0; i < _displayPixels.Length; i++)
				{
					_displayPixels[i] = color;
				}
				_displayTexture.SetPixels32(_displayPixels);
				_displayTexture.Apply(false, false);
			}
		}

		private void UpdateDisplayTexture(IReadOnlyList<byte> frameBuffer)
		{
			EnsureDisplayTexture();
			if (_displayPixels != null && !((Object)(object)_displayTexture == (Object)null) && frameBuffer.Count == 23040)
			{
				for (int i = 0; i < 23040; i++)
				{
					WriteDisplayPixel(i, frameBuffer[i]);
				}
				_displayTexture.SetPixels32(_displayPixels);
				_displayTexture.Apply(false, false);
			}
		}

		private void UpdateDisplayTextureFromPackedFrame(byte[] packedFrame)
		{
			EnsureDisplayTexture();
			if (_displayPixels != null && !((Object)(object)_displayTexture == (Object)null))
			{
				int num = 0;
				foreach (byte b in packedFrame)
				{
					WriteDisplayPixel(num++, (byte)(b & 3u));
					WriteDisplayPixel(num++, (byte)((uint)(b >> 2) & 3u));
					WriteDisplayPixel(num++, (byte)((uint)(b >> 4) & 3u));
					WriteDisplayPixel(num++, (byte)((uint)(b >> 6) & 3u));
				}
				_displayTexture.SetPixels32(_displayPixels);
				_displayTexture.Apply(false, false);
			}
		}

		private void WriteDisplayPixel(int sourcePixelIndex, byte paletteIndex)
		{
			//IL_003f: 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)
			if (_displayPixels != null)
			{
				int num = sourcePixelIndex % 160;
				int num2 = sourcePixelIndex / 160;
				int num3 = (143 - num2) * 160 + (159 - num);
				_displayPixels[num3] = _palette[paletteIndex & 3];
			}
		}

		private static byte[] PackFrame(IReadOnlyList<byte> frameBuffer)
		{
			byte[] array = new byte[5760];
			int num = 0;
			for (int i = 0; i < 23040; i += 4)
			{
				array[num++] = (byte)((frameBuffer[i] & 3u) | (uint)((frameBuffer[i + 1] & 3) << 2) | (uint)((frameBuffer[i + 2] & 3) << 4) | (uint)((frameBuffer[i + 3] & 3) << 6));
			}
			return array;
		}

		private bool TryGetLevelConsolePose(out Vector3 position, out Quaternion rotation)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			position = LevelConsoleHardcodedPosition;
			rotation = LevelConsoleHardcodedRotation;
			return true;
		}

		private bool TryGetLobbyConsolePose(out Vector3 position, out Quaternion rotation)
		{
			return TryGetTruckConsolePose(out position, out rotation);
		}

		private bool TryGetShopConsolePose(out Vector3 position, out Quaternion rotation)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			Transform val = GetShopExtractionAnchor();
			if ((Object)(object)val == (Object)null)
			{
				ExtractionPoint val2 = ((IEnumerable<ExtractionPoint>)Object.FindObjectsOfType<ExtractionPoint>(true)).FirstOrDefault((Func<ExtractionPoint, bool>)IsShopExtractionPoint);
				val = (((Object)(object)val2 != (Object)null) ? ((Component)val2).transform : null);
			}
			if ((Object)(object)val != (Object)null)
			{
				Vector3 right = val.right;
				Vector3 val3;
				if (!(((Vector3)(ref right)).sqrMagnitude > 0.1f))
				{
					val3 = Vector3.right;
				}
				else
				{
					right = val.right;
					val3 = ((Vector3)(ref right)).normalized;
				}
				Vector3 val4 = val3;
				position = val.position + val4 * 1.4f + Vector3.up * 0.45f;
				rotation = Quaternion.LookRotation(-val4, Vector3.up);
				return true;
			}
			position = Vector3.zero;
			rotation = Quaternion.identity;
			return false;
		}

		private static ExtractionPoint? FindStartRoomExtractionPoint()
		{
			return ((IEnumerable<ExtractionPoint>)Object.FindObjectsOfType<ExtractionPoint>(true)).FirstOrDefault((Func<ExtractionPoint, bool>)IsStartRoomExtractionPoint);
		}

		private bool TryGetTruckConsolePose(out Vector3 position, out Quaternion rotation)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0039: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_00f8: 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_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null)
			{
				Transform transform = ((Component)TruckSafetySpawnPoint.instance).transform;
				Vector3 val = FlattenDirection(transform.forward, Vector3.forward);
				Vector3 val2 = FlattenDirection(transform.right, Vector3.right);
				position = transform.position + val * 1.2f + val2 * 0.75f;
				rotation = Quaternion.LookRotation(-val, Vector3.up);
				return true;
			}
			if ((Object)(object)TruckScreenText.instance != (Object)null)
			{
				Transform transform2 = ((Component)TruckScreenText.instance).transform;
				Vector3 val3 = FlattenDirection(transform2.right, Vector3.right);
				position = transform2.position + val3 * 1.1f + Vector3.down * 0.78f;
				rotation = Quaternion.LookRotation(-val3, Vector3.up);
				return true;
			}
			position = Vector3.zero;
			rotation = Quaternion.identity;
			return false;
		}

		private bool CanUseConsolePrompt()
		{
			if (SemiFunc.NoTextInputsActive() && (Object)(object)GameDirector.instance != (Object)null && IsGameInMainState() && (Object)(object)PlayerController.instance != (Object)null)
			{
				return !IsLocalPlayerDisabled();
			}
			return false;
		}

		private bool CanMaintainControl()
		{
			//IL_0028: 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)
			if (CanUseConsolePrompt() && HasVisibleConsoleScene() && (Object)(object)_console != (Object)null)
			{
				return Vector3.Distance(((Component)PlayerController.instance).transform.position, ((Component)_console).transform.position) <= _config.InteractionDistance.Value + 1.4f;
			}
			return false;
		}

		private bool HasVisibleConsoleScene()
		{
			if (_sceneSignature.Length > 0)
			{
				return (Object)(object)_console != (Object)null;
			}
			return false;
		}

		private static bool IsLevelGenerated()
		{
			if ((Object)(object)LevelGenerator.Instance == (Object)null)
			{
				return false;
			}
			return ReadBoolField(LevelGeneratorGeneratedField, LevelGenerator.Instance);
		}

		private static bool IsGameInMainState()
		{
			if ((Object)(object)GameDirector.instance == (Object)null || GameDirectorCurrentStateField == null)
			{
				return false;
			}
			object value = GameDirectorCurrentStateField.GetValue(GameDirector.instance);
			if (value == null)
			{
				return false;
			}
			return Convert.ToInt32(value, CultureInfo.InvariantCulture) == 2;
		}

		private static bool IsLocalPlayerDisabled()
		{
			if ((Object)(object)PlayerController.instance == (Object)null)
			{
				return true;
			}
			object? obj = PlayerControllerAvatarField?.GetValue(PlayerController.instance);
			PlayerAvatar val = (PlayerAvatar)((obj is PlayerAvatar) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			return ReadBoolField(PlayerAvatarIsDisabledField, val);
		}

		private static Transform? GetShopExtractionAnchor()
		{
			if (!((Object)(object)ShopManager.instance != (Object)null))
			{
				return null;
			}
			object? obj = ShopManagerExtractionPointField?.GetValue(ShopManager.instance);
			return (Transform?)((obj is Transform) ? obj : null);
		}

		private static Transform? GetExtractionSafetySpawn(ExtractionPoint extractionPoint)
		{
			object? obj = ExtractionPointSafetySpawnField?.GetValue(extractionPoint);
			return (Transform?)((obj is Transform) ? obj : null);
		}

		private static bool IsShopExtractionPoint(ExtractionPoint? extractionPoint)
		{
			if ((Object)(object)extractionPoint != (Object)null)
			{
				return ReadBoolField(ExtractionPointIsShopField, extractionPoint);
			}
			return false;
		}

		private static bool IsStartRoomExtractionPoint(ExtractionPoint? extractionPoint)
		{
			if ((Object)(object)extractionPoint != (Object)null)
			{
				return ReadBoolField(ExtractionPointInStartRoomField, extractionPoint);
			}
			return false;
		}

		private static bool ReadBoolField(FieldInfo? field, object instance)
		{
			if (field == null)
			{
				return false;
			}
			object value = field.GetValue(instance);
			if (!(value is bool result))
			{
				if (value is IConvertible convertible)
				{
					return convertible.ToBoolean(CultureInfo.InvariantCulture);
				}
				return false;
			}
			return result;
		}

		private static Vector3 FlattenDirection(Vector3 direction, Vector3 fallback)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.ProjectOnPlane(direction, Vector3.up);
			if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
			{
				return ((Vector3)(ref val)).normalized;
			}
			return ((Vector3)(ref fallback)).normalized;
		}

		private bool HasHostAuthority()
		{
			if (!PhotonNetwork.InRoom)
			{
				return true;
			}
			return PhotonNetwork.IsMasterClient;
		}

		private bool CanClaimControl()
		{
			if ((Object)(object)_console == (Object)null)
			{
				return false;
			}
			if (!PhotonNetwork.InRoom)
			{
				return _hostEmulator != null;
			}
			if (_controllerActorNumber != 0 && _controllerActorNumber != GetLocalActorNumber())
			{
				return false;
			}
			return true;
		}

		private bool IsLocalController()
		{
			if (_controllerActorNumber != 0)
			{
				return _controllerActorNumber == GetLocalActorNumber();
			}
			return false;
		}

		private int GetLocalActorNumber()
		{
			if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null)
			{
				return PhotonNetwork.LocalPlayer.ActorNumber;
			}
			return 1;
		}

		private string GetPromptText()
		{
			string text = "Controls: Up " + EmpressGameboyConfig.FormatBindingLabel(_config.UpBinding.Value) + " | Down " + EmpressGameboyConfig.FormatBindingLabel(_config.DownBinding.Value) + " | Left " + EmpressGameboyConfig.FormatBindingLabel(_config.LeftBinding.Value) + " | Right " + EmpressGameboyConfig.FormatBindingLabel(_config.RightBinding.Value) + "\nA " + EmpressGameboyConfig.FormatBindingLabel(_config.ABinding.Value) + " | B " + EmpressGameboyConfig.FormatBindingLabel(_config.BBinding.Value) + " | Start " + EmpressGameboyConfig.FormatBindingLabel(_config.StartBinding.Value) + " | Select " + EmpressGameboyConfig.FormatBindingLabel(_config.SelectBinding.Value);
			if (_hoveringConsole)
			{
				if (_controllerActorNumber == 0)
				{
					if (_hostEmulator != null || PhotonNetwork.InRoom)
					{
						return "Press E to play " + _romDisplayName + ".\n" + text;
					}
					return "Empress Gameboy is unavailable. Add a ROM to the configured folder.";
				}
				if (IsLocalController())
				{
					return "Press E or Esc to stop playing " + _romDisplayName + ".\n" + text;
				}
				return "Empress Gameboy is in use by " + GetActorName(_controllerActorNumber) + ".\n" + text;
			}
			if (IsLocalController())
			{
				return "Empress Gameboy is active.\n" + text;
			}
			return string.Empty;
		}

		private string GetActorName(int actorNumber)
		{
			if (actorNumber == 0)
			{
				return "Nobody";
			}
			if (_actorNameCache.TryGetValue(actorNumber, out string value))
			{
				return value;
			}
			if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.Players.TryGetValue(actorNumber, out var value2))
			{
				string text = (string.IsNullOrWhiteSpace(value2.NickName) ? $"Player {actorNumber}" : value2.NickName);
				_actorNameCache[actorNumber] = text;
				return text;
			}
			return $"Player {actorNumber}";
		}

		private int ReadLocalInputMask()
		{
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return 0;
			}
			int num = 0;
			if (_config.IsPressed(_config.ABinding, current))
			{
				num |= 1;
			}
			if (_config.IsPressed(_config.BBinding, current))
			{
				num |= 2;
			}
			if (_config.IsPressed(_config.StartBinding, current))
			{
				num |= 8;
			}
			if (_config.IsPressed(_config.SelectBinding, current))
			{
				num |= 4;
			}
			if (_config.IsPressed(_config.RightBinding, current))
			{
				num |= 0x10;
			}
			if (_config.IsPressed(_config.LeftBinding, current))
			{
				num |= 0x20;
			}
			if (_config.IsPressed(_config.UpBinding, current))
			{
				num |= 0x40;
			}
			if (_config.IsPressed(_config.DownBinding, current))
			{
				num |= 0x80;
			}
			return num;
		}

		private string ResolveRomPath()
		{
			string text = ResolveConfiguredPath(_config.RomFolderPath.Value, "roms");
			Directory.CreateDirectory(text);
			string text2 = _config.PreferredRomFile.Value?.Trim() ?? string.Empty;
			if (!string.IsNullOrWhiteSpace(text2))
			{
				string path2 = (Path.IsPathRooted(text2) ? text2 : Path.Combine(text, text2));
				if (File.Exists(path2))
				{
					return Path.GetFullPath(path2);
				}
			}
			string[] array = Directory.EnumerateFiles(text, "*.*", SearchOption.TopDirectoryOnly).Where(delegate(string path)
			{
				string text3 = Path.GetExtension(path).ToLowerInvariant();
				return (text3 == ".gb" || text3 == ".gbc") ? true : false;
			}).OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase)
				.ToArray();
			if (array.Length == 0)
			{
				return string.Empty;
			}
			return Path.GetFullPath(array[0]);
		}

		private string? ResolveBootRomPath()
		{
			string text = _config.BootRomPath.Value?.Trim() ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			string text2 = (Path.IsPathRooted(text) ? text : Path.GetFullPath(Path.Combine(_pluginDirectory, text)));
			if (!File.Exists(text2))
			{
				return null;
			}
			return text2;
		}

		private string ResolveConfiguredPath(string configuredPath, string relativeFallback)
		{
			string text = (string.IsNullOrWhiteSpace(configuredPath) ? Path.Combine(_pluginDirectory, relativeFallback) : configuredPath.Trim());
			if (!Path.IsPathRooted(text))
			{
				return Path.GetFullPath(Path.Combine(_pluginDirectory, text));
			}
			return text;
		}
	}
	internal sealed class EmpressGameboySpeaker : MonoBehaviour
	{
		private readonly object _bufferLock = new object();

		private readonly float[] _sampleBuffer = new float[44100];

		private AudioSource? _audioSource;

		private AudioClip? _streamClip;

		private int _readIndex;

		private int _writeIndex;

		private int _queuedSamples;

		private bool _active;

		internal void Initialize(float volume, float minDistance, float maxDistance)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			_audioSource = ((Component)this).gameObject.AddComponent<AudioSource>();
			_audioSource.playOnAwake = false;
			_audioSource.loop = true;
			_audioSource.spatialBlend = 1f;
			_audioSource.rolloffMode = (AudioRolloffMode)1;
			_audioSource.dopplerLevel = 0f;
			_audioSource.spread = 0f;
			ApplySettings(volume, minDistance, maxDistance);
			_streamClip = AudioClip.Create("EmpressGameboyAudio", 11025, 1, 11025, true, new PCMReaderCallback(OnAudioRead));
			_audioSource.clip = _streamClip;
			_audioSource.Play();
			SetActive(active: false);
		}

		internal void ApplySettings(float volume, float minDistance, float maxDistance)
		{
			if (!((Object)(object)_audioSource == (Object)null))
			{
				_audioSource.volume = Mathf.Clamp01(volume);
				_audioSource.minDistance = Mathf.Max(0.1f, minDistance);
				_audioSource.maxDistance = Mathf.Max(_audioSource.minDistance + 0.1f, maxDistance);
			}
		}

		internal void SetActive(bool active)
		{
			_active = active;
			if ((Object)(object)_audioSource != (Object)null)
			{
				_audioSource.mute = !active;
			}
			if (!active)
			{
				ClearQueuedSamples();
			}
		}

		internal void QueueSamples(float[] samples, int sampleCount)
		{
			if (!_active || samples == null || sampleCount <= 0)
			{
				return;
			}
			lock (_bufferLock)
			{
				int num = Mathf.Min(sampleCount, samples.Length);
				for (int i = 0; i < num; i++)
				{
					if (_queuedSamples == _sampleBuffer.Length)
					{
						_readIndex = (_readIndex + 1) % _sampleBuffer.Length;
						_queuedSamples--;
					}
					_sampleBuffer[_writeIndex] = samples[i];
					_writeIndex = (_writeIndex + 1) % _sampleBuffer.Length;
					_queuedSamples++;
				}
			}
		}

		internal void ClearQueuedSamples()
		{
			lock (_bufferLock)
			{
				_readIndex = 0;
				_writeIndex = 0;
				_queuedSamples = 0;
			}
		}

		private void OnAudioRead(float[] data)
		{
			Array.Clear(data, 0, data.Length);
			if (!_active)
			{
				return;
			}
			lock (_bufferLock)
			{
				for (int i = 0; i < data.Length; i++)
				{
					if (_queuedSamples <= 0)
					{
						break;
					}
					data[i] = _sampleBuffer[_readIndex];
					_readIndex = (_readIndex + 1) % _sampleBuffer.Length;
					_queuedSamples--;
				}
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_audioSource != (Object)null)
			{
				_audioSource.Stop();
				_audioSource.clip = null;
				_audioSource = null;
			}
			if ((Object)(object)_streamClip != (Object)null)
			{
				Object.Destroy((Object)(object)_streamClip);
				_streamClip = null;
			}
		}
	}
}
namespace EmpressGameboy.Emulator
{
	internal sealed class CPU
	{
		public byte A;

		public byte B;

		public byte C;

		public byte D;

		public byte E;

		public byte H;

		public byte L;

		public byte F;

		public ushort PC;

		public ushort SP;

		public bool zero;

		public bool negative;

		public bool halfCarry;

		public bool carry;

		public bool IME;

		private bool halted;

		private MMU mmu;

		public CPU(MMU mmu)
		{
			A = (B = (C = (D = (E = (H = (L = (F = 0)))))));
			PC = 0;
			SP = 0;
			zero = (negative = (halfCarry = (carry = false)));
			IME = false;
			this.mmu = mmu;
		}

		public void Reset()
		{
			A = 1;
			F = 176;
			UpdateFlagsFromF();
			B = 0;
			C = 19;
			D = 0;
			E = 216;
			H = 1;
			L = 77;
			PC = 256;
			SP = 65534;
			mmu.JOYP = 207;
			mmu.DIV = 24;
			mmu.TIMA = 0;
			mmu.TMA = 0;
			mmu.TAC = 248;
			mmu.IF = 225;
			mmu.IE = 0;
			mmu.LCDC = 145;
			mmu.STAT = 133;
			mmu.SCY = 0;
			mmu.SCX = 0;
			mmu.LY = 0;
			mmu.LYC = 0;
			mmu.BGP = 252;
			mmu.OBP0 = byte.MaxValue;
			mmu.OBP1 = byte.MaxValue;
			mmu.WY = 0;
			mmu.WX = 0;
			mmu.Write(65360, A);
		}

		public int HandleInterrupts()
		{
			byte b = mmu.Read(65295);
			byte b2 = mmu.Read(ushort.MaxValue);
			byte b3 = (byte)(b & b2);
			if (b3 != 0)
			{
				halted = false;
				if (IME)
				{
					IME = false;
					for (int i = 0; i < 5; i++)
					{
						if ((b3 & (1 << i)) != 0)
						{
							mmu.Write(65295, (byte)(b & ~(1 << i)));
							SP--;
							mmu.Write(SP, (byte)((uint)(PC >> 8) & 0xFFu));
							SP--;
							mmu.Write(SP, (byte)(PC & 0xFFu));
							PC = GetInterruptHandlerAddress(i);
							return 20;
						}
					}
				}
				return 0;
			}
			if (halted && b3 != 0)
			{
				halted = false;
			}
			return 0;
		}

		private ushort GetInterruptHandlerAddress(int bit)
		{
			return bit switch
			{
				0 => 64, 
				1 => 72, 
				2 => 80, 
				3 => 88, 
				4 => 96, 
				_ => 0, 
			};
		}

		private void UpdateFFromFlags()
		{
			F = 0;
			if (zero)
			{
				F |= 128;
			}
			if (negative)
			{
				F |= 64;
			}
			if (halfCarry)
			{
				F |= 32;
			}
			if (carry)
			{
				F |= 16;
			}
		}

		public void UpdateFlagsFromF()
		{
			zero = (F & 0x80) != 0;
			negative = (F & 0x40) != 0;
			halfCarry = (F & 0x20) != 0;
			carry = (F & 0x10) != 0;
		}

		private ushort Get16BitReg(string pair)
		{
			return pair.ToLower() switch
			{
				"bc" => (ushort)((B << 8) | C), 
				"de" => (ushort)((D << 8) | E), 
				"hl" => (ushort)((H << 8) | L), 
				"af" => (ushort)((A << 8) | F), 
				_ => 0, 
			};
		}

		private void Load16BitReg(string pair, ushort value)
		{
			switch (pair.ToLower())
			{
			case "bc":
				B = (byte)(value >> 8);
				C = (byte)(value & 0xFFu);
				break;
			case "de":
				D = (byte)(value >> 8);
				E = (byte)(value & 0xFFu);
				break;
			case "hl":
				H = (byte)(value >> 8);
				L = (byte)(value & 0xFFu);
				break;
			case "af":
				A = (byte)(value >> 8);
				F = (byte)(value & 0xFFu);
				break;
			}
		}

		public void Log()
		{
		}

		private byte Fetch()
		{
			return mmu.Read(PC++);
		}

		public int ExecuteInstruction()
		{
			int num = HandleInterrupts();
			if (num > 0)
			{
				return num;
			}
			if (halted)
			{
				return 4;
			}
			byte b = Fetch();
			return b switch
			{
				0 => NOP(), 
				1 => LD_RR_U16(ref B, ref C), 
				2 => LD_ARR_R(ref A, "bc"), 
				3 => INC_RR("bc"), 
				4 => INC_R(ref B), 
				5 => DEC_R(ref B), 
				6 => LD_R_U8(ref B), 
				7 => RLCA(), 
				8 => LD_AU16_SP(), 
				9 => ADD_HL_RR("bc"), 
				10 => LD_R_ARR(ref A, "bc"), 
				11 => DEC_RR("bc"), 
				12 => INC_R(ref C), 
				13 => DEC_R(ref C), 
				14 => LD_R_U8(ref C), 
				15 => RRCA(), 
				16 => STOP(), 
				17 => LD_RR_U16(ref D, ref E), 
				18 => LD_ARR_R(ref A, "de"), 
				19 => INC_RR("de"), 
				20 => INC_R(ref D), 
				21 => DEC_R(ref D), 
				22 => LD_R_U8(ref D), 
				23 => RLA(), 
				24 => JR_CON_I8(flag: true), 
				25 => ADD_HL_RR("de"), 
				26 => LD_R_ARR(ref A, "de"), 
				27 => DEC_RR("de"), 
				28 => INC_R(ref E), 
				29 => DEC_R(ref E), 
				30 => LD_R_U8(ref E), 
				31 => RRA(), 
				32 => JR_CON_I8(!zero), 
				33 => LD_RR_U16(ref H, ref L), 
				34 => LD_AHLI_A(), 
				35 => INC_RR("hl"), 
				36 => INC_R(ref H), 
				37 => DEC_R(ref H), 
				38 => LD_R_U8(ref H), 
				39 => DAA(), 
				40 => JR_CON_I8(zero), 
				41 => ADD_HL_RR("hl"), 
				42 => LD_A_AHLI(), 
				43 => DEC_RR("hl"), 
				44 => INC_R(ref L), 
				45 => DEC_R(ref L), 
				46 => LD_R_U8(ref L), 
				47 => CPL(), 
				48 => JR_CON_I8(!carry), 
				49 => LD_SP_U16(), 
				50 => LD_AHLM_A(), 
				51 => INC_SP(), 
				52 => INC_AHL(), 
				53 => DEC_AHL(), 
				54 => LD_AHL_U8(), 
				55 => SCF(), 
				56 => JR_CON_I8(carry), 
				57 => ADD_HL_SP(), 
				58 => LD_A_AHLM(), 
				59 => DEC_SP(), 
				60 => INC_R(ref A), 
				61 => DEC_R(ref A), 
				62 => LD_R_U8(ref A), 
				63 => CCF(), 
				64 => LD_R1_R2(ref B, ref B), 
				65 => LD_R1_R2(ref B, ref C), 
				66 => LD_R1_R2(ref B, ref D), 
				67 => LD_R1_R2(ref B, ref E), 
				68 => LD_R1_R2(ref B, ref H), 
				69 => LD_R1_R2(ref B, ref L), 
				70 => LD_R_ARR(ref B, "hl"), 
				71 => LD_R1_R2(ref B, ref A), 
				72 => LD_R1_R2(ref C, ref B), 
				73 => LD_R1_R2(ref C, ref C), 
				74 => LD_R1_R2(ref C, ref D), 
				75 => LD_R1_R2(ref C, ref E), 
				76 => LD_R1_R2(ref C, ref H), 
				77 => LD_R1_R2(ref C, ref L), 
				78 => LD_R_ARR(ref C, "hl"), 
				79 => LD_R1_R2(ref C, ref A), 
				80 => LD_R1_R2(ref D, ref B), 
				81 => LD_R1_R2(ref D, ref C), 
				82 => LD_R1_R2(ref D, ref D), 
				83 => LD_R1_R2(ref D, ref E), 
				84 => LD_R1_R2(ref D, ref H), 
				85 => LD_R1_R2(ref D, ref L), 
				86 => LD_R_ARR(ref D, "hl"), 
				87 => LD_R1_R2(ref D, ref A), 
				88 => LD_R1_R2(ref E, ref B), 
				89 => LD_R1_R2(ref E, ref C), 
				90 => LD_R1_R2(ref E, ref D), 
				91 => LD_R1_R2(ref E, ref E), 
				92 => LD_R1_R2(ref E, ref H), 
				93 => LD_R1_R2(ref E, ref L), 
				94 => LD_R_ARR(ref E, "hl"), 
				95 => LD_R1_R2(ref E, ref A), 
				96 => LD_R1_R2(ref H, ref B), 
				97 => LD_R1_R2(ref H, ref C), 
				98 => LD_R1_R2(ref H, ref D), 
				99 => LD_R1_R2(ref H, ref E), 
				100 => LD_R1_R2(ref H, ref H), 
				101 => LD_R1_R2(ref H, ref L), 
				102 => LD_R_ARR(ref H, "hl"), 
				103 => LD_R1_R2(ref H, ref A), 
				104 => LD_R1_R2(ref L, ref B), 
				105 => LD_R1_R2(ref L, ref C), 
				106 => LD_R1_R2(ref L, ref D), 
				107 => LD_R1_R2(ref L, ref E), 
				108 => LD_R1_R2(ref L, ref H), 
				109 => LD_R1_R2(ref L, ref L), 
				110 => LD_R_ARR(ref L, "hl"), 
				111 => LD_R1_R2(ref L, ref A), 
				112 => LD_ARR_R(ref B, "hl"), 
				113 => LD_ARR_R(ref C, "hl"), 
				114 => LD_ARR_R(ref D, "hl"), 
				115 => LD_ARR_R(ref E, "hl"), 
				116 => LD_ARR_R(ref H, "hl"), 
				117 => LD_ARR_R(ref L, "hl"), 
				118 => HALT(), 
				119 => LD_ARR_R(ref A, "hl"), 
				120 => LD_R1_R2(ref A, ref B), 
				121 => LD_R1_R2(ref A, ref C), 
				122 => LD_R1_R2(ref A, ref D), 
				123 => LD_R1_R2(ref A, ref E), 
				124 => LD_R1_R2(ref A, ref H), 
				125 => LD_R1_R2(ref A, ref L), 
				126 => LD_R_ARR(ref A, "hl"), 
				127 => LD_R1_R2(ref A, ref A), 
				128 => ADD_A_R(ref B), 
				129 => ADD_A_R(ref C), 
				130 => ADD_A_R(ref D), 
				131 => ADD_A_R(ref E), 
				132 => ADD_A_R(ref H), 
				133 => ADD_A_R(ref L), 
				134 => ADD_A_ARR("hl"), 
				135 => ADD_A_R(ref A), 
				136 => ADC_A_R(ref B), 
				137 => ADC_A_R(ref C), 
				138 => ADC_A_R(ref D), 
				139 => ADC_A_R(ref E), 
				140 => ADC_A_R(ref H), 
				141 => ADC_A_R(ref L), 
				142 => ADC_A_ARR("hl"), 
				143 => ADC_A_R(ref A), 
				144 => SUB_A_R(ref B), 
				145 => SUB_A_R(ref C), 
				146 => SUB_A_R(ref D), 
				147 => SUB_A_R(ref E), 
				148 => SUB_A_R(ref H), 
				149 => SUB_A_R(ref L), 
				150 => SUB_A_ARR("hl"), 
				151 => SUB_A_R(ref A), 
				152 => SBC_A_R(ref B), 
				153 => SBC_A_R(ref C), 
				154 => SBC_A_R(ref D), 
				155 => SBC_A_R(ref E), 
				156 => SBC_A_R(ref H), 
				157 => SBC_A_R(ref L), 
				158 => SBC_A_ARR("hl"), 
				159 => SBC_A_R(ref A), 
				160 => AND_A_R(ref B), 
				161 => AND_A_R(ref C), 
				162 => AND_A_R(ref D), 
				163 => AND_A_R(ref E), 
				164 => AND_A_R(ref H), 
				165 => AND_A_R(ref L), 
				166 => AND_A_ARR("hl"), 
				167 => AND_A_R(ref A), 
				168 => XOR_A_R(ref B), 
				169 => XOR_A_R(ref C), 
				170 => XOR_A_R(ref D), 
				171 => XOR_A_R(ref E), 
				172 => XOR_A_R(ref H), 
				173 => XOR_A_R(ref L), 
				174 => XOR_A_ARR("hl"), 
				175 => XOR_A_R(ref A), 
				176 => OR_A_R(ref B), 
				177 => OR_A_R(ref C), 
				178 => OR_A_R(ref D), 
				179 => OR_A_R(ref E), 
				180 => OR_A_R(ref H), 
				181 => OR_A_R(ref L), 
				182 => OR_A_ARR("hl"), 
				183 => OR_A_R(ref A), 
				184 => CP_A_R(ref B), 
				185 => CP_A_R(ref C), 
				186 => CP_A_R(ref D), 
				187 => CP_A_R(ref E), 
				188 => CP_A_R(ref H), 
				189 => CP_A_R(ref L), 
				190 => CP_A_ARR("hl"), 
				191 => CP_A_R(ref A), 
				192 => RET_CON(!zero), 
				193 => POP_RR("bc"), 
				194 => JP_CON_U16(!zero), 
				195 => JP_CON_U16(flag: true), 
				196 => CALL_CON_U16(!zero), 
				197 => PUSH_RR("bc"), 
				198 => ADD_A_U8(), 
				199 => RST(0), 
				200 => RET_CON(zero), 
				201 => RET(), 
				202 => JP_CON_U16(zero), 
				203 => ExecuteCB(), 
				204 => CALL_CON_U16(zero), 
				205 => CALL_U16(), 
				206 => ADC_A_U8(), 
				207 => RST(8), 
				208 => RET_CON(!carry), 
				209 => POP_RR("de"), 
				210 => JP_CON_U16(!carry), 
				211 => DMG_EXIT(b), 
				212 => CALL_CON_U16(!carry), 
				213 => PUSH_RR("de"), 
				214 => SUB_A_U8(), 
				215 => RST(16), 
				216 => RET_CON(carry), 
				217 => RETI(), 
				218 => JP_CON_U16(carry), 
				219 => DMG_EXIT(b), 
				220 => CALL_CON_U16(carry), 
				221 => DMG_EXIT(b), 
				222 => SBC_A_U8(), 
				223 => RST(24), 
				224 => LD_FF00_U8_A(), 
				225 => POP_RR("hl"), 
				226 => LD_FF00_C_A(), 
				227 => DMG_EXIT(b), 
				228 => DMG_EXIT(b), 
				229 => PUSH_RR("hl"), 
				230 => AND_A_U8(), 
				231 => RST(32), 
				232 => ADD_SP_I8(), 
				233 => JP_HL(), 
				234 => LD_AU16_A(), 
				235 => DMG_EXIT(b), 
				236 => DMG_EXIT(b), 
				237 => DMG_EXIT(b), 
				238 => XOR_A_U8(), 
				239 => RST(40), 
				240 => LD_A_FF00_U8(), 
				241 => POP_AF(), 
				242 => LD_A_FF00_C(), 
				243 => DI(), 
				244 => DMG_EXIT(b), 
				245 => PUSH_RR("af"), 
				246 => OR_A_U8(), 
				247 => RST(48), 
				248 => LD_HL_SP_I8(), 
				249 => LD_SP_HL(), 
				250 => LD_A_AU16(), 
				251 => EI(), 
				252 => DMG_EXIT(b), 
				253 => DMG_EXIT(b), 
				254 => CP_A_U8(), 
				_ => RST(56), 
			};
		}

		public int ExecuteCB()
		{
			return Fetch() switch
			{
				0 => RLC_R(ref B), 
				1 => RLC_R(ref C), 
				2 => RLC_R(ref D), 
				3 => RLC_R(ref E), 
				4 => RLC_R(ref H), 
				5 => RLC_R(ref L), 
				6 => RLC_AHL(), 
				7 => RLC_R(ref A), 
				8 => RRC_R(ref B), 
				9 => RRC_R(ref C), 
				10 => RRC_R(ref D), 
				11 => RRC_R(ref E), 
				12 => RRC_R(ref H), 
				13 => RRC_R(ref L), 
				14 => RRC_AHL(), 
				15 => RRC_R(ref A), 
				16 => RL_R(ref B), 
				17 => RL_R(ref C), 
				18 => RL_R(ref D), 
				19 => RL_R(ref E), 
				20 => RL_R(ref H), 
				21 => RL_R(ref L), 
				22 => RL_AHL(), 
				23 => RL_R(ref A), 
				24 => RR_R(ref B), 
				25 => RR_R(ref C), 
				26 => RR_R(ref D), 
				27 => RR_R(ref E), 
				28 => RR_R(ref H), 
				29 => RR_R(ref L), 
				30 => RR_AHL(), 
				31 => RR_R(ref A), 
				32 => SLA_R(ref B), 
				33 => SLA_R(ref C), 
				34 => SLA_R(ref D), 
				35 => SLA_R(ref E), 
				36 => SLA_R(ref H), 
				37 => SLA_R(ref L), 
				38 => SLA_AHL(), 
				39 => SLA_R(ref A), 
				40 => SRA_R(ref B), 
				41 => SRA_R(ref C), 
				42 => SRA_R(ref D), 
				43 => SRA_R(ref E), 
				44 => SRA_R(ref H), 
				45 => SRA_R(ref L), 
				46 => SRA_AHL(), 
				47 => SRA_R(ref A), 
				48 => SWAP_R(ref B), 
				49 => SWAP_R(ref C), 
				50 => SWAP_R(ref D), 
				51 => SWAP_R(ref E), 
				52 => SWAP_R(ref H), 
				53 => SWAP_R(ref L), 
				54 => SWAP_AHL(), 
				55 => SWAP_R(ref A), 
				56 => SRL_R(ref B), 
				57 => SRL_R(ref C), 
				58 => SRL_R(ref D), 
				59 => SRL_R(ref E), 
				60 => SRL_R(ref H), 
				61 => SRL_R(ref L), 
				62 => SRL_AHL(), 
				63 => SRL_R(ref A), 
				64 => BIT_N_R(0, ref B), 
				65 => BIT_N_R(0, ref C), 
				66 => BIT_N_R(0, ref D), 
				67 => BIT_N_R(0, ref E), 
				68 => BIT_N_R(0, ref H), 
				69 => BIT_N_R(0, ref L), 
				70 => BIT_N_AHL(0), 
				71 => BIT_N_R(0, ref A), 
				72 => BIT_N_R(1, ref B), 
				73 => BIT_N_R(1, ref C), 
				74 => BIT_N_R(1, ref D), 
				75 => BIT_N_R(1, ref E), 
				76 => BIT_N_R(1, ref H), 
				77 => BIT_N_R(1, ref L), 
				78 => BIT_N_AHL(1), 
				79 => BIT_N_R(1, ref A), 
				80 => BIT_N_R(2, ref B), 
				81 => BIT_N_R(2, ref C), 
				82 => BIT_N_R(2, ref D), 
				83 => BIT_N_R(2, ref E), 
				84 => BIT_N_R(2, ref H), 
				85 => BIT_N_R(2, ref L), 
				86 => BIT_N_AHL(2), 
				87 => BIT_N_R(2, ref A), 
				88 => BIT_N_R(3, ref B), 
				89 => BIT_N_R(3, ref C), 
				90 => BIT_N_R(3, ref D), 
				91 => BIT_N_R(3, ref E), 
				92 => BIT_N_R(3, ref H), 
				93 => BIT_N_R(3, ref L), 
				94 => BIT_N_AHL(3), 
				95 => BIT_N_R(3, ref A), 
				96 => BIT_N_R(4, ref B), 
				97 => BIT_N_R(4, ref C), 
				98 => BIT_N_R(4, ref D), 
				99 => BIT_N_R(4, ref E), 
				100 => BIT_N_R(4, ref H), 
				101 => BIT_N_R(4, ref L), 
				102 => BIT_N_AHL(4), 
				103 => BIT_N_R(4, ref A), 
				104 => BIT_N_R(5, ref B), 
				105 => BIT_N_R(5, ref C), 
				106 => BIT_N_R(5, ref D), 
				107 => BIT_N_R(5, ref E), 
				108 => BIT_N_R(5, ref H), 
				109 => BIT_N_R(5, ref L), 
				110 => BIT_N_AHL(5), 
				111 => BIT_N_R(5, ref A), 
				112 => BIT_N_R(6, ref B), 
				113 => BIT_N_R(6, ref C), 
				114 => BIT_N_R(6, ref D), 
				115 => BIT_N_R(6, ref E), 
				116 => BIT_N_R(6, ref H), 
				117 => BIT_N_R(6, ref L), 
				118 => BIT_N_AHL(6), 
				119 => BIT_N_R(6, ref A), 
				120 => BIT_N_R(7, ref B), 
				121 => BIT_N_R(7, ref C), 
				122 => BIT_N_R(7, ref D), 
				123 => BIT_N_R(7, ref E), 
				124 => BIT_N_R(7, ref H), 
				125 => BIT_N_R(7, ref L), 
				126 => BIT_N_AHL(7), 
				127 => BIT_N_R(7, ref A), 
				128 => RES_N_R(0, ref B), 
				129 => RES_N_R(0, ref C), 
				130 => RES_N_R(0, ref D), 
				131 => RES_N_R(0, ref E), 
				132 => RES_N_R(0, ref H), 
				133 => RES_N_R(0, ref L), 
				134 => RES_N_AHL(0), 
				135 => RES_N_R(0, ref A), 
				136 => RES_N_R(1, ref B), 
				137 => RES_N_R(1, ref C), 
				138 => RES_N_R(1, ref D), 
				139 => RES_N_R(1, ref E), 
				140 => RES_N_R(1, ref H), 
				141 => RES_N_R(1, ref L), 
				142 => RES_N_AHL(1), 
				143 => RES_N_R(1, ref A), 
				144 => RES_N_R(2, ref B), 
				145 => RES_N_R(2, ref C), 
				146 => RES_N_R(2, ref D), 
				147 => RES_N_R(2, ref E), 
				148 => RES_N_R(2, ref H), 
				149 => RES_N_R(2, ref L), 
				150 => RES_N_AHL(2), 
				151 => RES_N_R(2, ref A), 
				152 => RES_N_R(3, ref B), 
				153 => RES_N_R(3, ref C), 
				154 => RES_N_R(3, ref D), 
				155 => RES_N_R(3, ref E), 
				156 => RES_N_R(3, ref H), 
				157 => RES_N_R(3, ref L), 
				158 => RES_N_AHL(3), 
				159 => RES_N_R(3, ref A), 
				160 => RES_N_R(4, ref B), 
				161 => RES_N_R(4, ref C), 
				162 => RES_N_R(4, ref D), 
				163 => RES_N_R(4, ref E), 
				164 => RES_N_R(4, ref H), 
				165 => RES_N_R(4, ref L), 
				166 => RES_N_AHL(4), 
				167 => RES_N_R(4, ref A), 
				168 => RES_N_R(5, ref B), 
				169 => RES_N_R(5, ref C), 
				170 => RES_N_R(5, ref D), 
				171 => RES_N_R(5, ref E), 
				172 => RES_N_R(5, ref H), 
				173 => RES_N_R(5, ref L), 
				174 => RES_N_AHL(5), 
				175 => RES_N_R(5, ref A), 
				176 => RES_N_R(6, ref B), 
				177 => RES_N_R(6, ref C), 
				178 => RES_N_R(6, ref D), 
				179 => RES_N_R(6, ref E), 
				180 => RES_N_R(6, ref H), 
				181 => RES_N_R(6, ref L), 
				182 => RES_N_AHL(6), 
				183 => RES_N_R(6, ref A), 
				184 => RES_N_R(7, ref B), 
				185 => RES_N_R(7, ref C), 
				186 => RES_N_R(7, ref D), 
				187 => RES_N_R(7, ref E), 
				188 => RES_N_R(7, ref H), 
				189 => RES_N_R(7, ref L), 
				190 => RES_N_AHL(7), 
				191 => RES_N_R(7, ref A), 
				192 => SET_N_R(0, ref B), 
				193 => SET_N_R(0, ref C), 
				194 => SET_N_R(0, ref D), 
				195 => SET_N_R(0, ref E), 
				196 => SET_N_R(0, ref H), 
				197 => SET_N_R(0, ref L), 
				198 => SET_N_AHL(0), 
				199 => SET_N_R(0, ref A), 
				200 => SET_N_R(1, ref B), 
				201 => SET_N_R(1, ref C), 
				202 => SET_N_R(1, ref D), 
				203 => SET_N_R(1, ref E), 
				204 => SET_N_R(1, ref H), 
				205 => SET_N_R(1, ref L), 
				206 => SET_N_AHL(1), 
				207 => SET_N_R(1, ref A), 
				208 => SET_N_R(2, ref B), 
				209 => SET_N_R(2, ref C), 
				210 => SET_N_R(2, ref D), 
				211 => SET_N_R(2, ref E), 
				212 => SET_N_R(2, ref H), 
				213 => SET_N_R(2, ref L), 
				214 => SET_N_AHL(2), 
				215 => SET_N_R(2, ref A), 
				216 => SET_N_R(3, ref B), 
				217 => SET_N_R(3, ref C), 
				218 => SET_N_R(3, ref D), 
				219 => SET_N_R(3, ref E), 
				220 => SET_N_R(3, ref H), 
				221 => SET_N_R(3, ref L), 
				222 => SET_N_AHL(3), 
				223 => SET_N_R(3, ref A), 
				224 => SET_N_R(4, ref B), 
				225 => SET_N_R(4, ref C), 
				226 => SET_N_R(4, ref D), 
				227 => SET_N_R(4, ref E), 
				228 => SET_N_R(4, ref H), 
				229 => SET_N_R(4, ref L), 
				230 => SET_N_AHL(4), 
				231 => SET_N_R(4, ref A), 
				232 => SET_N_R(5, ref B), 
				233 => SET_N_R(5, ref C), 
				234 => SET_N_R(5, ref D), 
				235 => SET_N_R(5, ref E), 
				236 => SET_N_R(5, ref H), 
				237 => SET_N_R(5, ref L), 
				238 => SET_N_AHL(5), 
				239 => SET_N_R(5, ref A), 
				240 => SET_N_R(6, ref B), 
				241 => SET_N_R(6, ref C), 
				242 => SET_N_R(6, ref D), 
				243 => SET_N_R(6, ref E), 
				244 => SET_N_R(6, ref H), 
				245 => SET_N_R(6, ref L), 
				246 => SET_N_AHL(6), 
				247 => SET_N_R(6, ref A), 
				248 => SET_N_R(7, ref B), 
				249 => SET_N_R(7, ref C), 
				250 => SET_N_R(7, ref D), 
				251 => SET_N_R(7, ref E), 
				252 => SET_N_R(7, ref H), 
				253 => SET_N_R(7, ref L), 
				254 => SET_N_AHL(7), 
				_ => SET_N_R(7, ref A), 
			};
		}

		private int LD_ARR_R(ref byte r, string regPair)
		{
			ushort address = Get16BitReg(regPair);
			mmu.Write(address, r);
			return 8;
		}

		private int LD_AHLM_A()
		{
			ushort num = Get16BitReg("hl");
			mmu.Write(num, A);
			num--;
			Load16BitReg("hl", num);
			return 8;
		}

		private int LD_AHLI_A()
		{
			ushort num = Get16BitReg("hl");
			mmu.Write(num, A);
			num++;
			Load16BitReg("hl", num);
			return 8;
		}

		private int LD_R_U8(ref byte r)
		{
			byte b = Fetch();
			r = b;
			return 8;
		}

		private int LD_AHL_U8()
		{
			ushort address = Get16BitReg("hl");
			byte value = Fetch();
			mmu.Write(address, value);
			return 12;
		}

		private int LD_R_ARR(ref byte r, string regPair)
		{
			ushort address = Get16BitReg(regPair);
			r = mmu.Read(address);
			return 8;
		}

		private int LD_A_AHLM()
		{
			ushort num = Get16BitReg("hl");
			A = mmu.Read(num);
			num--;
			Load16BitReg("hl", num);
			return 8;
		}

		private int LD_A_AHLI()
		{
			ushort num = Get16BitReg("hl");
			A = mmu.Read(num);
			num++;
			Load16BitReg("hl", num);
			return 8;
		}

		private int LD_R1_R2(ref byte r1, ref byte r2)
		{
			r1 = r2;
			return 4;
		}

		private int LD_FF00_U8_A()
		{
			byte b = Fetch();
			ushort address = (ushort)(65280 + b);
			mmu.Write(address, A);
			return 12;
		}

		private int LD_A_FF00_U8()
		{
			byte b = Fetch();
			ushort address = (ushort)(65280 + b);
			A = mmu.Read(address);
			return 12;
		}

		private int LD_FF00_C_A()
		{
			ushort address = (ushort)(65280 + C);
			mmu.Write(address, A);
			return 8;
		}

		private int LD_A_FF00_C()
		{
			ushort address = (ushort)(65280 + C);
			A = mmu.Read(address);
			return 8;
		}

		private int LD_AU16_A()
		{
			byte b = Fetch();
			ushort address = (ushort)((Fetch() << 8) | b);
			mmu.Write(address, A);
			return 16;
		}

		private int LD_A_AU16()
		{
			byte b = Fetch();
			ushort address = (ushort)((Fetch() << 8) | b);
			A = mmu.Read(address);
			return 16;
		}

		private int LD_SP_U16()
		{
			byte b = Fetch();
			ushort sP = (ushort)((Fetch() << 8) | b);
			SP = sP;
			return 12;
		}

		private int LD_RR_U16(ref byte r1, ref byte r2)
		{
			r2 = Fetch();
			r1 = Fetch();
			return 12;
		}

		private int LD_AU16_SP()
		{
			byte b = Fetch();
			ushort num = (ushort)((Fetch() << 8) | b);
			byte value = (byte)(SP & 0xFFu);
			byte value2 = (byte)((uint)(SP >> 8) & 0xFFu);
			mmu.Write(num, value);
			mmu.Write((ushort)(num + 1), value2);
			return 20;
		}

		private int PUSH_RR(string regPair)
		{
			ushort num = Get16BitReg(regPair);
			SP--;
			mmu.Write(SP, (byte)(num >> 8));
			SP--;
			mmu.Write(SP, (byte)(num & 0xFFu));
			return 16;
		}

		private int POP_RR(string regPair)
		{
			byte b = mmu.Read(SP);
			SP++;
			ushort value = (ushort)((mmu.Read(SP) << 8) | b);
			Load16BitReg(regPair, value);
			SP++;
			return 12;
		}

		private int POP_AF()
		{
			byte b = mmu.Read(SP);
			SP++;
			byte a = mmu.Read(SP);
			A = a;
			F = (byte)(b & 0xF0u);
			UpdateFlagsFromF();
			SP++;
			return 12;
		}

		private int LD_SP_HL()
		{
			SP = Get16BitReg("hl");
			return 8;
		}

		private int INC_R(ref byte r)
		{
			int num = r + 1;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (num & 0xF) == 0;
			UpdateFFromFlags();
			r = (byte)num;
			return 4;
		}

		private int INC_AHL()
		{
			ushort address = Get16BitReg("hl");
			int num = mmu.Read(address) + 1;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (num & 0xF) == 0;
			UpdateFFromFlags();
			mmu.Write(address, (byte)num);
			return 12;
		}

		private int DEC_R(ref byte r)
		{
			int num = r - 1;
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (r & 0xF) == 0;
			UpdateFFromFlags();
			r = (byte)num;
			return 4;
		}

		private int DEC_AHL()
		{
			ushort address = Get16BitReg("hl");
			byte b = mmu.Read(address);
			int num = b - 1;
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (b & 0xF) == 0;
			UpdateFFromFlags();
			mmu.Write(address, (byte)num);
			return 12;
		}

		private int ADD_A_R(ref byte r)
		{
			int num = A + r;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (A & 0xF) + (r & 0xF) > 15;
			carry = num > 255;
			UpdateFFromFlags();
			A = (byte)((uint)num & 0xFFu);
			return 4;
		}

		private int ADD_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A + b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (A & 0xF) + (b & 0xF) > 15;
			carry = num > 255;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int ADD_A_U8()
		{
			byte b = Fetch();
			int num = A + b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (A & 0xF) + (b & 0xF) > 15;
			carry = num > 255;
			UpdateFFromFlags();
			A = (byte)((uint)num & 0xFFu);
			return 8;
		}

		private int ADC_A_R(ref byte r)
		{
			int num = A + r + (carry ? 1 : 0);
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (A & 0xF) + (r & 0xF) + (carry ? 1 : 0) > 15;
			carry = num > 255;
			UpdateFFromFlags();
			A = (byte)((uint)num & 0xFFu);
			return 4;
		}

		private int ADC_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A + b + (carry ? 1 : 0);
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (A & 0xF) + (b & 0xF) + (carry ? 1 : 0) > 15;
			carry = num > 255;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int ADC_A_U8()
		{
			byte b = Fetch();
			int num = A + b + (carry ? 1 : 0);
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = (A & 0xF) + (b & 0xF) + (carry ? 1 : 0) > 15;
			carry = num > 255;
			UpdateFFromFlags();
			A = (byte)((uint)num & 0xFFu);
			return 8;
		}

		private int SUB_A_R(ref byte r)
		{
			int num = A - r;
			zero = num == 0;
			negative = true;
			halfCarry = (A & 0xF) < (r & 0xF);
			carry = A < r;
			UpdateFFromFlags();
			A = (byte)num;
			return 4;
		}

		private int SUB_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A - b;
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (A & 0xF) < (b & 0xF);
			carry = num < 0;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int SUB_A_U8()
		{
			byte b = Fetch();
			int num = A - b;
			zero = num == 0;
			negative = true;
			halfCarry = (A & 0xF) < (b & 0xF);
			carry = A < b;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int SBC_A_R(ref byte r)
		{
			int num = A - r - (carry ? 1 : 0);
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (A & 0xF) - (r & 0xF) - (carry ? 1 : 0) < 0;
			carry = num < 0;
			UpdateFFromFlags();
			A = (byte)num;
			return 4;
		}

		private int SBC_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A - b - (carry ? 1 : 0);
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (A & 0xF) - (b & 0xF) - (carry ? 1 : 0) < 0;
			carry = num < 0;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int SBC_A_U8()
		{
			byte b = Fetch();
			int num = A - b - (carry ? 1 : 0);
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (A & 0xF) - (b & 0xF) - (carry ? 1 : 0) < 0;
			carry = num < 0;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int AND_A_R(ref byte r)
		{
			int num = A & r;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = true;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 4;
		}

		private int AND_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A & b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = true;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int AND_A_U8()
		{
			byte b = Fetch();
			int num = A & b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = true;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int XOR_A_R(ref byte r)
		{
			int num = A ^ r;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = false;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 4;
		}

		private int XOR_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A ^ b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = false;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int XOR_A_U8()
		{
			byte b = Fetch();
			int num = A ^ b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = false;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int OR_A_R(ref byte r)
		{
			int num = A | r;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = false;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 4;
		}

		private int OR_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A | b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = false;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int OR_A_U8()
		{
			byte b = Fetch();
			int num = A | b;
			zero = (num & 0xFF) == 0;
			negative = false;
			halfCarry = false;
			carry = false;
			UpdateFFromFlags();
			A = (byte)num;
			return 8;
		}

		private int CP_A_R(ref byte r)
		{
			int num = A - r;
			zero = (num & 0xFF) == 0;
			negative = true;
			halfCarry = (A & 0xF) < (r & 0xF);
			carry = num < 0;
			UpdateFFromFlags();
			return 4;
		}

		private int CP_A_ARR(string regPair)
		{
			ushort address = Get16BitReg(regPair);
			byte b = mmu.Read(address);
			int num = A - b;
			zero = num == 0;
			negative = true;
			halfCarry = (A & 0xF) < (b & 0xF);
			carry = A < b;
			UpdateFFromFlags();
			return 8;
		}

		private int CP_A_U8()
		{
			byte b = Fetch();
			int num = A - b;
			zero = num == 0;
			negative = true;
			halfCarry = (A & 0xF) < (b & 0xF);
			carry = A < b;
			UpdateFFromFlags();
			return 8;
		}

		private int CPL()
		{
			A = (byte)(~A);
			negative = true;
			halfCarry = true;
			UpdateFFromFlags();
			return 4;
		}

		private int SCF()
		{
			carry = true;
			negative = false;
			halfCarry = false;
			UpdateFFromFlags();
			return 4;
		}

		private int CCF()
		{
			carry = !carry;
			negative = false;
			halfCarry = false;
			UpdateFFromFlags();
			return 4;
		}

		private int DAA()
		{
			byte b = (byte)(carry ? 96u : 0u);
			if (halfCarry)
			{
				b = (byte)(b | 6u);
			}
			if (negative)
			{
				A -= b;
			}
			else
			{
				if ((A & 0xF) > 9)
				{
					b = (byte)(b | 6u);
				}
				if (A > 153)
				{
					b = (byte)(b | 0x60u);
				}
				A += b;
			}
			zero = A == 0;
			halfCarry = false;
			carry = b >= 96;
			UpdateFFromFlags();
			return 4;
		}

		private int INC_RR(string regPair)
		{
			ushort num = Get16BitReg(regPair);
			num++;
			Load16BitReg(regPair, num);
			return 8;
		}

		private int INC_SP()
		{
			SP++;
			return 8;
		}

		private int DEC_RR(string regPair)
		{
			ushort num = Get16BitReg(regPair);
			num--;
			Load16BitReg(regPair, num);
			return 8;
		}

		private int DEC_SP()
		{
			SP--;
			return 8;
		}

		private int ADD_HL_RR(string regPair)
		{
			ushort num = Get16BitReg("hl");
			ushort num2 = Get16BitReg(regPair);
			int num3 = num + num2;
			negative = false;
			halfCarry = (num & 0xFFF) + (num2 & 0xFFF) > 4095;
			carry = num3 > 65535;
			UpdateFFromFlags();
			Load16BitReg("hl", (ushort)num3);
			return 8;
		}

		private int ADD_HL_SP()
		{
			ushort num = Get16BitReg("hl");
			int num2 = num + SP;
			negative = false;
			halfCarry = (num & 0xFFF) + (SP & 0xFFF) > 4095;
			carry = num2 > 65535;
			UpdateFFromFlags();
			Load16BitReg("hl", (ushort)num2);
			return 8;
		}

		private int ADD_SP_I8()
		{
			_ = SP;
			_ = SP;
			sbyte b = (sbyte)Fetch();
			int num = SP + (ushort)b;
			zero = false;
			negative = false;
			halfCarry = ((SP ^ b ^ (num & 0xFFFF)) & 0x10) == 16;
			carry = ((SP ^ b ^ (num & 0xFFFF)) & 0x100) == 256;
			UpdateFFromFlags();
			SP = (ushort)num;
			return 16;
		}

		private int LD_HL_SP_I8()
		{
			sbyte b = (sbyte)Fetch();
			int num = SP + b;
			zero = false;
			negative = false;
			halfCarry = ((SP ^ b ^ (num & 0xFFFF)) & 0x10) == 16;
			carry = ((SP ^ b ^ (num & 0xFFFF)) & 0x100) == 256;
			UpdateFFromFlags();
			Load16BitReg("hl", (ushort)num);
			return 12;
		}

		private int RLCA()
		{
			byte b = (byte)(A & 0x80u);
			byte a = (byte)((A << 1) | (b >> 7));
			zero = false;
			negative = false;
			halfCarry = false;
			carry = (A & 0x80) != 0;
			UpdateFFromFlags();
			A = a;
			return 4;
		}

		private int RRCA()
		{
			byte b = (byte)(A & 1u);
			byte a = (byte)((A >> 1) | (b << 7));
			zero = false;
			negative = false;
			halfCarry = false;
			carry = (A & 1) != 0;
			UpdateFFromFlags();
			A = a;
			return 4;
		}

		private int RLA()
		{
			byte b = (carry ? ((byte)1) : ((byte)0));
			byte a = (byte)((A << 1) | b);
			zero = false;
			negative = false;
			halfCarry = false;
			carry = (A & 0x80) != 0;
			UpdateFFromFlags();
			A = a;
			return 4;
		}

		private int RRA()
		{
			byte b = (carry ? ((byte)1) : ((byte)0));
			byte a = (byte)((A >> 1) | (b << 7));
			zero = false;
			negative = false;
			halfCarry = false;
			carry = (A & 1) != 0;
			UpdateFFromFlags();
			A = a;
			return 4;
		}

		private int RLC_R(ref byte r)
		{
			byte b = (byte)(r & 0x80u);
			byte b2 = (byte)((r << 1) | (b >> 7));
			zero = b2 == 0;
			negative = false;
			halfCarry = false;
			carry = (r & 0x80) != 0;
			UpdateFFromFlags();
			r = b2;
			return 8;
		}

		private int RLC_AHL()
		{
			ushort address = Get16BitReg("hl");
			byte b = mmu.Read(address);
			byte b2 = (byte)(b & 0x80u);
			byte b3 = (byte)((b << 1) | (b2 >> 7));
			zero = b3 == 0;
			negative = false;
			halfCarry = false;
			carry = (b & 0x80) != 0;
			UpdateFFromFlags();
			mmu.Write(address, b3);
			return 16;
		}

		private int RRC_R(ref byte r)
		{
			byte b = (byte)(r & 1u);
			byte b2 = (byte)((r >> 1) | (b << 7));
			zero = b2 == 0;
			negative = false;
			halfCarry = false;
			carry = (r & 1) != 0;
			UpdateFFromFlags();
			r = b2;
			return 8;
		}

		private int RRC_AHL()
		{
			ushort address = Get16BitReg("hl");
			byte b = mmu.Read(address);
			byte b2 = (byte)(b & 1u);
			byte b3 = (byte)((b >> 1) | (b2 << 7));
			zero = b3 == 0;
			negative = false;
			halfCarry = false;
			carry = (b & 1) != 0;
			UpdateFFromFlags();
			mmu.Write(address, b3);
			return 16;
		}

		private int RL_R(ref byte r)
		{
			byte b = (carry ? ((byte)1) : ((byte)0));
			byte b2 = (byte)((r << 1) | b);
			zero = b2 == 0;
			negative = false;
			halfCarry = false;
			carry = (r & 0x80) != 0;
			UpdateFFromFlags();
			r = b2;
			return 8;
		}

		private int RL_AHL()
		{
			usho