diff --git a/App.config b/App.config deleted file mode 100644 index 3ba592f..0000000 --- a/App.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Discord/DiscordBot.cs b/Discord/DiscordBot.cs index 67e31b3..3ceee60 100644 --- a/Discord/DiscordBot.cs +++ b/Discord/DiscordBot.cs @@ -8,16 +8,16 @@ namespace SysbotMacro.Discord { internal class DiscordBot { - string _token; - public Action LogAction { get; set; } + private readonly string _token; + public Action? LogAction { get; set; } - private DiscordSocketClient _client; + private DiscordSocketClient? _client; private readonly List _sudoUserIds; private readonly List _channelIds; private readonly List> _ipDict; private readonly List> _macroDict; - private MessageHandler _messageHandler; + private readonly MessageHandler _messageHandler; public DiscordBot(string token, List sudos, List channelids, List> ipDict, List> macroDict) { @@ -40,6 +40,8 @@ public DiscordBot(string token, List sudos, List channelids, List< public async Task MainAsync() { + if (_client == null) return; + _client.Log += LogAsync; _client.MessageReceived += MessageReceivedAsync; @@ -84,8 +86,11 @@ await _messageHandler.HandleMessage(message, async (response) => //call to stop the bot public async Task StopAsync() { - await _client.LogoutAsync(); - await _client.StopAsync(); + if (_client != null) + { + await _client.LogoutAsync(); + await _client.StopAsync(); + } } } } diff --git a/Discord/MessageHandler.cs b/Discord/MessageHandler.cs index 0b9184e..4c07425 100644 --- a/Discord/MessageHandler.cs +++ b/Discord/MessageHandler.cs @@ -12,7 +12,6 @@ using System.Text; using System.Net; using Newtonsoft.Json.Linq; -using static LibUsbDotNet.Main.UsbTransferQueue; using System.Runtime.InteropServices; using System.IO; @@ -21,6 +20,10 @@ namespace SysbotMacro.Discord { internal class MessageHandler { + private const int DEFAULT_SWITCH_PORT = 6000; + private const int PING_TIMEOUT_MS = 2000; + private const string COMMAND_PREFIX = "!"; + private readonly List> _ipDict; private readonly List> _macroDict; @@ -41,8 +44,8 @@ public async Task HandleMessage(SocketMessage message, Func sendRe var firstPart = parts.FirstOrDefault(); - // Check if the first word starts with '!' - if (firstPart != null && firstPart.StartsWith("!")) //edit the prefix here. Maybe i should add it to the form. + // Check if the first word starts with the command prefix + if (firstPart != null && firstPart.StartsWith(COMMAND_PREFIX)) { var command = firstPart.Substring(1); // Remove prefix @@ -64,7 +67,7 @@ public async Task HandleMessage(SocketMessage message, Func sendRe var embed = new EmbedBuilder { Title = "Macro and Switch IP Data", - Color = Color.Blue + Color = global::Discord.Color.Blue }; string macroField = ""; @@ -117,35 +120,44 @@ public async Task HandleMessage(SocketMessage message, Func sendRe return; } - Ping pingSender = new Ping(); - - // Ping the IP addres. - PingReply reply = await pingSender.SendPingAsync(switchIp, 2000); // 2000 ms timeout - - // Check the status and if down, exit to prevent hanging - if (reply.Status != IPStatus.Success) + using (var pingSender = new Ping()) { - await sendResponse($"Switch IP {switchIp} is not reachable"); - return; - } + // Ping the IP address + PingReply reply = await pingSender.SendPingAsync(switchIp, PING_TIMEOUT_MS); + // Check the status and if down, exit to prevent hanging + if (reply.Status != IPStatus.Success) + { + await sendResponse($"Switch IP {switchIp} is not reachable"); + return; + } + } // Execute await sendResponse($"Executing macro {command} on Switch {switchKey}"); var config = new SwitchConnectionConfig { IP = switchIp, - Port = 6000, + Port = DEFAULT_SWITCH_PORT, Protocol = SwitchProtocol.WiFi }; var bot = new Bot(config); - bot.Connect(); - var cancellationToken = new CancellationToken(); // not tied to anything yet... - await bot.ExecuteCommands(macro, () => false, cancellationToken); // not tied to anything yet... Should i? - bot.Disconnect(); - - await sendResponse($"Executed macro {command} on Switch {switchKey}"); + try + { + bot.Connect(); + var cancellationToken = new CancellationToken(); + await bot.ExecuteCommands(macro, () => false, cancellationToken); + await sendResponse($"Executed macro {command} on Switch {switchKey}"); + } + catch (Exception ex) + { + await sendResponse($"Error executing macro: {ex.Message}"); + } + finally + { + try { bot.Disconnect(); } catch { } + } } // command: to check the status of all switches else if (command == "status") @@ -159,14 +171,19 @@ public async Task HandleMessage(SocketMessage message, Func sendRe var ipAddress = switchDict.ContainsKey("IPAddress") ? switchDict["IPAddress"].ToString() : "Unknown"; // Initialize a new instance of the Ping class. - Ping pingSender = new Ping(); - - PingReply reply = await pingSender.SendPingAsync(ipAddress, 2000); // 2000 ms timeout - - // Check the status. - string status = reply.Status == IPStatus.Success ? "Up" : "Down"; - - embedDescription.AppendLine($"Name: {switchName}, IP: {ipAddress}, Status: {status}"); + using (var pingSender = new Ping()) + { + try + { + PingReply reply = await pingSender.SendPingAsync(ipAddress, PING_TIMEOUT_MS); + string status = reply.Status == IPStatus.Success ? "Up" : "Down"; + embedDescription.AppendLine($"Name: {switchName}, IP: {ipAddress}, Status: {status}"); + } + catch (Exception ex) + { + embedDescription.AppendLine($"Name: {switchName}, IP: {ipAddress}, Status: Error - {ex.Message}"); + } + } } // Create embed message with Discord.Net's EmbedBuilder. @@ -174,7 +191,7 @@ public async Task HandleMessage(SocketMessage message, Func sendRe { Title = "Switch Status Check", Description = embedDescription.ToString(), - Color = Color.Green // You can change the color based on your preferences. + Color = global::Discord.Color.Green // You can change the color based on your preferences. }; // Send the embed. @@ -206,39 +223,52 @@ public async Task HandleMessage(SocketMessage message, Func sendRe var config = new SwitchConnectionConfig { IP = switchIp, - Port = 6000, + Port = DEFAULT_SWITCH_PORT, Protocol = SwitchProtocol.WiFi }; var bot = new Bot(config); - bot.Connect(); - byte[] imageBytes = bot.PixelPeek(); - System.IO.File.WriteAllBytes("test.jpg", imageBytes); - string hexString = Encoding.UTF8.GetString(imageBytes); + try + { + bot.Connect(); + byte[]? imageBytes = bot.PixelPeek(); + + if (imageBytes == null || imageBytes.Length == 0) + { + await sendResponse("Failed to capture the screen - no data received."); + return; + } - // Convert the hexadecimal string back to a byte array - byte[] actualImageBytes = Enumerable.Range(0, hexString.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16)) - .ToArray(); + string hexString = Encoding.UTF8.GetString(imageBytes); - // Write the byte array to a .jpg file - System.IO.File.WriteAllBytes("test.jpg", actualImageBytes); + // Convert the hexadecimal string back to a byte array + byte[] actualImageBytes = Enumerable.Range(0, hexString.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16)) + .ToArray(); - // Send the image as an attachment in Discord - if (actualImageBytes != null && actualImageBytes.Length > 0) - { - using (var stream = new MemoryStream(actualImageBytes)) + // Send the image as an attachment in Discord + if (actualImageBytes.Length > 0) + { + using (var stream = new MemoryStream(actualImageBytes)) + { + stream.Seek(0, SeekOrigin.Begin); + var embed = new EmbedBuilder(); + embed.ImageUrl = "attachment://screenshot.jpg"; + await ((ISocketMessageChannel)message.Channel).SendFileAsync(stream, "screenshot.jpg", $"Here's the screenshot of {switchKey}", embed: embed.Build()); + } + } + else { - stream.Seek(0, SeekOrigin.Begin); - var embed = new EmbedBuilder(); - embed.ImageUrl = "attachment://screenshot.jpg"; - Console.WriteLine($"Received {actualImageBytes.Length} bytes. First bytes: {actualImageBytes[0]} {actualImageBytes[1]} ..."); - await ((ISocketMessageChannel)message.Channel).SendFileAsync(stream, "screenshot.jpg", $"Here's the screenshot of {switchKey}", embed: embed.Build()); + await sendResponse("Failed to process screen capture data."); } } - else + catch (Exception ex) + { + await sendResponse($"Error capturing screenshot: {ex.Message}"); + } + finally { - await sendResponse("Failed to capture the screen."); + try { bot.Disconnect(); } catch { } } diff --git a/Form1.cs b/Form1.cs index 86fe419..4de065d 100644 --- a/Form1.cs +++ b/Form1.cs @@ -24,6 +24,12 @@ namespace SysbotMacro { public partial class Form1 : Form { + private const int DEFAULT_SWITCH_PORT = 6000; + private const int DEFAULT_BUTTON_DELAY = 100; + private const int PING_TIMEOUT_MS = 2000; + private const int MAX_SWITCH_NAME_LENGTH = 50; + private const int DISCORD_BOT_DELAY = -1; + private bool canSaveData = false; // Declare the bots list that will be looped through when commands are sent. @@ -35,10 +41,8 @@ public partial class Form1 : Form private List> ipDict = new List>(); private List> macroDict = new List>(); - - - - private DiscordBot _bot; + private DiscordBot? _bot; + private CancellationTokenSource? cancellationTokenSource; @@ -91,9 +95,6 @@ private void SaveData() File.WriteAllText("macroListView.json", JsonConvert.SerializeObject(macroDict)); - //save discordTokenTB.text to a file - File.WriteAllText("discordTokenTB.json", JsonConvert.SerializeObject(discordTokenTB.Text)); - // Save userIDLV to a JSON file var userIDListViewData = new List>(); foreach (ListViewItem item in userIDLV.Items) @@ -213,15 +214,6 @@ private void LoadData() } } - // Load discordTokenTB.text from a file - if (File.Exists("discordTokenTB.json")) - { - var discordTokenTBDataJson = File.ReadAllText("discordTokenTB.json"); - var discordTokenTBData = JsonConvert.DeserializeObject(discordTokenTBDataJson); - - discordTokenTB.Text = discordTokenTBData; - } - canSaveData = true; } @@ -249,7 +241,7 @@ private void InitializeBots() var config = new SwitchConnectionConfig { IP = ip, - Port = 6000, + Port = DEFAULT_SWITCH_PORT, Protocol = SwitchProtocol.WiFi }; @@ -581,54 +573,84 @@ private void delayButton_Click(object sender, EventArgs e) private void addIpButton_Click(object sender, EventArgs e) { - string ipText = ipTextField.Text; - string switchName = switchNameTB.Text; + string ipText = ipTextField.Text?.Trim(); + string switchName = switchNameTB.Text?.Trim(); - if (string.IsNullOrEmpty(switchName)) + if (string.IsNullOrWhiteSpace(switchName)) { - UpdateLogger("Add a name for the switch"); + UpdateLogger("Switch name cannot be empty"); return; } - if (!string.IsNullOrEmpty(ipText)) + if (string.IsNullOrWhiteSpace(ipText)) { - if (IPAddress.TryParse(ipText, out IPAddress address)) - { - ListViewItem existingItem = null; - foreach (ListViewItem item in ipListView.Items) - { - if (item.SubItems[1].Text == switchName && item.SubItems[2].Text == ipText) - { - existingItem = item; - break; - } - } + UpdateLogger("IP address cannot be empty"); + return; + } - if (existingItem != null) - { - existingItem.SubItems[1].Text = switchName; - existingItem.SubItems[2].Text = ipText; - } - else - { - ListViewItem newItem = new ListViewItem(""); // New ListViewItem for the first column checkbox nonsense - newItem.SubItems.Add(switchName); - newItem.SubItems.Add(ipText); - ipListView.Items.Add(newItem); - ipListView.Invalidate(); - } + if (!IsValidIPAddress(ipText)) + { + UpdateLogger("Invalid IP address format. Please enter a valid IPv4 address."); + return; + } + + if (switchName.Length > MAX_SWITCH_NAME_LENGTH) + { + UpdateLogger($"Switch name is too long (max {MAX_SWITCH_NAME_LENGTH} characters)"); + return; + } - ipTextField.Clear(); - switchNameTB.Clear(); - SaveData(); + ListViewItem existingItem = null; + foreach (ListViewItem item in ipListView.Items) + { + if (item.SubItems[1].Text.Equals(switchName, StringComparison.OrdinalIgnoreCase)) + { + existingItem = item; + break; + } + } + + try + { + if (existingItem != null) + { + existingItem.SubItems[1].Text = switchName; + existingItem.SubItems[2].Text = ipText; + UpdateLogger($"Updated switch '{switchName}' with IP {ipText}"); } else { - UpdateLogger("Invalid IP address format."); + ListViewItem newItem = new ListViewItem(""); + newItem.SubItems.Add(switchName); + newItem.SubItems.Add(ipText); + ipListView.Items.Add(newItem); + ipListView.Invalidate(); + UpdateLogger($"Added switch '{switchName}' with IP {ipText}"); } + + ipTextField.Clear(); + switchNameTB.Clear(); + SaveData(); + } + catch (Exception ex) + { + UpdateLogger($"Error adding switch: {ex.Message}"); } } + private bool IsValidIPAddress(string ipAddress) + { + if (!IPAddress.TryParse(ipAddress, out IPAddress address)) + return false; + + if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) + return false; + + string[] parts = ipAddress.Split('.'); + return parts.Length == 4 && parts.All(part => + int.TryParse(part, out int value) && value >= 0 && value <= 255); + } + private void deleteIpButton_Click(object sender, EventArgs e) { if (ipListView.SelectedItems.Count > 0) // Make sure an item is selected @@ -652,46 +674,60 @@ private void deleteButton_Click(object sender, EventArgs e) } } - private CancellationTokenSource cancellationTokenSource; private async void playbButton_Click(object sender, EventArgs e) { - bots.Clear(); - //this was the best time to initialize bots. - InitializeBots(); - if (textBox1.Text == "") - { - UpdateLogger("No macro entered"); - return; - } - if (loopCheckbox.Checked == true) + try { - stopbButton.Enabled = true; - stopbButton.BackColor = Color.Aqua; - playbButton.Enabled = false; - UpdateLogger("Starting Macro Loop"); - - } - - cancellationTokenSource = new CancellationTokenSource(); // Create a new CancellationTokenSource that you call in the stop button code - - string commands = textBox1.Text; - Func loopFunc = () => loopCheckbox.Checked; + bots.Clear(); + InitializeBots(); + + if (string.IsNullOrWhiteSpace(textBox1.Text)) + { + UpdateLogger("No macro entered"); + return; + } - foreach (var bot in bots) - try + if (bots.Count == 0) { - bot.Connect(); - await bot.ExecuteCommands(commands, loopFunc, cancellationTokenSource.Token); - bot.Disconnect(); + UpdateLogger("No switches selected"); + return; } - catch (Exception ex) + + if (loopCheckbox.Checked) { - - UpdateLogger(ex.Message); + stopbButton.Enabled = true; + stopbButton.BackColor = Color.Aqua; + playbButton.Enabled = false; + UpdateLogger("Starting Macro Loop"); } + cancellationTokenSource = new CancellationTokenSource(); + string commands = textBox1.Text; + Func loopFunc = () => loopCheckbox.Checked; + foreach (var bot in bots) + { + try + { + bot.Connect(); + await bot.ExecuteCommands(commands, loopFunc, cancellationTokenSource.Token); + bot.Disconnect(); + } + catch (Exception ex) + { + UpdateLogger($"Error executing commands on bot: {ex.Message}"); + try { bot.Disconnect(); } catch { } + } + } + } + catch (Exception ex) + { + UpdateLogger($"Error in macro execution: {ex.Message}"); + playbButton.Enabled = true; + stopbButton.Enabled = false; + stopbButton.BackColor = Color.White; + } } //stop button terminates the macro loop @@ -745,84 +781,96 @@ private void livebButton_Click(object sender, EventArgs e) UpdateLogger(msg); } - //Use to send any button to the list of selected IPs private async Task SendLiveButtonPress(string button) { - InitializeBots(); - foreach (var bot in bots) + try { - try + InitializeBots(); + + if (bots.Count == 0) { - bot.Connect(); - switch (button) - { - case "A": - await bot.PressAButton(); - break; - case "B": - await bot.PressBButton(); - break; - case "X": - await bot.PressXButton(); - break; - case "Y": - await bot.PressYButton(); - break; - case "L": - await bot.PressLButton(); - break; - case "R": - await bot.PressRButton(); - break; - case "ZL": - await bot.PressZLButton(); - break; - case "ZR": - await bot.PressZRButton(); - break; - case "Up": - await bot.PressDPadUp(); - break; - case "Down": - await bot.PressDPadDown(); - break; - case "Left": - await bot.PressDPadLeft(); - break; - case "Right": - await bot.PressDPadRight(); - break; - case "Plus": - await bot.PressPlusButton(); - break; - case "Minus": - await bot.PressMinusButton(); - break; - case "Home": - await bot.PressHomeButton(); - break; - case "Capture": - await bot.PressCaptureButton(); - break; - case "LStick": - await bot.PressLeftStickButton(); - break; - case "RStick": - await bot.PressRightStickButton(); - break; - - - // Add cases for the other buttons as needed... - default: - throw new ArgumentException("Invalid button string."); - } - bot.Disconnect(); + UpdateLogger("No switches selected for live button press"); + return; } - catch (Exception ex) + + foreach (var bot in bots) { - UpdateLogger(ex.Message); + try + { + bot.Connect(); + switch (button) + { + case "A": + await bot.PressAButton(); + break; + case "B": + await bot.PressBButton(); + break; + case "X": + await bot.PressXButton(); + break; + case "Y": + await bot.PressYButton(); + break; + case "L": + await bot.PressLButton(); + break; + case "R": + await bot.PressRButton(); + break; + case "ZL": + await bot.PressZLButton(); + break; + case "ZR": + await bot.PressZRButton(); + break; + case "Up": + await bot.PressDPadUp(); + break; + case "Down": + await bot.PressDPadDown(); + break; + case "Left": + await bot.PressDPadLeft(); + break; + case "Right": + await bot.PressDPadRight(); + break; + case "Plus": + await bot.PressPlusButton(); + break; + case "Minus": + await bot.PressMinusButton(); + break; + case "Home": + await bot.PressHomeButton(); + break; + case "Capture": + await bot.PressCaptureButton(); + break; + case "LStick": + await bot.PressLeftStickButton(); + break; + case "RStick": + await bot.PressRightStickButton(); + break; + default: + UpdateLogger($"Unknown button: {button}"); + return; + } + bot.Disconnect(); + } + catch (Exception ex) + { + UpdateLogger($"Error pressing {button} on bot: {ex.Message}"); + try { bot.Disconnect(); } catch { } + } } } + catch (Exception ex) + { + UpdateLogger($"Error in live button press: {ex.Message}"); + } } private async void botStartBButton_Click(object sender, EventArgs e) diff --git a/LoadingScreen.cs b/LoadingScreen.cs new file mode 100644 index 0000000..263800e --- /dev/null +++ b/LoadingScreen.cs @@ -0,0 +1,359 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace SysbotMacro +{ + public partial class LoadingScreen : Form + { + private System.Windows.Forms.Timer animationTimer; + private System.Windows.Forms.Timer glitchTimer; + private System.Windows.Forms.Timer progressTimer; + private int animationFrame = 0; + private int glitchFrame = 0; + private int progressValue = 0; + private Random random = new Random(); + private bool glitchActive = false; + private Color neonBlue = Color.FromArgb(0, 255, 255); + private Color neonPink = Color.FromArgb(255, 20, 147); + private Color neonGreen = Color.FromArgb(57, 255, 20); + private Color switchRed = Color.FromArgb(230, 45, 75); + private Color switchBlue = Color.FromArgb(0, 174, 239); + + public LoadingScreen() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + this.SuspendLayout(); + + this.AutoScaleDimensions = new SizeF(6F, 13F); + this.AutoScaleMode = AutoScaleMode.Font; + this.BackColor = Color.Black; + this.ClientSize = new Size(800, 600); + this.ControlBox = false; + this.FormBorderStyle = FormBorderStyle.None; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "LoadingScreen"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = FormStartPosition.CenterScreen; + this.Text = "Loading"; + this.TopMost = true; + this.WindowState = FormWindowState.Normal; + + this.SetStyle(ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint | + ControlStyles.DoubleBuffer | + ControlStyles.ResizeRedraw, true); + + this.Paint += LoadingScreen_Paint; + this.Load += LoadingScreen_Load; + + this.ResumeLayout(false); + } + + private void LoadingScreen_Load(object sender, EventArgs e) + { + animationTimer = new System.Windows.Forms.Timer(); + animationTimer.Interval = 50; + animationTimer.Tick += AnimationTimer_Tick; + animationTimer.Start(); + + glitchTimer = new System.Windows.Forms.Timer(); + glitchTimer.Interval = 200; + glitchTimer.Tick += GlitchTimer_Tick; + glitchTimer.Start(); + + progressTimer = new System.Windows.Forms.Timer(); + progressTimer.Interval = 100; + progressTimer.Tick += ProgressTimer_Tick; + progressTimer.Start(); + + Task.Run(async () => + { + await Task.Delay(3500); + if (this.InvokeRequired) + { + this.Invoke(new Action(() => this.Close())); + } + else + { + this.Close(); + } + }); + } + + private void AnimationTimer_Tick(object sender, EventArgs e) + { + animationFrame++; + if (animationFrame > 360) animationFrame = 0; + this.Invalidate(); + } + + private void GlitchTimer_Tick(object sender, EventArgs e) + { + glitchFrame++; + glitchActive = random.Next(0, 10) < 3; + this.Invalidate(); + } + + private void ProgressTimer_Tick(object sender, EventArgs e) + { + if (progressValue < 100) + { + progressValue += random.Next(1, 5); + if (progressValue > 100) progressValue = 100; + } + } + + private void LoadingScreen_Paint(object sender, PaintEventArgs e) + { + Graphics g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + + DrawBackground(g); + DrawTitle(g); + DrawProgressBar(g); + DrawLoadingText(g); + DrawNeonEffects(g); + DrawSwitchLogo(g); + } + + private void DrawBackground(Graphics g) + { + Rectangle rect = this.ClientRectangle; + + using (LinearGradientBrush brush = new LinearGradientBrush( + rect, Color.FromArgb(10, 10, 30), Color.FromArgb(5, 5, 15), LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, rect); + } + + for (int i = 0; i < 50; i++) + { + int x = (int)((animationFrame * 2 + i * 20) % (Width + 100)); + int y = i * 12; + int alpha = (int)(50 * Math.Sin((animationFrame + i) * 0.1)); + if (alpha < 0) alpha = -alpha; + + using (Pen pen = new Pen(Color.FromArgb(alpha, neonBlue), 1)) + { + g.DrawLine(pen, x - 50, y, x, y); + } + } + + if (glitchActive) + { + using (SolidBrush glitchBrush = new SolidBrush(Color.FromArgb(20, neonPink))) + { + for (int i = 0; i < 5; i++) + { + Rectangle glitchRect = new Rectangle( + random.Next(0, Width), + random.Next(0, Height), + random.Next(50, 200), + random.Next(2, 10) + ); + g.FillRectangle(glitchBrush, glitchRect); + } + } + } + } + + private void DrawTitle(Graphics g) + { + string title = "NINTENDO SWITCH"; + string subtitle = "SYSBOT REMOTE"; + + using (Font titleFont = new Font("Consolas", 42, FontStyle.Bold)) + using (Font subtitleFont = new Font("Consolas", 28, FontStyle.Bold)) + { + SizeF titleSize = g.MeasureString(title, titleFont); + SizeF subtitleSize = g.MeasureString(subtitle, subtitleFont); + + float titleX = (Width - titleSize.Width) / 2; + float titleY = Height / 2 - 100; + float subtitleX = (Width - subtitleSize.Width) / 2; + float subtitleY = titleY + titleSize.Height + 10; + + if (glitchActive && glitchFrame % 3 == 0) + { + titleX += random.Next(-5, 6); + subtitleX += random.Next(-3, 4); + } + + using (GraphicsPath titlePath = new GraphicsPath()) + using (GraphicsPath subtitlePath = new GraphicsPath()) + { + titlePath.AddString(title, titleFont.FontFamily, (int)titleFont.Style, titleFont.Size, new PointF(titleX, titleY), StringFormat.GenericDefault); + subtitlePath.AddString(subtitle, subtitleFont.FontFamily, (int)subtitleFont.Style, subtitleFont.Size, new PointF(subtitleX, subtitleY), StringFormat.GenericDefault); + + using (Pen glowPen = new Pen(switchRed, 8)) + using (SolidBrush textBrush = new SolidBrush(Color.White)) + { + glowPen.LineJoin = LineJoin.Round; + g.DrawPath(glowPen, titlePath); + g.FillPath(textBrush, titlePath); + } + + using (Pen glowPen = new Pen(switchBlue, 6)) + using (SolidBrush textBrush = new SolidBrush(Color.White)) + { + glowPen.LineJoin = LineJoin.Round; + g.DrawPath(glowPen, subtitlePath); + g.FillPath(textBrush, subtitlePath); + } + } + } + } + + private void DrawProgressBar(Graphics g) + { + Rectangle progressRect = new Rectangle(Width / 2 - 200, Height - 150, 400, 20); + Rectangle fillRect = new Rectangle(progressRect.X, progressRect.Y, + (int)(progressRect.Width * progressValue / 100.0), progressRect.Height); + + using (Pen borderPen = new Pen(neonBlue, 2)) + { + g.DrawRectangle(borderPen, progressRect); + } + + if (fillRect.Width > 0) + { + using (LinearGradientBrush fillBrush = new LinearGradientBrush( + fillRect, neonGreen, neonBlue, LinearGradientMode.Horizontal)) + { + g.FillRectangle(fillBrush, fillRect); + } + + using (Pen glowPen = new Pen(Color.FromArgb(100, neonGreen), 6)) + { + g.DrawRectangle(glowPen, fillRect); + } + } + + string progressText = $"{progressValue}%"; + using (Font font = new Font("Consolas", 12, FontStyle.Bold)) + using (SolidBrush brush = new SolidBrush(Color.White)) + { + SizeF textSize = g.MeasureString(progressText, font); + g.DrawString(progressText, font, brush, + progressRect.X + progressRect.Width / 2 - textSize.Width / 2, + progressRect.Y + progressRect.Height + 10); + } + } + + private void DrawLoadingText(Graphics g) + { + string[] loadingMessages = { + "INITIALIZING SYSBOT REMOTE INTERFACE...", + "LOADING SYSBOT REMOTE INTERFACE...", + "READY FOR OPERATION" + }; + + int messageIndex = Math.Min((progressValue / 34), loadingMessages.Length - 1); + string message = loadingMessages[messageIndex]; + + if (glitchActive && random.Next(0, 10) < 3) + { + char[] chars = message.ToCharArray(); + for (int i = 0; i < chars.Length; i++) + { + if (random.Next(0, 10) < 2) + { + chars[i] = (char)random.Next(33, 127); + } + } + message = new string(chars); + } + + using (Font font = new Font("Consolas", 14, FontStyle.Regular)) + using (SolidBrush brush = new SolidBrush(neonGreen)) + { + SizeF textSize = g.MeasureString(message, font); + float x = (Width - textSize.Width) / 2; + float y = Height - 100; + + using (SolidBrush glowBrush = new SolidBrush(Color.FromArgb(50, neonGreen))) + { + g.DrawString(message, font, glowBrush, x + 1, y + 1); + g.DrawString(message, font, glowBrush, x - 1, y - 1); + } + g.DrawString(message, font, brush, x, y); + } + } + + private void DrawNeonEffects(Graphics g) + { + float pulse = (float)(0.5 + 0.5 * Math.Sin(animationFrame * 0.1)); + + using (Pen neonPen = new Pen(Color.FromArgb((int)(100 * pulse), neonPink), 3)) + { + g.DrawRectangle(neonPen, 20, 20, Width - 40, Height - 40); + } + + for (int i = 0; i < 8; i++) + { + float angle = (animationFrame + i * 45) * (float)Math.PI / 180; + int centerX = Width / 2; + int centerY = Height / 2 - 50; + int radius = 150 + (int)(30 * Math.Sin(animationFrame * 0.05 + i)); + + int x = centerX + (int)(radius * Math.Cos(angle)); + int y = centerY + (int)(radius * Math.Sin(angle)); + + using (SolidBrush dotBrush = new SolidBrush(Color.FromArgb((int)(150 * pulse), neonBlue))) + { + g.FillEllipse(dotBrush, x - 3, y - 3, 6, 6); + } + } + } + + private void DrawSwitchLogo(Graphics g) + { + int logoSize = 60; + int logoX = Width - logoSize - 30; + int logoY = 30; + + using (SolidBrush redBrush = new SolidBrush(switchRed)) + using (SolidBrush blueBrush = new SolidBrush(switchBlue)) + using (SolidBrush grayBrush = new SolidBrush(Color.FromArgb(80, 80, 80))) + { + Rectangle leftJoycon = new Rectangle(logoX, logoY, logoSize / 3, logoSize); + Rectangle rightJoycon = new Rectangle(logoX + logoSize * 2 / 3, logoY, logoSize / 3, logoSize); + Rectangle screen = new Rectangle(logoX + logoSize / 3, logoY + logoSize / 4, logoSize / 3, logoSize / 2); + + g.FillRectangle(blueBrush, leftJoycon); + g.FillRectangle(redBrush, rightJoycon); + g.FillRectangle(grayBrush, screen); + + using (Pen glowPen = new Pen(Color.FromArgb(100, Color.White), 2)) + { + g.DrawRectangle(glowPen, new Rectangle(logoX - 2, logoY - 2, logoSize + 4, logoSize + 4)); + } + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + animationTimer?.Stop(); + animationTimer?.Dispose(); + glitchTimer?.Stop(); + glitchTimer?.Dispose(); + progressTimer?.Stop(); + progressTimer?.Dispose(); + } + base.Dispose(disposing); + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index 9b6ae36..f39c524 100644 --- a/Program.cs +++ b/Program.cs @@ -13,6 +13,12 @@ static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + + using (LoadingScreen loadingScreen = new LoadingScreen()) + { + loadingScreen.ShowDialog(); + } + Application.Run(new Form1()); } } diff --git a/README.md b/README.md index da08036..53b8491 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ + +![image](https://github.com/user-attachments/assets/e9474228-d914-4cad-926d-bd53126658e2) ![image](https://github.com/CodeHedge/SysBot.Remote/assets/35341367/ec1b8901-8b20-4348-ad51-3fb41dbac4ba) ![image](https://github.com/CodeHedge/SysBot.Remote/assets/35341367/b9ef1424-03b4-44e9-adac-91d251bd24f5) diff --git a/RjControlsButton.cs b/RjControlsButton.cs index 51f55f8..445195b 100644 --- a/RjControlsButton.cs +++ b/RjControlsButton.cs @@ -20,6 +20,7 @@ public class RJButton : Button //Properties [Category("RJ Code Advance")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public int BorderSize { get { return borderSize; } @@ -31,6 +32,7 @@ public int BorderSize } [Category("RJ Code Advance")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public int BorderRadius { get { return borderRadius; } @@ -42,6 +44,7 @@ public int BorderRadius } [Category("RJ Code Advance")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Color BorderColor { get { return borderColor; } @@ -53,6 +56,7 @@ public Color BorderColor } [Category("RJ Code Advance")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Color BackgroundColor { get { return this.BackColor; } @@ -60,6 +64,7 @@ public Color BackgroundColor } [Category("RJ Code Advance")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Color TextColor { get { return this.ForeColor; } diff --git a/SysBot.cs b/SysBot.cs index 4abe9c4..9d3eaff 100644 --- a/SysBot.cs +++ b/SysBot.cs @@ -144,7 +144,7 @@ public async Task PressDPadRight() await Connection.SendAsync(new ArraySegment(command), SocketFlags.None); } - public byte[] PixelPeek() + public byte[]? PixelPeek() { // Original data in hexadecimal format byte[] hexData = SwitchCommand.PixelPeek(); @@ -195,36 +195,51 @@ public byte[] PixelPeek() public async Task ExecuteCommands(string commands, Func loopFunc, CancellationToken cancellationToken) { - var splitCommands = commands.TrimEnd().Split(' '); + if (string.IsNullOrWhiteSpace(commands)) + return; - int defaultDelay = 100; // Default delay after each command + var splitCommands = commands.TrimEnd().Split(' ', StringSplitOptions.RemoveEmptyEntries); + const int defaultDelay = 100; + int currentDelay = defaultDelay; - for (int i = 0; i < splitCommands.Length; i++) + try { - if (cancellationToken.IsCancellationRequested) + for (int i = 0; i < splitCommands.Length; i++) { - break; + if (cancellationToken.IsCancellationRequested) + break; + + var command = splitCommands[i]; + + if (i < splitCommands.Length - 1 && + splitCommands[i + 1].StartsWith("d") && + int.TryParse(splitCommands[i + 1].Substring(1), out var delay) && + delay > 0) + { + await PressButton(command); + await Task.Delay(delay, cancellationToken); + currentDelay = delay; + i++; + } + else + { + await PressButton(command); + await Task.Delay(currentDelay, cancellationToken); + } } - var command = splitCommands[i]; - // If the command is followed by a "d" and a number, interpret it as a delay - if (i < splitCommands.Length - 2 && splitCommands[i + 1].StartsWith("d") && int.TryParse(splitCommands[i + 1].Substring(1), out var delay)) - { - await PressButton(command); - await Task.Delay(delay); - defaultDelay = delay; // Update the default delay - i++; // Skip the next item in the loop, since we've already processed it - } - else + if (loopFunc() && !cancellationToken.IsCancellationRequested) { - await PressButton(command); - await Task.Delay(defaultDelay); // Wait for the default delay + await ExecuteCommands(commands, loopFunc, cancellationToken); } } - - if (loopFunc() && !cancellationToken.IsCancellationRequested) + catch (OperationCanceledException) + { + // Expected when cancellation is requested + } + catch (Exception ex) { - await ExecuteCommands(commands, loopFunc, cancellationToken); + throw new InvalidOperationException($"Error executing commands: {ex.Message}", ex); } } diff --git a/SysBotRemote.csproj b/SysBotRemote.csproj index e744345..c549878 100644 --- a/SysBotRemote.csproj +++ b/SysBotRemote.csproj @@ -1,211 +1,57 @@ - - - + + - Debug - AnyCPU - {0C2C7A73-4626-47C5-A540-2ADD8ADE42B1} WinExe + net9.0-windows + true SysbotMacro SysBotRemote - v4.7.2 - 512 - true - true - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 3 - 1.0.0.%2a - false - true - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - nintendo_switch_icon_136357.ico + enable + enable + true + false - - D9FB87FA2C4C586691921D9EC938EEBD447369E1 - - - SysbotMacro_TemporaryKey.pfx - - - true - - - false - + - - packages\Discord.Net.Commands.3.12.0\lib\net461\Discord.Net.Commands.dll - - - packages\Discord.Net.Core.3.12.0\lib\net461\Discord.Net.Core.dll - - - packages\Discord.Net.Interactions.3.12.0\lib\net461\Discord.Net.Interactions.dll - - - packages\Discord.Net.Rest.3.12.0\lib\net461\Discord.Net.Rest.dll - - - packages\Discord.Net.Webhook.3.12.0\lib\netstandard2.0\Discord.Net.Webhook.dll - - - packages\Discord.Net.WebSocket.3.12.0\lib\net461\Discord.Net.WebSocket.dll - - - packages\LibUsbDotNet.2.2.29\lib\net45\LibUsbDotNet.LibUsbDotNet.dll - - - packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - - - packages\Microsoft.Extensions.DependencyInjection.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - - packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll - - - packages\NLog.5.1.3\lib\net46\NLog.dll - - - packages\SysBot.Base.1.0.8\lib\netstandard2.0\SysBot.Base.dll - - - - packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll - - - packages\System.Collections.Immutable.5.0.0\lib\net461\System.Collections.Immutable.dll - - - - - packages\System.Interactive.Async.5.0.0\lib\net461\System.Interactive.Async.dll - - - - packages\System.Linq.Async.5.0.0\lib\net461\System.Linq.Async.dll - - - packages\System.Memory.4.5.4\lib\net461\System.Memory.dll - - - - packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll - - - packages\System.Reactive.5.0.0\lib\net472\System.Reactive.dll - - - packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - - packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - packages\System.ValueTuple.4.4.0\lib\net47\System.ValueTuple.dll - - - - - - - - - - - - + + + + + + + + + + + + - - - - - Component - - - Form - - - Form1.cs - - - - - Form1.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - + + + + + + + + True Resources.resx True - - - SettingsSingleFileGenerator - Settings.Designer.cs - - + True Settings.settings True - - - - + - - - - - - - False - Microsoft .NET Framework 4.7.2 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 - false - + + SettingsSingleFileGenerator + Settings.Designer.cs + - + \ No newline at end of file diff --git a/packages.config b/packages.config deleted file mode 100644 index a547795..0000000 --- a/packages.config +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file