From 87be12ec716fb3d6605cae9b20854764bf866298 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 17 Oct 2015 20:20:44 +1100 Subject: [PATCH] More work on new launcher. --- ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs | 7 + ClassicalSharp/2D/Drawing/IDrawer2D.cs | 3 + ClassicalSharp/Program.cs | 3 +- Launcher2/Client.cs | 52 ++++ Launcher2/Gui/ClassiCubeScreen.cs | 252 ++++++++++++++++++ Launcher2/Gui/DirectConnectScreen.cs | 226 ++++++++++++++++ Launcher2/Gui/LauncherScreen.cs | 87 ++++++ Launcher2/Gui/MainScreen.cs | 78 ++++++ .../Widgets/LauncherButtonWidget.cs} | 19 +- .../Gui/Widgets/LauncherTextInputWidget.cs | 55 ++++ Launcher2/Gui/Widgets/LauncherTextWidget.cs | 28 ++ Launcher2/Gui/Widgets/LauncherWidget.cs | 26 ++ Launcher2/Launcher2.csproj | 17 +- Launcher2/LauncherWindow.cs | 110 ++++++++ Launcher2/MainScreen.cs | 156 ----------- Launcher2/Program.cs | 68 +++-- Launcher2/Secure.cs | 52 ++++ 17 files changed, 1029 insertions(+), 210 deletions(-) create mode 100644 Launcher2/Client.cs create mode 100644 Launcher2/Gui/ClassiCubeScreen.cs create mode 100644 Launcher2/Gui/DirectConnectScreen.cs create mode 100644 Launcher2/Gui/LauncherScreen.cs create mode 100644 Launcher2/Gui/MainScreen.cs rename Launcher2/{FastButtonWidget.cs => Gui/Widgets/LauncherButtonWidget.cs} (65%) create mode 100644 Launcher2/Gui/Widgets/LauncherTextInputWidget.cs create mode 100644 Launcher2/Gui/Widgets/LauncherTextWidget.cs create mode 100644 Launcher2/Gui/Widgets/LauncherWidget.cs create mode 100644 Launcher2/LauncherWindow.cs delete mode 100644 Launcher2/MainScreen.cs create mode 100644 Launcher2/Secure.cs diff --git a/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs b/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs index 855c30801..8079a37be 100644 --- a/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs +++ b/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs @@ -91,6 +91,13 @@ namespace ClassicalSharp { g.Clear( colour ); } + public override void Clear( Color colour, int x, int y, int width, int height ) { + g.SmoothingMode = SmoothingMode.None; + Brush brush = GetOrCreateBrush( colour ); + g.FillRectangle( brush, x, y, width, height ); + g.SmoothingMode = SmoothingMode.HighQuality; + } + public override void Dispose() { g.Dispose(); g = null; diff --git a/ClassicalSharp/2D/Drawing/IDrawer2D.cs b/ClassicalSharp/2D/Drawing/IDrawer2D.cs index 52e674712..faa2fea86 100644 --- a/ClassicalSharp/2D/Drawing/IDrawer2D.cs +++ b/ClassicalSharp/2D/Drawing/IDrawer2D.cs @@ -35,6 +35,9 @@ namespace ClassicalSharp { /// Clears the entire bound bitmap to the specified colour. public abstract void Clear( Color colour ); + /// Clears the entire bound bitmap to the specified colour. + public abstract void Clear( Color colour, int x, int y, int width, int height ); + /// Disposes of any resources used by this class that are associated with the underlying bitmap. public abstract void Dispose(); diff --git a/ClassicalSharp/Program.cs b/ClassicalSharp/Program.cs index b247c9700..e00f8acf0 100644 --- a/ClassicalSharp/Program.cs +++ b/ClassicalSharp/Program.cs @@ -11,9 +11,8 @@ namespace ClassicalSharp { [STAThread] public static void Main( string[] args ) { - if( !Debugger.IsAttached ) { + if( !Debugger.IsAttached ) AppDomain.CurrentDomain.UnhandledException += UnhandledException; - } Utils.Log( "Starting " + Utils.AppName + ".." ); if( !File.Exists( "default.zip" ) ) { diff --git a/Launcher2/Client.cs b/Launcher2/Client.cs new file mode 100644 index 000000000..ca2ffc0b7 --- /dev/null +++ b/Launcher2/Client.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics; +using System.IO; +using ClassicalSharp; + +namespace Launcher2 { + + public static class Client { + + public static bool Start( GameStartData data, bool classicubeSkins ) { + string skinServer = classicubeSkins ? "http://www.classicube.net/static/skins/" : "http://s3.amazonaws.com/MinecraftSkins/"; + string args = data.Username + " " + data.Mppass + " " + + data.Ip + " " + data.Port + " " + skinServer; + + UpdateResumeInfo( data, classicubeSkins ); + return Start( args ); + } + + public static bool Start( string args ) { + Process process = null; + + if( !File.Exists( "ClassicalSharp.exe" ) ) + return false; + + if( Type.GetType( "Mono.Runtime" ) != null ) { + process = Process.Start( "mono", "\"ClassicalSharp.exe\" " + args ); + } else { + process = Process.Start( "ClassicalSharp.exe", args ); + } + return true; + } + + internal static void UpdateResumeInfo( GameStartData data, bool classiCubeSkins ) { + // If the client has changed some settings in the meantime, make sure we keep the changes + try { + Options.Load(); + } catch( IOException ) { + } + + Options.Set( "launcher-username", data.Username ); + Options.Set( "launcher-ip", data.Ip ); + Options.Set( "launcher-port", data.Port ); + Options.Set( "launcher-mppass", Secure.Encode( data.Mppass, data.Username ) ); + Options.Set( "launcher-ccskins", classiCubeSkins ); + + try { + Options.Save(); + } catch( IOException ) { + } + } + } +} diff --git a/Launcher2/Gui/ClassiCubeScreen.cs b/Launcher2/Gui/ClassiCubeScreen.cs new file mode 100644 index 000000000..5c146cf9c --- /dev/null +++ b/Launcher2/Gui/ClassiCubeScreen.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Net; +using ClassicalSharp; +using OpenTK; +using OpenTK.Input; + +namespace Launcher2 { + + public sealed class ClassiCubeScreen : LauncherScreen { + + public ClassiCubeScreen( LauncherWindow game ) : base( game ) { + textFont = new Font( "Arial", 16, FontStyle.Bold ); + widgets = new LauncherWidget[7]; + game.Window.Mouse.Move += MouseMove; + game.Window.Mouse.ButtonDown += MouseButtonDown; + + game.Window.KeyPress += KeyPress; + game.Window.Keyboard.KeyDown += KeyDown; + game.Window.Keyboard.KeyRepeat = true; + } + + void KeyDown( object sender, KeyboardKeyEventArgs e ) { + if( lastInput != null && e.Key == Key.BackSpace ) { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + lastInput.RemoveChar( textFont ); + Dirty = true; + } + } else if( e.Key == Key.Enter ) { // Click sign in button + LauncherWidget widget = widgets[4]; + if( widget.OnClick != null ) + widget.OnClick(); + } + } + + void KeyPress( object sender, KeyPressEventArgs e ) { + if( lastInput != null ) { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + lastInput.AddChar( e.KeyChar, textFont ); + Dirty = true; + } + } + } + + public override void Init() { + Resize(); + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + LoadSavedInfo( drawer ); + Dirty = true; + } + } + + void LoadSavedInfo( IDrawer2D drawer ) { + try { + Options.Load(); + } catch( IOException ) { + return; + } + + string user = Options.Get( "launcher-cc-username" ) ?? ""; + string pass = Options.Get( "launcher-cc-password" ) ?? ""; + pass = Secure.Decode( pass, user ); + + Set( 2, user ); + Set( 3, pass ); + } + + public override void Resize() { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + DrawButtons( drawer ); + } + Dirty = true; + } + + Font textFont; + static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 ); + void DrawButtons( IDrawer2D drawer ) { + widgetIndex = 0; + MakeTextAt( drawer, "Username", -180, -100 ); + MakeTextAt( drawer, "Password", -180, -50 ); + + MakeTextInputAt( drawer, false, Get( widgetIndex ), 30, -100 ); + MakeTextInputAt( drawer, true, Get( widgetIndex ), 30, -50 ); + + MakeButtonAt( drawer, "Sign in", 110, 35, -65, 0, StartClient ); + MakeButtonAt( drawer, "Back", 80, 35, 140, 0, () => game.SetScreen( new MainScreen( game ) ) ); + MakeTextAt( drawer, "", 0, 50 ); + } + + ClassicubeSession session = new ClassicubeSession(); + List servers = new List(); + void StartClient() { + if( String.IsNullOrEmpty( Get( 2 ) ) ) { + SetStatus( "&ePlease enter a username" ); return; + } + if( String.IsNullOrEmpty( Get( 3 ) ) ) { + SetStatus( "&ePlease enter a username" ); return; + } + System.Diagnostics.Debug.WriteLine( Get( 2 ) ); + System.Diagnostics.Debug.WriteLine( Get( 3 ) ); + + SetStatus( "&eSigning in..." ); + try { + session.Login( Get( 2 ), Get( 3 ) ); + } catch( WebException ex ) { + session.Username = null; + DisplayWebException( ex, "sign in" ); + return; + } catch( InvalidOperationException ex ) { + session.Username = null; + string text = "&eFailed to sign in: " + + Environment.NewLine + ex.Message; + SetStatus( text ); + return; + } + + SetStatus( "&eRetrieving public servers list.." ); + try { + servers = session.GetPublicServers(); + } catch( WebException ex ) { + servers = new List(); + DisplayWebException( ex, "retrieve servers list" ); + return; + } + SetStatus( "&eSigned in" ); + } + + string Get( int index ) { + LauncherWidget widget = widgets[index]; + return widget == null ? "" : ((widget as LauncherTextInputWidget)).Text; + } + + void Set( int index, string text ) { + (widgets[index] as LauncherTextInputWidget) + .Redraw( game.Drawer, text, textFont ); + } + + void SetStatus( string text ) { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + LauncherTextWidget widget = (LauncherTextWidget)widgets[6]; + drawer.Clear( LauncherWindow.clearColour, widget.X, widget.Y, + widget.Width, widget.Height ); + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 0, 50 ); + Dirty = true; + } + } + + void MakeTextAt( IDrawer2D drawer, string text, int x, int y ) { + LauncherTextWidget widget = new LauncherTextWidget( game ); + widget.Text = text; + + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, x, y ); + widgets[widgetIndex++] = widget; + } + + void MakeTextInputAt( IDrawer2D drawer, bool password, string text, int x, int y ) { + LauncherTextInputWidget widget = new LauncherTextInputWidget( game ); + widget.OnClick = InputClick; + widget.Password = password; + + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 300, 30, x, y ); + widgets[widgetIndex++] = widget; + } + + void MakeButtonAt( IDrawer2D drawer, string text, int width, int height, + int x, int y, Action onClick ) { + LauncherButtonWidget widget = new LauncherButtonWidget( game ); + widget.Text = text; + widget.OnClick = onClick; + + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, width, height, x, y ); + FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 ); + widgets[widgetIndex++] = widget; + } + + protected override void UnselectWidget( LauncherWidget widget ) { + LauncherButtonWidget button = widget as LauncherButtonWidget; + if( button != null ) { + button.Redraw( game.Drawer, button.Text, textFont ); + FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 ); + Dirty = true; + } + } + + protected override void SelectWidget( LauncherWidget widget ) { + LauncherButtonWidget button = widget as LauncherButtonWidget; + if( button != null ) { + button.Redraw( game.Drawer, button.Text, textFont ); + Dirty = true; + } + } + + LauncherTextInputWidget lastInput; + void InputClick() { + LauncherTextInputWidget input = selectedWidget as LauncherTextInputWidget; + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + if( lastInput != null ) { + lastInput.Active = false; + lastInput.Redraw( game.Drawer, lastInput.Text, textFont ); + } + + input.Active = true; + input.Redraw( game.Drawer, input.Text, textFont ); + } + lastInput = input; + Dirty = true; + } + + public override void Dispose() { + textFont.Dispose(); + game.Window.Mouse.Move -= MouseMove; + game.Window.Mouse.ButtonDown -= MouseButtonDown; + + game.Window.KeyPress -= KeyPress; + game.Window.Keyboard.KeyDown -= KeyDown; + game.Window.Keyboard.KeyRepeat = false; + } + + void DisplayWebException( WebException ex, string action ) { + Program.LogException( ex ); + if( ex.Status == WebExceptionStatus.Timeout ) { + string text = "&eFailed to " + action + ":" + + Environment.NewLine + "Timed out while connecting to classicube.net."; + SetStatus( text ); + } else if( ex.Status == WebExceptionStatus.ProtocolError ) { + HttpWebResponse response = (HttpWebResponse)ex.Response; + int errorCode = (int)response.StatusCode; + string description = response.StatusDescription; + string text = "&eFailed to " + action + ":" + + Environment.NewLine + " classicube.net returned: (" + errorCode + ") " + description; + SetStatus(text ); + } else if( ex.Status == WebExceptionStatus.NameResolutionFailure ) { + string text = "&eFailed to " + action + ":" + + Environment.NewLine + "Unable to resolve classicube.net" + + Environment.NewLine + "you may not be connected to the internet."; + SetStatus( text ); + } else { + string text = "&eFailed to " + action + ":" + + Environment.NewLine + ex.Status; + SetStatus( text ); + } + } + } +} diff --git a/Launcher2/Gui/DirectConnectScreen.cs b/Launcher2/Gui/DirectConnectScreen.cs new file mode 100644 index 000000000..c918a7d87 --- /dev/null +++ b/Launcher2/Gui/DirectConnectScreen.cs @@ -0,0 +1,226 @@ +using System; +using System.Drawing; +using System.IO; +using System.Net; +using ClassicalSharp; +using OpenTK; +using OpenTK.Input; + +namespace Launcher2 { + + public sealed class DirectConnectScreen : LauncherScreen { + + public DirectConnectScreen( LauncherWindow game ) : base( game ) { + textFont = new Font( "Arial", 16, FontStyle.Bold ); + widgets = new LauncherWidget[9]; + game.Window.Mouse.Move += MouseMove; + game.Window.Mouse.ButtonDown += MouseButtonDown; + + game.Window.KeyPress += KeyPress; + game.Window.Keyboard.KeyDown += KeyDown; + game.Window.Keyboard.KeyRepeat = true; + } + + void KeyDown( object sender, KeyboardKeyEventArgs e ) { + if( lastInput != null && e.Key == Key.BackSpace ) { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + lastInput.RemoveChar( textFont ); + Dirty = true; + } + } else if( e.Key == Key.Enter ) { // Click connect button + LauncherWidget widget = widgets[6]; + if( widget.OnClick != null ) + widget.OnClick(); + } + } + + void KeyPress( object sender, KeyPressEventArgs e ) { + if( lastInput != null ) { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + lastInput.AddChar( e.KeyChar, textFont ); + Dirty = true; + } + } + } + + public override void Init() { + Resize(); + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + LoadSavedInfo( drawer ); + Dirty = true; + } + } + + void LoadSavedInfo( IDrawer2D drawer ) { + try { + Options.Load(); + } catch( IOException ) { + return; + } + + string user = Options.Get( "launcher-username" ) ?? ""; + string ip = Options.Get( "launcher-ip" ) ?? "127.0.0.1"; + string port = Options.Get( "launcher-port" ) ?? "25565"; + + IPAddress address; + if( !IPAddress.TryParse( ip, out address ) ) ip = "127.0.0.1"; + ushort portNum; + if( !UInt16.TryParse( port, out portNum ) ) port = "25565"; + + string mppass = Options.Get( "launcher-mppass" ) ?? null; + mppass = Secure.Decode( mppass, user ); + + Set( 3, user ); + Set( 4, ip + ":" + port ); + Set( 5, mppass ); + } + + public override void Resize() { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + DrawButtons( drawer ); + } + Dirty = true; + } + + Font textFont; + static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 ); + void DrawButtons( IDrawer2D drawer ) { + widgetIndex = 0; + MakeTextAt( drawer, "Username", -180, -100 ); + MakeTextAt( drawer, "Address", -180, -50 ); + MakeTextAt( drawer, "Mppass", -180, 0 ); + + MakeTextInputAt( drawer, Get( widgetIndex ), 30, -100 ); + MakeTextInputAt( drawer, Get( widgetIndex ), 30, -50 ); + MakeTextInputAt( drawer, Get( widgetIndex ), 30, 0 ); + + MakeButtonAt( drawer, "Connect", 110, 35, -65, 50, StartClient ); + MakeButtonAt( drawer, "Back", 80, 35, 140, 50, () => game.SetScreen( new MainScreen( game ) ) ); + MakeTextAt( drawer, "", 0, 100 ); + } + + void StartClient() { + SetStatus( "" ); + + if( String.IsNullOrEmpty( Get( 3 ) ) ) { + SetStatus( "&ePlease enter a username" ); return; + } + + string address = Get( 4 ); + int index = address.LastIndexOf( ':' ); + if( index <= 0 || index == address.Length - 1 ) { + SetStatus( "&eInvalid address" ); return; + } + + string ipPart = address.Substring( 0, index ); IPAddress ip; + if( !IPAddress.TryParse( ipPart, out ip ) ) { + SetStatus( "&eInvalid ip" ); return; + } + string portPart = address.Substring( index + 1, address.Length - index - 1 ); ushort port; + if( !UInt16.TryParse( portPart, out port ) ) { + SetStatus( "&eInvalid port" ); return; + } + + string mppass = Get( 5 ); + if( String.IsNullOrEmpty( mppass ) ) mppass = "(none)"; + GameStartData data = new GameStartData( Get( 3 ), mppass, ipPart, portPart ); + Client.Start( data, false ); + } + + string Get( int index ) { + LauncherWidget widget = widgets[index]; + return widget == null ? "" : ((widget as LauncherTextInputWidget)).Text; + } + + void Set( int index, string text ) { + (widgets[index] as LauncherTextInputWidget) + .Redraw( game.Drawer, text, textFont ); + } + + void SetStatus( string text ) { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + LauncherTextWidget widget = (LauncherTextWidget)widgets[8]; + drawer.Clear( LauncherWindow.clearColour, widget.X, widget.Y, + widget.Width, widget.Height ); + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 0, 100 ); + Dirty = true; + } + } + + void MakeTextAt( IDrawer2D drawer, string text, int x, int y ) { + LauncherTextWidget widget = new LauncherTextWidget( game ); + widget.Text = text; + + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, x, y ); + widgets[widgetIndex++] = widget; + } + + void MakeTextInputAt( IDrawer2D drawer, string text, int x, int y ) { + LauncherTextInputWidget widget = new LauncherTextInputWidget( game ); + widget.OnClick = InputClick; + + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 300, 30, x, y ); + widgets[widgetIndex++] = widget; + } + + void MakeButtonAt( IDrawer2D drawer, string text, int width, int height, + int x, int y, Action onClick ) { + LauncherButtonWidget widget = new LauncherButtonWidget( game ); + widget.Text = text; + widget.OnClick = onClick; + + widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, width, height, x, y ); + FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 ); + widgets[widgetIndex++] = widget; + } + + protected override void UnselectWidget( LauncherWidget widget ) { + LauncherButtonWidget button = widget as LauncherButtonWidget; + if( button != null ) { + button.Redraw( game.Drawer, button.Text, textFont ); + FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 ); + Dirty = true; + } + } + + protected override void SelectWidget( LauncherWidget widget ) { + LauncherButtonWidget button = widget as LauncherButtonWidget; + if( button != null ) { + button.Redraw( game.Drawer, button.Text, textFont ); + Dirty = true; + } + } + + LauncherTextInputWidget lastInput; + void InputClick() { + LauncherTextInputWidget input = selectedWidget as LauncherTextInputWidget; + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + if( lastInput != null ) { + lastInput.Active = false; + lastInput.Redraw( game.Drawer, lastInput.Text, textFont ); + } + + input.Active = true; + input.Redraw( game.Drawer, input.Text, textFont ); + } + lastInput = input; + Dirty = true; + } + + public override void Dispose() { + textFont.Dispose(); + game.Window.Mouse.Move -= MouseMove; + game.Window.Mouse.ButtonDown -= MouseButtonDown; + + game.Window.KeyPress -= KeyPress; + game.Window.Keyboard.KeyDown -= KeyDown; + game.Window.Keyboard.KeyRepeat = false; + } + } +} diff --git a/Launcher2/Gui/LauncherScreen.cs b/Launcher2/Gui/LauncherScreen.cs new file mode 100644 index 000000000..c68b39089 --- /dev/null +++ b/Launcher2/Gui/LauncherScreen.cs @@ -0,0 +1,87 @@ +using System; +using ClassicalSharp; +using OpenTK.Input; + +namespace Launcher2 { + + public abstract class LauncherScreen { + protected LauncherWindow game; + + public bool Dirty; + protected int widgetIndex; + + public LauncherScreen( LauncherWindow game ) { + this.game = game; + } + + public abstract void Init(); + + public abstract void Resize(); + + public abstract void Dispose(); + + static uint clearColourBGRA = (uint)LauncherWindow.clearColour.ToArgb(); + protected unsafe void FilterArea( int x, int y, int width, int height, byte scale ) { + using( FastBitmap bmp = new FastBitmap( game.Framebuffer, true ) ) { + for( int yy = y; yy < y + height; yy++ ) { + int* row = bmp.GetRowPtr( yy ) + x; + for( int xx = 0; xx < width; xx++ ) { + uint pixel = (uint)row[xx]; + if( pixel == clearColourBGRA ) continue; + + uint a = pixel & 0xFF000000; + uint r = (pixel >> 16) & 0xFF; + uint g = (pixel >> 8) & 0xFF; + uint b = pixel & 0xFF; + + r = (r * scale) / 255; + g = (g * scale) / 255; + b = (b * scale) / 255; + row[xx] = (int)(a | (r << 16) | (g << 8) | b); + } + } + } + } + + protected LauncherWidget selectedWidget; + protected LauncherWidget[] widgets; + protected void MouseMove( object sender, MouseMoveEventArgs e ) { + for( int i = 0; i < widgets.Length; i++ ) { + LauncherWidget widget = widgets[i]; + if( e.X >= widget.X && e.Y >= widget.Y && + e.X < widget.X + widget.Width && e.Y < widget.Y + widget.Height ) { + if( selectedWidget == widget ) return; + + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + if( selectedWidget != null ) + UnselectWidget( selectedWidget ); + SelectWidget( widget ); + } + selectedWidget = widget; + return; + } + } + + if( selectedWidget == null ) return; + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + UnselectWidget( selectedWidget ); + } + selectedWidget = null; + } + + protected virtual void UnselectWidget( LauncherWidget widget ) { + } + + protected virtual void SelectWidget( LauncherWidget widget ) { + } + + protected void MouseButtonDown( object sender, MouseButtonEventArgs e ) { + if( e.Button != MouseButton.Left ) return; + + if( selectedWidget != null && selectedWidget.OnClick != null ) + selectedWidget.OnClick(); + } + } +} diff --git a/Launcher2/Gui/MainScreen.cs b/Launcher2/Gui/MainScreen.cs new file mode 100644 index 000000000..9aafa0091 --- /dev/null +++ b/Launcher2/Gui/MainScreen.cs @@ -0,0 +1,78 @@ +using System; +using System.Drawing; +using ClassicalSharp; +using OpenTK.Input; + +namespace Launcher2 { + + public sealed class MainScreen : LauncherScreen { + + public MainScreen( LauncherWindow game ) : base( game ) { + game.Window.Mouse.Move += MouseMove; + game.Window.Mouse.ButtonDown += MouseButtonDown; + textFont = new Font( "Arial", 16, FontStyle.Bold ); + widgets = new LauncherWidget[4]; + } + + protected override void UnselectWidget( LauncherWidget widget ) { + LauncherButtonWidget button = widget as LauncherButtonWidget; + button.Redraw( game.Drawer, button.Text, textFont ); + FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 ); + Dirty = true; + } + + protected override void SelectWidget( LauncherWidget widget ) { + LauncherButtonWidget button = widget as LauncherButtonWidget; + button.Redraw( game.Drawer, button.Text, textFont ); + Dirty = true; + } + + public override void Init() { Resize(); } + + public override void Resize() { + using( IDrawer2D drawer = game.Drawer ) { + drawer.SetBitmap( game.Framebuffer ); + DrawButtons( drawer ); + } + Dirty = true; + } + + Font textFont; + static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 ); + void DrawButtons( IDrawer2D drawer ) { + widgetIndex = 0; + MakeButtonAt( drawer, "Direct connect", Anchor.Centre, Anchor.Centre, + buttonWidth, buttonHeight, 0, -100, + () => game.SetScreen( new DirectConnectScreen( game ) ) ); + + MakeButtonAt( drawer, "ClassiCube.net", Anchor.Centre, Anchor.Centre, + buttonWidth, buttonHeight, 0, -50, + () => game.SetScreen( new ClassiCubeScreen( game ) ) ); + + MakeButtonAt( drawer, "Singleplayer", Anchor.LeftOrTop, Anchor.BottomOrRight, + sideButtonWidth, buttonHeight, 10, -10, + () => Client.Start( "default.zip" ) ); + + MakeButtonAt( drawer, "Resume", Anchor.BottomOrRight, Anchor.BottomOrRight, + sideButtonWidth, buttonHeight, -10, -10, null ); + } + + const int buttonWidth = 220, buttonHeight = 35, sideButtonWidth = 150; + void MakeButtonAt( IDrawer2D drawer, string text, Anchor horAnchor, + Anchor verAnchor, int width, int height, int x, int y, Action onClick ) { + LauncherButtonWidget widget = new LauncherButtonWidget( game ); + widget.Text = text; + widget.OnClick = onClick; + + widget.DrawAt( drawer, text, textFont, horAnchor, verAnchor, width, height, x, y ); + FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 ); + widgets[widgetIndex++] = widget; + } + + public override void Dispose() { + game.Window.Mouse.Move -= MouseMove; + game.Window.Mouse.ButtonDown -= MouseButtonDown; + textFont.Dispose(); + } + } +} diff --git a/Launcher2/FastButtonWidget.cs b/Launcher2/Gui/Widgets/LauncherButtonWidget.cs similarity index 65% rename from Launcher2/FastButtonWidget.cs rename to Launcher2/Gui/Widgets/LauncherButtonWidget.cs index 5750e18ea..ca20beded 100644 --- a/Launcher2/FastButtonWidget.cs +++ b/Launcher2/Gui/Widgets/LauncherButtonWidget.cs @@ -1,18 +1,17 @@ using System; using System.Drawing; using ClassicalSharp; -using OpenTK; namespace Launcher2 { - public sealed class FastButtonWidget { + public sealed class LauncherButtonWidget : LauncherWidget { public int ButtonWidth, ButtonHeight; - public int X, Y, Width, Height; - public NativeWindow Window; - public Action OnClick; public string Text; + public LauncherButtonWidget( LauncherWindow window ) : base( window ) { + } + static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 ); public void DrawAt( IDrawer2D drawer, string text, Font font, Anchor horAnchor, Anchor verAnchor, int width, int height, int x, int y ) { @@ -22,16 +21,6 @@ namespace Launcher2 { Redraw( drawer, text, font ); } - void CalculateOffset( int x, int y, Anchor horAnchor, Anchor verAnchor ) { - if( horAnchor == Anchor.LeftOrTop ) X = x; - else if( horAnchor == Anchor.Centre ) X = x + Window.Width / 2 - Width / 2; - else if( horAnchor == Anchor.BottomOrRight ) X = x + Window.Width - Width; - - if( verAnchor == Anchor.LeftOrTop ) Y = y; - else if( verAnchor == Anchor.Centre ) Y = y + Window.Height / 2 - Height / 2; - else if( verAnchor == Anchor.BottomOrRight ) Y = y + Window.Height - Height; - } - public void Redraw( IDrawer2D drawer, string text, Font font ) { Size size = drawer.MeasureSize( text, font, true ); int width = ButtonWidth, height = ButtonHeight; diff --git a/Launcher2/Gui/Widgets/LauncherTextInputWidget.cs b/Launcher2/Gui/Widgets/LauncherTextInputWidget.cs new file mode 100644 index 000000000..072ceb089 --- /dev/null +++ b/Launcher2/Gui/Widgets/LauncherTextInputWidget.cs @@ -0,0 +1,55 @@ +using System; +using System.Drawing; +using ClassicalSharp; + +namespace Launcher2 { + + public sealed class LauncherTextInputWidget : LauncherWidget { + + public int ButtonWidth, ButtonHeight; + public string Text; + public bool Active; + public bool Password; + + public LauncherTextInputWidget( LauncherWindow window ) : base( window ) { + } + + public void DrawAt( IDrawer2D drawer, string text, Font font, + Anchor horAnchor, Anchor verAnchor, int width, int height, int x, int y ) { + ButtonWidth = width; ButtonHeight = height; + Width = width; Height = height; + CalculateOffset( x, y, horAnchor, verAnchor ); + Redraw( drawer, text, font ); + } + + public void Redraw( IDrawer2D drawer, string text, Font font ) { + Text = text; + if( Password ) + text = new String( '*', text.Length ); + Size size = drawer.MeasureSize( text, font, true ); + Width = Math.Max( ButtonWidth, size.Width + 7 ); + + FastColour col = Active ? FastColour.White : new FastColour( 160, 160, 160 ); + drawer.DrawRectBounds( col, 2, X, Y, Width, Height ); + drawer.DrawRect( FastColour.Black, X + 2, Y + 2, Width - 4, Height - 4 ); + + DrawTextArgs args = new DrawTextArgs( text, true ); + args.SkipPartsCheck = true; + drawer.DrawText( font, ref args, X + 7, Y + 2 ); + } + + public void AddChar( char c, Font font ) { + if( c >= ' ' && c <= '~' ) { + Text += c; + Redraw( Window.Drawer, Text, font ); + } + } + + public void RemoveChar( Font font ) { + if( Text.Length == 0 ) return; + + Text = Text.Substring( 0, Text.Length - 1 ); + Redraw( Window.Drawer, Text, font ); + } + } +} diff --git a/Launcher2/Gui/Widgets/LauncherTextWidget.cs b/Launcher2/Gui/Widgets/LauncherTextWidget.cs new file mode 100644 index 000000000..c2f2813e5 --- /dev/null +++ b/Launcher2/Gui/Widgets/LauncherTextWidget.cs @@ -0,0 +1,28 @@ +using System; +using System.Drawing; +using ClassicalSharp; + +namespace Launcher2 { + + public sealed class LauncherTextWidget : LauncherWidget { + + public string Text; + + public LauncherTextWidget( LauncherWindow window ) : base( window ) { + } + + public void DrawAt( IDrawer2D drawer, string text, Font font, + Anchor horAnchor, Anchor verAnchor, int x, int y ) { + Size size = drawer.MeasureSize( text, font, true ); + Width = size.Width; Height = size.Height; + + CalculateOffset( x, y, horAnchor, verAnchor ); + Redraw( drawer, text, font ); + } + + public void Redraw( IDrawer2D drawer, string text, Font font ) { + DrawTextArgs args = new DrawTextArgs( text, true ); + drawer.DrawText( font, ref args, X, Y ); + } + } +} diff --git a/Launcher2/Gui/Widgets/LauncherWidget.cs b/Launcher2/Gui/Widgets/LauncherWidget.cs new file mode 100644 index 000000000..64819dbc0 --- /dev/null +++ b/Launcher2/Gui/Widgets/LauncherWidget.cs @@ -0,0 +1,26 @@ +using System; +using ClassicalSharp; + +namespace Launcher2 { + + public abstract class LauncherWidget { + + public int X, Y, Width, Height; + public LauncherWindow Window; + public Action OnClick; + + public LauncherWidget( LauncherWindow window ) { + Window = window; + } + + protected void CalculateOffset( int x, int y, Anchor horAnchor, Anchor verAnchor ) { + if( horAnchor == Anchor.LeftOrTop ) X = x; + else if( horAnchor == Anchor.Centre ) X = x + Window.Width / 2 - Width / 2; + else if( horAnchor == Anchor.BottomOrRight ) X = x + Window.Width - Width; + + if( verAnchor == Anchor.LeftOrTop ) Y = y; + else if( verAnchor == Anchor.Centre ) Y = y + Window.Height / 2 - Height / 2; + else if( verAnchor == Anchor.BottomOrRight ) Y = y + Window.Height - Height; + } + } +} diff --git a/Launcher2/Launcher2.csproj b/Launcher2/Launcher2.csproj index 403586032..e0bc3e176 100644 --- a/Launcher2/Launcher2.csproj +++ b/Launcher2/Launcher2.csproj @@ -45,16 +45,27 @@ + + - + - + + + + + + + + + + @@ -71,6 +82,8 @@ + + diff --git a/Launcher2/LauncherWindow.cs b/Launcher2/LauncherWindow.cs new file mode 100644 index 000000000..0a038158e --- /dev/null +++ b/Launcher2/LauncherWindow.cs @@ -0,0 +1,110 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Threading; +using ClassicalSharp; +using OpenTK; +using OpenTK.Graphics; +using OpenTK.Platform.Windows; + +namespace Launcher2 { + + public sealed class LauncherWindow { + + public NativeWindow Window; + public IDrawer2D Drawer; + public LauncherScreen screen; + public bool Dirty; + + public int Width { get { return Window.Width; } } + + public int Height { get { return Window.Height; } } + + public Bitmap Framebuffer; + Font logoFont, logoItalicFont; + public void Init() { + Window.Resize += Resize; + Window.FocusedChanged += FocusedChanged; + logoFont = new Font( "Times New Roman", 28, FontStyle.Bold ); + logoItalicFont = new Font( "Times New Roman", 28, FontStyle.Italic ); + } + + void FocusedChanged( object sender, EventArgs e ) { + MakeBackground(); + screen.Resize(); + } + + void Resize( object sender, EventArgs e ) { + MakeBackground(); + screen.Resize(); + } + + public void SetScreen( LauncherScreen screen ) { + if( this.screen != null ) + this.screen.Dispose(); + + MakeBackground(); + this.screen = screen; + screen.Init(); + } + + public void Run() { + Window = new NativeWindow( 480, 480, Program.AppName, 0, + GraphicsMode.Default, DisplayDevice.Default ); + Window.Visible = true; + screen = new MainScreen( this ); + Drawer = new GdiPlusDrawer2D( null ); + Init(); + SetScreen( new MainScreen( this ) ); + + while( true ) { + Window.ProcessEvents(); + if( !Window.Exists ) break; + + if( Dirty || (screen != null && screen.Dirty) ) + Display(); + Thread.Sleep( 1 ); + } + } + + void Display() { + Dirty = false; + if( screen != null ) + screen.Dirty = false; + + WinWindowInfo info = (WinWindowInfo)Window.WindowInfo; + IntPtr dc = info.DeviceContext; + + using( Graphics g = Graphics.FromHdc( dc ) ) + g.DrawImage( Framebuffer, 0, 0, Framebuffer.Width, Framebuffer.Height ); + } + + internal static FastColour clearColour = new FastColour( 30, 30, 30 ); + public void MakeBackground() { + if( Framebuffer != null ) + Framebuffer.Dispose(); + + Framebuffer = new Bitmap( Width, Height ); + using( IDrawer2D drawer = Drawer ) { + drawer.SetBitmap( Framebuffer ); + drawer.Clear( clearColour ); + + Size size1 = drawer.MeasureSize( "&eClassical", logoItalicFont, true ); + Size size2 = drawer.MeasureSize( "&eSharp", logoFont, true ); + int xStart = Width / 2 - (size1.Width + size2.Width ) / 2; + + DrawTextArgs args = new DrawTextArgs( "&eClassical", true ); + drawer.DrawText( logoItalicFont, ref args, xStart, 20 ); + args.Text = "&eSharp"; + drawer.DrawText( logoFont, ref args, xStart + size1.Width, 20 ); + } + Dirty = true; + } + + public void Dispose() { + logoFont.Dispose(); + logoItalicFont.Dispose(); + } + } +} diff --git a/Launcher2/MainScreen.cs b/Launcher2/MainScreen.cs deleted file mode 100644 index b58a26c3f..000000000 --- a/Launcher2/MainScreen.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Drawing; -using ClassicalSharp; -using OpenTK; -using OpenTK.Graphics; -using OpenTK.Platform.Windows; -using OpenTK.Platform; -using OpenTK.Input; - -namespace Launcher2 { - - public sealed class MainScreen { - public IDrawer2D Drawer; - public NativeWindow Window; - - public void Init() { - Window.Resize += HandleResize; - Window.Mouse.Move += MouseMove; - Window.Mouse.ButtonDown += MouseButtonDown; - - logoFont = new Font( "Times New Roman", 28, FontStyle.Bold ); - logoItalicFont = new Font( "Times New Roman", 28, FontStyle.Italic ); - textFont = new Font( "Arial", 16, FontStyle.Bold ); - } - - FastButtonWidget selectedWidget; - void MouseMove( object sender, MouseMoveEventArgs e ) { - //System.Diagnostics.Debug.Print( "moved" ); - for( int i = 0; i < widgets.Length; i++ ) { - FastButtonWidget widget = widgets[i]; - if( e.X >= widget.X && e.Y >= widget.Y && - e.X < widget.X + widget.Width && e.Y < widget.Y + widget.Height ) { - if( selectedWidget != widget && selectedWidget != null ) { - using( IDrawer2D drawer = Drawer ) { - drawer.SetBitmap( background ); - selectedWidget.Redraw( Drawer, selectedWidget.Text, textFont ); - FilterButton( selectedWidget.X, selectedWidget.Y, - selectedWidget.Width, selectedWidget.Height, 180 ); - widget.Redraw( Drawer, widget.Text, textFont ); - } - } - selectedWidget = widget; - break; - } - } - } - - void MouseButtonDown( object sender, MouseButtonEventArgs e ) { - if( e.Button != MouseButton.Left ) return; - - if( selectedWidget != null && selectedWidget.OnClick != null ) - selectedWidget.OnClick(); - } - - public void Display() { - WinWindowInfo info = ((WinWindowInfo)Window.WindowInfo); - IntPtr dc = info.DeviceContext; - - - using( Graphics g = Graphics.FromHdc( dc ) ) { - g.DrawImage( background, 0, 0, background.Width, background.Height ); - } - } - - Bitmap background; - Font textFont, logoFont, logoItalicFont; - static FastColour clearColour = new FastColour( 30, 30, 30 ); - static uint clearColourBGRA = (uint)(new FastColour( 30, 30, 30 ).ToArgb()); - public void RecreateBackground() { - System.Diagnostics.Debug.Print( "DISPLAY" ); - if( background != null ) - background.Dispose(); - - background = new Bitmap( Window.Width, Window.Height ); - using( IDrawer2D drawer = Drawer ) { - drawer.SetBitmap( background ); - drawer.Clear( clearColour ); - - Size size1 = drawer.MeasureSize( "&eClassical", logoItalicFont, true ); - Size size2 = drawer.MeasureSize( "&eSharp", logoFont, true ); - int xStart = Window.Width / 2 - (size1.Width + size2.Width ) / 2; - - DrawTextArgs args = new DrawTextArgs( "&eClassical", true ); - drawer.DrawText( logoItalicFont, ref args, xStart, 20 ); - args.Text = "&eSharp"; - drawer.DrawText( logoFont, ref args, xStart + size1.Width, 20 ); - DrawButtons( drawer ); - } - - logoFont.Dispose(); - logoItalicFont.Dispose(); - } - - static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 ); - void DrawButtons( IDrawer2D drawer ) { - widgetIndex = 0; - MakeButtonAt( drawer, "Direct connect", Anchor.Centre, Anchor.Centre, - buttonWidth, buttonHeight, 0, -100 ); - MakeButtonAt( drawer, "ClassiCube.net", Anchor.Centre, Anchor.Centre, - buttonWidth, buttonHeight, 0, -50 ); - MakeButtonAt( drawer, "Default texture pack", Anchor.Centre, Anchor.Centre, - buttonWidth, buttonHeight, 0, 50 ); - - MakeButtonAt( drawer, "Singleplayer", Anchor.LeftOrTop, Anchor.BottomOrRight, - sideButtonWidth, buttonHeight, 10, -10 ); - widgets[widgetIndex - 1].OnClick - = () => Program.StartClient( "default.zip" ); - MakeButtonAt( drawer, "Resume", Anchor.BottomOrRight, Anchor.BottomOrRight, - sideButtonWidth, buttonHeight, -10, -10 ); - } - - FastButtonWidget[] widgets = new FastButtonWidget[5]; - int widgetIndex = 0; - const int buttonWidth = 220, buttonHeight = 35, sideButtonWidth = 150; - void MakeButtonAt( IDrawer2D drawer, string text, Anchor horAnchor, - Anchor verAnchor, int width, int height, int x, int y ) { - FastButtonWidget widget = new FastButtonWidget(); - widget.Window = Window; - widget.Text = text; - widget.DrawAt( drawer, text, textFont, horAnchor, verAnchor, width, height, x, y ); - FilterButton( widget.X, widget.Y, widget.Width, widget.Height, 180 ); - widgets[widgetIndex++] = widget; - } - - void HandleResize( object sender, EventArgs e ) { - RecreateBackground(); - } - - public void Dispose() { - logoFont.Dispose(); - logoItalicFont.Dispose(); - textFont.Dispose(); - } - - unsafe void FilterButton( int x, int y, int width, int height, byte scale ) { - using( FastBitmap bmp = new FastBitmap( background, true ) ) { - for( int yy = y; yy < y + height; yy++ ) { - int* row = bmp.GetRowPtr( yy ) + x; - for( int xx = 0; xx < width; xx++ ) { - uint pixel = (uint)row[xx]; - if( pixel == clearColourBGRA ) continue; - uint a = pixel & 0xFF000000; - uint r = (pixel >> 16) & 0xFF; - uint g = (pixel >> 8) & 0xFF; - uint b = pixel & 0xFF; - - r = (r * scale) / 255; - g = (g * scale) / 255; - b = (b * scale) / 255; - row[xx] = (int)(a | (r << 16) | (g << 8) | b); - } - } - } - } - } -} diff --git a/Launcher2/Program.cs b/Launcher2/Program.cs index 4231e5381..930b9b265 100644 --- a/Launcher2/Program.cs +++ b/Launcher2/Program.cs @@ -1,9 +1,7 @@ using System; -using ClassicalSharp; -using OpenTK; -using OpenTK.Graphics; -using System.IO; using System.Diagnostics; +using System.IO; +using System.Windows.Forms; namespace Launcher2 { @@ -13,43 +11,43 @@ namespace Launcher2 { [STAThread] private static void Main( string[] args ) { - NativeWindow window = new NativeWindow( 480, 480, AppName, 0, - GraphicsMode.Default, DisplayDevice.Default ); - window.Visible = true; + if( !Debugger.IsAttached ) + AppDomain.CurrentDomain.UnhandledException += UnhandledException; - MainScreen screen = new MainScreen(); - screen.Drawer = new GdiPlusDrawer2D( null ); - screen.Window = window; - screen.Init(); - screen.RecreateBackground(); - - while( true ) { - window.ProcessEvents(); - if( !window.Exists ) break; - - screen.Display(); - System.Threading.Thread.Sleep( 10 ); - } + LauncherWindow window = new LauncherWindow(); + window.Run(); } - static string missingExeMessage = "Failed to start ClassicalSharp. (classicalsharp.exe was not found)" - + Environment.NewLine + Environment.NewLine + - "This application is only the launcher, it is not the actual client. " + - "Please place the launcher in the same directory as the client (classicalsharp.exe)."; + static void UnhandledException( object sender, UnhandledExceptionEventArgs e ) { + // So we don't get the normal unhelpful crash dialog on Windows. + Exception ex = (Exception)e.ExceptionObject; + bool wroteToCrashLog = LogException( ex ); + string error = wroteToCrashLog ? null : + (ex.GetType().FullName + ": " + ex.Message + Environment.NewLine + ex.StackTrace); + + string message = wroteToCrashLog ? + "The cause of the crash has been logged to \"crash-launcher.log\". Please consider reporting the crash " + + "(and the circumstances that caused it) to github.com/UnknownShadow200/ClassicalSharp/issues" : + + "Failed to write the cause of the crash to \"crash-launcher.log\". Please consider reporting the crash " + + "(and the circumstances that caused it) to github.com/UnknownShadow200/ClassicalSharp/issues" + + Environment.NewLine + Environment.NewLine + error; + + MessageBox.Show( "Oh dear. ClassicalSharp has crashed." + Environment.NewLine + Environment.NewLine + message, "ClassicalSharp has crashed" ); + Environment.Exit( 1 ); + } - public static bool StartClient( string args ) { - Process process = null; - - if( !File.Exists( "ClassicalSharp.exe" ) ) { - // TODO: show message popup + internal static bool LogException( Exception ex ) { + string error = ex.GetType().FullName + ": " + ex.Message + Environment.NewLine + ex.StackTrace; + try { + using( StreamWriter writer = new StreamWriter( "crash_launcher.log", true ) ) { + writer.WriteLine( "Crash time: " + DateTime.Now.ToString() ); + writer.WriteLine( error ); + writer.WriteLine(); + } + } catch( Exception ) { return false; } - - if( Type.GetType( "Mono.Runtime" ) != null ) { - process = Process.Start( "mono", "\"ClassicalSharp.exe\" " + args ); - } else { - process = Process.Start( "ClassicalSharp.exe", args ); - } return true; } } diff --git a/Launcher2/Secure.cs b/Launcher2/Secure.cs new file mode 100644 index 000000000..7799d0536 --- /dev/null +++ b/Launcher2/Secure.cs @@ -0,0 +1,52 @@ +using System; +using System.Security.Cryptography; + +namespace Launcher2 { + + public static class Secure { + + public static string Encode( string decoded, string key ) { + if( String.IsNullOrEmpty( decoded ) || String.IsNullOrEmpty( key ) ) return ""; + + byte[] data = new byte[decoded.Length]; + for( int i = 0; i < decoded.Length; i++ ) + data[i] = (byte)decoded[i]; + + try { + byte[] v2 = ProtectedData.Protect( data, null, DataProtectionScope.CurrentUser ); + return Convert.ToBase64String( v2 ); + } catch { + // XORs data. *NOT* very secure, only designed to prevent reading from options.txt. + for( int i = 0; i < data.Length; i++ ) + data[i] = (byte)(data[i] ^ key[i % key.Length] ^ 0x43); + return Convert.ToBase64String( data ); + } + } + + public static string Decode( string encoded, string key ) { + if( String.IsNullOrEmpty( encoded ) || String.IsNullOrEmpty( key ) ) return null; + + byte[] data; + try { + data = Convert.FromBase64String( encoded ); + } catch( FormatException ) { + return null; + } + + try { + data = ProtectedData.Unprotect( data, null, DataProtectionScope.CurrentUser ); + char[] c = new char[data.Length]; + for( int i = 0; i < c.Length; i++ ) + c[i] = (char)data[i]; + return new String( c ); + } catch { + if( encoded.Length > 64 || data.Length > 64 ) return null; + + char[] c = new char[data.Length]; + for( int i = 0; i < c.Length; i++ ) + c[i] = (char)(data[i] ^ key[i % key.Length] ^ 0x43); + return new String( c ); + } + } + } +}