From 33fccf85b57aa2feddf24ad28718456453c5495e Mon Sep 17 00:00:00 2001 From: Anon Date: Sun, 22 Mar 2026 12:45:52 +0100 Subject: [PATCH] Added changes from 2927 --- MinecraftClientGUI/Form1.Designer.cs | 132 +-- MinecraftClientGUI/Form1.cs | 989 +++++++++++++----- MinecraftClientGUI/MinecraftClient.cs | 109 +- MinecraftClientGUI/MinecraftClientGUI.csproj | 54 +- MinecraftClientGUI/Program.cs | 26 +- .../Properties/Resources.Designer.cs | 46 +- .../Properties/Settings.Designer.cs | 24 +- MinecraftClientGUI/app.config | 3 + MinecraftClientGUI/screenshot.png | Bin 0 -> 49804 bytes 9 files changed, 875 insertions(+), 508 deletions(-) create mode 100644 MinecraftClientGUI/app.config create mode 100644 MinecraftClientGUI/screenshot.png diff --git a/MinecraftClientGUI/Form1.Designer.cs b/MinecraftClientGUI/Form1.Designer.cs index ba2c948a..e31ea78b 100644 --- a/MinecraftClientGUI/Form1.Designer.cs +++ b/MinecraftClientGUI/Form1.Designer.cs @@ -28,146 +28,20 @@ /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); - this.groupBox_Login = new System.Windows.Forms.GroupBox(); - this.btn_connect = new System.Windows.Forms.Button(); - this.box_ip = new System.Windows.Forms.TextBox(); - this.box_password = new System.Windows.Forms.TextBox(); - this.box_Login = new System.Windows.Forms.TextBox(); - this.box_output = new System.Windows.Forms.RichTextBox(); - this.box_input = new System.Windows.Forms.TextBox(); - this.btn_send = new System.Windows.Forms.Button(); - this.btn_about = new System.Windows.Forms.Button(); - this.groupBox_Login.SuspendLayout(); + this.components = new System.ComponentModel.Container(); this.SuspendLayout(); // - // groupBox_Login - // - this.groupBox_Login.BackColor = System.Drawing.Color.Transparent; - this.groupBox_Login.Controls.Add(this.btn_connect); - this.groupBox_Login.Controls.Add(this.box_ip); - this.groupBox_Login.Controls.Add(this.box_password); - this.groupBox_Login.Controls.Add(this.box_Login); - this.groupBox_Login.Location = new System.Drawing.Point(13, 11); - this.groupBox_Login.Name = "groupBox_Login"; - this.groupBox_Login.Size = new System.Drawing.Size(564, 46); - this.groupBox_Login.TabIndex = 0; - this.groupBox_Login.TabStop = false; - this.groupBox_Login.Text = " "; - // - // btn_connect - // - this.btn_connect.Location = new System.Drawing.Point(513, 15); - this.btn_connect.Name = "btn_connect"; - this.btn_connect.Size = new System.Drawing.Size(40, 23); - this.btn_connect.TabIndex = 6; - this.btn_connect.Text = "Go!"; - this.btn_connect.UseVisualStyleBackColor = true; - this.btn_connect.Click += new System.EventHandler(this.btn_connect_Click); - // - // box_ip - // - this.box_ip.Location = new System.Drawing.Point(400, 17); - this.box_ip.Name = "box_ip"; - this.box_ip.Size = new System.Drawing.Size(100, 20); - this.box_ip.TabIndex = 5; - this.box_ip.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_password - // - this.box_password.Location = new System.Drawing.Point(235, 17); - this.box_password.Name = "box_password"; - this.box_password.PasswordChar = '•'; - this.box_password.Size = new System.Drawing.Size(100, 20); - this.box_password.TabIndex = 3; - this.box_password.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_Login - // - this.box_Login.Location = new System.Drawing.Point(67, 17); - this.box_Login.Name = "box_Login"; - this.box_Login.Size = new System.Drawing.Size(100, 20); - this.box_Login.TabIndex = 1; - this.box_Login.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_output - // - this.box_output.Location = new System.Drawing.Point(13, 66); - this.box_output.Name = "box_output"; - this.box_output.ReadOnly = true; - this.box_output.Size = new System.Drawing.Size(564, 292); - this.box_output.TabIndex = 1; - this.box_output.Text = ""; - this.box_output.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(this.LinkClicked); - // - // box_input - // - this.box_input.AcceptsTab = true; - this.box_input.Location = new System.Drawing.Point(13, 365); - this.box_input.MaxLength = 100; - this.box_input.Multiline = true; - this.box_input.Name = "box_input"; - this.box_input.Size = new System.Drawing.Size(490, 20); - this.box_input.TabIndex = 2; - this.box_input.KeyDown += new System.Windows.Forms.KeyEventHandler(this.inputBox_KeyDown); - // - // btn_send - // - this.btn_send.Location = new System.Drawing.Point(509, 364); - this.btn_send.Name = "btn_send"; - this.btn_send.Size = new System.Drawing.Size(40, 22); - this.btn_send.TabIndex = 3; - this.btn_send.Text = "Send"; - this.btn_send.UseVisualStyleBackColor = true; - this.btn_send.Click += new System.EventHandler(this.btn_send_Click); - // - // btn_about - // - this.btn_about.Location = new System.Drawing.Point(555, 364); - this.btn_about.Name = "btn_about"; - this.btn_about.Size = new System.Drawing.Size(22, 22); - this.btn_about.TabIndex = 4; - this.btn_about.Text = "?"; - this.btn_about.UseVisualStyleBackColor = true; - this.btn_about.Click += new System.EventHandler(this.btn_about_Click); - // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.SystemColors.Control; - this.ClientSize = new System.Drawing.Size(589, 398); - this.Controls.Add(this.btn_about); - this.Controls.Add(this.btn_send); - this.Controls.Add(this.box_input); - this.Controls.Add(this.box_output); - this.Controls.Add(this.groupBox_Login); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; + this.ClientSize = new System.Drawing.Size(1100, 700); this.Name = "Form1"; + this.Text = "MCC Multibox Commander"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Minecraft Console Client GUI"; - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.onClose); - this.Load += new System.EventHandler(this.Form1_Load); - this.groupBox_Login.ResumeLayout(false); - this.groupBox_Login.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); - } #endregion - - private System.Windows.Forms.GroupBox groupBox_Login; - private System.Windows.Forms.Button btn_connect; - private System.Windows.Forms.TextBox box_ip; - private System.Windows.Forms.TextBox box_password; - private System.Windows.Forms.TextBox box_Login; - private System.Windows.Forms.RichTextBox box_output; - private System.Windows.Forms.TextBox box_input; - private System.Windows.Forms.Button btn_send; - private System.Windows.Forms.Button btn_about; } } - diff --git a/MinecraftClientGUI/Form1.cs b/MinecraftClientGUI/Form1.cs index 189cab59..60698982 100644 --- a/MinecraftClientGUI/Form1.cs +++ b/MinecraftClientGUI/Form1.cs @@ -1,337 +1,794 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Data; +using System.Diagnostics; using System.Drawing; +using System.IO; using System.Linq; -using System.Text; -using System.Windows.Forms; +using System.Runtime.InteropServices; using System.Threading; +using System.Windows.Forms; namespace MinecraftClientGUI { - /// - /// The main graphical user interface - /// + static class Theme + { + public static Color BgDark = Color.FromArgb(15, 15, 18); + public static Color BgPanel = Color.FromArgb(22, 22, 28); + public static Color BgHeader = Color.FromArgb(28, 28, 36); + public static Color BgCard = Color.FromArgb(32, 32, 42); + public static Color BgInput = Color.FromArgb(20, 20, 26); + public static Color TabActive = Color.FromArgb(38, 38, 52); + public static Color TabInactive = Color.FromArgb(22, 22, 28); + public static Color Accent = Color.FromArgb(82, 130, 255); + public static Color AccentHover = Color.FromArgb(110, 155, 255); + public static Color AccentRed = Color.FromArgb(220, 70, 70); + public static Color AccentGreen = Color.FromArgb(60, 200, 100); + public static Color Text = Color.FromArgb(220, 220, 230); + public static Color TextDim = Color.FromArgb(120, 120, 140); + public static Color TextMuted = Color.FromArgb(70, 70, 90); + public static Color Border = Color.FromArgb(40, 40, 55); + } + + class DarkComboBox : ComboBox + { + public DarkComboBox() + { + DrawMode = DrawMode.OwnerDrawFixed; + FlatStyle = FlatStyle.Flat; + BackColor = Theme.BgInput; + ForeColor = Theme.Text; + Font = new Font("Segoe UI", 9f); + } + protected override void OnDrawItem(DrawItemEventArgs e) + { + if (e.Index < 0) return; + e.Graphics.FillRectangle( + new SolidBrush((e.State & DrawItemState.Selected) != 0 ? Theme.TabActive : Theme.BgInput), + e.Bounds); + TextRenderer.DrawText(e.Graphics, Items[e.Index].ToString(), Font, e.Bounds, + Theme.Text, TextFormatFlags.VerticalCenter | TextFormatFlags.Left); + } + } + + class FlatBtn : Button + { + private Color _back, _hover; + public FlatBtn(string text, Color back, Color? hover = null) + { + Text = text; + _back = back; + _hover = hover ?? ControlPaint.Light(back, 0.2f); + FlatStyle = FlatStyle.Flat; + FlatAppearance.BorderSize = 0; + BackColor = _back; + ForeColor = Theme.Text; + Font = new Font("Segoe UI", 9f, FontStyle.Bold); + Cursor = Cursors.Hand; + MouseEnter += (s, e) => BackColor = _hover; + MouseLeave += (s, e) => BackColor = _back; + } + } public partial class Form1 : Form { - private LinkedList previous = new LinkedList(); - private MinecraftClient Client; - private Thread t_clientread; - - #region Aero Glass Low-level Windows API - - [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] - public struct MARGINS + private const string SettingsFile = "settings_v3.txt"; + private const string MacrosFile = "macros.txt"; + private static readonly string[] DefaultSettingsContent = new[] { "", "", "" }; + private static readonly string[] DefaultMacrosContent = new[] { - public int Left; - public int Right; - public int Top; - public int Bottom; - } + "Creative|/gamemode creative|Gold", + "Survival|/gamemode survival|Gray", + "Hello|Hello everyone!|Green", + "Login|/login password123|Purple", + "Spawn|/spawn|Blue" + }; + private string currentLang = "en"; - [System.Runtime.InteropServices.DllImport("dwmapi.dll")] - public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins); + private Panel tabBar, contentArea, topPanel, bottomPanel, rightPanel; + private Label lblLogin, lblPass, lblIP, lblActive; + private DarkComboBox cmbLogin, cmbIP; + private TextBox txtPassword; + private FlatBtn btnAddBot; + private TextBox boxGlobalInput; + private FlatBtn btnGlobalSend; + private CheckBox chkSendToAll; + private Label lblMacrosTitle; + private FlowLayoutPanel macroPanel; + private FlatBtn btnEditMacros, btnRefreshMacros, btnLangSwitch; - #endregion + private List historyLogins = new List(); + private List historyIPs = new List(); + private List tabs = new List(); + private ConsoleTab activeTab = null; public Form1(string[] args) { InitializeComponent(); - if (args.Length > 0) { initClient(new MinecraftClient(args)); } + BuildUI(); + EnsureRuntimeFiles(); + LoadSettings(); + LoadMacros(); + UpdateLanguage(); + if (args.Length > 0) AddNewTab("Auto-Bot", args); + this.FormClosing += (s, e) => { foreach (var t in tabs.ToList()) t.CloseTab(); }; } - /// - /// Define some element properties and init Aero Glass if using Vista or newer - /// - - private void Form1_Load(object sender, EventArgs e) + private static void EnsureRuntimeFiles() { - box_output.ScrollBars = RichTextBoxScrollBars.None; - box_output.Font = new Font("Consolas", 8); - box_output.BackColor = Color.White; + EnsureFileExists(SettingsFile, DefaultSettingsContent); + EnsureFileExists(MacrosFile, DefaultMacrosContent); + } - if (Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor == 1) + private static void EnsureFileExists(string path, string[] defaultContent) + { + if (!File.Exists(path)) { - this.BackColor = Color.DarkMagenta; this.TransparencyKey = Color.DarkMagenta; - MARGINS marg = new MARGINS() { Left = -1, Right = -1, Top = -1, Bottom = -1 }; - DwmExtendFrameIntoClientArea(this.Handle, ref marg); + File.WriteAllLines(path, defaultContent); } } - /// - /// Launch the Minecraft Client by clicking the "Go!" button. - /// If a client is already running, it will be closed. - /// - - private void btn_connect_Click(object sender, EventArgs e) + private void BuildUI() { - if (Client != null) + this.Text = "MCC Multibox Commander"; + this.Size = new Size(1200, 780); + this.MinimumSize = new Size(900, 600); + this.BackColor = Theme.BgDark; + this.ForeColor = Theme.Text; + this.Font = new Font("Segoe UI", 9f); + this.StartPosition = FormStartPosition.CenterScreen; + + // TOP PANEL + topPanel = new Panel { Dock = DockStyle.Top, Height = 60, BackColor = Theme.BgHeader, Padding = new Padding(12, 0, 12, 0) }; + topPanel.Paint += PaintBottomBorder; + this.Controls.Add(topPanel); + + int y = 17; + lblLogin = MkLabel("Username / Email:", 10, 2); topPanel.Controls.Add(lblLogin); + cmbLogin = new DarkComboBox { Location = new Point(10, y), Size = new Size(195, 26) }; topPanel.Controls.Add(cmbLogin); + + lblPass = MkLabel("Password:", 215, 2); topPanel.Controls.Add(lblPass); + txtPassword = new TextBox { Location = new Point(215, y), Size = new Size(155, 26), BackColor = Theme.BgInput, ForeColor = Theme.Text, BorderStyle = BorderStyle.FixedSingle, UseSystemPasswordChar = true, Font = new Font("Segoe UI", 9f) }; + topPanel.Controls.Add(txtPassword); + + lblIP = MkLabel("Server IP:", 380, 2); topPanel.Controls.Add(lblIP); + cmbIP = new DarkComboBox { Location = new Point(380, y), Size = new Size(215, 26) }; topPanel.Controls.Add(cmbIP); + + btnAddBot = new FlatBtn("+ Add Account", Theme.Accent, Theme.AccentHover) { Location = new Point(608, y - 1), Size = new Size(148, 28) }; + btnAddBot.Click += BtnAddBot_Click; + topPanel.Controls.Add(btnAddBot); + + lblActive = new Label { Location = new Point(770, y), AutoSize = true, ForeColor = Theme.AccentGreen, Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + topPanel.Controls.Add(lblActive); + + var timer = new System.Windows.Forms.Timer { Interval = 1000 }; + timer.Tick += (s, e) => lblActive.Text = (currentLang == "en" ? "Active accounts: " : "Aktywne konta: ") + tabs.Count; + timer.Start(); + + // RIGHT PANEL + rightPanel = new Panel { Dock = DockStyle.Right, Width = 185, BackColor = Theme.BgPanel }; + rightPanel.Paint += PaintLeftBorder; + this.Controls.Add(rightPanel); + + // Language toggle - large button at the top + btnLangSwitch = new FlatBtn("PL", Color.FromArgb(45, 75, 145), Color.FromArgb(60, 100, 185)) { - Client.Close(); - t_clientread.Abort(); - box_output.Text = ""; - } - string username = box_Login.Text; - string password = box_password.Text; - string serverip = box_ip.Text; - if (password == "") { password = "-"; } - if (username != "" && serverip != "") - { - initClient(new MinecraftClient(username, password, serverip)); - } + Dock = DockStyle.Top, + Height = 36, + Font = new Font("Segoe UI", 11f, FontStyle.Bold), + ForeColor = Color.White + }; + btnLangSwitch.Click += (s, e) => { currentLang = currentLang == "en" ? "pl" : "en"; UpdateLanguage(); }; + rightPanel.Controls.Add(btnLangSwitch); + + // Macro header + var macroHeader = new Panel { Dock = DockStyle.Top, Height = 52, BackColor = Theme.BgPanel }; + macroHeader.Paint += PaintBottomBorder; + rightPanel.Controls.Add(macroHeader); + + lblMacrosTitle = new Label { Text = "Quick Actions", Location = new Point(8, 8), Size = new Size(169, 18), ForeColor = Theme.Text, Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + macroHeader.Controls.Add(lblMacrosTitle); + + btnEditMacros = new FlatBtn("Edit", Color.FromArgb(40, 40, 58)) { Location = new Point(8, 28), Size = new Size(76, 20), Font = new Font("Segoe UI", 8f, FontStyle.Bold) }; + btnEditMacros.Click += BtnEditMacros_Click; + macroHeader.Controls.Add(btnEditMacros); + + btnRefreshMacros = new FlatBtn("Reload", Color.FromArgb(40, 40, 58)) { Location = new Point(90, 28), Size = new Size(76, 20), Font = new Font("Segoe UI", 8f, FontStyle.Bold) }; + btnRefreshMacros.Click += (s, e) => LoadMacros(); + macroHeader.Controls.Add(btnRefreshMacros); + + macroPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.TopDown, WrapContents = false, AutoScroll = true, BackColor = Theme.BgPanel, Padding = new Padding(8, 8, 0, 8) }; + rightPanel.Controls.Add(macroPanel); + + rightPanel.Controls.SetChildIndex(macroPanel, 0); + rightPanel.Controls.SetChildIndex(macroHeader, 1); + rightPanel.Controls.SetChildIndex(btnLangSwitch, 2); + + // BOTTOM PANEL + bottomPanel = new Panel { Dock = DockStyle.Bottom, Height = 40, BackColor = Theme.BgHeader, Padding = new Padding(6, 6, 6, 0) }; + bottomPanel.Paint += PaintTopBorder; + this.Controls.Add(bottomPanel); + + btnGlobalSend = new FlatBtn("Send", Color.FromArgb(50, 90, 160), Theme.Accent) { Dock = DockStyle.Right, Width = 80 }; + btnGlobalSend.Click += BtnGlobalSend_Click; + bottomPanel.Controls.Add(btnGlobalSend); + + chkSendToAll = new CheckBox { Text = "Send to all", Dock = DockStyle.Right, Width = 120, ForeColor = Color.FromArgb(255, 160, 90), Padding = new Padding(8, 0, 0, 0), Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + bottomPanel.Controls.Add(chkSendToAll); + + boxGlobalInput = new TextBox { Dock = DockStyle.Fill, BackColor = Theme.BgInput, ForeColor = Theme.Text, BorderStyle = BorderStyle.FixedSingle, Font = new Font("Consolas", 11f) }; + boxGlobalInput.KeyDown += (s, e) => { if (e.KeyCode == Keys.Enter) { BtnGlobalSend_Click(s, e); e.SuppressKeyPress = true; } }; + bottomPanel.Controls.Add(boxGlobalInput); + + // TAB BAR + tabBar = new Panel { Dock = DockStyle.Top, Height = 38, BackColor = Theme.BgPanel }; + tabBar.Paint += PaintBottomBorder; + this.Controls.Add(tabBar); + + // CONTENT AREA + contentArea = new Panel { Dock = DockStyle.Fill, BackColor = Theme.BgDark }; + this.Controls.Add(contentArea); + + this.Controls.SetChildIndex(contentArea, 0); + this.Controls.SetChildIndex(tabBar, 1); + this.Controls.SetChildIndex(bottomPanel, 2); + this.Controls.SetChildIndex(rightPanel, 3); + this.Controls.SetChildIndex(topPanel, 4); } - /// - /// Handle a new Minecraft Client - /// - /// Client to handle - - private void initClient(MinecraftClient client) + private void AddNewTab(string title, string[] args) { - Client = client; - t_clientread = new Thread(new ThreadStart(t_clientread_loop)); - t_clientread.Start(); - box_input.Select(); + var tab = new ConsoleTab(title, args, currentLang) { Dock = DockStyle.Fill }; + tabs.Add(tab); + contentArea.Controls.Add(tab); + RebuildTabBar(); + ActivateTab(tab); } - /// - /// Thread reading output from the Minecraft Client - /// - - private void t_clientread_loop() + private void ActivateTab(ConsoleTab tab) { - while (true && !Client.Disconnected) - { - printstring(Client.ReadLine()); - } + activeTab = tab; + foreach (Control c in contentArea.Controls) c.Visible = (c == tab); + RebuildTabBar(); } - /// - /// Print a Minecraft-Formatted string to the console area - /// - /// String to print - - private void printstring(string str) + private void RebuildTabBar() { - if (!String.IsNullOrEmpty(str)) + tabBar.Controls.Clear(); + int x = 4; + foreach (var tab in tabs) { - Color color = Color.Black; - FontStyle style = FontStyle.Regular; - string[] subs = str.Split('§'); - if (subs[0].Length > 0) { AppendTextBox(box_output, subs[0], Color.Black, FontStyle.Regular); } - for (int i = 1; i < subs.Length; i++) + var t = tab; + bool active = (t == activeTab); + + var btn = new Panel { Location = new Point(x, active ? 2 : 5), Size = new Size(148, active ? 32 : 27), BackColor = active ? Theme.TabActive : Theme.TabInactive, Cursor = Cursors.Hand }; + btn.Paint += (s, e) => { + if (t == activeTab) + e.Graphics.FillRectangle(new SolidBrush(Theme.Accent), 0, btn.Height - 2, btn.Width, 2); + }; + + var lbl = new Label { - if (subs[i].Length > 0) + Text = t.TabTitle, + Location = new Point(8, 0), + Size = new Size(108, 30), + ForeColor = active ? Theme.Text : Theme.TextDim, + Font = new Font("Segoe UI", 9f, active ? FontStyle.Bold : FontStyle.Regular), + TextAlign = ContentAlignment.MiddleLeft, + Cursor = Cursors.Hand + }; + lbl.Click += (s, e) => ActivateTab(t); + btn.Click += (s, e) => ActivateTab(t); + btn.Controls.Add(lbl); + + var btnX = new Label + { + Text = "x", + Location = new Point(120, 0), + Size = new Size(25, 30), + ForeColor = Theme.TextMuted, + Font = new Font("Segoe UI", 9f), + TextAlign = ContentAlignment.MiddleCenter, + Cursor = Cursors.Hand + }; + btnX.MouseEnter += (s, e) => btnX.ForeColor = Theme.AccentRed; + btnX.MouseLeave += (s, e) => btnX.ForeColor = Theme.TextMuted; + btnX.Click += (s, e) => { + t.CloseTab(); + tabs.Remove(t); + contentArea.Controls.Remove(t); + if (activeTab == t) { activeTab = tabs.LastOrDefault(); if (activeTab != null) ActivateTab(activeTab); } + RebuildTabBar(); + }; + btn.Controls.Add(btnX); + tabBar.Controls.Add(btn); + x += 152; + } + } + + private void UpdateLanguage() + { + bool en = currentLang == "en"; + btnLangSwitch.Text = en ? "Switch to PL" : "Switch to EN"; + lblLogin.Text = en ? "Username / Email:" : "Login / Email:"; + lblPass.Text = en ? "Password:" : "Haslo:"; + lblIP.Text = en ? "Server IP:" : "IP Serwera:"; + btnAddBot.Text = en ? "+ Add Account" : "+ Dodaj Konto"; + lblMacrosTitle.Text = en ? "Quick Actions" : "Szybkie Akcje"; + btnEditMacros.Text = en ? "Edit" : "Edytuj"; + btnRefreshMacros.Text = en ? "Reload" : "Odswiez"; + btnGlobalSend.Text = en ? "Send" : "Wyslij"; + chkSendToAll.Text = en ? "Send to all" : "Wyslij do wszystkich"; + foreach (var tab in tabs) tab.UpdateLang(currentLang); + } + + private void BtnEditMacros_Click(object sender, EventArgs e) + { + EnsureFileExists(MacrosFile, DefaultMacrosContent); + Process.Start("notepad.exe", MacrosFile); + } + + private void LoadMacros() + { + macroPanel.Controls.Clear(); + if (!File.Exists(MacrosFile)) return; + try + { + foreach (var line in File.ReadAllLines(MacrosFile)) + { + if (string.IsNullOrWhiteSpace(line)) continue; + var parts = line.Split('|'); + if (parts.Length >= 2) { - if (subs[i].Length > 1) - { - switch (subs[i][0]) - { - //Font colors - case '0': color = Color.Black; break; - case '1': color = Color.DarkBlue; break; - case '2': color = Color.DarkGreen; break; - case '3': color = Color.DarkCyan; break; - case '4': color = Color.DarkRed; break; - case '5': color = Color.DarkMagenta; break; - case '6': color = Color.DarkGoldenrod; break; - case '7': color = Color.DimGray; break; - case '8': color = Color.Gray; break; - case '9': color = Color.Blue; break; - case 'a': color = Color.Green; break; - case 'b': color = Color.CornflowerBlue; break; - case 'c': color = Color.Red; break; - case 'd': color = Color.Magenta; break; - case 'e': color = Color.Goldenrod; break; - - //White on white = invisible so use gray instead - case 'f': color = Color.DimGray; break; - - //Font styles. Can use several styles eg Bold + Underline - case 'l': style = style | FontStyle.Bold; break; - case 'm': style = style | FontStyle.Strikeout; break; - case 'n': style = style | FontStyle.Underline; break; - case 'o': style = style | FontStyle.Italic; break; - - //Reset font color & style - case 'r': color = Color.Black; style = FontStyle.Regular; break; - } - - AppendTextBox(box_output, subs[i].Substring(1, subs[i].Length - 1), color, style); - } + Color c = parts.Length > 2 ? Color.FromName(parts[2]) : Theme.Accent; + if (c.IsEmpty) c = Theme.Accent; + AddMacroBtn(parts[1], parts[0], c); } } - AppendTextBox(box_output, "\n", Color.Black, FontStyle.Regular); } - Console.ForegroundColor = ConsoleColor.Gray; + catch (Exception ex) { MessageBox.Show("Error loading macros: " + ex.Message); } } - /// - /// Append text to a RichTextBox with font customization - /// - /// Target RichTextBox - /// Text to add - /// Color of the text - /// Font style of the text - - private void AppendTextBox(RichTextBox box, string text, Color color, FontStyle style) + private void AddMacroBtn(string cmd, string label, Color color) { - if (InvokeRequired) + Color bg = Color.FromArgb(32, 32, 48); + Color hov = Color.FromArgb(44, 44, 64); + var btn = new Button { - this.Invoke(new Action(AppendTextBox), new object[] { box, text, color, style }); + Text = " " + label, + Width = macroPanel.Width - 22, + Height = 34, + FlatStyle = FlatStyle.Flat, + BackColor = bg, + ForeColor = Theme.Text, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + Cursor = Cursors.Hand, + Margin = new Padding(0, 0, 0, 4), + TextAlign = ContentAlignment.MiddleLeft, + Tag = color + }; + btn.FlatAppearance.BorderSize = 0; + btn.MouseEnter += (s, e) => btn.BackColor = hov; + btn.MouseLeave += (s, e) => btn.BackColor = bg; + btn.Paint += (s, e) => e.Graphics.FillRectangle(new SolidBrush(color), 0, 0, 4, btn.Height); + btn.Click += (s, e) => { + if (chkSendToAll.Checked) foreach (var tab in tabs) tab.Send(cmd); + else activeTab?.Send(cmd); + }; + macroPanel.Controls.Add(btn); + } + + private void BtnAddBot_Click(object sender, EventArgs e) + { + string user = cmbLogin.Text.Trim(), pass = txtPassword.Text.Trim(), ip = cmbIP.Text.Trim(); + if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(ip)) + { + MessageBox.Show(currentLang == "en" ? "Please enter username and IP!" : "Podaj login i IP serwera!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + SaveSettings(user, ip); + string tabTitle = user.Contains("@") ? user.Split('@')[0] : user; + AddNewTab(tabTitle, new[] { user, pass, ip }); + } + + private void BtnGlobalSend_Click(object sender, EventArgs e) + { + string cmd = boxGlobalInput.Text.Trim(); + if (string.IsNullOrEmpty(cmd)) return; + if (chkSendToAll.Checked) foreach (var tab in tabs) tab.Send(cmd); + else activeTab?.Send(cmd); + boxGlobalInput.Clear(); + } + + private void LoadSettings() + { + try + { + if (!File.Exists(SettingsFile)) return; + var lines = File.ReadAllLines(SettingsFile).ToList(); + while (lines.Count < 3) + { + lines.Add(string.Empty); + } + if (lines.Count > 0) txtPassword.Text = lines[0]; + if (lines.Count > 1) { historyLogins = lines[1].Split('|').ToList(); cmbLogin.Items.AddRange(historyLogins.ToArray()); if (cmbLogin.Items.Count > 0) cmbLogin.SelectedIndex = 0; } + if (lines.Count > 2) { historyIPs = lines[2].Split('|').ToList(); cmbIP.Items.AddRange(historyIPs.ToArray()); if (cmbIP.Items.Count > 0) cmbIP.SelectedIndex = 0; } + } + catch { } + } + + private void SaveSettings(string user, string ip) + { + historyLogins.Remove(user); historyLogins.Insert(0, user); if (historyLogins.Count > 10) historyLogins.RemoveAt(10); + historyIPs.Remove(ip); historyIPs.Insert(0, ip); if (historyIPs.Count > 10) historyIPs.RemoveAt(10); + cmbLogin.Items.Clear(); cmbLogin.Items.AddRange(historyLogins.ToArray()); cmbLogin.Text = user; + cmbIP.Items.Clear(); cmbIP.Items.AddRange(historyIPs.ToArray()); cmbIP.Text = ip; + try { File.WriteAllLines(SettingsFile, new[] { txtPassword.Text, string.Join("|", historyLogins), string.Join("|", historyIPs) }); } catch { } + } + + private void PaintBottomBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, c.Height - 1, c.Width, c.Height - 1); } + private void PaintTopBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, 0, c.Width, 0); } + private void PaintLeftBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, 0, 0, c.Height); } + private Label MkLabel(string text, int x, int y) => new Label { Text = text, Location = new Point(x, y), AutoSize = true, ForeColor = Theme.TextDim, Font = new Font("Segoe UI", 8f) }; + } + + // ===================================================== + // LINE TYPES (for filtering) + // ===================================================== + enum LineType { Chat, System, Error } + + struct LogLine + { + public string Raw; + public string Display; + public LineType Type; + public DateTime Time; + } + + public class ConsoleTab : Panel + { + private MinecraftClient Client; + private Thread t_read; + private RichTextBox boxOutput; + private Button btnDisconnect; + private Label lblStatus; + private Label lblTimer; + private CheckBox chkAutoScroll; + private bool autoScroll = true; + + private Button btnFilterAll, btnFilterChat, btnFilterSystem, btnFilterError; + private LineType? activeFilter = null; + + private List allLines = new List(); + private object logLock = new object(); + + private StreamWriter logWriter; + private DateTime connectedAt; + private System.Windows.Forms.Timer timerClock; + private bool isConnected = false; + + public string TabTitle { get; set; } + + [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); + [DllImport("uxtheme.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] + private static extern int SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList); + private const int WM_VSCROLL = 0x115, SB_BOTTOM = 7; + + public ConsoleTab(string title, string[] args, string lang) + { + this.TabTitle = title; + this.BackColor = Theme.BgDark; + + InitLogFile(title); + + // Top bar + var topBar = new Panel { Dock = DockStyle.Top, Height = 36, BackColor = Theme.BgCard }; + topBar.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Theme.Border), 0, topBar.Height - 1, topBar.Width, topBar.Height - 1); + + btnDisconnect = new Button + { + Text = lang == "en" ? "Disconnect" : "Rozlacz", + Dock = DockStyle.Right, + Width = 105, + FlatStyle = FlatStyle.Flat, + BackColor = Color.FromArgb(160, 45, 45), + ForeColor = Color.White, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + Cursor = Cursors.Hand + }; + btnDisconnect.FlatAppearance.BorderSize = 0; + btnDisconnect.Click += (s, e) => CloseTab(); + topBar.Controls.Add(btnDisconnect); + + chkAutoScroll = new CheckBox + { + Text = "Auto-scroll", + Dock = DockStyle.Right, + Width = 95, + ForeColor = Theme.TextDim, + Font = new Font("Segoe UI", 8f), + Checked = true, + Padding = new Padding(0, 0, 12, 0) + }; + chkAutoScroll.CheckedChanged += (s, e) => autoScroll = chkAutoScroll.Checked; + topBar.Controls.Add(chkAutoScroll); + + lblTimer = new Label + { + Text = "00:00:00", + Dock = DockStyle.Right, + Width = 70, + ForeColor = Theme.TextDim, + Font = new Font("Consolas", 8f), + TextAlign = ContentAlignment.MiddleCenter + }; + topBar.Controls.Add(lblTimer); + + lblStatus = new Label + { + Text = " ● " + title, + Dock = DockStyle.Fill, + ForeColor = Theme.TextMuted, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + TextAlign = ContentAlignment.MiddleLeft + }; + topBar.Controls.Add(lblStatus); + this.Controls.Add(topBar); + + // Filter bar + var filterBar = new Panel { Dock = DockStyle.Top, Height = 30, BackColor = Theme.BgPanel }; + filterBar.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Theme.Border), 0, filterBar.Height - 1, filterBar.Width, filterBar.Height - 1); + + btnFilterAll = MakeFilterBtn("All", null, filterBar, 4); + btnFilterChat = MakeFilterBtn("Chat", LineType.Chat, filterBar, 54); + btnFilterSystem = MakeFilterBtn("System", LineType.System, filterBar, 118); + btnFilterError = MakeFilterBtn("Errors", LineType.Error, filterBar, 192); + SetFilterActive(btnFilterAll); + this.Controls.Add(filterBar); + + // Console + boxOutput = new RichTextBox + { + Dock = DockStyle.Fill, + BackColor = Theme.BgDark, + ForeColor = Color.FromArgb(200, 200, 215), + Font = new Font("Consolas", 10f), + BorderStyle = BorderStyle.None, + ReadOnly = true, + ScrollBars = RichTextBoxScrollBars.Vertical + }; + var ctx = new ContextMenuStrip { BackColor = Theme.BgCard, ForeColor = Theme.Text }; + ctx.Items.Add("Disconnect / Close", null, (s, e) => CloseTab()); + ctx.Items.Add("Clear Console", null, (s, e) => { boxOutput.Clear(); lock (logLock) allLines.Clear(); }); + boxOutput.ContextMenuStrip = ctx; + this.Controls.Add(boxOutput); + + try { SetWindowTheme(boxOutput.Handle, "DarkMode_Explorer", null); } catch { } + + timerClock = new System.Windows.Forms.Timer { Interval = 1000 }; + timerClock.Tick += (s, e) => { + if (isConnected && !lblTimer.IsDisposed) + { + var elapsed = DateTime.Now - connectedAt; + lblTimer.Text = elapsed.ToString(@"hh\:mm\:ss"); + } + }; + timerClock.Start(); + + PrintSystem("Initializing...", LineType.System); + if (args.Length == 3) new Thread(() => InitClient(new MinecraftClient(args[0], args[1], args[2]))).Start(); + else new Thread(() => InitClient(new MinecraftClient(args))).Start(); + } + + private void InitLogFile(string title) + { + try + { + string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); + Directory.CreateDirectory(dir); + string safeName = string.Concat(title.Split(Path.GetInvalidFileNameChars())); + string date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm"); + string path = Path.Combine(dir, safeName + "_" + date + ".txt"); + logWriter = new StreamWriter(path, append: true, encoding: System.Text.Encoding.UTF8) { AutoFlush = true }; + logWriter.WriteLine("=== Session started: " + DateTime.Now + " | Account: " + title + " ==="); + } + catch { } + } + + private void WriteLog(string text, LineType type) + { + try { logWriter?.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss") + "][" + type + "] " + text); } catch { } + } + + private Button MakeFilterBtn(string label, LineType? type, Panel parent, int x) + { + int w = label == "All" ? 44 : label == "System" ? 68 : 58; + var btn = new Button + { + Text = label, + Location = new Point(x, 4), + Size = new Size(w, 22), + FlatStyle = FlatStyle.Flat, + BackColor = Theme.BgCard, + ForeColor = Theme.TextDim, + Font = new Font("Segoe UI", 8f, FontStyle.Bold), + Cursor = Cursors.Hand + }; + btn.FlatAppearance.BorderSize = 1; + btn.FlatAppearance.BorderColor = Theme.Border; + btn.Click += (s, e) => { activeFilter = type; SetFilterActive(btn); RedrawFiltered(); }; + parent.Controls.Add(btn); + return btn; + } + + private void SetFilterActive(Button active) + { + foreach (var b in new[] { btnFilterAll, btnFilterChat, btnFilterSystem, btnFilterError }) + { + if (b == null) continue; + b.BackColor = b == active ? Theme.Accent : Theme.BgCard; + b.ForeColor = b == active ? Color.White : Theme.TextDim; + } + } + + private void RedrawFiltered() + { + InvokeUI(() => { + boxOutput.Clear(); + List snapshot; + lock (logLock) snapshot = new List(allLines); + foreach (var line in snapshot) + if (activeFilter == null || line.Type == activeFilter) + RenderLine(line); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + }); + } + + private void RenderLine(LogLine line) + { + if (line.Type == LineType.System || line.Type == LineType.Error) + { + boxOutput.SelectionColor = line.Type == LineType.Error ? Color.FromArgb(220, 80, 80) : Color.FromArgb(85, 85, 105); + boxOutput.AppendText(line.Display + "\n"); } else { - box.SelectionStart = box.TextLength; - box.SelectionLength = 0; - box.SelectionColor = color; - box.SelectionFont = new Font(box.Font, style); - box.AppendText(text); - box.SelectionColor = box.ForeColor; - box.SelectionStart = box.Text.Length; - box.ScrollToCaret(); - } - } - - /// - /// Properly disconnect the client when clicking the [X] close button - /// - - protected void onClose(object sender, EventArgs e) - { - if (t_clientread != null) { t_clientread.Abort(); } - if (Client != null) { new Thread(new ThreadStart(Client.Close)).Start(); } - } - - /// - /// Allows an Enter keypress in "Login", "Password" or "Server IP" box to be considered as a click on the "Go!" button - /// - /// - /// - - public void loginBox_KeyUp(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - { - btn_connect_Click(sender, e); - e.Handled = true; - } - } - - /// - /// Handle special functions in the input box : send with Enter key, command history and tab-complete - /// - /// - /// - - public void inputBox_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - { - btn_send_Click(sender, e); - e.Handled = true; - } - else if (e.KeyCode == Keys.Down) - { - if (previous.Count > 0) + string[] subs = line.Raw.Split('\u00a7'); + boxOutput.SelectionColor = Color.FromArgb(200, 200, 215); + if (subs.Length > 0) boxOutput.AppendText(subs[0]); + for (int i = 1; i < subs.Length; i++) { - box_input.Text = previous.First.Value; - previous.AddLast(box_input.Text); - previous.RemoveFirst(); - box_input.Select(box_input.Text.Length, 0); - } - e.Handled = true; - } - else if (e.KeyCode == Keys.Up) - { - if (previous.Count > 0) - { - box_input.Text = previous.Last.Value; - previous.AddFirst(box_input.Text); - previous.RemoveLast(); - box_input.Select(box_input.Text.Length, 0); - } - e.Handled = true; - } - else if (e.KeyCode == Keys.Tab) - { - if (box_input.SelectionStart > 0) - { - string behind_cursor = box_input.Text.Substring(0, box_input.SelectionStart); - string after_cursor = box_input.Text.Substring(box_input.SelectionStart); - string[] behind_temp = behind_cursor.Split(' '); - string autocomplete = Client.tabAutoComplete(behind_temp[behind_temp.Length - 1]); - if (!String.IsNullOrEmpty(autocomplete)) + if (subs[i].Length > 1) { - behind_temp[behind_temp.Length - 1] = autocomplete; - behind_cursor = String.Join(" ", behind_temp); - box_input.Text = behind_cursor + after_cursor; - box_input.SelectionStart = behind_cursor.Length; + boxOutput.SelectionColor = GetColor(subs[i][0]); + boxOutput.SelectionFont = GetFont(subs[i][0], boxOutput.Font); + boxOutput.AppendText(subs[i].Substring(1)); } } - e.SuppressKeyPress = true; - e.Handled = true; + boxOutput.AppendText("\n"); } } - /// - /// Send the input in the input box, if any, by pressing the "Send" button. - /// Handle "/quit" command to properly disconnect and close the GUI. - /// + public void UpdateLang(string lang) { if (btnDisconnect != null) btnDisconnect.Text = lang == "en" ? "Disconnect" : "Rozlacz"; } - private void btn_send_Click(object sender, EventArgs e) + private void InitClient(MinecraftClient client) { - if (Client != null) + Client = client; + t_read = new Thread(ReadLoop) { IsBackground = true }; + t_read.Start(); + connectedAt = DateTime.Now; + isConnected = true; + InvokeUI(() => { + lblStatus.Text = " ● " + TabTitle; + lblStatus.ForeColor = Color.FromArgb(100, 210, 130); + PrintSystem("Connected.", LineType.System); + }); + } + + private void ReadLoop() + { + try { - if (box_input.Text.Trim().ToLower() == "/quit") + while (Client != null && !Client.Disconnected) { - Close(); - } - else - { - Client.SendText(box_input.Text); - previous.AddLast(box_input.Text); - box_input.Text = ""; + string line = Client.ReadLine(); + if (!string.IsNullOrEmpty(line)) PrintChat(line); } } - } - - /// - /// Draw text on glass pane without ClearType, only black pixels - /// - - protected override void OnPaint(PaintEventArgs e) - { - e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit; - e.Graphics.DrawString("Login Details", this.Font, Brushes.Black, 20, 11); - e.Graphics.DrawString("Username:", this.Font, Brushes.Black, 20, 31); - e.Graphics.DrawString("Password:", this.Font, Brushes.Black, 191, 31); - e.Graphics.DrawString("Server IP:", this.Font, Brushes.Black, 355, 31); - } - - /// - /// Show the "About" message box, open the official topic in an internet browser if the user press OK. - /// - - private void btn_about_Click(object sender, EventArgs e) - { - if (MessageBox.Show("MCC GUI version 1.0 - (c) 2013 ORelio\nAllows to send commands to any Minecraft server\nand receive text messages in a fast and easy way.\n\nPress OK to visit the official topic on Minecraft Forums.", - "About Minecraft Console Client", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK) + catch (ThreadAbortException) { - System.Diagnostics.Process.Start("http://www.minecraftforum.net/topic/1314800-/"); + } + catch (Exception ex) { InvokeUI(() => PrintSystem("Error: " + ex.Message, LineType.Error)); } + finally + { + isConnected = false; + InvokeUI(() => { + PrintSystem("Disconnected.", LineType.Error); + if (lblStatus != null) { lblStatus.Text = " ● " + TabTitle; lblStatus.ForeColor = Color.FromArgb(220, 80, 80); } + }); } } - /// - /// Open a link located in the console window - /// - - private void LinkClicked(object sender, LinkClickedEventArgs e) + public void Send(string text) { - try { System.Diagnostics.Process.Start(e.LinkText); } - catch (Exception ex) { MessageBox.Show("An error occured while opening the link :\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } + if (Client != null && !Client.Disconnected) + { + Client.SendText(text); + InvokeUI(() => PrintSystem("> " + text, LineType.System, Color.FromArgb(100, 180, 255))); + } + } + + public void CloseTab() + { + try + { + isConnected = false; + timerClock?.Stop(); + logWriter?.WriteLine("=== Session ended: " + DateTime.Now + " ==="); + logWriter?.Close(); + if (Client != null) { Client.Close(); Client = null; } + if (t_read != null && t_read.IsAlive) { t_read.Abort(); t_read = null; } + } + catch { } + } + + private void PrintSystem(string text, LineType type, Color? color = null) + { + string prefix = type == LineType.Error ? "[ERR] " : "[SYS] "; + Color c = color ?? (type == LineType.Error ? Color.FromArgb(220, 80, 80) : Color.FromArgb(85, 85, 105)); + var entry = new LogLine { Raw = prefix + text, Display = prefix + text, Type = type, Time = DateTime.Now }; + lock (logLock) allLines.Add(entry); + WriteLog(text, type); + InvokeUI(() => { + if (activeFilter == null || activeFilter == type) + { + boxOutput.SelectionColor = c; + boxOutput.AppendText(entry.Display + "\n"); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + } + }); + } + + private void PrintChat(string raw) + { + string plain = System.Text.RegularExpressions.Regex.Replace(raw, @"§.", ""); + var entry = new LogLine { Raw = raw, Display = plain, Type = LineType.Chat, Time = DateTime.Now }; + lock (logLock) allLines.Add(entry); + WriteLog(plain, LineType.Chat); + InvokeUI(() => { + if (activeFilter == null || activeFilter == LineType.Chat) + { + boxOutput.SuspendLayout(); + RenderLine(entry); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + boxOutput.ResumeLayout(); + } + }); + } + + private void InvokeUI(Action a) { if (!boxOutput.IsDisposed) { if (boxOutput.InvokeRequired) try { boxOutput.Invoke(a); } catch { } else a(); } } + private Font GetFont(char c, Font f) => c == 'l' ? new Font(f, FontStyle.Bold) : f; + private Color GetColor(char c) + { + switch (c) + { + case '0': return Color.FromArgb(20, 20, 20); + case '1': return Color.FromArgb(85, 85, 255); + case '2': return Color.FromArgb(85, 200, 85); + case '3': return Color.FromArgb(85, 220, 220); + case '4': return Color.FromArgb(220, 85, 85); + case '5': return Color.FromArgb(200, 85, 200); + case '6': return Color.FromArgb(255, 180, 30); + case '7': return Color.Silver; + case '8': return Color.FromArgb(120, 120, 140); + case '9': return Color.FromArgb(100, 130, 255); + case 'a': return Color.FromArgb(85, 255, 85); + case 'b': return Color.FromArgb(85, 255, 255); + case 'c': return Color.FromArgb(255, 85, 85); + case 'd': return Color.FromArgb(255, 130, 255); + case 'e': return Color.FromArgb(255, 255, 85); + case 'f': return Color.White; + default: return Color.FromArgb(200, 200, 215); + } } } } diff --git a/MinecraftClientGUI/MinecraftClient.cs b/MinecraftClientGUI/MinecraftClient.cs index abf9b4ef..fb070136 100644 --- a/MinecraftClientGUI/MinecraftClient.cs +++ b/MinecraftClientGUI/MinecraftClient.cs @@ -24,33 +24,18 @@ namespace MinecraftClientGUI private Process Client; private Thread Reader; - /// - /// Start a client using command-line arguments - /// - /// Arguments to pass - public MinecraftClient(string[] args) { initClient("\"" + String.Join("\" \"", args) + "\" BasicIO"); } - /// - /// Start the client using username, password and server IP - /// - /// Username or email - /// Password for the given username - /// Server IP to join - public MinecraftClient(string username, string password, string serverip) { + // If the password is empty, pass an empty string to support Microsoft/Browser login + if (password == null) password = ""; initClient('"' + username + "\" \"" + password + "\" \"" + serverip + "\" BasicIO"); } - /// - /// Inner function for launching the external console application - /// - /// Arguments to pass - private void initClient(string arguments) { if (File.Exists(ExePath)) @@ -59,7 +44,10 @@ namespace MinecraftClientGUI Client.StartInfo.FileName = ExePath; Client.StartInfo.Arguments = arguments; Client.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - Client.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage); + + // FIX: Forcing UTF-8 fixes Polish characters and colors + Client.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; + Client.StartInfo.UseShellExecute = false; Client.StartInfo.RedirectStandardOutput = true; Client.StartInfo.RedirectStandardInput = true; @@ -69,66 +57,58 @@ namespace MinecraftClientGUI Reader = new Thread(new ThreadStart(t_reader)); Reader.Start(); } - else throw new FileNotFoundException("Cannot find Minecraft Client Executable!", ExePath); + else throw new FileNotFoundException("Nie znaleziono pliku MinecraftClient.exe!", ExePath); } - /// - /// Thread for reading output and app messages from the console - /// - private void t_reader() { while (true) { try { - string line = ""; - while (line.Trim() == "") + if (Client.HasExited) { disconnected = true; break; } + + string line = Client.StandardOutput.ReadLine(); + if (line != null) { - line = Client.StandardOutput.ReadLine() + Client.MainWindowTitle; - if (line.Length > 0) + if (line.Trim() != "") { - if (line == "Server was successfuly joined.") { disconnected = false; } - if (line == "You have left the server.") { disconnected = true; } - if (line[0] == (char)0x00) + if (line.Contains("Server was successfuly joined")) { disconnected = false; } + if (line.Contains("You have left the server")) { disconnected = true; } + + if (line.Length > 0 && line[0] == (char)0x00) { - //App message from the console string[] command = line.Substring(1).Split((char)0x00); - switch (command[0].ToLower()) + if (command[0].ToLower() == "autocomplete") { - case "autocomplete": - if (command.Length > 1) { tabAutoCompleteBuffer.AddLast(command[1]); } - else tabAutoCompleteBuffer.AddLast(""); - break; + if (command.Length > 1) { tabAutoCompleteBuffer.AddLast(command[1]); } + else tabAutoCompleteBuffer.AddLast(""); } } - else OutputBuffer.AddLast(line); + else + { + OutputBuffer.AddLast(line); + } } } + else { Thread.Sleep(10); } // Small pause to avoid overloading the CPU } - catch (NullReferenceException) { break; } + catch (Exception) { break; } } } - /// - /// Get the first queuing output line to print - /// - /// - public string ReadLine() { - while (OutputBuffer.Count < 1) { } + while (OutputBuffer.Count < 1) + { + if (disconnected) return ""; + Thread.Sleep(10); // Save CPU while waiting for data + } string line = OutputBuffer.First.Value; OutputBuffer.RemoveFirst(); return line; } - /// - /// Complete a playername or a command, usually by pressing the TAB key - /// - /// Text to complete - /// Returns an autocompletion for the provided text - public string tabAutoComplete(string text_behindcursor) { tabAutoCompleteBuffer.Clear(); @@ -136,7 +116,12 @@ namespace MinecraftClientGUI { text_behindcursor = text_behindcursor.Trim(); SendText((char)0x00 + "autocomplete" + (char)0x00 + text_behindcursor); - int maxwait = 30; while (tabAutoCompleteBuffer.Count < 1 && maxwait > 0) { Thread.Sleep(100); maxwait--; } + int maxwait = 30; + while (tabAutoCompleteBuffer.Count < 1 && maxwait > 0) + { + Thread.Sleep(100); + maxwait--; + } if (tabAutoCompleteBuffer.Count > 0) { string text_completed = tabAutoCompleteBuffer.First.Value; @@ -148,14 +133,9 @@ namespace MinecraftClientGUI else return ""; } - /// - /// Send a message or a command to the server - /// - /// Text to send - public void SendText(string text) { - if (text != null) + if (text != null && !Client.HasExited) { text = text.Replace("\t", ""); text = text.Replace("\r", ""); @@ -168,17 +148,16 @@ namespace MinecraftClientGUI } } - /// - /// Properly disconnect from the server and dispose the client - /// - public void Close() { - Client.StandardInput.WriteLine("/quit"); - if (Reader.IsAlive) { Reader.Abort(); } - if (!Client.WaitForExit(3000)) + if (!Client.HasExited) { - try { Client.Kill(); } catch { } + Client.StandardInput.WriteLine("/quit"); + if (Reader.IsAlive) { Reader.Abort(); } + if (!Client.WaitForExit(2000)) + { + try { Client.Kill(); } catch { } + } } } } diff --git a/MinecraftClientGUI/MinecraftClientGUI.csproj b/MinecraftClientGUI/MinecraftClientGUI.csproj index ce8642c4..1066164e 100644 --- a/MinecraftClientGUI/MinecraftClientGUI.csproj +++ b/MinecraftClientGUI/MinecraftClientGUI.csproj @@ -1,5 +1,5 @@  - + Debug x86 @@ -10,9 +10,26 @@ Properties MinecraftClientGUI MinecraftClientGUI - v4.0 - Client + v4.8 + + 512 + false + C:\Users\Admin\Desktop\publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 3 + 1.0.0.%2a + false + true + true x86 @@ -23,6 +40,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -32,10 +50,23 @@ TRACE prompt 4 + false AppIcon.ico + + 4C76A63F11DB91E010AF3039521927BEB537A320 + + + MinecraftClientGUI_TemporaryKey.pfx + + + true + + + true + @@ -69,7 +100,10 @@ True Resources.resx + True + + SettingsSingleFileGenerator Settings.Designer.cs @@ -83,6 +117,18 @@ + + + False + Microsoft .NET Framework 4.8 %28x86 i x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + - \ No newline at end of file + diff --git a/MinecraftClientGUI/Program.cs b/MinecraftClientGUI/Program.cs index 45876950..dd080198 100644 --- a/MinecraftClientGUI/Program.cs +++ b/MinecraftClientGUI/Program.cs @@ -1,12 +1,13 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; using System.Windows.Forms; namespace MinecraftClientGUI { static class Program { + private const string ReleasesUrl = "https://github.com/MCCTeam/Minecraft-Console-Client/releases"; + /// /// Minecraft Console Client GUI by ORelio (c) 2013. /// Allows to use Minecraft Console Client in a more user friendly interface @@ -18,7 +19,26 @@ namespace MinecraftClientGUI { if (!System.IO.File.Exists(MinecraftClient.ExePath)) { - MessageBox.Show("File not found: " + MinecraftClient.ExePath, "Minecraft client not found", MessageBoxButtons.OK, MessageBoxIcon.Error); + DialogResult result = MessageBox.Show( + "File not found: " + MinecraftClient.ExePath + Environment.NewLine + Environment.NewLine + + "Place MinecraftClient.exe in the same folder as MinecraftClientGUI.exe." + Environment.NewLine + Environment.NewLine + + "Download MinecraftClient.exe from:" + Environment.NewLine + + ReleasesUrl + Environment.NewLine + Environment.NewLine + + "Open the releases page now?", + "Minecraft client not found", + MessageBoxButtons.YesNo, + MessageBoxIcon.Error); + + if (result == DialogResult.Yes) + { + try + { + Process.Start(new ProcessStartInfo(ReleasesUrl) { UseShellExecute = true }); + } + catch + { + } + } } else { diff --git a/MinecraftClientGUI/Properties/Resources.Designer.cs b/MinecraftClientGUI/Properties/Resources.Designer.cs index 9b33fcd6..a32a90af 100644 --- a/MinecraftClientGUI/Properties/Resources.Designer.cs +++ b/MinecraftClientGUI/Properties/Resources.Designer.cs @@ -1,17 +1,17 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18046 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace MinecraftClientGUI.Properties -{ - - +namespace MinecraftClientGUI.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace MinecraftClientGUI.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinecraftClientGUI.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/MinecraftClientGUI/Properties/Settings.Designer.cs b/MinecraftClientGUI/Properties/Settings.Designer.cs index 52f889e8..e41b8583 100644 --- a/MinecraftClientGUI/Properties/Settings.Designer.cs +++ b/MinecraftClientGUI/Properties/Settings.Designer.cs @@ -1,28 +1,24 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18046 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace MinecraftClientGUI.Properties -{ - - +namespace MinecraftClientGUI.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/MinecraftClientGUI/app.config b/MinecraftClientGUI/app.config new file mode 100644 index 00000000..3e0e37cf --- /dev/null +++ b/MinecraftClientGUI/app.config @@ -0,0 +1,3 @@ + + + diff --git a/MinecraftClientGUI/screenshot.png b/MinecraftClientGUI/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6684c44336b2da8f5e7c2c3836240be1482f1494 GIT binary patch literal 49804 zcmdSB2UJtp_dgm7Dk9*Bii&`hBGp1KQLqeB2c$`f2myfr(xoIpL75Q+mC&0}0jZ&P z63ZYEN)Ut)s)`{%2!TKX>F?sqe1GNpTfg^T>%Fz!dUvg?Bsce-yU#xR?DP5Ty-)tI zvAQO{`^at(2qbRy>t$OINCXN3Z42ME12`jnAS(%Y+Y)Mf%>-20e{>%BvfbyB)PnZFp=UWy- zcK?1c?yhLYBc)?{Z{xHsQQs%OEcDgIUnxEP%SrKJyAyLSN0Ty!Mh_TcuG13gHhw(TpgZIn(Dmyo#vR4=*7(E z{Xnf6FV|4g`iZoIG7pDzW~*MdzO&UmyD^rey-f839`vZ$*9pNq|m1eA?=}*TWVR{8v#!-!@=mkT|l=l^{Aw*q=4I_^f zA6zH%Y~#}bCI)W!rJ>aEOe5t+9!8In@#%Y4Pb{H{QcsUcj>g*!PEU7pJ9y{$Y_r+O z2nFTU;i1m3=7TC2{YnJn(0OZwpV?gQ-PZdEIK}HaQIES`pdQ&X5i`GgDk$8dptxgf zt^PNCFhiA-{4lwdb`ZZtAL`O#y7wGG$_KOarw4J=+5-5x{soDQ${7Re49ZqQHh0^>XccW z?I{o1#Hw%z+}|jzQ0&=RKKGO1W7`~7`lqpVeuK#x&(nlCF|(vQQN1Aue1>26fs5~Lt`yrBJrS*n6~H?e&ai^-_ZoK8 zd44*ZdKT;>RBwH$iw*4uL;7#FIyh9gt6eh|1T#*y3sr?Hoe0(H507dT%k{71hkUzI zvlVV{J4hm4FbsX_SCAk-*9LnR|Ku{$a_Eoa-58|f4gB}@Z0=_xw9|Zr@}jBYd*x5 zII=bFfZ`$sa1-Hrae+g)ZRsxnSuCf)Adse#Y_?VQl01#6xIS@G1tTd8lG%X#e9l;;EUO$yuxvM?S)-Z|Z3hgj~Leg@uPo@@&TfnE>dx`1vi z4bp{$MV*O-g>*>}$jMLtvaHS|#BO5E9Cr%LI0-(X;!wWK`6e&$Xr3MOCw~q?w%&+T z&f~sWg%aq3%2*0gK(NiKrdtaxj~K5UWq*S3?Mq;XPCl%G+#}4?YO;(N@j-$j-mR51 zM*L2QDjms8iKaGL8MF6CeNIB6!zP-3947qQ)alM`fhbSB(}1(@w=Lo(qkPbdDS{>* z89M9qwzm$7w=ih2I6o7?jc5+YEC^@o4rhhB{8_0CG8b+ol0jqEI6}te==`*lf&G7X z<7oJo-^}8CKlCKxm=!NH5bPet3|KJJjV*0ivOVW%9KiG=@!w0PKOi}gs{9gbj0>VV zXI`XLgYV59WDDYP$EJnf$qYWhO^F`cfh<3OU${G5wWUM`pS|pmSf@Rp<_r4B=!Z3yiW}Q(Tb*L%*|57`->zUeQL&mS{n;%V_KT2ac$xlsbHmtAKyqzTEY)Y4+l@RGg_np5! z3g$eO&ye@>SX>wIPOzuoKHcla@Z2^wRETy^@_%qyO0%W2&tE4&%cE4|S1pTY#UH!( zjwfq?>O4h7$~hB-d!Rc3#ceE!f-(f;ik>ZXIfxVut$!x^OYyKKeTkNo!ogXak-Mg& zDTwbkCCN1c{^x4OzMJQ~#wmmO-D|48f1r3CK$if!2PN^g47_?_B$ z@OWBd!vt-pa>odCwjdA`Fg6rQoLN81gpEgcR2wL)*0!o8$hNjSL?qApMD_*>(b&aN zc=%!|^yAt$cb&%Gs_@13+L@8i9WCaWgV5t--$|wBoUV8P(-T*u#@`zyJQ>d;K#E zn0L2?UudG_VhvbLvb~Y>L~LH{H)ZP=<0(chb-41Bz)t8k3!ONo?9lsSmHn=qjj=V^ z^WYQ}R5-WMGn`vzA*26pm@*f!7Tj3L9TV4Q-kS`a44yx`5WQp=@YauAnaaIMu+Nx> zoQYWElWk;hb>A2^C=9I5w=m$M$`BQc)SnV&2)JFkbH?c=RqBJK*?2TH& z;}1dR<{mvA+64{Y3(J}b95^BdEt@UpYRyy!%&#OL!7s!}wOF+zOQd<;ufPPn zyGh(Sx(2z`S;2E>DW{Yj#&62izVewUI#f&-1zh*qbil$uz!UQvz_(^Zvwu#tkz%2nceZEC?6isiH^5%g!RuhO<~_*M*@;hqU!md0NstDTKU=9i}jz0-B*;7 z$fm+qfQgQo;Tm&zf{~e{X@NNLwWDMDDV3ksw}U`UVRJXH5alyoC+lBdVy_jGTZJ1b zh3958q@`28vZ&%>P|t7vXYCxd>5PI{l49a+D9xW+D3&m(Kyz{yFaq?Ew%Y} zsP;@%=VaW?#IlHqPnziRfRDesDe}i2El0!#W{LZ%#iZJ-I5(@3=QdbxjQP)JqCz)D*2ZC3(z6fI!BajM_~Z=cbkaalfj3`6^Hx*-Xeq zo3FXHUL;ie`aP@x#hHD^W&WquGgaaI>iuiTJzGI7SA?ZUg{IIfcBQTaYN%J7RE+EueRrVZZ8}nKZuo8bfcI7brXh z^ar{n`jn#afE;nwl7mQ?i*_rfNP;fb&OZyh(E@*R6*3(>#J4|8z-s zr6Qj(nthQ_;g4TOS2F4sw2c5Lm~AfPB94a1AIirzq`u_lo&t0NirX@eYkL|mFu!R; zuGYGr?;@Q>zamTauQ!~d0w{2O4yW=3J^pTatv+x2B<1Zc=oS!Y#~a}T?Optgvy&0m z3uazZFC^xN<_xzkagNHn`80nfZUe=gcIp)+YAs77%Y}2Mc9jN_=l$`gRbzTKq zbM!*;`GdL>>jnDjr4@!5ZfOZM3d?J#ET6Bl0(!L+!;|GqI^#Hb%UJE!$DxXlRiHnY7}5ff*ZbfXx>d!3sR0RYxpD52`_+g0a_J{znW+Wy;8v%XWv*1oTgO&TSO zpPstY#i>S{)*<^)4>{CCAO6{4s)1pk35kr}$T$07PxphZlLiRZY^TBGRrls^j~vPL z(>hgZ35~OC+b!iXJ2Lvo&5Pyrgk~)^JUy{2D+v>A7qQsQ$j!*)25qxjg&Cd}y=gg_ zQ#&x68hM+D6-jFMYEx+iaWdq(cYw)Tu!g!Paly z^F9#aW6_4(I6N3Purff1PnMZ{T>w^eWg5$1*Pl@`r38xHV-Q&5)PQj2hA1%K7qJqF%ZbSE{dKP@7>D(95z%d!R!px~(cKo_uG7Bek%MhcB}U zpOcEBA!5h)PT`tfAxH@(UGtK|@(d~36+3gkV5h8(ycGTAyH%-JZPjT9nGVm{c1obj zv&BDR?Snfaea#=-x>Yu%2!m8vGy$;9g**Ps8bNtyQMZE#sWv^|1s7OdegdTh+2~;X z6e3RIus*5}e4WPCjo7#5)oCtYKSH6}D6z%@i0rMe2KBH7ru=m_?rV?iwH421-@6AE ztc8Z}nnZE>zk-QND+9IYX;Z8xpC-xham0;;$I~%SxW?a-uq|cF+yTCACCvXptjty} z^j7}6FN;jSe93jElE70i%e{DjRDwY11;3b(Dy!v%Bb`478*py1KYhyPkGCHG_m4%3 zQ5beUua~yiy)!;$J20 zg5LiIT&(ix2;QA%;k;g~@ghkA>(n3fZ9e3~ANirBZC^v0mkI8t&SUf)t#rHXBs%p% zSUZvK>Mit+ZY|v0*HtL((GWR7YAp(A*Z<$zJ&ez+y0%9dfwWZrY8+pv?y9NUEIVu>=(4ErXld&v0*|A^`gkIyPOV+iDlPw2}EPT zn>j*|abTL^%3NAuaLU4Wq8zHD;~IAP4$gnu>Uyd$+<(Ip%r!B=#dn{5+!>O zt(slLM8htr-g}xheAbVDCUrNoHUb$kLHlqFb;a?C3d*ukz3ahf*_VUc^)12|(j4$D zAKOx-Dl9SBYz&G*tjcG z-DssI(}Qif?NA?gS$!bz64w0=UEVs#^z6)Y-F<$1$zV&11@WwqH?X3ZA&4iGKWnGd zXRLiBbzCFo$JBT#WDa}KZtTTNB-tFaG*xmTzGar}#W(zdUW}V!STlSi3y~S@u9zzb zR(O7BpmfxHqR>GC`~ysR)V5e{>pa!zF@EjqQPdMfR9}PoUB`BPz{HyMCjCbmtzp27 zRCdwIt_uwu>0JHMo9dY1P%#^ehWV1q-1?~H5qCz70(<>W$mQEYwtL+7xg~6Wvcz1r26uGQ3ZClT!xx43Y6m7G1TZ%C%^%$cj)|RCr z1%1}Sa;duPW7=-wVxh+7*f3q_rOhjiEO-F z6)?_P4cN!1Zn^|hMbQ#F8{~}|KCEo%Y(N1J`^v|}nw+7p%of1BCE+S?%#>h{9_Nmo z!yYK6ebt3wBQSs_9fhx*W@KB#&6ir1nihF8Ki>LB_Lvq##~b!x`xhpeA7&qktaUXd3Z!>7n!MjE0#E-9cnL1zmA|F#d_5C|CgDTf}Wyuf*DGqH~stoGR zBkFmEZW~U5XcV-C)*AM^s;q`ijl5>K+fypn>xMVCM(1|M3 z==MttEs*VHyu$%+wHv*746BG+6&jzBgbP|3&j~q5L28@o-s=+bc!V{A5?Y!9`K9^8 zP^WD(KOdL;1Udf%V5n&SeX-u}Z+&yRQxyZyJfyP>P?eqdg#P*Ah8Hm$a(UE0uY zMDV6ICE)l`V>c_(()MQb*|e>*i}&*2p0!`=_trTi)S&n3I&blJ6vZt!3ut0r(swU~ z{~VLMO8&e_(c+8`-S;~_5Yk0zqhvezW!7ffdb|q#g0C!gU?x*VvipOj!VY42DhFRV zwDmvujKYY!fm0JI@p|+y; zrOy0P6KAPPZxv!M9c6e3ekY}Ed2Z3C{<^lugQ>+0F?ySZ+iwMJ+V2acupATT;=r+d z3D{(HS#&?%q4k`$q)vdWQ3G`<6N{KQ|3jfRzVL*M=W)-5@cYi~ zoEos^`-N$IYb0x%UF3lZsHt-#(;wF><>&1py`OWhtcy?@GJX~Nf~6)rg)B{04DEKP zf0R5<^p0gO5L;{X3xqDsgyi*A;VB)#b?~!3N^nqzR?e%>rljwsqlSCy?|XbTZ|11D z)-HbzGh1%WNMhiS!D zN-mkgag7(}r#Y|QOym(n*>dgq6$b?t0n@jrO+)}sh4tH@%h@Sxdj7APF0UStE*D3Cmjk3r={1%60!Ka`W{3huheGbcaa(v7|V<~nP78pNb{yr4uUs@xPX(#mImyG z>E0au{;pDc@?>O|kUg{Gjn!z*_QviE8CIiO(`uy=n|ezr=V9(Qn%Gu!x%jn7BX4uc zo6Z;2(D8$B;IRtwE=cpO?eP0k3h1L%*ya9fKkP8|Wzfmh58<4dkusPlRI%g?O3Ol1 zX15m&Cp~r;?>+uYQ(3LlbQ(l0;P75uo$(J}!vlA5TfVbIa4#|nk9*(aK7diTLgbxw z{TAlo-T^V}Goy?5lnv{@RfzgdyPQOYe{fx&ux%}*wp{oN$@;vp8QC5%-H-jcySO{j z3i*J?&$8}Lalj37hyO{pDMxbd*Y8M1V62WhIdyQwd{D(3T8wTD-@3F!WuvahiQdROP|HT%Q8XOi z-el;qiMnL6v-lD&$Q9}77=5SCC=0vJW(^+hJd=zxKuDP?K=mSY@sn{E~w*O0DuSljX6M7!3bRq)n$r z>bsnt07w}vrQ`&PpEWMLf)L(yXtaZ@0iBBTJ&CI53l!5VN%?LtF7d_LEa^VLMv~GM zQ1vcHMvdIV^#&w>uI>IA?O`29b<|u^N>V>!j1sx1lvMebl=3FdsxrVuCrq4g-TU{8 z0S5Kx+W*PL)%yXqAr5e{<0uh;%zZupgvWrK;CUOR9)Ok=Bb3B0JQe-P-JS>~0fO=~ zYITwOMPifsYsw|m98&y8OrUF-=S-ZRNNonTE^>eJM~lN~)lYx?!@m9@kpIRSfk*xi zT=M^mvktjYvm!r6X_6|NlKX%w*W*ACm{q_x{1AE9RZ18~a+$H(`3Q z4Z_&YZvX2u9bt2npOL`U0Z!WW1Y6sA^!vp zu~=$rkhw>;ba-VUm5BGT(lr$b5{6Uj8Fh?mn=E)9f0e3YoiTkPH&34*`%~{NXan>j zmrO&lF$^T~*h4UqGZKjxAq-O-?#=uB=n-;b7~9yDv0rDW)58fXxpwoOjA|rDhpqDq zyS5Q)mv1aMJc*=!XO@#i9GKPO~uA9W=^V8MnIVG!I2^GdIz1V(%@26Eo#XS4$ zk4hHDDjb(?CcOJOeLw^Dle?f`1$iL_DV3_W0QMAv(yD{t?g#;8N`W$`f7jZW-U zEDtw`aKBMj@C*QMg3(*)HVq1q;ib;-mbn$j&H{yLchb6!igntx;j=R?Dt+wY9}=?2 zQeMYOahG%o*xney>n7U-JlW-~Xr1VH7NZ{(5FK>m7)Ms>P4v=q7wlpVZ&4HqhqCHJ zdE+XMJw8^3ulK*=G?)x@P886MSDpaDE2*tQoMQfS;#rDD<4XrHWYc`(0_QF(aXCB5 z^1{=W@~L}SjKvKMSVD>>(wdZAr zzykks3Q`~3+g2(PYQi%wj#OIlt>gmJww=--G%MV}k2`Ng2J?IYBdIQY|7OlcZB~r} zyIvqlfcx!{*;_^-8F$_+L0GorIl177bGKL~S(XK<(ohciLxvaTpdt2L(#zb??& zl=Y%c7!V%gF^q&Kgp`;QlE{rnvNCuzJq$_%7pnO8RTpP_;sh^wA91meAE5=PBzFOL zw+a-(pRdDmDc>Z8?l%eIFfnMsT@%uDYAe>yu>#>}T*YsMPD061z0<22z84pGX5;7X zmlpFv3oIVkPg>!n@%k#BmUFH10X2Cx4P%|jcH<&*05oq(k3TJ=&PF7EzVyWjiXQVx z0jJ%K=nZ>&Fnr#MO;rTD2@vE%4a%9?gjL9a(MMC;(Au8DZ8JtsCA@XIu7H_G^>l>A z>KN<)JPw0|u5D(&XY+q6bH?18hDbX$kAbwsv^6-S#@Y9N`v#}NAT*nX{itXY5~Bdq zjd5p3M7_wq)>I*x(0HQj!!YCVO^8y@b<+J&H8F|Sn5OKiE-5+kLV8DkXcPK|Qtm=Z zpFAuorNp>*L@Yw)5vJ169^1q~4Z1gkXK5VCiQ)ZGNNsinhG}6iFNFU6H6xOeJC1V< zC|s|sc=4qqST#y=zp4AU&+B;SS`$`rzJ_!jRaqtuf!!P)(B!}$KyurmXBJuWu3ml& zVTmB)8+&raeQRx2DagQ-w$*3s7gaDxQbyW3iWvyG#mds+;to3WBP^x_Sht#faVSlf zp{d(;bEFmiUcBHrw-zB-3?Mn|!g@YzxE+H>I7S2r1G1W*7(v@%hg>)&YC0Df53hOG zd%jf#`z+rYF~TvD3QTC6WRX!&`4?84wtS53{FwIp&({i$Qw(WrMqOzCuaJTjRdz|S zo2sI$n{nhJ{ToSm6Hz&RTpeW@Yhf%P%;#4}q?JxOvaF{$W+b78 zPyxbSrsKuH8#-ohImPmVwpbbjrwG35FcP7R3SUPJ?}Bc_zKnkbODpC6vS6!}z0+E7 z%dC;YZ%%IEFL=t$IQ`;MSpB}-`8d~(kdbvB4}Cp=zKPlm)qwL0dLWRfyx16~b2k-d zFke2%bPdGHMj_n#Ncfi>=T8%vzHStmydS-ErwT)*xc=qW!w3!xqR<*U9Tj{!jPg<=|`Q$r{M-u1K3w z(k-QfuNFdM0~S~(1D1@~URi*zAfL79mUrJMA9+~ANy1v`Q=-h*uRwYR-Z%&r5G^ME za%@+X#1eq^ZVLHLc3whYLF-I5Z(kU-C)31lC@qX=g8M8lYzv44Z|i10Ra3=$kJKGE+SKBASX_FxwbMW zFT#SISv@COxdS?`&*@bmpVnx$?LRuSdys6B==V-(Z54p8Akf?nAn@+?+Jxk5=CCsI zl6u%1H|&G+y3`Pas}FaPx^Q1&IPSOq@-XOO{O$QU8}UOA|M7U6ApIn&jS4eakc8hTs#&#f&9X`6t5^U%EVSDXLK$3h#2pz?{ZXkI{GXVqF; z#yF2UNc3rTQ#9HvV+0OW*&rqE`9^b@C$s2IQ&7VXC zRFjQ`E$Q6w$9+I`&E_$5O=>~By}3kG)&}z7n}>xw z8kVHC&L-ARVC-OD!gdL{{EM?|?1f-DRU;$STDK%DKVr>35-%-x&uIzH z4~?hD!@p5~yoPWd9mTHqh0K@Qk(~&79jieZ;0qKD<1_O2x~;oPXL=|%^f;yKT@+ky zG8HAopD~H~1>Z3GU;A-U+LbwuRF*t?Zp)l=$lwb9W%$ar@gHAOt<>}A*_V}KzEvPk zK=50?pSmGl=Q4Lv%maMa^vL&p_z0PQm%q+!U*?ohT(5lV=!LiZ@-_r(kKVV@koHS^ z_HE9%9nDNh`4>Cs>}RoG^ApJfMnUQ3*2Zc#JDV0{6Myt5IQ5%2yCnx6Mk;Zwo?kn~ zUj?)DJJmt?TPYcqnYlUu`0ItFj{-7`xC}hiiYEP@S6+iWX#U14dbXaFptr z;(BL=JN$Xj0UtP}26+s7`Yh1xnMgGed(F6@KA=If3&_p%s#WF>t3W4dVpfRHF+lQQ zn@+cHnN_-2kXhq3O-g=Po;Y;q7|XY4QNx0Cq**Wtd9ESPDR28WSo`D8UmV3e+dj$R z1u3de_yg00@2NQf&f_M|vKB2crI`5I$0Iw!oSj$hHTYcs3(m34CTI65Xdzqmn{*=$ zb{eo2nc~E*WDmlw@|hd!oyoN!gW^YD*g9lF-`WxAz0~Fe=0(49eV7)5?5z@C+K;aX3hcnotqZ0h4QM zP(@kBn?x!k5`jEoNgMpqISm=jaZvsDa1TUvjjb^LkG~v6T!R?AB%`%>5%0fK3>y9}C$T1} zAT5RtzbqjyfO(id5JYvPRi%KV<0oDu)XlRq+FEcx4TOxrA2tnjwD;`>KS)}v#*QCP zwNUU?$H6M5DLsspUhi_KP8B7-qvFvR_GMd5-J!8UO1%=wa=)YBg66Qe_#`}%tD>K@ zIvlgAVDZ*%*U8D@Ml4l7cLh@XuKi0{*)Y|1V8Qs>R47wR*6JUZbYEbr5lMaERp4}B zqKE>%xf|*al;xG{;5$>}Sf-2@cGej)dBJahptR$MmQE||56Ad%=_Z$bb2cM^kY{x< z-_3x?{#G>X9HO;R6%F%zqJ^?-P+#VmHE7;(Rw^^CdGM=}KQbb|A>vWZ;Z{FwnMU!l zpaIBta%E+>V|!_I;j_4VhNM3DerD{HtJeK0HzO+h6~^ld^|QtTQkkd;StE-=8`~&V zt0K1x(sT@S+MzkOfi&O2I^&R+$lgGYXGi^kT(|M{pcFO$bj2lymOt}1+JSWFyXuMQ zuL@eiP>mR8uI zF*~t}f1r+Ic&p@z8mb<$mD!Te{o-yz9)JF8QIc^{YTbX^W9Y7j zORH)hZ|Gvk7r0fiuWzMY=zm=F|NhDa$}|pA8dvyV7Suf3f9kC+kPiD8I`=e>ttPHkG=mSfKdKwsbdcm()(FmVDRF z_X(9VnhtY8en3(g2*%}3)kjoJ{BhL<9>T_MG2eWk(nBZp|KR~~f1E%C7@Ig>5JxZG zd-&g;{(ttPWvvM*iX8;uwH&<}d*k5woe1ickSQsRGr5uTw1RZ5JTI3w^2w)*jPpYv zE=%x6lE`M(vDgX0%9W_Q1b;F>%x8U(3=@y)_qb?PfHO zFk^nKLJ&^I8;^auqx_A?+p^&_1p7NA)@rJ4zW7J37MbzeLqG$6*Rx1nQ5ug;sJWAU z^-+y{?9*JNmF|sjQ<9?KaZQLMXh^8>-OK$I37z{+8M0xW7P?_K-hE`e&%ilNt#u|bzIwO_&PK7>GF|~CDc!pt z{^8l4dC`bYWG{MEOy%Li)bgWGqn$2U?`?hYMmxF`9yq&;$I*eVb{ z_C0bKuP@&t{Mv)O-XGn6YWVW!K{h$|vzJaoNn@oDG-N+076RoNap$FX+YOgc>X2 zpr%eT42ApDj*!_bgqD(~`A3etbTxIKhOgDPb zygmPQgXZ1%(My0gYVUVeis|nN;kR1ea%tQ7*NlyW0sxntjF|0Xm=ck;4zMXm4-zp_ zMt)F{dvPzZpgcVES(fHGS)^i3l497`Avn_7RUu6VYyJsK@lpZnE#s} zcs5;%@N81CJbha4+Y?*x&B@i+*zoCy!NWBX!_I|gf8srkf)qY510(Fgb$i|b-c#Q~HmZ2f6l2BNpVj0s5>P_(b!`Ifx;j`W8gjjY_6y=> zzVNZjPF>_+%}V+C8Mbefpod1_7+P%oFHQs4L!A);7_EE7GiSM%}#qx}iK|GOf5zG15a#(hcs@5Up{DyNRzni!eRV1_xj5EhA)#j6xJd~Y z`V~W-#;pz21LaqO9u=~PeOMD&$#-UdfC$QXU#o-g85n)Y z6!)@QF`PBcNB-bWXPlOVS{~yPg&RSPUfJ~?A^r6F$S$Y`N?5}#WUSd~q?Tzz)U5TH zFg-oJ%6LaEC#2a=ARe;H5&eL;AzG0W*DA^Et2_a`3?2gk<_)~wdlL>|OjAa^dY zac0MIgnG}OHixNQ^)V$CV^_pNfaE~=-hqXS4I?ZOZ|VbSSB2vI=msFhfz zja6xi--0kE%KH{`+ftH-j5!_w@qnqgA0y&!mz7&@wUUa)x>w#5Ue;q8!g)k;DU;Gw zQv8Ogju2qFdBavVE`Ewe!n*Rj3m5y6gLJQ1)zW&Qw9&6x{>8C~<|R#_xZ)*k2Qc7i zJxe$@_uNrc>+WPpS^Sab%OOf?qTaaLb8g~1reb;8bgSUDUTavz<}`h2kbVN-F2jy| zGda9m69oc9$81#Y1RN_H0|r&KDLj5^9eN2P?jn2{NYWc8`+K)V9m;)JrEmu>kew$4 z{+g4;x=zxWJ*_!eg`Z=7Z@*g6U-j|sTNwEP+B5Ha6oWV)ZT5OG6^ z8bFDnlIs-RP7}exb9xoG(*h1`#$AB@G|G{q7qwTfg_>m^+5>gWS6DY%F=No|2&x$+ ztBYVE>&-BOmR5Xv(7q|X9N$NULW^>-R+*USmZntGg3dc?75w-n( zqb70GMZH$x67sn`@7;E^fv6oRY-lLbJvy)4C145O{PfVKLi%h*+)lf=s&QwfjL?htp&w-mC?kql z$TU>CXFAta;s%p_DS!2o?Bf;%Qbcbnsee-i7q3}rpz49h_)UrO7+Y*!T%`GfAgrUr z1k;{TNfW5iRu^V|ykH@|nW}HCPzh9iryvXVUdV3C~R{q2?h3VLz z-R|Ajxs+#Lw~E2D9uFU1JT@-v>Y05b@E@Q>62oVJh1QED`>@h!!*~%}L2+!c==^N4?qJ z9Jt;WEgig;4(^QPZOdK$*rR^S8k~@Rl>iSL!7B`7fpjqTa|Y>S_^MV2V&9=0xC=x% zBX4s)PrO?(3}Y=SQw?yXveWrB*i}n|ewIkllvrz?2NV6wVSB8Kh4C)bCymmJ|?`}&h zCt;Wo&V_oL4$?Mr^%sf1;I)D1X!fI@*?5EPQUelF{R_ahB!J22{S6Amf5t{5wkzcF z|HR+BSO#pC0Vq(2>EAfL)Q0sYHD9UBA#a;%vuCni^;+!6-@9HimHtBrwZCEa)60L| z2W(TS6Zs4CzkCiTCFN&bZBoO}Dh(hayYFvw4s`M0->C2Ezxa~s*w4arz%+gZlmV0x z6dgxharwLO{s<7WU<0=SRW5gu(557LZnb=dJN*bLcsVc&68Yv7hB&DZBRO?ECwhsB z>3ZLrY&jncAx*Kx;-P`Epo%$Lsl2MBNHOV@iHgqoB9+;1mPw{@cxf?E5{%p{a2}om$-l z?Kb?jn}lX8NL9Q(Y`|p?ChX;`KV)4VLwRB#>M^~EE3?V1P}{%%QmEt*D?rh-Oe}7oLvuRVvdG=c+YFnFWNR?(?qU(!mOVubKE_6ealQ0x?HB8 z%~6x@;VQY-A`GGt)%G>%MufW7NcyB1vL*12D*7B;7Ee^YYowAd3W3;%E>HTbM|~zF z=V#ClKgs%&_p{^g?g2)s_GvG^2%Rea1~yt8-}=ljq4z;`=`%GNQ9kgr=dYX#Fc%=r5hxbyr$E#if}>LcTIGl5wJ)~0w{obx+3zfihtjpwz4 z4)i9|h_~;?bkd!&46Iz_c)O6wRm^qI=Cb=@fFv(5SqL{-JWH7Pz?cwYq^OB=LcUJ_ zNg1j(i>B0o?GwKf*wP)IjBt1Fpu+F_QJCP#&QETtZfR$#0g#0M

