Decompiled source of Tamarush v1.1.1
TamaRush.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Configuration; using CommonAPI.Phone; using HarmonyLib; using Reptile; using Reptile.Phone; using TMPro; using TamaRush.Phone; using TamaRush.TMEmulator; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("TamaRush")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("TamaRush")] [assembly: AssemblyTitle("TamaRush")] [assembly: AssemblyVersion("1.0.0.0")] namespace TamaRush { [RequireComponent(typeof(AudioSource))] public sealed class TamaRushAudio : MonoBehaviour { private AudioSource _audioSource; private TamaEmulator _emu; private double _phase; private void Awake() { _audioSource = ((Component)this).GetComponent<AudioSource>(); _audioSource.playOnAwake = true; _audioSource.loop = true; _audioSource.volume = 1f; _audioSource.pitch = 1f; _audioSource.spatialBlend = 0f; _audioSource.bypassEffects = true; _audioSource.bypassListenerEffects = true; _audioSource.bypassReverbZones = true; _audioSource.outputAudioMixerGroup = null; if (!_audioSource.isPlaying) { _audioSource.Play(); } } public void SetEmulator(TamaEmulator emu) { _emu = emu; } private void OnAudioFilterRead(float[] data, int channels) { if (_emu != null && _emu.BuzzerEnabled) { ConfigEntry<bool> audioEnabled = TamaRushPlugin.AudioEnabled; if (audioEnabled == null || audioEnabled.Value) { float buzzerFreqHz = _emu.BuzzerFreqHz; if (buzzerFreqHz <= 0f) { for (int i = 0; i < data.Length; i++) { data[i] = 0f; } return; } int outputSampleRate = AudioSettings.outputSampleRate; double num = buzzerFreqHz / (float)outputSampleRate; int num2 = data.Length / channels; float num3 = Mathf.Clamp01((float)(TamaRushPlugin.AudioVolume?.Value ?? 5) / 10f) * 0.5f; for (int j = 0; j < num2; j++) { float num4 = ((_phase < 0.5) ? num3 : (0f - num3)); for (int k = 0; k < channels; k++) { data[j * channels + k] = num4; } _phase += num; if (_phase >= 1.0) { _phase -= 1.0; } } return; } } for (int l = 0; l < data.Length; l++) { data[l] = 0f; } _phase = 0.0; } } public class TamaRushPlayMode : MonoBehaviour { public static Action OnExitPlayMode; private GameObject _canvasGo; private TextMeshProUGUI _label; public static bool IsActive { get; private set; } public static TamaRushPlayMode Instance { get; private set; } private void Awake() { Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void EnsureUICreated() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvasGo != (Object)null)) { Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { UIManager uIManager = instance.UIManager; obj = ((uIManager != null) ? ((Component)uIManager).transform : null); } Transform val = (Transform)obj; if (!((Object)(object)val == (Object)null)) { _canvasGo = new GameObject("TamaRush_PlayModeHint"); _canvasGo.transform.SetParent(val, false); Canvas obj2 = _canvasGo.AddComponent<Canvas>(); obj2.renderMode = (RenderMode)0; obj2.sortingOrder = 200; _canvasGo.AddComponent<CanvasScaler>(); _canvasGo.AddComponent<GraphicRaycaster>(); GameObject val2 = new GameObject("HintLabel"); val2.transform.SetParent(_canvasGo.transform, false); _label = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_label).text = "-Dance Button To Exit Play Mode"; ((TMP_Text)_label).fontSize = 22f; ((Graphic)_label).color = Color.green; ((TMP_Text)_label).alignment = (TextAlignmentOptions)260; ((TMP_Text)_label).enableWordWrapping = true; RectTransform component = val2.GetComponent<RectTransform>(); component.anchorMin = new Vector2(1f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(1f, 1f); component.sizeDelta = new Vector2(400f, 80f); component.anchoredPosition = new Vector2(-16f, -10f); GameObject val3 = new GameObject("ControlsLabel"); val3.transform.SetParent(_canvasGo.transform, false); TextMeshProUGUI obj3 = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj3).text = "-Phone Left: Left Button\n-Phone Up: Center Button\n-Phone Right: Right Button\n-Phone Down: Tap"; ((TMP_Text)obj3).fontSize = 20f; ((Graphic)obj3).color = Color.yellow; ((TMP_Text)obj3).alignment = (TextAlignmentOptions)260; ((TMP_Text)obj3).enableWordWrapping = false; RectTransform component2 = val3.GetComponent<RectTransform>(); component2.anchorMin = new Vector2(1f, 1f); component2.anchorMax = new Vector2(1f, 1f); component2.pivot = new Vector2(1f, 1f); component2.sizeDelta = new Vector2(400f, 100f); component2.anchoredPosition = new Vector2(-16f, -90f); _canvasGo.SetActive(false); } } } public static void Enter() { IsActive = true; Instance?.EnsureUICreated(); if ((Object)(object)Instance?._canvasGo != (Object)null) { Instance._canvasGo.SetActive(true); } } public static void Exit() { if (IsActive) { IsActive = false; if ((Object)(object)Instance?._canvasGo != (Object)null) { Instance._canvasGo.SetActive(false); } OnExitPlayMode?.Invoke(); } } private void Update() { if (IsActive) { WorldHandler instance = WorldHandler.instance; Player val = ((instance != null) ? instance.GetCurrentPlayer() : null); if (!((Object)(object)val == (Object)null) && val.danceButtonNew) { Exit(); } } } } [BepInPlugin("com.tamarush", "TamaRush", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class TamaRushPlugin : BaseUnityPlugin { private static string _modFolder; private Harmony _harmony; public static ConfigEntry<bool> RunInBackground { get; private set; } public static ConfigEntry<int> GameSpeed { get; private set; } public static ConfigEntry<bool> AutoSave { get; private set; } public static ConfigEntry<int> LcdOption { get; private set; } public static ConfigEntry<string> SelectedBackground { get; private set; } public static ConfigEntry<string> SelectedIcons { get; private set; } public static ConfigEntry<bool> AudioEnabled { get; private set; } public static ConfigEntry<int> AudioVolume { get; private set; } public static ConfigEntry<int> PixelSize { get; private set; } public static ConfigEntry<string> SelectedRom { get; private set; } public static ConfigEntry<bool> DebugMode { get; private set; } public static string TamaRushFolderPath { get; private set; } public static string TamaRushAssetsFolderPath { get; private set; } public static string GetAppIconPath(string filename) { if (!string.IsNullOrEmpty(_modFolder)) { return Path.Combine(_modFolder, filename); } return null; } private void Awake() { //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Expected O, but got Unknown //IL_0243: Unknown result type (might be due to invalid IL or missing references) _modFolder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); RunInBackground = ((BaseUnityPlugin)this).Config.Bind<bool>("Game/ROM", "RunInBackground", false, "When enabled, the Tamagotchi keeps running after you leave play mode."); GameSpeed = ((BaseUnityPlugin)this).Config.Bind<int>("Game/ROM", "GameSpeed", 1, "Emulation speed multiplier. 1=Normal, 2=2x, 4=4x, 6=6x."); AutoSave = ((BaseUnityPlugin)this).Config.Bind<bool>("Saves", "AutoSave", true, "When enabled, automatically saves every 3 minutes while the emulator is running."); LcdOption = ((BaseUnityPlugin)this).Config.Bind<int>("Game/ROM", "LcdOption", 0, "LCD color scheme. 0=Mono, 1=Classic, 2=Green, 3=Inverted."); SelectedBackground = ((BaseUnityPlugin)this).Config.Bind<string>("Customize", "SelectedBackground", "", "Full path to the background image file."); SelectedIcons = ((BaseUnityPlugin)this).Config.Bind<string>("Customize", "SelectedIcons", "", "Full path to the icons spritesheet file."); AudioEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "AudioEnabled", true, "When enabled, the Tamagotchi buzzer sound plays."); AudioVolume = ((BaseUnityPlugin)this).Config.Bind<int>("Audio", "AudioVolume", 1, "Buzzer volume level 1-10."); PixelSize = ((BaseUnityPlugin)this).Config.Bind<int>("Game/ROM", "PixelSize", 32, "LCD pixel size in Unity units."); SelectedRom = ((BaseUnityPlugin)this).Config.Bind<string>("Game/ROM", "SelectedRom", "", "Full path to the ROM to load."); DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("DEBUG", "DebugMode", false, "Extra options that may run bad on lower end devices."); TamaRushFolderPath = Path.Combine(Paths.BepInExRootPath, "TamaRush"); Directory.CreateDirectory(TamaRushFolderPath); Directory.CreateDirectory(Path.Combine(TamaRushFolderPath, "Saves")); Directory.CreateDirectory(Path.Combine(TamaRushFolderPath, "Roms")); Directory.CreateDirectory(Path.Combine(TamaRushFolderPath, "Assets")); TamaRushAssetsFolderPath = Path.Combine(TamaRushFolderPath, "Assets"); Directory.CreateDirectory(Path.Combine(TamaRushAssetsFolderPath, "Icons")); Directory.CreateDirectory(Path.Combine(TamaRushAssetsFolderPath, "Background")); RenameBFilesToBin(); _harmony = new Harmony("com.tamarush"); _harmony.PatchAll(); new GameObject("TamaRush_PlayMode").AddComponent<TamaRushPlayMode>(); AppTamaRush.Initialize(); } public static string GetAssetSubfolderPath() { if (!string.IsNullOrEmpty(TamaRushAssetsFolderPath)) { return TamaRushAssetsFolderPath; } return null; } public static string GetAssetFolderFile(string subfolder) { if (string.IsNullOrEmpty(TamaRushAssetsFolderPath)) { return null; } string text = ((!(subfolder == "Background")) ? ((!(subfolder == "Icons")) ? null : SelectedIcons?.Value) : SelectedBackground?.Value); if (!string.IsNullOrEmpty(text) && File.Exists(text)) { return text; } string path = Path.Combine(TamaRushAssetsFolderPath, subfolder); if (!Directory.Exists(path)) { return null; } string[] files = Directory.GetFiles(path, "*.*"); foreach (string text2 in files) { switch (Path.GetExtension(text2).ToLowerInvariant()) { case ".png": case ".jpg": case ".jpeg": return text2; } } return null; } private static void RenameBFilesToBin() { string text = Path.Combine(TamaRushFolderPath, "Roms"); if (!Directory.Exists(text)) { return; } string[] files = Directory.GetFiles(text, "*.b"); foreach (string text2 in files) { try { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text2); string text3 = Path.Combine(text, fileNameWithoutExtension + ".bin"); if (!File.Exists(text3)) { File.Move(text2, text3); } } catch (Exception) { } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.tamarush"; public const string PLUGIN_NAME = "TamaRush"; public const string PLUGIN_VERSION = "1.0.0"; } public static class MyPluginInfo { public const string PLUGIN_GUID = "TamaRush"; public const string PLUGIN_NAME = "TamaRush"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace TamaRush.TMEmulator { public enum TamaButton { Left, Middle, Right, Tap } public class TamaEmulator { public readonly bool[,] LcdMatrix = new bool[32, 16]; public readonly bool[] LcdIcons = new bool[8]; public readonly object Lock = new object(); public volatile bool LcdDirty; public volatile bool BuzzerEnabled; public volatile float BuzzerFreqHz; public const int LCD_W = 32; public const int LCD_H = 16; public const int ICON_NUM = 8; private const int TICK_FREQ = 32768; private const int OSC1_FREQ = 32768; private const int OSC3_FREQ = 1000000; private const int TIMER_2HZ = 16384; private const int TIMER_4HZ = 8192; private const int TIMER_8HZ = 4096; private const int TIMER_16HZ = 2048; private const int TIMER_32HZ = 1024; private const int TIMER_64HZ = 512; private const int TIMER_128HZ = 256; private const int TIMER_256HZ = 128; private const int MEM_RAM_ADDR = 0; private const int MEM_RAM_SIZE = 640; private const int MEM_DISP1_ADDR = 3584; private const int MEM_DISP1_SIZE = 80; private const int MEM_DISP2_ADDR = 3712; private const int MEM_DISP2_SIZE = 80; private const int MEM_IO_ADDR = 3840; private const int MEM_IO_SIZE = 128; private const int MEM_BUF_SIZE = 464; private readonly byte[] _mem = new byte[464]; private const int REG_CLK_INT_FACTOR = 3840; private const int REG_SW_INT_FACTOR = 3841; private const int REG_PROG_INT_FACTOR = 3842; private const int REG_SERIAL_INT_FACTOR = 3843; private const int REG_K00_INT_FACTOR = 3844; private const int REG_K10_INT_FACTOR = 3845; private const int REG_CLK_INT_MASK = 3856; private const int REG_SW_INT_MASK = 3857; private const int REG_PROG_INT_MASK = 3858; private const int REG_SERIAL_INT_MASK = 3859; private const int REG_K00_INT_MASK = 3860; private const int REG_K10_INT_MASK = 3861; private const int REG_CLK_TIMER_DATA1 = 3872; private const int REG_CLK_TIMER_DATA2 = 3873; private const int REG_PROG_TIMER_DATA_L = 3876; private const int REG_PROG_TIMER_DATA_H = 3877; private const int REG_PROG_TIMER_RLD_L = 3878; private const int REG_PROG_TIMER_RLD_H = 3879; private const int REG_K00_INPUT_PORT = 3904; private const int REG_K00_INPUT_REL = 3905; private const int REG_K10_INPUT_PORT = 3906; private const int REG_CPU_OSC3_CTRL = 3952; private const int REG_LCD_CTRL = 3953; private const int REG_SVD_CTRL = 3955; private const int REG_BUZZER_CTRL1 = 3956; private const int REG_BUZZER_CTRL2 = 3957; private const int REG_PROG_TIMER_CTRL = 3960; private const int REG_PROG_TIMER_CLK = 3961; private const int REG_R40_BZ_OUTPUT = 3924; private int _pc; private int _nextPc; private int _x; private int _y; private byte _a; private byte _b; private byte _np; private byte _sp; private byte _flags; private const byte FLAG_C = 1; private const byte FLAG_Z = 2; private const byte FLAG_D = 4; private const byte FLAG_I = 8; private const int INT_PROG = 0; private const int INT_SERIAL = 1; private const int INT_K10 = 2; private const int INT_K00 = 3; private const int INT_SW = 4; private const int INT_CLK = 5; private const int INT_NUM = 6; private readonly byte[] _intFactor = new byte[6]; private readonly byte[] _intMask = new byte[6]; private readonly bool[] _intTriggered = new bool[6]; private readonly byte[] _intVector = new byte[6] { 12, 10, 8, 6, 4, 2 }; private uint _tickCounter; private uint _clk2hz; private uint _clk4hz; private uint _clk8hz; private uint _clk16hz; private uint _clk32hz; private uint _clk64hz; private uint _clk128hz; private uint _clk256hz; private uint _progTimerTs; private bool _progTimerEnabled; private byte _progTimerData; private byte _progTimerRld; private long _refTicks; private uint _scaledCycleAcc; private int _cpuFreq = 32768; private byte _prevCycles; private bool _cpuHalted; private byte _inputK0 = 15; private byte _inputK1 = 15; private ushort[] _rom; private static readonly byte[] SegPos; private static readonly float[] _buzzerFreqTable; private readonly Stopwatch _sw = Stopwatch.StartNew(); private static readonly (int code, int mask, int shiftArg0, int maskArg0, byte cycles)[] _ops; private static readonly byte[] _opCycles; public int LcdWriteCount { get; private set; } public long StepCount { get; private set; } public int PC_Snapshot { get; private set; } public int IntCount { get; private set; } public bool InterruptsEnabled => (_flags & 8) != 0; public int PC { get { return _pc; } set { _pc = value; } } public int X { get { return _x; } set { _x = value; } } public int Y { get { return _y; } set { _y = value; } } public byte A { get { return _a; } set { _a = (byte)(value & 0xFu); } } public byte B { get { return _b; } set { _b = (byte)(value & 0xFu); } } public byte NP { get { return _np; } set { _np = (byte)(value & 0x1Fu); } } public byte SP { get { return _sp; } set { _sp = value; } } public byte Flags { get { return _flags; } set { _flags = (byte)(value & 0xFu); } } public uint TickCounter { get { return _tickCounter; } set { _tickCounter = value; } } public uint Clk2Hz { get { return _clk2hz; } set { _clk2hz = value; } } public uint Clk4Hz { get { return _clk4hz; } set { _clk4hz = value; } } public uint Clk8Hz { get { return _clk8hz; } set { _clk8hz = value; } } public uint Clk16Hz { get { return _clk16hz; } set { _clk16hz = value; } } public uint Clk32Hz { get { return _clk32hz; } set { _clk32hz = value; } } public uint Clk64Hz { get { return _clk64hz; } set { _clk64hz = value; } } public uint Clk128Hz { get { return _clk128hz; } set { _clk128hz = value; } } public uint Clk256Hz { get { return _clk256hz; } set { _clk256hz = value; } } public uint ProgTimerTs { get { return _progTimerTs; } set { _progTimerTs = value; } } public bool ProgTimerEnabled { get { return _progTimerEnabled; } set { _progTimerEnabled = value; } } public byte ProgTimerData { get { return _progTimerData; } set { _progTimerData = value; } } public byte ProgTimerRld { get { return _progTimerRld; } set { _progTimerRld = value; } } public byte[] IntFactor => _intFactor; public byte[] IntMask => _intMask; public bool[] IntTriggered => _intTriggered; private byte PCS => (byte)((uint)_pc & 0xFFu); private byte PCSL => (byte)((uint)_pc & 0xFu); private byte PCSH => (byte)((uint)(_pc >> 4) & 0xFu); private byte PCP => (byte)((uint)(_pc >> 8) & 0xFu); private byte PCB => (byte)((uint)(_pc >> 12) & 1u); private byte NBP => (byte)((uint)(_np >> 4) & 1u); private byte NPP => (byte)(_np & 0xFu); private byte XHL => (byte)((uint)_x & 0xFFu); private byte XL => (byte)((uint)_x & 0xFu); private byte XH => (byte)((uint)(_x >> 4) & 0xFu); private byte XP => (byte)((uint)(_x >> 8) & 0xFu); private byte YHL => (byte)((uint)_y & 0xFFu); private byte YL => (byte)((uint)_y & 0xFu); private byte YH => (byte)((uint)(_y >> 4) & 0xFu); private byte YP => (byte)((uint)(_y >> 8) & 0xFu); private byte SPL => (byte)(_sp & 0xFu); private byte SPH => (byte)((uint)(_sp >> 4) & 0xFu); private bool C => (_flags & 1) != 0; private bool Z => (_flags & 2) != 0; private bool D => (_flags & 4) != 0; public byte GetRamNibble(int i) { return GetNibble(RamIdx(i), i); } public void SetRamNibble(int i, byte v) { SetNibble(RamIdx(i), i, v); } public byte GetIONibble(int i) { return GetIOMem(3840 + i); } public void SetIONibble(int i, byte v) { SetIOMem(3840 + i, v); } public void Init(ushort[] rom) { _rom = rom; Reset(); } public void Reset() { Array.Clear(_mem, 0, _mem.Length); Array.Clear(_intFactor, 0, 6); Array.Clear(_intMask, 0, 6); Array.Clear(_intTriggered, 0, 6); _pc = ToPC(0, 1, 0); _np = ToNP(0, 1); _a = (_b = 0); _x = (_y = 0); _sp = 0; _flags = 0; _tickCounter = 0u; _clk2hz = (_clk4hz = (_clk8hz = (_clk16hz = 0u))); _clk32hz = (_clk64hz = (_clk128hz = (_clk256hz = 0u))); _progTimerTs = 0u; _progTimerEnabled = false; _progTimerData = (_progTimerRld = 0); _cpuHalted = false; _cpuFreq = 32768; _scaledCycleAcc = 0u; _prevCycles = 0; SetIOMem(3953, 8); SetIOMem(3905, 15); SetIOMem(3924, 15); _inputK0 = 15; _inputK1 = 15; _refTicks = _sw.ElapsedTicks; } public void Step(int speedMultiplier = 1) { int num = 12; if (!_cpuHalted) { int num2 = _pc & 0x1FFF; ushort op = (ushort)((num2 < _rom.Length) ? _rom[num2] : 4091); num = FindOp(op); if (num < 0) { num = 12; } _nextPc = (_pc + 1) & 0x1FFF; _refTicks = WaitForCycles(_refTicks, _prevCycles, speedMultiplier); ExecuteOp(num, op); _pc = _nextPc; _prevCycles = _opCycles[num]; if (num != 0) { _np = (byte)((uint)(_pc >> 8) & 0x1Fu); } } else { _refTicks = WaitForCycles(_refTicks, 5, speedMultiplier); _prevCycles = 0; num = -1; } HandleTimers(); if ((_flags & 8u) != 0 && num != 0 && num != 58) { ProcessInterrupts(speedMultiplier); } StepCount++; PC_Snapshot = _pc; } public void SetButton(TamaButton btn, bool pressed) { byte state = (byte)((!pressed) ? 1 : 0); switch (btn) { case TamaButton.Left: SetInputPin(0, 0, state); break; case TamaButton.Middle: SetInputPin(0, 1, state); break; case TamaButton.Right: SetInputPin(0, 2, state); break; case TamaButton.Tap: SetInputPin(0, 3, state); break; } } private int RamIdx(int n) { return n / 2; } private int Disp1Idx(int n) { return (n - 3584 + 640) / 2; } private int Disp2Idx(int n) { return (n - 3712 + 640 + 80) / 2; } private int IOIdx(int n) { return (n - 3840 + 640 + 80 + 80) / 2; } private byte GetNibble(int bufIdx, int addr) { int num = (addr & 1) << 2; return (byte)((uint)(_mem[bufIdx] >> num) & 0xFu); } private void SetNibble(int bufIdx, int addr, byte v) { int num = (addr & 1) << 2; _mem[bufIdx] = (byte)((_mem[bufIdx] & ~(15 << num)) | ((v & 0xF) << num)); } private byte GetMem(int n) { if (n < 640) { return GetNibble(RamIdx(n), n); } if (n >= 3584 && n < 3664) { return GetNibble(Disp1Idx(n), n); } if (n >= 3712 && n < 3792) { return GetNibble(Disp2Idx(n), n); } if (n >= 3840 && n < 3968) { return GetIO(n); } return 0; } private void SetMem(int n, byte v) { if (n < 640) { SetNibble(RamIdx(n), n, v); } else if (n >= 3584 && n < 3664) { SetNibble(Disp1Idx(n), n, v); SetLcd(n, v); } else if (n >= 3712 && n < 3792) { SetNibble(Disp2Idx(n), n, v); SetLcd(n, v); } else if (n >= 3840 && n < 3968) { SetNibble(IOIdx(n), n, v); SetIO(n, v); } } private byte GetIOMem(int n) { return GetNibble(IOIdx(n), n); } private void SetIOMem(int n, byte v) { SetNibble(IOIdx(n), n, v); } private byte GetIO(int n) { switch (n) { case 3840: { byte result2 = _intFactor[5]; _intFactor[5] = 0; return result2; } case 3841: { byte result = _intFactor[4]; _intFactor[4] = 0; return result; } case 3842: { byte result3 = _intFactor[0]; _intFactor[0] = 0; return result3; } case 3843: { byte result6 = _intFactor[1]; _intFactor[1] = 0; return result6; } case 3844: { byte result5 = _intFactor[3]; _intFactor[3] = 0; return result5; } case 3845: { byte result4 = _intFactor[2]; _intFactor[2] = 0; return result4; } case 3856: return _intMask[5]; case 3857: return (byte)(_intMask[4] & 3u); case 3858: return (byte)(_intMask[0] & 1u); case 3859: return (byte)(_intMask[1] & 1u); case 3860: return _intMask[3]; case 3861: return _intMask[2]; case 3872: return GetIOMem(n); case 3873: return GetIOMem(n); case 3876: return (byte)(_progTimerData & 0xFu); case 3877: return (byte)((uint)(_progTimerData >> 4) & 0xFu); case 3878: return (byte)(_progTimerRld & 0xFu); case 3879: return (byte)((uint)(_progTimerRld >> 4) & 0xFu); case 3904: return _inputK0; case 3905: return GetIOMem(n); case 3906: return _inputK1; case 3952: return GetIOMem(n); case 3953: return GetIOMem(n); case 3955: return (byte)(GetIOMem(n) & 7u); case 3960: return (byte)(_progTimerEnabled ? 1u : 0u); default: return 0; } } private void SetIO(int n, byte v) { switch (n) { case 3856: _intMask[5] = v; break; case 3857: _intMask[4] = v; break; case 3858: _intMask[0] = v; break; case 3859: _intMask[1] = v; break; case 3860: _intMask[3] = v; break; case 3861: _intMask[2] = v; break; case 3878: _progTimerRld = (byte)((_progTimerRld & 0xF0u) | (v & 0xFu)); break; case 3879: _progTimerRld = (byte)((_progTimerRld & 0xFu) | (uint)((v & 0xF) << 4)); break; case 3952: if ((v & 8u) != 0 && _cpuFreq != 1000000) { _cpuFreq = 1000000; _scaledCycleAcc = 0u; } if ((v & 8) == 0 && _cpuFreq != 32768) { _cpuFreq = 32768; _scaledCycleAcc = 0u; } break; case 3924: BuzzerEnabled = (v & 8) == 0; break; case 3956: BuzzerFreqHz = _buzzerFreqTable[v & 7]; break; case 3960: if ((v & 2u) != 0) { _progTimerData = _progTimerRld; } if (((uint)v & (true ? 1u : 0u)) != 0 && !_progTimerEnabled) { _progTimerTs = _tickCounter; } _progTimerEnabled = (v & 1) != 0; break; } } private void SetLcd(int n, byte v) { LcdWriteCount++; int seg = (n & 0x7F) >> 1; int num = ((n & 0x80) >> 7) * 8 + (n & 1) * 4; for (int i = 0; i < 4; i++) { SetLcdPin(seg, num + i, (byte)((uint)(v >> i) & 1u)); } } private void SetLcdPin(int seg, int com, byte val) { if (seg >= SegPos.Length) { return; } int num = SegPos[seg]; if (num < 32) { lock (Lock) { LcdMatrix[num, com] = val != 0; LcdDirty = true; return; } } if (seg == 8 && com < 4) { lock (Lock) { LcdIcons[com] = val != 0; LcdDirty = true; return; } } if (seg != 28 || com < 12) { return; } lock (Lock) { LcdIcons[com - 8] = val != 0; LcdDirty = true; } } private void SetInputPin(int port, int bit, byte state) { if (port == 0) { byte b = (byte)((uint)(_inputK0 >> bit) & 1u); if (state != b) { byte iOMem = GetIOMem(3905); if (state != ((iOMem >> bit) & 1)) { GenerateInterrupt(3, bit); } } _inputK0 = (byte)((_inputK0 & ~(1 << bit)) | (state << bit)); } else { byte b2 = (byte)((uint)(_inputK1 >> bit) & 1u); if (state != b2 && state == 0) { GenerateInterrupt(2, bit); } _inputK1 = (byte)((_inputK1 & ~(1 << bit)) | (state << bit)); } } private void GenerateInterrupt(int slot, int bit) { _intFactor[slot] |= (byte)(1 << bit); if ((_intMask[slot] & (1 << bit)) != 0) { _intTriggered[slot] = true; } } private void HandleTimers() { TickTimer(ref _clk2hz, 16384u, delegate { SetIOMem(3873, (byte)(GetIOMem(3873) ^ 8u)); if ((GetIOMem(3873) & 8) == 0) { GenerateInterrupt(5, 3); } }); TickTimer(ref _clk4hz, 8192u, delegate { SetIOMem(3873, (byte)(GetIOMem(3873) ^ 4u)); if ((GetIOMem(3873) & 4) == 0) { GenerateInterrupt(5, 2); } }); TickTimer(ref _clk8hz, 4096u, delegate { SetIOMem(3873, (byte)(GetIOMem(3873) ^ 2u)); }); TickTimer(ref _clk16hz, 2048u, delegate { SetIOMem(3873, (byte)(GetIOMem(3873) ^ 1u)); if ((GetIOMem(3873) & 1) == 0) { GenerateInterrupt(5, 1); } }); TickTimer(ref _clk32hz, 1024u, delegate { SetIOMem(3872, (byte)(GetIOMem(3872) ^ 8u)); }); TickTimer(ref _clk64hz, 512u, delegate { SetIOMem(3872, (byte)(GetIOMem(3872) ^ 4u)); if ((GetIOMem(3872) & 4) == 0) { GenerateInterrupt(5, 0); } }); TickTimer(ref _clk128hz, 256u, delegate { SetIOMem(3872, (byte)(GetIOMem(3872) ^ 2u)); }); TickTimer(ref _clk256hz, 128u, delegate { SetIOMem(3872, (byte)(GetIOMem(3872) ^ 1u)); }); if (!_progTimerEnabled) { return; } while (_tickCounter - _progTimerTs >= 128) { _progTimerTs += 128u; _progTimerData--; if (_progTimerData == 0) { _progTimerData = _progTimerRld; GenerateInterrupt(0, 0); } } } private void TickTimer(ref uint ts, uint period, Action onTick) { if (_tickCounter - ts >= period) { do { ts += period; } while (_tickCounter - ts >= period); onTick(); } } private void ProcessInterrupts(int speedMultiplier = 1) { for (int i = 0; i < 6; i++) { if (_intTriggered[i]) { SetMem((_sp - 1) & 0xFF, PCP); SetMem((_sp - 2) & 0xFF, PCSH); SetMem((_sp - 3) & 0xFF, PCSL); _sp = (byte)((uint)(_sp - 3) & 0xFFu); _flags &= 247; _np = ToNP(NBP, 1); _pc = ToPC(PCB, 1, _intVector[i]); _cpuHalted = false; IntCount++; _refTicks = WaitForCycles(_refTicks, 12, speedMultiplier); _intTriggered[i] = false; break; } } } private long GetTimestampUs() { return _sw.ElapsedTicks * 1000000 / Stopwatch.Frequency; } private long WaitForCycles(long sinceTicks, byte cycles, int speedMultiplier = 1) { if (cycles == 0) { return sinceTicks; } _scaledCycleAcc += (uint)(cycles * 32768); uint num = _scaledCycleAcc / (uint)_cpuFreq; if (num != 0) { _tickCounter += num; _scaledCycleAcc -= (uint)((int)num * _cpuFreq); } int num2 = ((speedMultiplier <= 0) ? 1 : speedMultiplier); long num3 = sinceTicks + cycles * Stopwatch.Frequency / (_cpuFreq * num2); if (speedMultiplier == 0) { return _sw.ElapsedTicks; } long num4 = num3 - _sw.ElapsedTicks; if (num4 > 0) { long num5 = num4 * 1000 / Stopwatch.Frequency; if (num5 > 1) { Thread.Sleep((int)(num5 - 1)); } while (_sw.ElapsedTicks < num3) { } } return num3; } private static int ToPC(int bank, int page, int step) { return (step & 0xFF) | ((page & 0xF) << 8) | ((bank & 1) << 12); } private static byte ToNP(int bank, int page) { return (byte)(((uint)page & 0xFu) | (uint)((bank & 1) << 4)); } private byte GetRQ(int rq) { return (rq & 3) switch { 0 => _a, 1 => _b, 2 => GetMem(_x), 3 => GetMem(_y), _ => 0, }; } private void SetRQ(int rq, byte v) { switch (rq & 3) { case 0: _a = (byte)(v & 0xFu); break; case 1: _b = (byte)(v & 0xFu); break; case 2: SetMem(_x, v); break; case 3: SetMem(_y, v); break; } } static TamaEmulator() { SegPos = new byte[40] { 0, 1, 2, 3, 4, 5, 6, 7, 32, 8, 9, 10, 11, 12, 13, 14, 15, 33, 34, 35, 31, 30, 29, 28, 27, 26, 25, 24, 36, 23, 22, 21, 20, 19, 18, 17, 16, 37, 38, 39 }; _buzzerFreqTable = new float[8] { 4096f, 3276.8f, 2730.7f, 2340.6f, 2048f, 1638.4f, 1365.3f, 1170.3f }; _ops = new(int, int, int, int, byte)[108] { (3648, 4064, 0, 0, 5), (0, 3840, 0, 0, 5), (512, 3840, 0, 0, 5), (768, 3840, 0, 0, 5), (1536, 3840, 0, 0, 5), (1792, 3840, 0, 0, 5), (4072, 4095, 0, 0, 5), (1024, 3840, 0, 0, 7), (1280, 3840, 0, 0, 7), (4063, 4095, 0, 0, 7), (4062, 4095, 0, 0, 12), (256, 3840, 0, 0, 12), (4091, 4095, 0, 0, 5), (4095, 4095, 0, 0, 7), (4088, 4095, 0, 0, 5), (3808, 4095, 0, 0, 5), (3824, 4095, 0, 0, 5), (2816, 3840, 0, 0, 5), (2048, 3840, 0, 0, 5), (3712, 4092, 0, 0, 5), (3716, 4092, 0, 0, 5), (3720, 4092, 0, 0, 5), (3728, 4092, 0, 0, 5), (3732, 4092, 0, 0, 5), (3736, 4092, 0, 0, 5), (3744, 4092, 0, 0, 5), (3748, 4092, 0, 0, 5), (3752, 4092, 0, 0, 5), (3760, 4092, 0, 0, 5), (3764, 4092, 0, 0, 5), (3768, 4092, 0, 0, 5), (2560, 4080, 0, 0, 7), (2576, 4080, 0, 0, 7), (2592, 4080, 0, 0, 7), (2608, 4080, 0, 0, 7), (2624, 4080, 0, 0, 7), (2640, 4080, 0, 0, 7), (2656, 4080, 0, 0, 7), (2672, 4080, 0, 0, 7), (3584, 4032, 4, 48, 5), (3776, 4080, 2, 12, 5), (4000, 4080, 0, 0, 5), (4016, 4080, 0, 0, 5), (3968, 4080, 0, 0, 5), (3984, 4080, 0, 0, 5), (3680, 4080, 0, 0, 5), (3808, 4080, 2, 12, 5), (3696, 4080, 0, 0, 5), (3824, 4080, 2, 12, 5), (2304, 3840, 0, 0, 5), (3904, 4080, 0, 0, 7), (3920, 4080, 0, 0, 7), (3905, 4095, 0, 0, 7), (3934, 4095, 0, 0, 7), (3906, 4095, 0, 0, 7), (3933, 4095, 0, 0, 7), (3908, 4095, 0, 0, 7), (3931, 4095, 0, 0, 7), (3912, 4095, 0, 0, 7), (3927, 4095, 0, 0, 7), (4059, 4095, 0, 0, 5), (4043, 4095, 0, 0, 5), (4032, 4092, 0, 0, 5), (4036, 4095, 0, 0, 5), (4037, 4095, 0, 0, 5), (4038, 4095, 0, 0, 5), (4039, 4095, 0, 0, 5), (4040, 4095, 0, 0, 5), (4041, 4095, 0, 0, 5), (4042, 4095, 0, 0, 5), (4048, 4092, 0, 0, 5), (4052, 4095, 0, 0, 5), (4053, 4095, 0, 0, 5), (4054, 4095, 0, 0, 5), (4055, 4095, 0, 0, 5), (4056, 4095, 0, 0, 5), (4057, 4095, 0, 0, 5), (4058, 4095, 0, 0, 5), (4064, 4092, 0, 0, 5), (4080, 4092, 0, 0, 5), (4068, 4092, 0, 0, 5), (4084, 4092, 0, 0, 5), (3072, 4032, 4, 48, 7), (2688, 4080, 2, 12, 7), (3136, 4032, 4, 48, 7), (2704, 4080, 2, 12, 7), (2720, 4080, 2, 12, 7), (3392, 4032, 4, 48, 7), (2736, 4080, 2, 12, 7), (3200, 4032, 4, 48, 7), (2752, 4080, 2, 12, 7), (3264, 4032, 4, 48, 7), (2768, 4080, 2, 12, 7), (3328, 4032, 4, 48, 7), (2784, 4080, 2, 12, 7), (3520, 4032, 4, 48, 7), (3840, 4080, 2, 12, 7), (3456, 4032, 4, 48, 7), (3856, 4080, 2, 12, 7), (2800, 4080, 0, 0, 7), (3724, 4092, 0, 0, 5), (3936, 4080, 0, 0, 7), (3952, 4080, 0, 0, 7), (3880, 4092, 0, 0, 7), (3884, 4092, 0, 0, 7), (3896, 4092, 0, 0, 7), (3900, 4092, 0, 0, 7), (3343, 4047, 4, 0, 7) }; _opCycles = new byte[_ops.Length]; for (int i = 0; i < _ops.Length; i++) { _opCycles[i] = _ops[i].cycles; } } private int FindOp(ushort op) { for (int i = 0; i < _ops.Length; i++) { if ((op & _ops[i].mask) == _ops[i].code) { return i; } } return -1; } private void ExecuteOp(int opIdx, int op) { (int, int, int, int, byte) tuple = _ops[opIdx]; int num = ((tuple.Item4 != 0) ? ((op & tuple.Item4) >> tuple.Item3) : ((op & ~tuple.Item2) >> tuple.Item3)); int num2 = ((tuple.Item4 != 0) ? (op & ~(tuple.Item2 | tuple.Item4)) : 0); switch (opIdx) { case 0: _np = (byte)((uint)num & 0x1Fu); break; case 1: _nextPc = (op & 0xFF) | (_np << 8); break; case 2: if (C) { _nextPc = (op & 0xFF) | (_np << 8); } break; case 3: if (!C) { _nextPc = (op & 0xFF) | (_np << 8); } break; case 4: if (Z) { _nextPc = (op & 0xFF) | (_np << 8); } break; case 5: if (!Z) { _nextPc = (op & 0xFF) | (_np << 8); } break; case 6: _nextPc = _a | (_b << 4) | (_np << 8); break; case 7: { int num24 = (_pc + 1) & 0x1FFF; SetMem((_sp - 1) & 0xFF, (byte)((uint)(num24 >> 8) & 0xFu)); SetMem((_sp - 2) & 0xFF, (byte)((uint)(num24 >> 4) & 0xFu)); SetMem((_sp - 3) & 0xFF, (byte)((uint)num24 & 0xFu)); _sp = (byte)((uint)(_sp - 3) & 0xFFu); _nextPc = ToPC(PCB, NPP, op & 0xFF); break; } case 8: { int num21 = (_pc + 1) & 0x1FFF; SetMem((_sp - 1) & 0xFF, (byte)((uint)(num21 >> 8) & 0xFu)); SetMem((_sp - 2) & 0xFF, (byte)((uint)(num21 >> 4) & 0xFu)); SetMem((_sp - 3) & 0xFF, (byte)((uint)num21 & 0xFu)); _sp = (byte)((uint)(_sp - 3) & 0xFFu); _nextPc = ToPC(PCB, 0, op & 0xFF); break; } case 9: _nextPc = GetMem(_sp) | (GetMem((_sp + 1) & 0xFF) << 4) | (GetMem((_sp + 2) & 0xFF) << 8) | (PCB << 12); _sp = (byte)((uint)(_sp + 3) & 0xFFu); break; case 10: _nextPc = GetMem(_sp) | (GetMem((_sp + 1) & 0xFF) << 4) | (GetMem((_sp + 2) & 0xFF) << 8) | (PCB << 12); _sp = (byte)((uint)(_sp + 3) & 0xFFu); _nextPc = (_nextPc + 1) & 0x1FFF; break; case 11: { _nextPc = GetMem(_sp) | (GetMem((_sp + 1) & 0xFF) << 4) | (GetMem((_sp + 2) & 0xFF) << 8) | (PCB << 12); _sp = (byte)((uint)(_sp + 3) & 0xFFu); byte b3 = (byte)((uint)op & 0xFFu); SetMem(_x, (byte)(b3 & 0xFu)); SetMem(((_x + 1) & 0xFF) | (XP << 8), (byte)((uint)(b3 >> 4) & 0xFu)); _x = ((_x + 2) & 0xFF) | (XP << 8); break; } case 14: _cpuHalted = true; break; case 15: _x = ((_x + 1) & 0xFF) | (XP << 8); break; case 16: _y = ((_y + 1) & 0xFF) | (YP << 8); break; case 17: _x = (op & 0xFF) | (XP << 8); break; case 18: _y = (op & 0xFF) | (YP << 8); break; case 19: _x = XHL | (GetRQ(op & 3) << 8); break; case 20: _x = XL | (GetRQ(op & 3) << 4) | (XP << 8); break; case 21: _x = GetRQ(op & 3) | (XH << 4) | (XP << 8); break; case 22: _y = YHL | (GetRQ(op & 3) << 8); break; case 23: _y = YL | (GetRQ(op & 3) << 4) | (YP << 8); break; case 24: _y = GetRQ(op & 3) | (YH << 4) | (YP << 8); break; case 25: SetRQ(op & 3, XP); break; case 26: SetRQ(op & 3, XH); break; case 27: SetRQ(op & 3, XL); break; case 28: SetRQ(op & 3, YP); break; case 29: SetRQ(op & 3, YH); break; case 30: SetRQ(op & 3, YL); break; case 31: { int num22 = XH + (op & 0xF) + (C ? 1 : 0); _x = XL | ((num22 & 0xF) << 4) | (XP << 8); if (num22 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if ((num22 & 0xF) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 32: { int num18 = XL + (op & 0xF) + (C ? 1 : 0); _x = (num18 & 0xF) | (XH << 4) | (XP << 8); if (num18 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if ((num18 & 0xF) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 33: { int num4 = YH + (op & 0xF) + (C ? 1 : 0); _y = YL | ((num4 & 0xF) << 4) | (YP << 8); if (num4 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if ((num4 & 0xF) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 34: { int num26 = YL + (op & 0xF) + (C ? 1 : 0); _y = (num26 & 0xF) | (YH << 4) | (YP << 8); if (num26 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if ((num26 & 0xF) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 35: { int num23 = op & 0xF; if (XH < num23) { _flags |= 1; } else { _flags &= 254; } if (XH == num23) { _flags |= 2; } else { _flags &= 253; } break; } case 36: { int num20 = op & 0xF; if (XL < num20) { _flags |= 1; } else { _flags &= 254; } if (XL == num20) { _flags |= 2; } else { _flags &= 253; } break; } case 37: { int num17 = op & 0xF; if (YH < num17) { _flags |= 1; } else { _flags &= 254; } if (YH == num17) { _flags |= 2; } else { _flags &= 253; } break; } case 38: { int num14 = op & 0xF; if (YL < num14) { _flags |= 1; } else { _flags &= 254; } if (YL == num14) { _flags |= 2; } else { _flags &= 253; } break; } case 39: SetRQ(num, (byte)((uint)num2 & 0xFu)); break; case 40: SetRQ(num, GetRQ(num2)); break; case 41: _a = (byte)(GetMem(op & 0xF) & 0xFu); break; case 42: _b = (byte)(GetMem(op & 0xF) & 0xFu); break; case 43: SetMem(op & 0xF, _a); break; case 44: SetMem(op & 0xF, _b); break; case 45: SetMem(_x, (byte)((uint)op & 0xFu)); _x = ((_x + 1) & 0xFF) | (XP << 8); break; case 46: SetRQ(num, GetRQ(num2)); _x = ((_x + 1) & 0xFF) | (XP << 8); break; case 47: SetMem(_y, (byte)((uint)op & 0xFu)); _y = ((_y + 1) & 0xFF) | (YP << 8); break; case 48: SetRQ(num, GetRQ(num2)); _y = ((_y + 1) & 0xFF) | (YP << 8); break; case 49: { byte b2 = (byte)((uint)op & 0xFFu); SetMem(_x, (byte)(b2 & 0xFu)); SetMem(((_x + 1) & 0xFF) | (XP << 8), (byte)((uint)(b2 >> 4) & 0xFu)); _x = ((_x + 2) & 0xFF) | (XP << 8); break; } case 50: _flags |= (byte)(op & 0xF); break; case 51: _flags &= (byte)(op & 0xF); break; case 52: _flags |= 1; break; case 53: _flags &= 254; break; case 54: _flags |= 2; break; case 55: _flags &= 253; break; case 56: _flags |= 4; break; case 57: _flags &= 251; break; case 58: _flags |= 8; break; case 59: _flags &= 247; break; case 60: _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 61: _sp = (byte)((uint)(_sp - 1) & 0xFFu); break; case 62: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, GetRQ(op & 3)); break; case 63: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, XP); break; case 64: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, XH); break; case 65: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, XL); break; case 66: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, YP); break; case 67: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, YH); break; case 68: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, YL); break; case 69: _sp = (byte)((uint)(_sp - 1) & 0xFFu); SetMem(_sp, _flags); break; case 70: SetRQ(op & 3, GetMem(_sp)); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 71: _x = XL | (XH << 4) | (GetMem(_sp) << 8); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 72: _x = XL | (GetMem(_sp) << 4) | (XP << 8); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 73: _x = GetMem(_sp) | (XH << 4) | (XP << 8); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 74: _y = YL | (YH << 4) | (GetMem(_sp) << 8); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 75: _y = YL | (GetMem(_sp) << 4) | (YP << 8); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 76: _y = GetMem(_sp) | (YH << 4) | (YP << 8); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 77: _flags = (byte)(GetMem(_sp) & 0xFu); _sp = (byte)((uint)(_sp + 1) & 0xFFu); break; case 78: _sp = (byte)(SPL | (GetRQ(op & 3) << 4)); break; case 79: _sp = (byte)(GetRQ(op & 3) | (SPH << 4)); break; case 80: SetRQ(op & 3, SPH); break; case 81: SetRQ(op & 3, SPL); break; case 82: { int num27 = GetRQ(num) + num2; if (D) { if (num27 >= 10) { SetRQ(num, (byte)((uint)(num27 - 10) & 0xFu)); _flags |= 1; } else { SetRQ(num, (byte)num27); _flags &= 254; } } else { SetRQ(num, (byte)((uint)num27 & 0xFu)); if (num27 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 83: { int num25 = GetRQ(num) + GetRQ(num2); if (D) { if (num25 >= 10) { SetRQ(num, (byte)((uint)(num25 - 10) & 0xFu)); _flags |= 1; } else { SetRQ(num, (byte)num25); _flags &= 254; } } else { SetRQ(num, (byte)((uint)num25 & 0xFu)); if (num25 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 84: { int num16 = GetRQ(num) + num2 + (C ? 1 : 0); if (D) { if (num16 >= 10) { SetRQ(num, (byte)((uint)(num16 - 10) & 0xFu)); _flags |= 1; } else { SetRQ(num, (byte)num16); _flags &= 254; } } else { SetRQ(num, (byte)((uint)num16 & 0xFu)); if (num16 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 85: { int num11 = GetRQ(num) + GetRQ(num2) + (C ? 1 : 0); if (D) { if (num11 >= 10) { SetRQ(num, (byte)((uint)(num11 - 10) & 0xFu)); _flags |= 1; } else { SetRQ(num, (byte)num11); _flags &= 254; } } else { SetRQ(num, (byte)((uint)num11 & 0xFu)); if (num11 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 86: { int num10 = GetRQ(num) - GetRQ(num2); if (D) { if (num10 >> 4 != 0) { SetRQ(num, (byte)((uint)(num10 - 6) & 0xFu)); } else { SetRQ(num, (byte)((uint)num10 & 0xFu)); } } else { SetRQ(num, (byte)((uint)num10 & 0xFu)); } if (num10 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 87: { int num7 = GetRQ(num) - num2 - (C ? 1 : 0); if (D) { if (num7 >> 4 != 0) { SetRQ(num, (byte)((uint)(num7 - 6) & 0xFu)); } else { SetRQ(num, (byte)((uint)num7 & 0xFu)); } } else { SetRQ(num, (byte)((uint)num7 & 0xFu)); } if (num7 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 88: { int num5 = GetRQ(num) - GetRQ(num2) - (C ? 1 : 0); if (D) { if (num5 >> 4 != 0) { SetRQ(num, (byte)((uint)(num5 - 6) & 0xFu)); } else { SetRQ(num, (byte)((uint)num5 & 0xFu)); } } else { SetRQ(num, (byte)((uint)num5 & 0xFu)); } if (num5 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 89: SetRQ(num, (byte)(GetRQ(num) & num2)); if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 90: SetRQ(num, (byte)(GetRQ(num) & GetRQ(num2))); if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 91: SetRQ(num, (byte)(GetRQ(num) | num2)); if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 92: SetRQ(num, (byte)(GetRQ(num) | GetRQ(num2))); if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 93: SetRQ(num, (byte)(GetRQ(num) ^ num2)); if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 94: SetRQ(num, (byte)(GetRQ(num) ^ GetRQ(num2))); if (GetRQ(num) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 95: if (GetRQ(num) < num2) { _flags |= 1; } else { _flags &= 254; } if (GetRQ(num) == num2) { _flags |= 2; } else { _flags &= 253; } break; case 96: if (GetRQ(num) < GetRQ(num2)) { _flags |= 1; } else { _flags &= 254; } if (GetRQ(num) == GetRQ(num2)) { _flags |= 2; } else { _flags &= 253; } break; case 97: if ((GetRQ(num) & num2) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 98: if ((GetRQ(num) & GetRQ(num2)) == 0) { _flags |= 2; } else { _flags &= 253; } break; case 99: { byte rQ2 = GetRQ(op & 3); byte num19 = (byte)((rQ2 >> 3) & 1); SetRQ(op & 3, (byte)(((uint)(rQ2 << 1) | (C ? 1u : 0u)) & 0xFu)); if (num19 != 0) { _flags |= 1; } else { _flags &= 254; } break; } case 100: { byte rQ = GetRQ(op & 3); byte num15 = (byte)(rQ & 1); SetRQ(op & 3, (byte)((uint)((rQ >> 1) | ((C ? 1 : 0) << 3)) & 0xFu)); if (num15 != 0) { _flags |= 1; } else { _flags &= 254; } break; } case 101: { int num13 = GetMem(op & 0xF) + 1; SetMem(op & 0xF, (byte)((uint)num13 & 0xFu)); if (num13 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if ((num13 & 0xF) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 102: { int num12 = GetMem(op & 0xF) - 1; SetMem(op & 0xF, (byte)((uint)num12 & 0xFu)); if (num12 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if ((num12 & 0xF) == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 103: { int num9 = GetMem(_x) + GetRQ(op & 3) + (C ? 1 : 0); if (D) { if (num9 >= 10) { SetMem(_x, (byte)((uint)(num9 - 10) & 0xFu)); _flags |= 1; } else { SetMem(_x, (byte)((uint)num9 & 0xFu)); _flags &= 254; } } else { SetMem(_x, (byte)((uint)num9 & 0xFu)); if (num9 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } } if (GetMem(_x) == 0) { _flags |= 2; } else { _flags &= 253; } _x = ((_x + 1) & 0xFF) | (XP << 8); break; } case 104: { int num8 = GetMem(_y) + GetRQ(op & 3) + (C ? 1 : 0); if (D) { if (num8 >= 10) { SetMem(_y, (byte)((uint)(num8 - 10) & 0xFu)); _flags |= 1; } else { SetMem(_y, (byte)((uint)num8 & 0xFu)); _flags &= 254; } } else { SetMem(_y, (byte)((uint)num8 & 0xFu)); if (num8 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } } if (GetMem(_y) == 0) { _flags |= 2; } else { _flags &= 253; } _y = ((_y + 1) & 0xFF) | (YP << 8); break; } case 105: { int num6 = GetMem(_x) - GetRQ(op & 3) - (C ? 1 : 0); if (D) { if (num6 >> 4 != 0) { SetMem(_x, (byte)((uint)(num6 - 6) & 0xFu)); } else { SetMem(_x, (byte)((uint)num6 & 0xFu)); } } else { SetMem(_x, (byte)((uint)num6 & 0xFu)); } if (num6 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if (GetMem(_x) == 0) { _flags |= 2; } else { _flags &= 253; } _x = ((_x + 1) & 0xFF) | (XP << 8); break; } case 106: { int num3 = GetMem(_y) - GetRQ(op & 3) - (C ? 1 : 0); if (D) { if (num3 >> 4 != 0) { SetMem(_y, (byte)((uint)(num3 - 6) & 0xFu)); } else { SetMem(_y, (byte)((uint)num3 & 0xFu)); } } else { SetMem(_y, (byte)((uint)num3 & 0xFu)); } if (num3 >> 4 != 0) { _flags |= 1; } else { _flags &= 254; } if (GetMem(_y) == 0) { _flags |= 2; } else { _flags &= 253; } _y = ((_y + 1) & 0xFF) | (YP << 8); break; } case 107: { byte b = (byte)((uint)(~GetRQ(num)) & 0xFu); SetRQ(num, b); if (b == 0) { _flags |= 2; } else { _flags &= 253; } break; } case 12: case 13: break; } } } public static class TamaState { private const string Magic = "TLST"; private const byte Version = 3; private const int INT_SLOT_NUM = 6; private const int MEM_RAM_SIZE = 640; private const int MEM_IO_SIZE = 128; private const int MEM_IO_ADDR = 3840; private const int MaxSaveSlots = 5; public static byte[] Snapshot(TamaEmulator emu) { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter w = new BinaryWriter(memoryStream); WriteState(w, emu); return memoryStream.ToArray(); } public static void WriteSnapshot(byte[] snapshot, string savesFolder, string romName) { if (!Directory.Exists(savesFolder)) { Directory.CreateDirectory(savesFolder); } File.WriteAllBytes(NextSavePath(savesFolder, romName), snapshot); List<string> sortedSlots = GetSortedSlots(savesFolder, romName); while (sortedSlots.Count > 5) { File.Delete(sortedSlots[0]); sortedSlots.RemoveAt(0); } } public static void Save(TamaEmulator emu, string savesFolder, string romName) { WriteSnapshot(Snapshot(emu), savesFolder, romName); } public static bool Load(TamaEmulator emu, string savesFolder, string romName) { string text = LastSavePath(savesFolder, romName); if (text == null) { return false; } return LoadFrom(emu, text); } public static bool LoadFrom(TamaEmulator emu, string path) { if (!File.Exists(path)) { return false; } try { using (FileStream input = new FileStream(path, FileMode.Open, FileAccess.Read)) { using BinaryReader binaryReader = new BinaryReader(input); byte[] array = binaryReader.ReadBytes(4); for (int i = 0; i < 4; i++) { if (array[i] != (byte)"TLST"[i]) { return false; } } if (binaryReader.ReadByte() != 3) { return false; } lock (emu.Lock) { emu.PC = binaryReader.ReadByte() | ((binaryReader.ReadByte() & 0x1F) << 8); emu.X = binaryReader.ReadByte() | ((binaryReader.ReadByte() & 0xF) << 8); emu.Y = binaryReader.ReadByte() | ((binaryReader.ReadByte() & 0xF) << 8); emu.A = (byte)(binaryReader.ReadByte() & 0xFu); emu.B = (byte)(binaryReader.ReadByte() & 0xFu); emu.NP = (byte)(binaryReader.ReadByte() & 0x1Fu); emu.SP = binaryReader.ReadByte(); emu.Flags = (byte)(binaryReader.ReadByte() & 0xFu); emu.TickCounter = ReadU32(binaryReader); emu.Clk2Hz = ReadU32(binaryReader); emu.Clk4Hz = ReadU32(binaryReader); emu.Clk8Hz = ReadU32(binaryReader); emu.Clk16Hz = ReadU32(binaryReader); emu.Clk32Hz = ReadU32(binaryReader); emu.Clk64Hz = ReadU32(binaryReader); emu.Clk128Hz = ReadU32(binaryReader); emu.Clk256Hz = ReadU32(binaryReader); emu.ProgTimerTs = ReadU32(binaryReader); emu.ProgTimerEnabled = binaryReader.ReadByte() != 0; emu.ProgTimerData = binaryReader.ReadByte(); emu.ProgTimerRld = binaryReader.ReadByte(); ReadU32(binaryReader); for (int j = 0; j < 6; j++) { emu.IntFactor[j] = (byte)(binaryReader.ReadByte() & 0xFu); emu.IntMask[j] = (byte)(binaryReader.ReadByte() & 0xFu); emu.IntTriggered[j] = binaryReader.ReadByte() != 0; } for (int k = 0; k < 640; k++) { emu.SetRamNibble(k, (byte)(binaryReader.ReadByte() & 0xFu)); } for (int l = 0; l < 128; l++) { emu.SetIONibble(l, (byte)(binaryReader.ReadByte() & 0xFu)); } } } return true; } catch { return false; } } private static void WriteState(BinaryWriter w, TamaEmulator emu) { string text = "TLST"; foreach (char c in text) { w.Write((byte)c); } w.Write((byte)3); lock (emu.Lock) { w.Write((byte)((uint)emu.PC & 0xFFu)); w.Write((byte)((uint)(emu.PC >> 8) & 0x1Fu)); w.Write((byte)((uint)emu.X & 0xFFu)); w.Write((byte)((uint)(emu.X >> 8) & 0xFu)); w.Write((byte)((uint)emu.Y & 0xFFu)); w.Write((byte)((uint)(emu.Y >> 8) & 0xFu)); w.Write((byte)(emu.A & 0xFu)); w.Write((byte)(emu.B & 0xFu)); w.Write((byte)(emu.NP & 0x1Fu)); w.Write((byte)(emu.SP & 0xFFu)); w.Write((byte)(emu.Flags & 0xFu)); WriteU32(w, emu.TickCounter); WriteU32(w, emu.Clk2Hz); WriteU32(w, emu.Clk4Hz); WriteU32(w, emu.Clk8Hz); WriteU32(w, emu.Clk16Hz); WriteU32(w, emu.Clk32Hz); WriteU32(w, emu.Clk64Hz); WriteU32(w, emu.Clk128Hz); WriteU32(w, emu.Clk256Hz); WriteU32(w, emu.ProgTimerTs); w.Write((byte)(emu.ProgTimerEnabled ? 1u : 0u)); w.Write((byte)(emu.ProgTimerData & 0xFFu)); w.Write((byte)(emu.ProgTimerRld & 0xFFu)); WriteU32(w, 0u); for (int j = 0; j < 6; j++) { w.Write((byte)(emu.IntFactor[j] & 0xFu)); w.Write((byte)(emu.IntMask[j] & 0xFu)); w.Write((byte)(emu.IntTriggered[j] ? 1u : 0u)); } for (int k = 0; k < 640; k++) { w.Write((byte)(emu.GetRamNibble(k) & 0xFu)); } for (int l = 0; l < 128; l++) { w.Write((byte)(emu.GetIONibble(l) & 0xFu)); } } } public static string NextSavePath(string savesFolder, string romName) { List<string> sortedSlots = GetSortedSlots(savesFolder, romName); int slot = ((sortedSlots.Count != 0) ? (ParseSlot(sortedSlots[sortedSlots.Count - 1], romName) + 1) : 0); return SavePath(savesFolder, romName, slot); } public static string LastSavePath(string savesFolder, string romName) { List<string> sortedSlots = GetSortedSlots(savesFolder, romName); if (sortedSlots.Count != 0) { return sortedSlots[sortedSlots.Count - 1]; } return null; } private static List<string> GetSortedSlots(string savesFolder, string romName) { if (!Directory.Exists(savesFolder)) { return new List<string>(); } List<string> list = new List<string>(Directory.GetFiles(savesFolder, romName + "_save*.bin")); list.Sort((string a, string b) => ParseSlot(a, romName).CompareTo(ParseSlot(b, romName))); return list; } private static int ParseSlot(string path, string romName) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); string text = romName + "_save"; if (fileNameWithoutExtension.StartsWith(text) && int.TryParse(fileNameWithoutExtension.Substring(text.Length), out var result)) { return result; } return 0; } public static string SavePath(string savesFolder, string romName, int slot) { return Path.Combine(savesFolder, $"{romName}_save{slot}.bin"); } private static void WriteU32(BinaryWriter w, uint v) { w.Write((byte)(v & 0xFFu)); w.Write((byte)((v >> 8) & 0xFFu)); w.Write((byte)((v >> 16) & 0xFFu)); w.Write((byte)((v >> 24) & 0xFFu)); } private static uint ReadU32(BinaryReader r) { byte num = r.ReadByte(); uint num2 = r.ReadByte(); uint num3 = r.ReadByte(); uint num4 = r.ReadByte(); return num | (num2 << 8) | (num3 << 16) | (num4 << 24); } } } namespace TamaRush.Phone { public class AppTamaRush : CustomApp { private TamaEmulator _emu; private Thread _emuThread; private volatile bool _emuRunning; private TamaRushAudio _audio; private GameObject _gameScreen; private RawImage _lcdImage; private Texture2D _lcdTex; private Image[] _iconImages; private const int IconSheetCols = 4; private const int IconSrcSize = 64; private const float IconSize = 144f; private const float IconRowGap = 8f; private string _romName; private GameObject _canvasGo; private TextMeshProUGUI _label; private Coroutine _hideCoroutine; private readonly Color32[] _lcdPixels = (Color32[])(object)new Color32[512]; private readonly bool[] _iconStates = new bool[8]; private float _autoSaveTimer; private bool _emuStartedThisSession; private const float AutoSaveInterval = 180f; private volatile int _cachedGameSpeed = 1; private static float LcdDisplayW => TamaRushPlugin.PixelSize.Value * 32; private static float LcdDisplayH => TamaRushPlugin.PixelSize.Value * 16; private static float TotalH => LcdDisplayH + 304f; public static void Initialize() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) Sprite val = null; string appIconPath = TamaRushPlugin.GetAppIconPath("AppIcon.png"); if (!string.IsNullOrEmpty(appIconPath) && File.Exists(appIconPath)) { try { byte[] array = File.ReadAllBytes(appIconPath); Texture2D val2 = new Texture2D(2, 2); if (ImageConversion.LoadImage(val2, array)) { ((Texture)val2).wrapMode = (TextureWrapMode)1; ((Texture)val2).filterMode = (FilterMode)1; val2.Apply(); val = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f)); } } catch { } } if ((Object)(object)val != (Object)null) { PhoneAPI.RegisterApp<AppTamaRush>("TamaRush", val); } else { PhoneAPI.RegisterApp<AppTamaRush>("TamaRush", (Sprite)null); } } public override void OnAppInit() { ((CustomApp)this).OnAppInit(); ((CustomApp)this).CreateIconlessTitleBar("TamaRush", 80f); base.ScrollView = PhoneScrollView.Create((CustomApp)(object)this, 275f, 1600f); TamaRushPlayMode.OnExitPlayMode = (Action)Delegate.Combine(TamaRushPlayMode.OnExitPlayMode, new Action(OnExitPlayMode)); _audio = ((Component)TamaRushPlayMode.Instance).gameObject.GetComponent<TamaRushAudio>() ?? ((Component)TamaRushPlayMode.Instance).gameObject.AddComponent<TamaRushAudio>(); ShowMainMenu(); } public override void OnAppEnable() { ((App)this).OnAppEnable(); if (!TamaRushPlayMode.IsActive) { ShowMainMenu(); } } public override void OnAppDisable() { ((App)this).OnAppDisable(); if (!TamaRushPlugin.RunInBackground.Value) { StopEmulator(); } } public override void OnPressLeft() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Left, pressed: true); } else { ((App)this).OnPressLeft(); } } public override void OnReleaseLeft() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Left, pressed: false); } else { ((App)this).OnReleaseLeft(); } } public override void OnPressUp() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Middle, pressed: true); } else { ((CustomApp)this).OnPressUp(); } } public override void OnReleaseUp() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Middle, pressed: false); } else { ((CustomApp)this).OnReleaseUp(); } } public override void OnPressRight() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Right, pressed: true); } else { ((CustomApp)this).OnPressRight(); } } public override void OnReleaseRight() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Right, pressed: false); } else { ((CustomApp)this).OnReleaseRight(); } } public override void OnPressDown() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Tap, pressed: true); } else { ((CustomApp)this).OnPressDown(); } } public override void OnReleaseDown() { if (TamaRushPlayMode.IsActive) { _emu?.SetButton(TamaButton.Tap, pressed: false); } else { ((CustomApp)this).OnReleaseDown(); } } private void Update() { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) if (_emuStartedThisSession && _emuRunning && _emu != null && _romName != null && TamaRushPlugin.AutoSave.Value) { _autoSaveTimer += Time.deltaTime; if (_autoSaveTimer >= 180f) { _autoSaveTimer = 0f; SaveAsync(); } } _cachedGameSpeed = TamaRushPlugin.GameSpeed?.Value ?? 1; if (_cachedGameSpeed < 0) { _cachedGameSpeed = 1; } if (!TamaRushPlayMode.IsActive || _emu == null || (Object)(object)_lcdTex == (Object)null || !_emuRunning || !_emu.LcdDirty) { return; } _emu.LcdDirty = false; bool[] array = ((_iconImages != null) ? _iconStates : null); int option = TamaRushPlugin.LcdOption?.Value ?? 0; lock (_emu.Lock) { for (int i = 0; i < 32; i++) { for (int j = 0; j < 16; j++) { int num = 15 - j; _lcdPixels[num * 32 + i] = GetLcdPixelColor(_emu.LcdMatrix[i, j], option); } } if (array != null) { for (int k = 0; k < 8; k++) { array[k] = _emu.LcdIcons[k]; } } } _lcdTex.SetPixels32(_lcdPixels); _lcdTex.Apply(false, false); if (array != null) { for (int l = 0; l < 8; l++) { ((Graphic)_iconImages[l]).color = (array[l] ? new Color(1f, 1f, 1f, 1f) : new Color(1f, 1f, 1f, 0.5f)); } } } private void ShowMainMenu() { SetGameScreenVisible(visible: false); base.ScrollView.RemoveAllButtons(); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Play"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, new Action(OnPlay)); base.ScrollView.AddButton((PhoneButton)(object)val); if (_emuRunning && _emu != null) { SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Save Game"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate { SaveAsync(); }); base.ScrollView.AddButton((PhoneButton)(object)val2); } SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Settings"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, new Action(ShowSettingsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val3); } private void ShowSettingsMenu() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Game/ROM Options"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, new Action(ShowGameRomOptionsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Save Options"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, new Action(ShowSaveOptionsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Customize Options"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, new Action(ShowCustomizeMenu)); base.ScrollView.AddButton((PhoneButton)(object)val3); SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Audio Options"); ((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, new Action(ShowAudioOptionsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val4); if (TamaRushPlugin.DebugMode.Value) { SimplePhoneButton val5 = PhoneUIUtility.CreateSimpleButton("Debug Options"); ((PhoneButton)val5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val5).OnConfirm, new Action(ShowDebugOptionsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val5); } SimplePhoneButton val6 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val6).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val6).OnConfirm, new Action(ShowMainMenu)); base.ScrollView.AddButton((PhoneButton)(object)val6); } private void ShowSaveOptionsMenu() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton autoSaveBtn = PhoneUIUtility.CreateSimpleButton(GetAutoSaveLabel()); SimplePhoneButton obj = autoSaveBtn; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { TamaRushPlugin.AutoSave.Value = !TamaRushPlugin.AutoSave.Value; ((TMP_Text)autoSaveBtn.Label).text = GetAutoSaveLabel(); }); base.ScrollView.AddButton((PhoneButton)(object)autoSaveBtn); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Backup Save"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, new Action(SaveFileBackedUp)); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Open Saves Folder"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, new Action(OpenSavesFolder)); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Open Backups Folder"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, new Action(OpenBackupsFolder)); base.ScrollView.AddButton((PhoneButton)(object)val3); SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Delete Save File"); ((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, new Action(SaveFileDeleted)); base.ScrollView.AddButton((PhoneButton)(object)val4); SimplePhoneButton val5 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val5).OnConfirm, new Action(ShowMainMenu)); base.ScrollView.AddButton((PhoneButton)(object)val5); } private void ShowGameRomOptionsMenu() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton(GetSelectedRomLabel()); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, new Action(ShowRomSelectMenu)); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton ribBtn = PhoneUIUtility.CreateSimpleButton(GetRunInBackgroundLabel()); SimplePhoneButton obj = ribBtn; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { TamaRushPlugin.RunInBackground.Value = !TamaRushPlugin.RunInBackground.Value; ((TMP_Text)ribBtn.Label).text = GetRunInBackgroundLabel(); }); base.ScrollView.AddButton((PhoneButton)(object)ribBtn); SimplePhoneButton speedBtn = PhoneUIUtility.CreateSimpleButton(GetGameSpeedLabel()); SimplePhoneButton obj2 = speedBtn; ((PhoneButton)obj2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj2).OnConfirm, (Action)delegate { int value = TamaRushPlugin.GameSpeed.Value; TamaRushPlugin.GameSpeed.Value = value switch { 4 => 6, 2 => 4, 1 => 2, _ => 1, }; ((TMP_Text)speedBtn.Label).text = GetGameSpeedLabel(); }); base.ScrollView.AddButton((PhoneButton)(object)speedBtn); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Game Folder"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, new Action(OpenRomsFolder)); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, new Action(ShowSettingsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val3); } private void ShowRomSelectMenu() { base.ScrollView.RemoveAllButtons(); string path = Path.Combine(TamaRushPlugin.TamaRushFolderPath, "Roms"); string[] array = (Directory.Exists(path) ? Directory.GetFiles(path, "*.bin") : new string[0]); if (array.Length == 0) { base.ScrollView.AddButton((PhoneButton)(object)PhoneUIUtility.CreateSimpleButton("No ROMs found")); } else { string effectiveRomPath = GetEffectiveRomPath(); string[] array2 = array; foreach (string text in array2) { string captured = text; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton((string.Equals(captured, effectiveRomPath, StringComparison.OrdinalIgnoreCase) ? "> " : "") + fileNameWithoutExtension); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { TamaRushPlugin.SelectedRom.Value = captured; ShowGameRomOptionsMenu(); }); base.ScrollView.AddButton((PhoneButton)(object)val); } } SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, new Action(ShowGameRomOptionsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val2); } private void ShowDebugOptionsMenu() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton DebugspeedBtn = PhoneUIUtility.CreateSimpleButton(GetDebugGameSpeedLabel()); SimplePhoneButton obj = DebugspeedBtn; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { int value = TamaRushPlugin.GameSpeed.Value; TamaRushPlugin.GameSpeed.Value = value switch { 6 => 0, 4 => 6, 2 => 4, 1 => 2, _ => 1, }; ((TMP_Text)DebugspeedBtn.Label).text = GetDebugGameSpeedLabel(); }); base.ScrollView.AddButton((PhoneButton)(object)DebugspeedBtn); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, new Action(ShowSettingsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val); } private void ShowAssetSelectMenu(string subfolder, ConfigEntry<string> config, Action onSelected) { base.ScrollView.RemoveAllButtons(); string path = Path.Combine(TamaRushPlugin.GetAssetSubfolderPath(), subfolder); List<string> list = new List<string>(); if (Directory.Exists(path)) { string[] files = Directory.GetFiles(path, "*.*"); foreach (string text in files) { switch (Path.GetExtension(text).ToLowerInvariant()) { case ".png": case ".jpg": case ".jpeg": list.Add(text); break; } } } if (list.Count == 0) { base.ScrollView.AddButton((PhoneButton)(object)PhoneUIUtility.CreateSimpleButton("No files found")); } else { string assetFolderFile = TamaRushPlugin.GetAssetFolderFile(subfolder); foreach (string item in list) { string captured = item; string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(item); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton((string.Equals(captured, assetFolderFile, StringComparison.OrdinalIgnoreCase) ? "> " : "") + fileNameWithoutExtension); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { config.Value = captured; onSelected?.Invoke(); ShowCustomizeMenu(); }); base.ScrollView.AddButton((PhoneButton)(object)val); } } SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, new Action(ShowCustomizeMenu)); base.ScrollView.AddButton((PhoneButton)(object)val2); } private void RebuildGameScreen() { if ((Object)(object)_gameScreen != (Object)null) { Object.Destroy((Object)(object)_gameScreen); _gameScreen = null; _lcdImage = null; _lcdTex = null; _iconImages = null; } Transform val = ((Transform)((App)this).Content).Find("TamaRush_BG"); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } if (_emuRunning) { EnsureGameScreen(); SetGameScreenVisible(visible: true); } } private static string GetRunInBackgroundLabel() { return "Run In Background: " + (TamaRushPlugin.RunInBackground.Value ? "On" : "Off"); } private static string GetGameSpeedLabel() { int value = TamaRushPlugin.GameSpeed.Value; return "Game Speed: " + ((value >= 6) ? "6x" : ((value >= 4) ? "4x" : ((value >= 2) ? "2x" : "Normal"))); } private static string GetDebugGameSpeedLabel() { int value = TamaRushPlugin.GameSpeed.Value; return "Game Speed: " + ((value == 0) ? "Unlimited" : ((value >= 6) ? "6x" : ((value >= 4) ? "4x" : ((value >= 2) ? "2x" : "Normal")))); } private void ShowCustomizeMenu() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton lcdBtn = PhoneUIUtility.CreateSimpleButton(GetLcdOptionLabel()); SimplePhoneButton obj = lcdBtn; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { TamaRushPlugin.LcdOption.Value = (TamaRushPlugin.LcdOption.Value + 1) % 18; ((TMP_Text)lcdBtn.Label).text = GetLcdOptionLabel(); if (_emu != null) { _emu.LcdDirty = true; } }); base.ScrollView.AddButton((PhoneButton)(object)lcdBtn); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton(GetSelectedAssetLabel("Background", TamaRushPlugin.SelectedBackground?.Value)); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { ShowAssetSelectMenu("Background", TamaRushPlugin.SelectedBackground, RebuildGameScreen); }); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton(GetSelectedAssetLabel("Icons", TamaRushPlugin.SelectedIcons?.Value)); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate { ShowAssetSelectMenu("Icons", TamaRushPlugin.SelectedIcons, RebuildGameScreen); }); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton pixelBtn = PhoneUIUtility.CreateSimpleButton(GetPixelSizeLabel()); SimplePhoneButton obj2 = pixelBtn; ((PhoneButton)obj2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj2).OnConfirm, (Action)delegate { int[] array = new int[10] { 8, 10, 12, 14, 16, 18, 20, 24, 28, 32 }; int value = TamaRushPlugin.PixelSize.Value; int num = Array.IndexOf(array, value); TamaRushPlugin.PixelSize.Value = array[(num + 1) % array.Length]; ((TMP_Text)pixelBtn.Label).text = GetPixelSizeLabel(); RebuildGameScreen(); }); base.ScrollView.AddButton((PhoneButton)(object)pixelBtn); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Background Folder"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, new Action(OpenBackgroundFolder)); base.ScrollView.AddButton((PhoneButton)(object)val3); SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Icons Folder"); ((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, new Action(OpenIconsFolder)); base.ScrollView.AddButton((PhoneButton)(object)val4); SimplePhoneButton val5 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val5).OnConfirm, new Action(ShowSettingsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val5); } private void ShowAudioOptionsMenu() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton audioBtn = PhoneUIUtility.CreateSimpleButton(GetAudioLabel()); SimplePhoneButton obj = audioBtn; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { TamaRushPlugin.AudioEnabled.Value = !TamaRushPlugin.AudioEnabled.Value; ((TMP_Text)audioBtn.Label).text = GetAudioLabel(); }); base.ScrollView.AddButton((PhoneButton)(object)audioBtn); SimplePhoneButton volumeBtn = PhoneUIUtility.CreateSimpleButton(GetVolumeLabel()); SimplePhoneButton obj2 = volumeBtn; ((PhoneButton)obj2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj2).OnConfirm, (Action)delegate { int value = TamaRushPlugin.AudioVolume.Value; TamaRushPlugin.AudioVolume.Value = ((value >= 10) ? 1 : (value + 1)); ((TMP_Text)volumeBtn.Label).text = GetVolumeLabel(); }); base.ScrollView.AddButton((PhoneButton)(object)volumeBtn); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, new Action(ShowSettingsMenu)); base.ScrollView.AddButton((PhoneButton)(object)val); } private static string GetAutoSaveLabel() { return "Auto Save: " + (TamaRushPlugin.AutoSave.Value ? "On" : "Off"); } private static string GetAudioLabel() { return "Audio: " + (TamaRushPlugin.AudioEnabled.Value ? "On" : "Off"); } private static string GetVolumeLabel() { return $"Volume: {TamaRushPlugin.AudioVolume.Value}/10"; } private static string GetPixelSizeLabel() { return "Pixel Size: " + ((TamaRushPlugin.PixelSize.Value == 32) ? "Default" : TamaRushPlugin.PixelSize.Value.ToString()); } private static string GetLcdOptionLabel() { string[] array = new string[18] { "Mono", "Classic", "Green", "Inverted", "Blue", "Red", "Lime", "Cyan", "Yellow", "Grey", "HotPink", "Orange", "Purple", "Pink", "Crimson", "Bisque", "SaddleBrown", "SpringGreen" }; return "LCD: " + array[(TamaRushPlugin.LcdOption?.Value ?? 0) % array.Length]; } private static string GetSelectedRomLabel() { string effectiveRomPath = GetEffectiveRomPath(); return "Selected ROM: " + ((effectiveRomPath != null) ? Path.GetFileNameWithoutExtension(effectiveRomPath) : "None"); } private static string GetSelectedAssetLabel(string assetType, string path) { if (!string.IsNullOrEmpty(path) && File.Exists(path)) { return assetType + ": " + Path.GetFileNameWithoutExtension(path); } string assetFolderFile = TamaRushPlugin.GetAssetFolderFile(assetType); return assetType + ": " + ((assetFolderFile != null) ? Path.GetFileNameWithoutExtension(assetFolderFile) : "None"); } private static string GetEffectiveRomPath() { string path = Path.Combine(TamaRushPlugin.TamaRushFolderPath, "Roms"); string value = TamaRushPlugin.SelectedRom.Value; if (!string.IsNullOrEmpty(value) && File.Exists(value)) { return value; } if (!Directory.Exists(path)) { return null; } string[] files = Directory.GetFiles(path, "*.bin"); if (files.Length == 0) { return null; } return files[0]; } private static Color32 GetLcdPixelColor(bool on, int option) { //IL_007a: 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_00a5: 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_00d0: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: 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_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) switch (option) { case 1: if (!on) { return new Color32((byte)216, (byte)216, (byte)192, byte.MaxValue); } return new Color32((byte)0, (byte)0, (byte)128, byte.MaxValue); case 2: if (!on) { return new Color32((byte)139, (byte)172, (byte)15, byte.MaxValue); } return new Color32((byte)15, (byte)56, (byte)15, byte.MaxValue); case 3: if (!on) { return new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); } return new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); case 4: if (!on) { return new Color32((byte)0, (byte)20, (byte)40, byte.MaxValue); } return new Color32((byte)0, (byte)100, byte.MaxValue, byte.MaxValue); case 5: if (!on) { return new Color32((byte)40, (byte)0, (byte)0, byte.MaxValue); } return new Color32(byte.MaxValue, (byte)0, (byte)0, byte.MaxValue); case 6: if (!on) { return new Color32((byte)0, (byte)40, (byte)0, byte.MaxValue); } return new Color32((byte)0, byte.MaxValue, (byte)0, byte.MaxValue); case 7: if (!on) { return new Color32((byte)0, (byte)40, (byte)40, byte.MaxValue); } return new Color32((byte)0, byte.MaxValue, byte.MaxValue, byte.MaxValue); case 8: if (!on) { return new Color32((byte)40, (byte)40, (byte)0, byte.MaxValue); } return new Color32(byte.MaxValue, byte.MaxValue, (byte)0, byte.MaxValue); case 9: if (!on) { return new Color32((byte)32, (byte)32, (byte)32, byte.MaxValue); } return new Color32((byte)192, (byte)192, (byte)192, byte.MaxValue); case 10: if (!on) { return new Color32((byte)60, (byte)20, (byte)40, byte.MaxValue); } return new Color32(byte.MaxValue, (byte)105, (byte)180, byte.MaxValue); case 11: if (!on) { return new Color32((byte)50, (byte)30, (byte)0, byte.MaxValue); } return new Color32(byte.MaxValue, (byte)165, (byte)0, byte.MaxValue); case 12: if (!on) { return new Color32((byte)30, (byte)0, (byte)50, byte.MaxValue); } return new Color32((byte)148, (byte)0, (byte)211, byte.MaxValue); case 13: if (!on) { return new Color32((byte)50, (byte)30, (byte)35, byte.MaxValue); } return new Color32(byte.MaxValue, (byte)192, (byte)203, byte.MaxValue); case 14: if (!on) { return new Color32((byte)45, (byte)5, (byte)15, byte.MaxValue); } return new Color32((byte)220, (byte)20, (byte)60, byte.MaxValue); case 15: if (!on) { return new Color32((byte)50, (byte)40, (byte)35, byte.MaxValue); } return new Color32(byte.MaxValue, (byte)228, (byte)196, byte.MaxValue); case 16: if (!on) { return new Color32((byte)30, (byte)15, (byte)5, byte.MaxValue); } return new Color32((byte)139, (byte)69, (byte)19, byte.MaxValue); case 17: if (!on) { return new Color32((byte)0, (byte)50, (byte)25, byte.MaxValue); } return new Color32((byte)0, byte.MaxValue, (byte)127, byte.MaxValue); default: if (!on) { return new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); } return new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); } } private void OnPlay() { if (_emuRunning && _emu != null) { SetGameScreenVisible(visible: true); TamaRushPlayMode.Enter(); return; } string effectiveRomPath = GetEffectiveRomPath(); if (effectiveRomPath != null) { _romName = Path.GetFileNameWithoutExtension(effectiveRomPath); byte[] array = File.ReadAllBytes(effectiveRomPath); ushort[] array2 = new ushort[array.Length / 2]; for (int i = 0; i < array2.Length; i++) { array2[i] = (ushort)(array[i * 2 + 1] | ((array[i * 2] & 0xF) << 8)); } _emu = new TamaEmulator(); _emu.Init(array2); TamaState.Load(_emu, GetSavesFolder(), _romName); EnsureGameScreen(); SetGameScreenVisible(visible: true); TamaRushPlayMode.Enter(); _audio?.SetEmulator(_emu); StartEmulator(); } } private void OnExitPlayMode() { if (TamaRushPlugin.RunInBackground.Value) { SetGameScreenVisible(visible: false); ShowMainMenu(); return; } if (_emu != null && _romName != null) { SaveAsync(); } StopEmulator(); ShowMainMenu(); } private string GetSavesFolder() { return Path.Combine(TamaRushPlugin.TamaRushFolderPath, "Saves", _romName ?? "unknown"); } private void SaveAsync() { if (_emu == null || _romName == null) { return; } string savesFolder = GetSavesFolder(); byte[] snapshot = TamaState.Snapshot(_emu); ThreadPool.QueueUserWorkItem(delegate { try { TamaState.WriteSnapshot(snapshot, savesFolder, _romName); } catch (Exception ex) { Debug.LogError((object)("[TamaRush] Save failed: " + ex)); } }); } private void StartEmulator() { _emuRunning = true; _emuStartedThisSession = true; _emuThread = new Thread((ThreadStart)delegate { try { while (_emuRunning) { _emu.Step(_cachedGameSpeed); } } catch (Exception ex) { _emuRunning = false; Debug.LogError((object)("[TamaRush] Emulator thread crashed: " + ex)); } }); _emuThread.IsBackground = true; _emuThread.Start(); } private void StopEmulator() { _emuRunning = false; _emuThread?.Join(200); _emuThread = null; _audio?.SetEmulator(null); } private void EnsureGameScreen() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_0076: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Expected O, but got Unknown //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Expected O, but got Unknown //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_0398: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Unknown result type (might be due to invalid IL or missing references) //IL_0464: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0477: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Unknown result type (might be due to invalid IL or missing references) //IL_04ca: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_gameScreen != (Object)null) { return; } GameObject val = new GameObject("TamaRush_BG", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage) }); RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)((App)this).Content, false); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component.pivot = val2; Vector2 anchorMin = (component.anchorMax = val2); component.anchorMin = anchorMin; component.anchoredPosition = new Vector2(0f, -40f); component.sizeDelta = new Vector2(2000f, 2000f); ((Transform)component).SetAsFirstSibling(); RawImage component2 = val.GetComponent<RawImage>(); ((Graphic)component2).raycastTarget = false; Texture2D val4 = LoadTexture(TamaRushPlugin.GetAssetFolderFile("Background")); if ((Object)(object)val4 != (Object)null) { component2.texture = (Texture)(object)val4; } else { ((Graphic)component2).color = new Color(0.85f, 0.85f, 0.75f); } _gameScreen = new GameObject("TamaRush_GameScreen", new Type[1] { typeof(RectTransform) }); RectTransform component3 = _gameScreen.GetComponent<RectTransform>(); ((Transform)component3).SetParent((Transform)(object)((App)this).Content, false); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component3.pivot = val2; anchorMin = (component3.anchorMax = val2); component3.anchorMin = anchorMin; component3.anchoredPosition = new Vector2(0f, -70f); component3.sizeDelta = new Vector2(LcdDisplayW, TotalH); GameObject val6 = new GameObject("LCD", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage) }); RectTransform component4 = val6.GetComponent<RectTransform>(); ((Transform)component4).SetParent((Transform)(object)component3, false); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component4.pivot = val2; anchorMin = (component4.anchorMax = val2); component4.anchorMin = anchorMin; component4.anchoredPosition = Vector2.zero; component4.sizeDelta = new Vector2(LcdDisplayW, LcdDisplayH); _lcdImage = val6.GetComponent<RawImage>(); ((Graphic)_lcdImage).color = Color.white; ((Graphic)_lcdImage).raycastTarget = false; _lcdTex = new Texture2D(32, 16, (TextureFormat)4, false); ((Texture)_lcdTex).filterMode = (FilterMode)0; ((Texture)_lcdTex).wrapMode = (TextureWrapMode)1; Color32[] array = (Color32[])(object)new Color32[512]; for (int i = 0; i < array.Length; i++) { array[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); } _lcdTex.SetPixels32(array); _lcdTex.Apply(false, false); _lcdImage.texture = (Texture)(object)_lcdTex; Texture2D val8 = LoadTexture(TamaRushPlugin.GetAssetFolderFile("Icons")); _iconImages = (Image[])(object)new Image[8]; float num = LcdDisplayH * 0.5f + 8f + 72f; float num2 = 0f - LcdDisplayH * 0.5f - 8f - 72f; float num3 = LcdDisplayW / 4f; Sprite[] array2 = null; if ((Object)(object)val8 != (Object)null) { array2 = (Sprite[])(object)new Sprite[8]; int num4 = ((Texture)val8).height / 64; Rect val9 = default(Rect); for (int j = 0; j < 8; j++) { int num5 = j % 4; int num6 = j / 4; int num7 = num4 - 1 - num6; ((Rect)(ref val9))..ctor((float)(num5 * 64), (float)(num7 * 64), 64f, 64f); array2[j] = Sprite.Create(val8, val9, new Vector2(0.5f, 0.5f)); } } for (int k = 0; k < 8; k++) { int num8 = k % 4; int num9 = k / 4; float num10 = (0f - LcdDisplayW) * 0.5f + num3 * (float)num8 + num3 * 0.5f; float num11 = ((num9 == 1) ? num2 : num); GameObject val10 = new GameObject($"Icon{k}", new Type[3] { typeof(RectTra