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