using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Ports; using System.Timers; using Eto.Forms; using Eto.Drawing; namespace PC2Player { public partial class MainForm : Form { SerialPort Port = null; string[] Signals; System.Timers.Timer FrameTimer; long Index = 0; int FrameSkip = 6; byte[] AudioData; Process MPVProcess; int SkipSeconds = 0; string ShowtapeName = ""; string ShowtapeStageType = ""; string ShowtapeCharacter = ""; string ShowtapeBits = ""; string ShowtapeFormattedLength = ""; string ControllerCharacter = ""; string ControllerBits = ""; string ControllerHeader = ""; bool ControllerConnected = false; bool ShowtapeLoaded = false; bool Playing = false; DropDown SerialPortDropDown = new DropDown(); Label ShowtapeNameLabel = new Label { Text = "Show Name: Not Loaded" }; Label ShowtapeStageTypeLabel = new Label { Text = "Stage Type: Not Loaded" }; Label ShowtapeCharacterLabel = new Label { Text = "Character: Not Loaded" }; Label ShowtapeBitsLabel = new Label { Text = "Bits: Not Loaded" }; Label ShowtapeLengthLabel = new Label { Text = "Length: Not Loaded" }; Label ControllerCharacterLabel = new Label { Text = "Character: Disconnected" }; Label ControllerBitsLabel = new Label { Text = "Bits: Disconnected" }; Button RefreshPortsButton = new Button { Text = "Refresh Ports", Width = 150 }; Button LoadShowtapeButton = new Button { Text = "Load Showtape", Width = 150 }; Button PlayButton = new Button { Text = "Play" }; Button StopButton = new Button { Text = "Stop" }; public MainForm() { Title = "PinkConnection2 Showtape Player"; Size = new Size(500, 450); Maximizable = false; Resizable = false; foreach (string s in SerialPort.GetPortNames()) { SerialPortDropDown.Items.Add(s); } SerialPortDropDown.SelectedValueChanged += (sender, e) => Event_SelectSerialPort(); MPVProcess = new Process(); MPVProcess.StartInfo.FileName = "mpv"; MPVProcess.StartInfo.Arguments = $"--start={SkipSeconds} tmp.wav"; FrameTimer = new System.Timers.Timer((1000d/60d)*FrameSkip); FrameTimer.Elapsed += Event_FrameTimerTick; FrameTimer.AutoReset = true; PlayButton.Enabled = false; StopButton.Enabled = false; PlayButton.Command = new Command( (sender, e) => Event_PlayButton() ); StopButton.Command = new Command( (sender, e) => Event_StopButton() ); RefreshPortsButton.Command = new Command( (sender, e) => Event_RefreshPortsButton() ); LoadShowtapeButton.Command = new Command( (sender, e) => Event_LoadShowtapeButton() ); Content = BuildLayout(); } DynamicLayout BuildLayout() { GroupBox showtapeInfoGroupBox = new GroupBox{ Text = "Showtape" }; DynamicLayout showtapeInfoGroupLayout = new DynamicLayout { DefaultSpacing = new Size(5, 5), DefaultPadding = new Padding(5, 5, 5, 5) }; showtapeInfoGroupLayout.AddRow(ShowtapeNameLabel); showtapeInfoGroupLayout.AddRow(ShowtapeStageTypeLabel); showtapeInfoGroupLayout.AddRow(ShowtapeCharacterLabel); showtapeInfoGroupLayout.AddRow(ShowtapeBitsLabel); showtapeInfoGroupLayout.AddRow(ShowtapeLengthLabel); showtapeInfoGroupBox.Content = showtapeInfoGroupLayout; GroupBox controllerInfoGroupBox = new GroupBox{ Text = "Controller" }; DynamicLayout controllerInfoGroupLayout = new DynamicLayout { DefaultSpacing = new Size(5, 5), DefaultPadding = new Padding(5, 5, 5, 5) }; controllerInfoGroupLayout.AddRow(ControllerCharacterLabel); controllerInfoGroupLayout.AddRow(ControllerBitsLabel); controllerInfoGroupBox.Content = controllerInfoGroupLayout; DynamicLayout layout = new DynamicLayout { DefaultSpacing = new Size(10, 10), DefaultPadding = new Padding(5, 5, 5, 5) }; layout.BeginVertical(); layout.BeginHorizontal(); layout.Add(PlayButton, true); layout.Add(StopButton, true); layout.EndHorizontal(); layout.EndVertical(); layout.BeginVertical(); layout.BeginHorizontal(); layout.Add(new Label { Text = "Serial Port" }); layout.EndHorizontal(); layout.BeginHorizontal(); layout.Add(SerialPortDropDown, true); layout.EndHorizontal(); layout.BeginHorizontal(); layout.Add(showtapeInfoGroupBox, true); layout.EndHorizontal(); layout.BeginHorizontal(); layout.Add(controllerInfoGroupBox, true); layout.EndHorizontal(); layout.EndVertical(); layout.BeginVertical(); layout.BeginHorizontal(); layout.Add(null, true); layout.Add(RefreshPortsButton); layout.Add(LoadShowtapeButton); layout.EndHorizontal(); layout.EndVertical(); layout.BeginVertical(); layout.Add(null, true); layout.EndVertical(); return layout; } void CheckReady() { if (ControllerConnected && ShowtapeLoaded) { PlayButton.Enabled = true; StopButton.Enabled = true; } else { PlayButton.Enabled = false; StopButton.Enabled = false; } } void Event_SelectSerialPort() { if (Port != null) Port.Close(); Port = new SerialPort((string)SerialPortDropDown.SelectedKey, 9600, Parity.None, 8, StopBits.One); Port.DataReceived += new SerialDataReceivedEventHandler(Event_PortRecievedData); ControllerHeader = ""; ControllerCharacterLabel.Text = "Character: Disconnected"; ControllerBitsLabel.Text = "Bits: Disconnected"; ControllerConnected = false; CheckReady(); Port.Open(); } void Event_PortRecievedData(object sender, SerialDataReceivedEventArgs e) { ControllerHeader += Port.ReadExisting(); if (ControllerHeader.EndsWith(",END")) { ControllerBits = ControllerHeader.Split(',')[1]; ControllerCharacter = ControllerHeader.Split(',')[2]; ControllerCharacterLabel.Text = $"Character: {ControllerCharacter}"; ControllerBitsLabel.Text = $"Bits: {ControllerBits}"; ControllerConnected = true; CheckReady(); } } void Event_RefreshPortsButton() { foreach (string s in SerialPort.GetPortNames()) { SerialPortDropDown.Items.Add(s); } } void Event_LoadShowtapeButton() { OpenFileDialog fileDialog = new OpenFileDialog(); fileDialog.Filters.Add(new FileFilter("UST Files (.ust)", new string[]{ ".ust" })); fileDialog.Title = "Select Show File."; fileDialog.ShowDialog(this); string tempUSTData = File.ReadAllText(fileDialog.FileName); if (!tempUSTData.StartsWith("UST,1,")) { MessageBox.Show("This is not a UST Version 1 showtape."); return; } string[] headerData = tempUSTData.Split(';')[0].Split(','); string[] stringyBits = tempUSTData.Split(';')[1].Split(','); AudioData = Convert.FromBase64String(tempUSTData.Split(';')[2]); ShowtapeName = headerData[2]; ShowtapeBits = headerData[3]; ShowtapeStageType = headerData[4]; ShowtapeCharacter = headerData[5]; if (headerData[3] == "256") { MessageBox.Show("Can't load a full stage showtape right now!"); return; } TimeSpan time = TimeSpan.FromSeconds(stringyBits.Length/60); ShowtapeFormattedLength = time.ToString(@"hh\:mm\:ss"); ShowtapeNameLabel.Text = $"Show Name: {ShowtapeName}"; ShowtapeStageTypeLabel.Text = $"Stage Type: {ShowtapeStageType}"; ShowtapeCharacterLabel.Text = $"Character: {ShowtapeCharacter}"; ShowtapeBitsLabel.Text = $"Bits: {ShowtapeBits}"; ShowtapeLengthLabel.Text = $"Length: {ShowtapeFormattedLength}"; List tmpOut = new List(); foreach (string frame in stringyBits) { int b = int.Parse(frame, NumberStyles.HexNumber); char[] bytesOut = new char[8]; int bytes = int.Parse(ShowtapeBits)/4; for (int i = 0; i < bytes; i++) { int offset = 4 * i; int mask = 0b1111 << offset; int masked_value = (b & mask) >> offset; bytesOut[i] = (char)(64 | masked_value); } tmpOut.Add(new string(bytesOut)); } Signals = tmpOut.ToArray(); ShowtapeLoaded = true; CheckReady(); } void Event_PlayButton() { if (Playing) return; if (ShowtapeBits != ControllerBits) { MessageBox.Show("This showtape does not have the correct amount of bits for your controller."); return; } File.WriteAllBytes("tmp.wav", AudioData); Index = SkipSeconds*60; MPVProcess.StartInfo.Arguments = $"--start={SkipSeconds} tmp.wav"; MPVProcess.Start(); FrameTimer.Start(); Playing = true; PlayButton.Enabled = false; RefreshPortsButton.Enabled = false; LoadShowtapeButton.Enabled = false; SerialPortDropDown.Enabled = false; } void Event_StopButton() { FrameTimer.Stop(); MPVProcess.Kill(); File.Delete("tmp.wav"); Playing = false; PlayButton.Enabled = true; RefreshPortsButton.Enabled = true; LoadShowtapeButton.Enabled = true; SerialPortDropDown.Enabled = true; Index = 0; } void Event_FrameTimerTick(Object sender, ElapsedEventArgs e) { if (Index >= Signals.Length) { Event_StopButton(); return; } Port.Write(Signals[Index]); Index += FrameSkip; } } }