!ZuZAIW4s!W8GHAdE(NxA{+RZvJ@+Fuj2sNCU9Zk)}Vrn>c#Cvn_x3 z(x;MzlZ>Au2I@HgSjqz%ZIi^BaO3SPx{Yx;;euT04{atZPH$W-cfz6W+v;myaRy@ll6Q}2tUH8wSDSH*5tkd=^YvFL>)4rFP#L`J6Jr3 z+M$!SamUZIN=pKLu}ZvU-(ttor98c4Dyd?K*knF)o5vd_Vld)k1;!dswp z^;0B*FIWscBx4$#jFjjIe)MrTOrirz{gPz%VRVqkIqu7DSsO3Nim_wM4exRsI;K5Q z;5=NyJwG#XuJyOs-W4?&sCJAXp!sl;!A_lx^T1!9$XZuY`Z!s-t>BHHjw(J(5NGT(;ugR*_4KS-pmjY0&e!TX zcXax=&TgDlS7#Y`{I6G7_U)KXSl5-Et}|2$q<{75JyFQDR+o$~{XK5l(g#K28()5i zw+qmJ>oD|*Trs<6C`-ibs+7rff-bQ~v%^H)!svoQ`An#PrYAHRQ$)R2;XkmKq<)Hv z)_bKD8JKymKLwV$slB)>wjs^Jj6YzCK@{sX?mKETuV+SF@x%Mic-n}ctF4+9#N_+V zyZDB=pLtgrjK2&P=i^f*%f!56U28^!<@sy;2Xl$n_TG7S#qnL%5K4@wICQzL%+Z)2 zt~uuNKqXtsH#Ib_#mCYAz_zT52<4I+)I-LowGT~RQf|*@J%yfkNpo#|p^-~=&onc5 zqS~82Rj8#mX3W~qq=^xillU`w&~b8rTYa3J~&O?Iq6HpOWGFS%d&o1 zt7&Hcp-K5vPASOF<>aow3LyPlBsJ?o17*LZQVWf^qXybFUoPlT{w z+rd-zrx&L^NIEIT^v#ld5^J72{K0K9vhdXKTT;6W7o2mkIM14e7JmK%SY;`YBO!3A zdG}e`HNfr$T(js2G$d}jGXB&sveYz!H`hz+HCER>+7q zN?!v9J8FDme?~_^d=K~hxe+%XDIb3W65!6~W`}_pKYF%#O4dn2)%$(8oq|_i*>(o{ z8Kj?Nc#$Z%e2sMmfz{IlLI`2h!L7Wft`=i-zfH8c2Hm>*%EVyh*e%|V8=kV-i@JkF zqS>p_rb_NYn06H}NG293m_Rw+E7$P|Em{+l3o-hXrJQA)-&klmIA))i-1=a$qWye) zSSx+4@{+wn&r@74ioaW6Ywy)S#`o}gZI~&I^h9Iv8P&!I>tM&bmNY}ekb6>%?_~R> zt)TU-0*W>ts|4pGR1fa1G~2dB9_8DoV_i6UC*zS!&}=z&T^Gi8h#)TNG8oRYZT8XV z>->X-DnkOH+NMyM5gpVcj7!;Uu*G*lTIcF?wQzQ!lINu4;@AP#y@@NYmM`gr4Aaz6 z9|aSu7uJ^4r)Y4BknYI6K?Dcw(P;OuZZJZPqurQO)zg#gj!&xJ=Ty0MYjqLQlbR{H zoEs7!u&dvgw=Kov6y&dVnKwea!Y(@&KayF}t#4kSw^6q>f(QRt5KkfIIThbWcwI25 zu~&R2>GEFoYG821%FsgC!_7^<&gTWkBt6%!N7K_save0hEEc|_3msf|=M?Ty2bRGZ z6ENo@p2quwS;%rpLKQ+7U zp-w37a<{R@Caq>5wm1y2Yi)}?$j*LHCP&sRtiYDT3>?2v&68>PC;S9FLgk`IPOVh4 zXYKt~VTaMOQxaa-nW>d_TAAE!}Rpz&e!Aj&L6Jc_68v-m#3AcBX6+Jip|KCM;B^h|#R zaU5M-z$N1TO`q}F%JK$TC_4v7D&!DhE27*EL(39|Jqy`+OI4Al4C~}Jg1~t{1manQ zqvE1o7L+Ktg1`uuJ1=EKgf3&fF>UitI@x|7s1=aHpXGx4Ry*5W;UCPLi(5s$;HwDd z>D3;eFg%RneX+OlEV5C#5K^WOn>gQIlb<=w5;_Q)1hp&{}jh(R?9U6|nR&M9TAe?c0^_x;9$~ z>bCd2QA|K#uk7DzZ<{9=iEZoX6p@UUL#fwyhGMa~%G1tyuv49{Jv(Wh>%@r0Sp5lmpJHR#Oa1qWrp$$}qG!Hz8yRYiYL zpq9TP3z&&HlNN7O`mT(oE@e~08~2~-A!7X6w-SrLBz|L_ievt$bO8o!+GjQG{Xd1B zE`ll~Ekd}z$~*2Boo1=SdAmv70gQ2@^Wm=zZdsZq3g!xMlU&AXf~2#0v&vy(=MHVn zD_SBOyJ(76)TkCdLoRwZZ)6SG%||&q!8##{HRV8L<&nb&qVXuqiWk}$ndV%9T?&-? z(eeG?4Vj;iM4X`*E2ZQKXvG=}QLJ#ISAONGK=U8a95 z2hMs9CddRr9w1<_Sm6Goz^je==Cf-`uErm1px~`J(+=%|pYW0}BTvjQ78{Qd`e)fN zhLtkYLD);U{6pgd43st&C~X2}hBG8PkagvtNuVw8?_#`Pzzs*nfqx1OFXicwMk!Bl zaKI}%GBH|!c-rB6amqf9Se1Ozi_dOGTav#{tOV-t0-Bt!Yt_|`I~MuzM}7N+7Ck|_ zp1&|o!6Aiwgd1=371GaxG3?<^8a(XsL`Pz;OGx21-y&a#L(~KsMO{)j+e2#=EHm&8 z>>j&T-b|yE)F80mRGnS@;ijW=ZBqfHxFafl+1m(10z7H$&B zvFUdx$1LRkF9e+mEs*6j&h!`c5yxh*Hr1~2e&2&bJU-W*0CbuskbYBXU5UuQ!f_3K zPK-r#HT^t!o1v=~ywzTh<9XlE`AYEt`;piRyk)+gw?@l`{m4RzCQQq=qjZ+HCriZB zdLymxK3)%I+IK;Ro_o+}kA3dojmDRhgGT4jg{BgJ>aKN89h&V-tVFa{uMB1|W)@!} zwxx|+Okbr?k4%pxg4piFk{`u~w29Ol{6QwpcWOiwoqJ9GL53W4xL~eFhR`bYs}oK> zmTt!$dme|?tz{%uD8XPyee}l2hTeXSf9ZwC7FkVFuIW#MSop%@miG z*4Yb@7?#YYzj^Zl-vBjAojm4hzjZM4Bdf=WAGNp6f> zWH@}%vjt^L!4G+hhCOvJ-Z-7HJLb(dtZQ>zbQ4#2ehJSZsk|V95(qLw61Bg{X(uCp zJjh)j^lc)!o)qV%oux)VzC(n9d0H`A?l-gAGe^R_{9PrdOpxqtmq1?Dj9_wekcZ6Q6Bbev7Y!qf#&{T19E5 zTg==hodRlh!~L6vKbPw7%1d;9Mzf$6K5lPJ4F79$(^xB_MV_2ofmq(wk)%HVFtHt{>=T_y6nmrUB0F$4>me&4$qlYJfN2qELVpQ%xhOb>u z`%B6S*{XB>y|ZHN$rzrt&cbk3S#6MWHwM%)`azgfB%H&zut#Ue*i#E7%acmOjz-mh zY(O+WE~9VM9PsBd&Bugy1`-#GYbD^g;00=Rp2oA6j5fli;(HYsr;vK?2YFAInj|Bq zGp_Iaa}B?($@Hd-xasK`o=;y8lp?bt=eU^mg807TWH`$Hl}n--HZAjN;OcN>C-g>s z?M6zSnl>CAP_MC=gvD0X)-eGdMscl-oaIktjTZ?p;i9^}>rG$yj7M;rp1qnf2Olp} zV_W_Fw&c&79MCacF&VX=jm7nF!+X%Gu_HGDFJ%D8IBx~B%(t?3Q5-F%g>u@Wlu=M*Ey|7Oo|CEsY_ zqRaN!Dh>Z=W#7a#rgwo7;;4LCF27=ECB&z-*jLvvJgl5jIOQHXSjc2=-nB%nKPY*! z@UDs|#}BH!g{D!D7JY54W6uL8Bo5mWfg_V)-K8e?E#zV>kotIm*^ubHgtMOiFS!0c z6kO|_0P{Lh=*7)>Ag55n0r1`Xa)p53_ao_OH%N+Q=X3cH(@Hz!_`MpMfv$%^gl$jU z4(o=`a%D;P-a!5T6wy+hw(>~HEn|7QUA!;2;KAPk_f-@op`wTXsdDTh?2Mj>P|*7u zXJQH#zq1_TSy1%?lAega7OrH+pS6?nH#hSStv*dF+o5+9d{Pha3x)&-W)FKQgNp{C z{!O0qKXR9UWmJD$L>$8JFY~wn|*%>O_I-GB2IRfj>@=1H{Y&$hjs|oEiy(?9O zR=JHd-nRbW?uy7Ng+;>N`)jcDFiTODI=u(dow;=zuR2UkeW0la=1yXJv|4Jv6k0xg zvCS+kL#7{=ZwWOWG*bT7lecYzSVVm}wEUg!aQlheNh6tF zfJ1;8W4^@Xj}#kXH+!s2F;TqQsaCq!Zck2;>SN^N!6r3^pM)N+u1EIeozWd!4n?ax zKJU`ZUwIY}Vw*sp^k?_NKT^Wha&Kw4m_8I*o1qG!HK`IW~9GIf{C9v=kQzcb}?>Cx^z`v{k7c3q3-5qvm_xNz~6tK6T& zyjxiz=X4@&UQX@OU74AnVj&`j9S*^&2{nIA*2je{abCG)j7_3zrX$xDV`s<>MEl3{ zo9j_ejuI|!I_S`pBcF&FVQ)_jM?#W+5m65VUAsY>62M!_^2&K0qntP1M*V8r?{oFQ zn{3V$!*>&WnBmQZ<%nG#l;!M(truX4O1-y;`4_UkAwMxzf2{TFK*$*x>~s!WJYjpJ zOzI>bN1wo*8RuCI)+0Pw+q1Uyy1G6CodD@4Ml0LT8Pk+|{E7Y=ui(!VX9v9e;~D8& zLo8XVLQ4s_ZdyZPn0)IH-RdtLF_{;Qomy#^JDn;Pt~4PzUUAkgmEP*)8eE9(JteAl zq4{2H(>V7afzd$Wxnm?DBN`RtNk z<62~FqGE*44r!Q)Q)n8O>P~g+VOYZOeEnmE&6Kdl{k|HuwI^&4%S}gXqL$IrRhG$$ z1DX_G79wl`L!gAuuE=8-!fCx?Qc3;>07^hHG)b6GWYv@cwk0)S4c{#wyQN3ATfm>4 z1?JEL+8e*w92{Dw{s}#d6yt&WQe9tE@2vepRZ-aVOmdm{6R|8}C1h$P;1aLM4S;66 z_0sPtoxG#;DNA^y(lr^SmY~@QJ_|kLwtI0nU|OfVrET8`o>j}%CH4!k7|l}s$|7#a z@SJwBGEtIGGP)4|c=7Yd`P#EW+1aqMBJpmbnx@9*QrPvLs(#mp>Q@6%j~z`00#UgH ziiuL-O5H>Yb@pABm)~&Q7t`x&8(ks5jrL6*m0p=#$?hx}SxhZf;<+<>csCtoE?I3^ zJFDjw@YZV&a|XMPGM@sBEW07plnH}j&sYFf0&U<^;D|WJs_+dl;4~dgY>ql9FH9SJ zO9JPit%dfAcRp_Ah$fms>4PrdDIh~eyFC3Rt;ow#?54Z-` zhAfq84dz-O41DonoE!LN)PrD6coGoQK%DyoDSP+w?T?$ji|1&YyUPMDJP)_@ZuTon zf)I6$tND%VpkaB}Bvb>Rw9U)fRzs%9*p{eFdbmhrQaT6{P<$%EcNX?7`INgBwWTH_ zdI}4ru2WZAbB*2c)~{6URhs}sz_W`^8TN)%Zz$)!XNh5S1%e>^9t=3 zn)5$hCQx%WWcbw&Pa{isA(g)FWt;@qHn`8l=Gendyn=+a2hie2$oY*+nOX|U-hkvn z={L!_a|&dhW|*!HM_o75Lfnv#DH!}|n4T`8Yy8JxUq+ag6kjQBbL+85;*Bo5HCahJ za{C$5SMrzZr18ctq2^95*M3Z!eoBg0p5V1OHAZQ^R~XZ0iz4544hU8bo$8q{SL^pG zRN2slHO+;%4mAXV^-}k`ej~CW?4Mnwg_H)w}+Wc4AC2QTDt!FS#1LJt!pxFx}T_e@uqSsWH2YD}u zsQ>J`H{v=i(ok56O;w>UJnda<8$!F9IBzvq3KEu&DMbuuDU_h0#|${3N)LIkDSDJ7 zs@c9J)au1}y=g^iX7SNV&d;{>5ABfZjTCEb((9;2TCU98d28qMA`bNYAp@MD;@+w$x<71J zBU@><%Y_%;QHf)#o>!R`a!DzW%uN6)1ne-#~v$679wH0-xx^aS=j|&e^~YUXu4urLUu5d2p&P2zBE{ z|Ca6gU>JdG@OR)G60lousks1T8upQj$=1q$N>tMR4rPl2HABS_ndJtC3|;WPf$6#! zOlZTmjS}!l?+G_ZZqd(|`xm!8erI6sM!!rtAC9{Gi3MPDJopergLH&7ZCtEUYO#3;vfzWLeKlxDyh@ZeowI}W@}W7wHm z?6#u!Zzmt30x?`DxjEbgE)BwPxi|nb$AF^zOZLV@?l3=WHFz=(2&63N(-WHT80z@9u^ zkndX9a}QxNKAh1=GP?ETe3QwD3F+kH!Lf|ajklfTxAs#Y%ufWFfDaenZVEpSe?Q|a zG%)C#Vp;s<=hbdHYt7A`+l25ZJpQCjdbBs^-Gj2DiicW9y6lSE?D8l}*Gs$3D8UjD zx1w9&>&Zx?mI_AVxsG=HtctaPmDJ`TZw5N{7?dy66SHaKWrlKTfaDI_d!!vmBcUky zf{)t`R*bW&P1Jvu&PBKs6`h>jQHX>lM%UPf&x}QtQW$#y4z|1@O>A1VqR z)puAH`U|Mc?EGE~!C8?%X3Q*xuRE6EJ_~jA{8Y9iz(t1QWgekk|c1MoaN^ znV-}5jEpQNr$VJ$sPdn`V}tT|jjl7^#Co<|NL3cc^tL_ATwHRl7vP|;(|5XKa0A0g z2E+_~wn!1Vj>KkTVJq*d1J*Xq6rnLSlo&pb8tkkYAgU9$S2qZp*l% zsZ+FWc-PzBQ}n9Lj-22XUpuK!=rSqeqqUpIwyddqoMTl6WRO@Hd_b@fd5%^{7n~lgx*av&QW*V&fL8HJ!{P99u%Ic zxIRw$EE?;^8|EPd*->cl5NipCJp-|n_8PIRw=d70F(07Qu-XHO1U)FB;hCMUKxhF%{DMkhhU=t zPpmR~(|0H7h{mL)2OxfI%?PNSd@60kpX~d1D2}JeMF=@~qb9`><>@XWTBCe7B>W)= z98DxsjvZ-z3m5g_$f@bNu6b;39ityugR@TeX{#{$2{AHvEA#P{Wl<|>KMy7(E9c&3aW_rSzb2@Dhc9}2%4yw@?A|!ile9kP$!HI%)}K4w z6;0Zx%I}Rg_pO|i=h`V*IPhcksJ7_s?-SDv=x~|GXF7+Xh&=rS__dMdVI8joho+SI z8PiYpnxT(YUac>-zc)6@9#Nq`TI~9J%;C(B*8KP@=3uTb$B${$f%W%SGy@wn+Ds%z z=Yxwr1{mC~&er)IavWeq8Cr6`7!5;b`Rg(^_W3Y31xHX8JBPTRu%nMO%K9&kjMMDo zU?|RV`PDMU{|-COU{co+MKqQ$CitKiCy4TXk;4HMfeo(w3Sd|<2za-?)e7e;#YO^q z6PDt&>Gq0be+0t5A0a*x7M7htafp18NV9KiyASgOb z8V7F$KPbIrba7jICU&u})RU^~mS$o)UX=s_!p4_Fau}?z&>*WH1{e{jQl7@6F*F)F zF;(vUj9_A_MY^!r0iHS=c;=#0I1b-#=~mt5WAuHk%u05A7C;koC9fzIqS(rA>nHFDHTvY~q^yAWda~cWeoMAX8vCK;3#nV=|gAw~tDvXokR{c@y#nE$0 z%5mF*Rpf8Lvi(<#xw6f)dii$yGmbzf_Hgw{S`-^fP}@WmHOr+uENCX$E28rA-4{}Q zotvJQkGEZ9i~Vtw=s+dbb+8-Orr(neRU9yYnXPRVw-Rhzx&)hq^oQHK4wT-;xQx2; z@4%Z0#2D-H8{Y|k&{Cy$%C6VX+WM|!*w;HVd+*g{bP-{r4t6P`fHQtCj*xP$fnYU+7*<{sHF7xEp~OE zTmR7NVRD%Jm@Z~A=pqJNtL*%jGkM0tJC0Yv zFiegg01c=%mc}DHz3HABQ_a=VmMdS50>uc~L1T87T^?vk4%+;IagjJ$f^k9X3nt7& z#~Q~@B6t9$czNPS<~^}3sXZ{O`EKo0wPtEDd6lqx5;gh8do&~hSG4H|uUWkdr7sll zJeQ#kCPb^E=565Dd_Ln3W7eM%rH6Hd=T!VvR{B%xhi`lzteh+gVC^<7Pv&VQzMr69 zFN={9H`v*VY-R6GprFl+_8UwZ9uQ5~T!uQ(Lgf>`?QS^Z*uC`74_Ah{;6kVPs~VZC zFz}D{jl;^nWp0DfLTn$C_9e?mRdb0u4vNl{ ztg~JS2VgQCAZ)&@qDI1A=tM>nZ00<1?yI*ZAKzN4h@zrem@+rKQs&am9T%|XQSgG; z-wt7ho?;B+;qd+C7jWcEsfMPGMC3z|mPUomynkHTT~a<1web!<^j9omaNk|5_Y>NM z`w?xOV-~r$`s~}@GSjQzpA|mq072uIGo1k8uTKUHxg9v~)(yOTvllEoIO~@E)}HF} z7QAJZlv^c}2SpEi4QNxUg)>Ce`w=d=B>Ogyq+VlPjU%DzO(-tD>Y7FX@J+v^nN8A) zJK~|sr6Yr%>r}rkD9Cv{5+Ycx38i_SeR>PKuI173CAhOCxIX&zWLeu%_4t!8v`?LL zRN$L|QBQ<$$p^r0^c>z17iWredeCliRZy~cHdLoOKq4u;?gcNL@o(ehjntp!dyvI_rD0Ehe5!h~@Z-~;Ao=lXNFX7dQxa+mUsdJ|e~lb` ze53?q$Xt$@m5=`7vv7P%)|UXnHXcKpE|-8tLVaq)WUvn7XZ*bJPfr5%k!{zM@&g*M=_A=G#JkF8h)PjXZg8*rCluS(f3Z`#J;Olxz0FxaO z%6W|)c#5%|lwW%KM_ z9LgtY;9BCg?bJ9G!AAw=0>(+cuVcC%W{Ga)1j@tG0s8!DzM?=bNghE?1*SiL?s&8Zh#0 zc~3)Mx#KaF7==X?6^|TVrkU6mFY4OfD|h`CvkpX?i&kfF1IT-=+KY>Q#gkA^_M&E` zJ3xhn^H!x|ZB&2$bG-n49n#6yd$#2kohKe=KAHR;8uq1Lh=EWczwxIm?7FAkhb!}x zp#X{Gl!+O$o#ph#FD^T+32L)z0}zvRm;=yNEwzC=nx(D22^#PhPOfxi`RN>Wpg{=Y zToE`WQFCYdrowUhvNMm3v6LGXqBcIGj>#SI=|0+KD=8ilLWf&-Ve&KUNdp_Z$-Yy2 zCFT4UbtyTEi{z8%y+;&TGaJM@VZMMe0V%HixQsqfz#k@yec8&CUYJ9v;tvVqh|daMtb zyi@>dno1*fZnYY_A0f@Q?;}j0IpL}VmHX@S2~&yWv==)b)WoJ2;i?L@=nx>uQ0ojH z=ptG_-A6V#en__bLZHxYahQiu?u*>wJ07RPPT{1Ds2Mt#kaC_ zNEgdQ*bCHz7c4X+P$mhy;cvM6SRhOvlkZ^0VbsFj*h^r4Q5V?w7wFyd0F+b4YI;Rh z**7?QWT(F?T-tu4PX6)6B^di^S_l3aNC%Zk)x^`jK+0rE?v8`ZXkx`TIblg5=)!gs zXPN4aRD5^YI!uKehIu!-OQ^H~->HeJ`^{miZ`M;>;-HuP|JmgWEG{GuIYlvBIgvyoQm z(D1!ivWL+JN_3QVGcZdge!d?6xdI|HM7sXn8Sd-^4pH3yi4Ye1bp^J|KI?$3m(06+7N zN{Tdgw>%{xI1HMo$N}mNp8CapZ=13++okkXf6c3wL$AQKjZEXx0iBqyW70gMk+P@h{RV<#s$+c1IpK#S~5Pv@U~XH50VsX~nIzDg z2yyN^q+dO5n}#QqJ8yX@`IM2-1-E91;u~uIj$YB2spKo8`WoS16b@&$@AaL19@``* z1aBQ)3;1T?8D=jsyyHXbUJDt)A54ePv_U~KMIb>Zr%k}6ajHu*`zXpNmY)8X%{p=h zH{fB5m$~2oAo+O@(XJF-C9LXw z;5q(c|1L!M>qP}vTwGY4XEOY3<=mW~%R0u&ci^e?!K$ehNg3|3+WV#(NnwqK*?+%% z|LNq!N-+{nXT>za)p*Q|F=%{Bou3PSf{6byy30IXYGb*NwPUPKvzEnv!kYHWulM;0 zS>)I6pKku}f|`!@gL9?kLHEsFZ=WA0oc10p*bULy(kFpf8*nz<*q;|*61Su14d4N$ zG7yNjQ|Z1}q1Aq^9DV7vuI;!w1xeyP?bfTN*7<2c^71=j$z>xZrUA2CJO31}S<(N# ziX1yNUIdznY&vJBE&Ab!aM!G=FT^-LIZf8|hy!hS#JOioLj*_$j@@P)dp-!T^L9b# zf8+1&92JenCO*4$eUcLoc$VYA5tDTv`fyW^7YUOl{GgxLxkm^d$QAsV{#pgLIeiGK z#?2m|j-dxW_T9hx*ra&OrH}#XlQjMV$eDs|;tUa%-O9+jvyZTK;L&&IDG^E)Xcw>*6RSqiec*bvp4_LbZKn4g>x$DY+sRlQE<{@UApftkBw zjf!JB^i4QU@#%U9rG>QjA z@%Px~D7|rig1pJ zLEC8R)O-i0IuX>2>$*L1yrzS{Uw5}rz3^Q4hd#@B?Zh!NjZqn_NVZYm)LZesxeH;9 zSAP|?<~xD_f+@g&e@`{|d40|E4k~PT8OU*ca|gN%0FP$&t#C4wjf(o;p7!b>+B zj0^4&3Y0*cRkhbqDAw*v)11#QK^hkh_GQ2LVdC%BBkbYcq|jiSTN^?$U#eOv+lh(Z zTsPj!$6x0JMNpLYc(gwVc6hDlJ?ENOGrw%^8)%CN|2UO4cES@@jJv0%i?`D{Pkg$5xpeAZ;XhaYU;pz-ui0m1E*=(t@n_w?mnt zjz3IXblpxsL>w-}%$A$-u4^}4+2_<#9Z*Ar`pw2oiB#}SI!`@raCCU8s4#YT9dV)4 zaDPYCC~SWydd>oxe9dGw6k8_jKYARH4zonqE>Nq5oDK;V&4tT|L22@NsUGmRADys;jD~+sk`)WhBdDo!w=5z!D@Nfq8 z<-G4XKfdxK6GFCVHNWV~GcvGLvx;2&fISV*=WAiZcFconrb2P1d!5VGjz0}M{|TbV zmHfQ!R>a%2Qs7)a=(+bj%nYxoO`@56)tHV=S;H^QS2`MRuLxGm`_Y&n!Z-Hn=347e z`FL^Vex>ezzOdFXc)zwiQ6RHb^9~H4cH`ON_Xr3>>qZ#*+QaxAc8WhFqZ(jwM843l z@XGTZ=6(pg=eO#@FUdMIjp^kV(ZqP|WmZ}&scA63UKN~tvD)>_3lJl2U5fvc%&4u{l4JDr+bn;1@$MM7${Iz4KyIyGf|F(1ZNFP1B> z2Ms!CR0S+WE# zF)q`#q#j=Q&ff*%UHZSy$K{FH8fnhown!ETRIxl0_P6WgnFjp#AOFk3B_>*U7}T57 z`&-Jwwwp>+Tr0egF7wM#tMg)!HVUTcii8iF3|y^Uxyx^n;JO%akAK6B0Pxv=rY&rE zh_Knk$9R=*A_~y0s&{Pv?%O5UZxeWz7fi)J^0>aS%CkDnpY&A}iU*M5W?O9;09`y_xeH6{}HfRs3b@hkAoCju>|d2YaJj5~WY zr&Q8cz6SKnvvHFP2-xv#%DI=w-?P@mbpY^Q`*0`&l3Ge>FoOT1hFnSRGTvfg`+?E3 z=F*+3r3NdV6-!DBpZop&v8z6mD_Kf86r7FTH>>?Ok%I&vw@$Ao zxnxo!i(_$$p~&TQ3+Lr@XdY8NN|_waM-bS&kEK)#GAjB8!!&z>GX-X6#qtV4chW}35`DNvGtpY)*2#(Bg60t$4&VeeuU zYaj@7o1d^h#s=~)EqUONaPM&}fuM-4Bq&bn_lVvwy_-G41WLs3@a#I+1^-zk<3Fbl z!kOspp%bI?tjJ^mYD3F2MD z0;bugMo{LT|0}P^^%AyRzf4-HS-VBgWBaep6hKoSY(wJz?os;Ru~yFVz`bYVEX25f zzn}!La=k`&|HGLH8d?2|`*P%wrCX|tnaR_z=gD7p*G#j&#&PR1dm|xY!WirDz!q21 z-%K!TrvBx(_b)%l|3|xGA^0CS5!3iXYZ!Jw!V$Nhr55(fviw6Qh5ukX;}2Djs*uXw z9_JQQYB}Whi48t*WIbl?z?Z}!3GVfk*3IW5I7Bn>DpW5o`!C%m4GI-%u$N|>43n;^ z7QiFT$~NW*lAs(!>|u#JJpX%cQAc zE!m+o$27C4SUO6j}cZePdq$^i9%+8dpP> zPd+0GPsAi1dP~?5;1EUQ&emQR$vCSeBe zT4e91E@5ES@cVQ~UbJDsd=l#?3m}Y_83ksOM30KsLz}QCVN|rpf$p z?QZ#=WxDgv4ecR=0mX9Vl~?W-g(l6>>jZzB6~*+B9@mj|NW)4>!=U`caOrH;5(7sS zC&l$CO+txc%X=uBHj8=qIM(>Qwt&}hR)ml?ABs71Luz|K3)SW-1;~O@#Kxgkk=tOt zA>HroZh-i9EmvVD8BIbh`M9dqgYpG_;p`+|s89_xRa1tfeqG1Jo3~@*Mk3l(+t0s` zD&yB~quptWev+bm0o$|kU`9sx1=rP{43vdWN^YvhG5VZNJ}WQ}Dr6pI&~V&xdq%x! z^@C31CV`R$3N*~AW|<;^9nBXYOnDlOTe$FMsqw$AkhFcV%q=?VjtGy@);+W%e2R#L z)r=M?(iuW938881m!z=id-!2r8Pwd`T!;$P|^MeBPdSqq*iclZfes$*K8 zmYlzqzwh0Q)fr{}Hv-(P+OyD42vBDqr3UddcHS{%^>J(O>z}U+6LlFJa{cyKPfP4+ zv*^UaYW=G725%T;Tu#I61p;yms73K!#*KHt>8ETiDYP~n0vu#Gdv=*Rg|2Uzl1gPl zT@)q#fSOZxBm0WUO%G0aXF3YcB_J4hNqFn*J4tPZ=Ue|!Lbvi<9Kv|Oy+8bZefg0u z;lZn=JZ&Qa2gB!K$4|C%l0DXuKjsi6wU3*E0at=}BSR(TD=@PCZp%9ZCGu|lw;lAV<0R`wzv&nnC$(6G* z{09f2(Q7F;2lupSZWd07B&TEB;^IGCIdqWUm>}3k99$R~%H6afN@b~C5zgUnvW_y2 zz`6Q1ILIySiEG+d%U7?)wkajdaf1iw-=(&t)k4@Qdex{F^^7-~WnSvOF<$^^HYp@b z;H|9I68dFVWLXRk`IwPXGgPe9UV_c>n&=^!2q+Gu(^#`P0g~PS8$RJPKjoee52Rv- zPa1>Jmc6@~JubaQ9xa(ouxxtAr2j4+FqTRBS_Z+R62^W=We(n7=%y5ed&OnaY^? zhCFc`K!+)!o$baRB5vql%dex(y2kjMC9CU8Rx8n)l7}Oj`dj-?P*OGZ?eqE6%Wf`L z)2`0=5VFODn~LFNF~|WLZu#%*(;0;$GHvHoHvEvDKSK)dB1jfhj}T* zeQ`F~kxJFSZj^_|q+haCBKoX#@nfX9e7MRuT<9qR6w8dbR1qwlU7wq za83=wnGuPC*}Mn*bEj&MuzOOj$yqLA`sJNo0V-`Jo&>D7Rf>vN|3y@erh4|~@Ky|^ zpT02g<;KFO2br8^>~rkK@i%ii(7Hi0?_`=fpmeBw0a~Om1}G$G(xGVBNy3pAk_xT# zU~czYizhLua=6Z3)0AhaBy+uy6FBr*%?jEyo%F~YW~w62_A>4=eNYSe7-3YViy$g% z3*u*KrONVsl9P5jQTUdVTyLQT)S3DklQWdn1LOZFE@?a4DZa;lL;~d00Ud zd&HqV2uBo>ijnTu;~e~|;$RD)BYSfh)jx!>6}<5@`HQn>l)vn{C6lbv*-v%5@|GUG z=0APz^#of8HYkf3DFEn5VHoUtA$c>E-mX+nw(8bso@jJ^I8bs?wh6iKrT?Wj$=(0C!`XR9LcAKlYP19x1Qqse! z<9PlYv1Q0AsR0$vb6KF=djxOEFOZ(v+Zp}lJEg3?DH1R?Ci0I!^c7;j__(cl>iXKM zj}cVp=)AAe`jw6uLwtzVe|%h!so2_lGDn{L!>BzA>Hj_-J7N;#WLaspCyu=qnL>iKKSBh8{N~Wm-3xuZJC0R7RE;iNlFjg z=8Yw9$Av$agp=WlDWzMkYwjw|vq$DodE`%Qanj^CyHmH}P~l7hKj_gcm@}xcD80`H zahb3-!PAaW8$09p*_d}=4ZbNW%ODlzluBw9<4lsDq1ViTuI{>aauJx={kNQYM)to? z3^}a}H0pS-aldyhdq7Of9+I-m4%kJjv(U*T>A)~l@RN>00psSEEU6=^dYTda7z zP4iKDft2+27fxYW>zM{K6a#}*tCn;@?>yED&m-LYCO^cAgBSvR>0w8eP$PYNUW^A?l>D*$yjF| zQ;rgq&gHZ-rgse_sHF2&z0kZOMO%1+2oPe{E#JM%O-yzV``*)_^UVLDIQCK8-y%0W zs3!Wy$nF1~^8awk3;hjcV#c^&O^cNy$1an~OMm3k%L)ZZ<(JXb3OEDBM+fuc3#ykk zcvRD+!Qm7;c!RQ&vQ##AVA0_oS+1K{?aa9!IWayMxbhD)cf;NP{?N~F>tvYYwGyUq+Uw%s;d#O>+ z(#TcfL+JHu3kkwMuNV_LP6?CntejAt2Xo_rwv5l^K!9w7kVvl8|3C_O>vRcJ@*E+3 z4lP$q(EHHca0zq}bT*u0HW#~dSCz}KC!=3PH>pi~bn|dGq<;t2XK!+7p5u?CIC{vy z$sg@aeSGkq^z41jg)yn*F!HFPkgLh*=<0Qoij&-BEZ3D;7|*)z;`Y1=Ch7{sd7k45 z1g(Gya7ePv{+Or}F$&gBpxxMmewT*-(cX22HI;7bacqMQHbzk)U||piL8S=@DAfjn z1&EY@7#K_fN=T3z6$M8GMhqZbKxrWf1WAApKo~>=1QkOFp{NWs1!;j0f_EqA%$f6? z^W1Zud(VCDkNLaV`Plp0>s$4G*D8Tpx`F8VHxb=rCAxp_tV?LE?~wtZ*aDzP#M(%U zQI-)7R({CD$vt2qIxh-J_86IaSH)#w)1b@4(KSkqYslx1_7dF-qGs73iITv8ric08V$3+Jp<=&Ig{n5cfbf7{Y zTq%sLnTH(pq39g995@!mc6^HSD!)Kb?>Ue2jU6u9Ok|v7FC0)LrHI}V0bot6Pg05dP3jqR zw<#=BM|hHf2PVa~X8|*LcxrVcr;rrItINB)LIB!^lO^t4Z#~f5CIE6E8bHjwTHYxO zr?mDjIrqtK0gd;yPF7dk2tnL(dH}xw+w>ysD3iI~mtRE)(&x>8ycWzXQJ@bI?S9za zea>lhX)Q3*DbTKw8JSK_m`+f5T?}+R^ZkXG+hzgoZB}!NNT%%2h)i|%Tf5wMutaPP zi8WMC7nrBter4Oj2)3f**{z)wB5%7Dthr9|1-}>}P z1MfkkB38!1=fuBbvNI0PbV}zAL|UWaSA^vow-4uoBryHs;Nu;AngF$Zr1eALS&`Z! zm?TI1b}=+-GJg~=CQjyv1;1z}JYvWQZcE;<{yr=~Di2oat2|xEdiGe(i)I)6^0^d4 zo;JIWMrjK3FuVbniv~vi&v6~A*ip$My$M=k>j%sA=tEf4umSu_t=U1E$%g^uN0G&k z{Rj2VZ6b^VtoI{U{T#iT8rLD#{_HRiFk}O2HXmy69PmB;mTHVQfR-lKtO_0A^c^r& zjiul=tOon}aWaCPMw}|Pt$8^{SjR*-&G@$TB$ROvxfAWvFCY`-Dn=orZlHBzVmz@d zRU98eXU1^C3;Oq9U4NR8BXuH4|jfeK&x6b7zqp zgqor?t!kg>j5ostM4a=*b)0;;uIc18DCZdUWqEs6#LhSy8_Lz+d;WxCSMl+k41`!5D~O#2;vDr#?OPiXb(;+`E&{VooPJ(T@c`Q#LCHpA~# zU+Eq3FZgx(h@RmU&j7ODBVIz0GJSrG24=Rj+gZ3E{mq}<$Df|Td)RR>4nUx7Aa~&5 z1_Qa|4x`G5Jy83(Z3A3QqpaHmvZ6)6SR{$5WLeyJ$c}TIEml*cYsLJVGS-m38QAu!u5;R?Dv-ZILIirr4}>dP2T0N zTPd$}{>2cX*^ScHWpps)?no+y7_t9{i}vE`^QH4S2t!9Teh>rOa)#J zbZWx6qiA}1sFryEAZLMU*6-V#Tl-i~UQdjuJT|Td;OsBpwr9&fOmN_HuU6Dtp;X=z zTC(Xb9WfGc!jjsTdPl4{2l7zovhGrZI`q%&J@-Xf?q3h?-JF`U`0mOJ2ZusV#`(Fe zS9H<#_f=x^Ijmxf7C*NX<8~`}Mq{!FJM=!9^kBH;v!byoqBoL-NaoB^ z{Msk(WE~9EN?T`GVVpB~o^Yy3ccap9Db(z#5tcky*q^Mqm7ZoWu8T-+nIFDx!9H$^ zH09e$+PFd4E|_mpvs|^NRz59r00>!f#w~o7*NcK#RGdG=p^LtwSFw{2&765#T3KoR zJl}Bqp-421Sy3uuQy*27Dna9obRonDvb(aSL4S6j`%V(} z=HhAa_FwabjQ->VDygl3yxM*P_~7g#hxE>8K+EhN{z3%wVU2c~Z-ZfS*I_PpbTPtv zqD^OS!V%wtISvNz^1;hsjc+EEdpw`H>5R6E8VC!iXPgp~InO@efc$2GxN_bS%*pQ4 z&JlRvn{@brx4Ie(R!9c&?MaE9@#Az@xdheX%{KX*7A^?EfT;q-7^&ZJGzCCCbAdsP z-`4A~btL-;Hf>IIp$y2CSJ!P_*fNQ+TXIuZmhwh{89`(3pW6_u5R+Zf6X5Cgw~-NG z7fQ9}$fiGi+y|!OZ}!#6$|KcMKjZ(T=o|Njb6|HY$%CwNkUcvXo!Oq;d^ztf9NUzM zdopY!E2G^w@ulQ^a(iTc&gDz}X>X0pXnp(GRh5oHL}shlXFEWbeyPoP`J=6JDq}b& zbc$WwUga?fXNR1)SOCy5Fw>7*WhuayY9oAvyH0}XPzKe@MkTZaLJ(_A&)O;n>+zluEe0DUQhl~TgdwTW+sq0sx&)6(Ft?kn3(3!>E>&yO^I+%&dz(W zLb|M!a3{^4zAsz@jn%XeFDsGTI!ZCLP|b1XlvPxK(VvS>($wYAEq+pLSio;?W6>bi6%tSs``aKn@yAc#tVR zEyi6QX6UmIE=9X8f+dgsM&jxc`_j?8M&|32Y=Er8ao+Sct^Odmsyjf3e71A{Deg?v zpo=N1Ga}#KX?Bxt3J(P|B#mc!7?z*xRMELjVkEB|GA{VQ3uXH`y&5`(pgE9BH{a~= zt+l&}Je`oVlmkrzf?jD&c*R@;SYh85=f?OwSSAV%@lm=_6R??}J{YRS*w?*C{zkI@ z+aOiR!L%}B3O+wU?RJIhwvq=C>o3HR?vNAT>-P>+)D*xm>%r9SnY))!Fk`erVW&Ja zLKEtqbM;H1D-9T&B>i7jPi_{M2Qt~+M`Lgu-$QXujq1dQ)q@e2A`*qIqWty<*xSeN zqRXSANhd~95j!`?9gTQ?<j4B^X-)Dfma<1l7>w|Y3%p!HGPANDI<>-Byx^?3bWYZo;tT=S8|^ee(G z64~Xx#?SAnce)2hy!KxfE2ozK{MY=^c)xuQ^uU+kck``#P8?s@oc*nz66YmQ=DYg6t5)eNZ2@Tn0EqkZW7yCmC`Q(zuTpcdiANptXWVv zp}f}@(_sy5XFIkZI$VboaGz>rR@W}P2%n{mr9FUYt8%kDY6+3TA>$+v#Hb&W>>q?U z(mvVjoT(}OesJxj=f-`@zhlMLD&9(&zLONl7rd#nRNQXTuc0`fvAP&Ry6}ziHMp+b z8lpl1^BB5S*U6C#(r3M0Pm24*?u;wP<&DLk8;&6qMrZ|IhmvoXVJc9eu`<>&>b@$P0T^N})<>L9* z-rRdn_%rE5mIAchbgGKfb^jSxxyZq~9yabuJ4L1<(Hm@R`A7L4hRRTrWvJHUQTD$e zn);v5NBRC#CvXeyA0@rM+rTvKoXU83zk4r7o}@YW?q**ao^=6>h(9u7)ot?(^i;`$ zrD=V@o>rjLx_;#&y)Xs z#P_G|Ga}$~c72h5m<_w>N&!r##2|sVu5=)mH+RFB@kETT@DW#OTluEIcVCYW4LHwJ zR)pf%&oV(cekMi1MlFcom)U=xI$$v#nuSbsbVR=kYx`84^&Q7xZ;ici=Y102o5_vi zVG%a$#WZ|K*+4E7dHYz{bbVzei9cgv0yd#SsLRL~iSs61bu~TWR#YJfLnIl=({xR)TrQzoaMQCuwMeW$t@ktV2T()zyBN<72!@|#G-~^EI zLXd(#;J`l0l8LuE*NQnnJGh)ywp?OFP1Dmr6aj+XeLUZr3B?7Q@Ev6G?veZbeF93l9a8?-1r?+n=QB)#>m4UKy%@9+%6+h^ z&DYNyCp(H&vZKh>Q=Es$2>CV}p|9*^#$?A2fU2u@6c!b#@f4bL7RJa8?+w;ElcRA-_#b zn%LI9SjDfr-0o1m!yGWt>Nf0H?Rh)k2G2F)MnOIrdZfr)o=}aRm?;C|>#EW1ZLz&s zGUE@pU!Kuc?9sa<&cm+}W9u&(CLl+2*jEdGW7{TZ!EU<$nOrUQ&r(<5fMqT($coKB zA@zc!2|}Oa*vxnhs_9n)UjO~iq>hbH${uOJB=rm)?Q}#N|NTSw;x)oL>?qh6mHF_H zPtV&4qaR{awJCk`Ie`;R)acHgTmJNOm^GU^zUUINC>l?4_6T`eBkYDL&^|S+r@zU| z)>yli8q+x{j#8q-Fk!ZPAtrCj&#PP)6G*Sk0;2qDxXF{*3820GRjA;Q<{ulI4ViD{ z2bsFT*c9_nYc??EeK^705v^0>J=f#;$HtJg=#dlR!G6em2pBA~ZeKgNNX^oUP7l7ri<|#}R9dfAHc&idrDazzbTitEMJ% zP{xFCi0DJA37u$;L{)|R8oEU;oHN9p5%lD0P=hQ(_^qHplhwA$obMdBG;ESm5j~^H zYV^KYH_@=Fq$cBYb3h7Aus~&mHP?zHf#aFEJRlU^_h^yjZA))ajRsaS0y`!nj!LEL#tcU2F1M^=jO)0iYJ6kdxnU9-rYL@TvaWO*SFw#sx@52;-lc7GeRl;IwhUs z!QaA6iao0879P*IF=;v*p*yvf#NsE-uZxdA!P_bw`Ai&)2R3jVPY}OpZ=p{Ow;+cx zZ%jQ@VYj=UAHV&ZBe~2Kz2O-5Fx6%8195%IXT0}ll9+mv94dLP6y``)vP{`d_0kbH zdX)0rS^e_VtV|N>7fCB~Fw>;p(Z7-#)r;E0e*)Tk$r}8f?U$EM)ZldOcWv%CmLVya zoKeI$MY<&zRE|@l99LsR0W=Mcd>KVQxN*^1r%OmM5w>L~D`GOt>7A>t2|Yu2!NEzV z0>cK+v!c6@A-WSN58VI~pb5g|UBBG^l+nsjk~iR`hc=MZzd08f(kGoDtW} zP*~LAu-XM?LAF+ysvoe%8C|-t0Y=j$uc@BJaD8#Rc;u~+*Joqc7(gzo2G6LsrsJCx zYmPaR%>v+eH$!(Sf7wS|9{lOL^8UF=R!dIv(p`5+Aj3YCrBMWv5I7n?HbbR5s~EX0 z4*%R|&u*GcQq3wt=V~Xd?yJRVoo8wBbs%g`m$k_*x=Kyhrzygn;G~BM%G!0Imx8eT z)tt_`TVvh5Yg6L3K|XN?f7=~HCuSXXQR$~pdlnuDjB>SpRhMQ!>c2-=V@%@mXs_pIAk@j{WNCCRu+@rZCAQ@-w zh8J~Az7_Et(KAQbizBz}pMWN;AHd4yn@)=+>z89Z zRKh=%dtYc)jhy=s$i=JfQ-svOega+k8tKTCW4ROdN2#t;w~;D4Ph)45knlg$Zf}=N z@17nvjp1W6qyLGX9NL1eMUE({0t<90|E^!lDYYoyR1R&})Us|4_*4DRhOfFf)twAG zlNvUg#QI+HQ3}sUxBOiT3kjNiu0xTS(V@Umj%2wsC0M@WceNkNi9gkXQk^>r{JM5F z2}dwJFx?`p@SZ;N`wA7;+iFR{pBE2Nt~T`rwuu9@oZZ7RLFK?*rLTTB7zWIBY#M99 jqe?i>Z3*D_x-PEQP%a>6jt<~~8^1YWZEr=e@Vxb3lWQ`p literal 0 HcmV?d00001