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 );
+ }
+ }
+ }
+}