From 1a9b09c7362312ebe1c351a725656af286ca4b81 Mon Sep 17 00:00:00 2001
From: Ian C <ianc@noddybox.co.uk>
Date: Wed, 6 Jun 2012 23:21:05 +0000
Subject: Updated to use new input mechanism.

---
 non-solution-items/button.pspimage                 | Bin 17105 -> 12348 bytes
 non-solution-items/controls.pspimage               | Bin 0 -> 12050 bytes
 non-solution-items/joystick.pspimage               | Bin 28195 -> 23605 bytes
 non-solution-items/joystick_background.pspimage    | Bin 317374 -> 36817 bytes
 non-solution-items/keyboard.pspimage               | Bin 283395 -> 211392 bytes
 non-solution-items/keypad.pspimage                 | Bin 475500 -> 0 bytes
 wpspec.sln                                         |  22 +--
 wpspec/wpspec/App.xaml.cs                          |   5 +
 wpspec/wpspec/Converters/ConvertCamelToHuman.cs    |  12 +-
 wpspec/wpspec/GamePage.xaml.cs                     | 141 ++++++++++++++++----
 wpspec/wpspec/Resources/Spectrum.keyboard          | Bin 1032 -> 921 bytes
 wpspec/wpspec/Resources/Strings.Designer.cs        |  45 +++++++
 wpspec/wpspec/Resources/Strings.resx               |  15 +++
 wpspec/wpspec/Resources/controls.keyboard          | Bin 0 -> 125 bytes
 wpspec/wpspec/Settings.cs                          | 147 ++++++++++++++++-----
 wpspec/wpspec/SettingsPage.xaml                    |  33 ++++-
 wpspec/wpspec/SettingsPage.xaml.cs                 |  14 +-
 wpspec/wpspec/Shared.cs                            |  10 ++
 wpspec/wpspec/Spectrum/Emulation.cs                |   2 +-
 .../wpspec/ViewModels/OpenRemoteTapeViewModel.cs   |   6 +-
 wpspec/wpspec/wpspec.csproj                        |   7 +-
 wpspec/wpspecLibContent/button.png                 | Bin 0 -> 4370 bytes
 wpspec/wpspecLibContent/controls.png               | Bin 0 -> 2518 bytes
 wpspec/wpspecLibContent/joystick.png               | Bin 0 -> 12582 bytes
 wpspec/wpspecLibContent/joystick_background.png    | Bin 0 -> 7401 bytes
 wpspec/wpspecLibContent/keyboard.png               | Bin 27208 -> 3137 bytes
 .../wpspecLibContent/wpspecLibContent.contentproj  |  22 +++
 27 files changed, 389 insertions(+), 92 deletions(-)
 create mode 100644 non-solution-items/controls.pspimage
 delete mode 100644 non-solution-items/keypad.pspimage
 create mode 100644 wpspec/wpspec/Resources/controls.keyboard
 create mode 100644 wpspec/wpspecLibContent/button.png
 create mode 100644 wpspec/wpspecLibContent/controls.png
 create mode 100644 wpspec/wpspecLibContent/joystick.png
 create mode 100644 wpspec/wpspecLibContent/joystick_background.png

diff --git a/non-solution-items/button.pspimage b/non-solution-items/button.pspimage
index 8d52f85..34153c6 100644
Binary files a/non-solution-items/button.pspimage and b/non-solution-items/button.pspimage differ
diff --git a/non-solution-items/controls.pspimage b/non-solution-items/controls.pspimage
new file mode 100644
index 0000000..17a2ec7
Binary files /dev/null and b/non-solution-items/controls.pspimage differ
diff --git a/non-solution-items/joystick.pspimage b/non-solution-items/joystick.pspimage
index f26ed29..681f71b 100644
Binary files a/non-solution-items/joystick.pspimage and b/non-solution-items/joystick.pspimage differ
diff --git a/non-solution-items/joystick_background.pspimage b/non-solution-items/joystick_background.pspimage
index c6ce15d..2fb2861 100644
Binary files a/non-solution-items/joystick_background.pspimage and b/non-solution-items/joystick_background.pspimage differ
diff --git a/non-solution-items/keyboard.pspimage b/non-solution-items/keyboard.pspimage
index 90cc1cc..ae87976 100644
Binary files a/non-solution-items/keyboard.pspimage and b/non-solution-items/keyboard.pspimage differ
diff --git a/non-solution-items/keypad.pspimage b/non-solution-items/keypad.pspimage
deleted file mode 100644
index 29a3003..0000000
Binary files a/non-solution-items/keypad.pspimage and /dev/null differ
diff --git a/wpspec.sln b/wpspec.sln
index ab33399..9bc05bf 100644
--- a/wpspec.sln
+++ b/wpspec.sln
@@ -17,7 +17,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Noddybox.Emulation.EightBit
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Noddybox.Emulation.Keyboard.Schema", "..\EmulationKeyboard\WindowsPhone\Noddybox.Emulation.Keyboard.Schema\Noddybox.Emulation.Keyboard.Schema.csproj", "{0F5AA96A-9253-4E06-A3F9-5517A2A9C558}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Noddybox.Emulation.Xna.Keyboard", "..\EmulationKeyboard\WindowsPhone\Noddybox.Emulation.Xna.Keyboard\Noddybox.Emulation.Xna.Keyboard.csproj", "{3B034AAA-A9FD-4307-95F2-D87BE0A51990}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Noddybox.Emulation.Xna.Input", "..\EmulationKeyboard\WindowsPhone\Noddybox.Emulation.Xna.Input\Noddybox.Emulation.Xna.Input.csproj", "{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -109,16 +109,16 @@ Global
 		{0F5AA96A-9253-4E06-A3F9-5517A2A9C558}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
 		{0F5AA96A-9253-4E06-A3F9-5517A2A9C558}.Release|Mixed Platforms.Build.0 = Release|Any CPU
 		{0F5AA96A-9253-4E06-A3F9-5517A2A9C558}.Release|Windows Phone.ActiveCfg = Release|Any CPU
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Debug|Any CPU.ActiveCfg = Debug|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Debug|Mixed Platforms.ActiveCfg = Debug|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Debug|Mixed Platforms.Build.0 = Debug|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Debug|Windows Phone.ActiveCfg = Debug|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Debug|Windows Phone.Build.0 = Debug|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Release|Any CPU.ActiveCfg = Release|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Release|Mixed Platforms.ActiveCfg = Release|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Release|Mixed Platforms.Build.0 = Release|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Release|Windows Phone.ActiveCfg = Release|Windows Phone
-		{3B034AAA-A9FD-4307-95F2-D87BE0A51990}.Release|Windows Phone.Build.0 = Release|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Debug|Any CPU.ActiveCfg = Debug|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Debug|Mixed Platforms.ActiveCfg = Debug|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Debug|Mixed Platforms.Build.0 = Debug|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Debug|Windows Phone.ActiveCfg = Debug|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Debug|Windows Phone.Build.0 = Debug|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Release|Any CPU.ActiveCfg = Release|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Release|Mixed Platforms.ActiveCfg = Release|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Release|Mixed Platforms.Build.0 = Release|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Release|Windows Phone.ActiveCfg = Release|Windows Phone
+		{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}.Release|Windows Phone.Build.0 = Release|Windows Phone
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/wpspec/wpspec/App.xaml.cs b/wpspec/wpspec/App.xaml.cs
index 5edf742..70a17b4 100644
--- a/wpspec/wpspec/App.xaml.cs
+++ b/wpspec/wpspec/App.xaml.cs
@@ -92,6 +92,11 @@ namespace wpspec
             {
                 Shared.SpectrumKeyboard = KeyboardDefinition.Load(stream);
             }
+
+            using (BinaryReader stream = new BinaryReader(App.GetResourceStream(new Uri("Resources/controls.keyboard", UriKind.Relative)).Stream))
+            {
+                Shared.ControlKeyboard = KeyboardDefinition.Load(stream);
+            }
         }
 
         // Code to execute when the application is activated (brought to foreground)
diff --git a/wpspec/wpspec/Converters/ConvertCamelToHuman.cs b/wpspec/wpspec/Converters/ConvertCamelToHuman.cs
index 3d9f5c6..dae96b6 100644
--- a/wpspec/wpspec/Converters/ConvertCamelToHuman.cs
+++ b/wpspec/wpspec/Converters/ConvertCamelToHuman.cs
@@ -38,7 +38,17 @@ namespace wpspec.Converters
 
         public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
-            throw new NotImplementedException();
+            string s = String.Empty;
+
+            foreach (char c in value.ToString())
+            {
+                if (c != ' ')
+                {
+                    s += c;
+                }
+            }
+
+            return Enum.Parse(targetType, s, false);
         }
 
         #endregion
diff --git a/wpspec/wpspec/GamePage.xaml.cs b/wpspec/wpspec/GamePage.xaml.cs
index c20fde4..b6ca5a1 100644
--- a/wpspec/wpspec/GamePage.xaml.cs
+++ b/wpspec/wpspec/GamePage.xaml.cs
@@ -23,7 +23,9 @@ using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework.Content;
 using Microsoft.Xna.Framework.Graphics;
 using wpspec.Spectrum;
-using Noddybox.Emulation.Xna.Keyboard;
+using Noddybox.Emulation.Xna.Input;
+using Noddybox.Emulation.Xna.Input.Keyboard;
+using Noddybox.Emulation.Xna.Input.Joystick;
 using Microsoft.Xna.Framework.Input.Touch;
 
 namespace wpspec
@@ -32,15 +34,30 @@ namespace wpspec
     {
         #region Private Data
 
+        private enum ControlKeySymbol
+        {
+            KeyReset, KeyTurbo, KeyJoystick, KeyKeyboard
+        }
+
         private ContentManager contentManager;
         private GameTimer timer;
         private SpriteBatch spriteBatch;
         private Texture2D screen;
         private Vector2 location;
         private Rectangle locationZoomed;
+
         private Texture2D keyboardImage;
+        private Texture2D controlImage;
+        private Texture2D joystickBackgroundImage;
+        private Texture2D joystickButtonImage;
+        private Texture2D joystickImage;
+
+        private InputManager inputManager;
         private KeyboardDriver<SpectrumKeySymbol> keyboard;
+        private KeyboardDriver<ControlKeySymbol> control;
+        private JoystickDriver joystick;
         private bool resetPressed;
+        private bool turbo;
 
         #endregion
 
@@ -101,20 +118,59 @@ namespace wpspec
             //
             Shared.Spectrum.ResetKeyboard();
 
+            // Create a keyboard driver
+            //
             // Create a keyboard driver
             //
             resetPressed = false;
 
+            inputManager = new InputManager();
+
             keyboardImage = contentManager.Load<Texture2D>("keyboard");
+            controlImage = contentManager.Load<Texture2D>("controls");
+            joystickBackgroundImage = contentManager.Load<Texture2D>("joystick_background");
+            joystickImage = contentManager.Load<Texture2D>("joystick");
+            joystickButtonImage = contentManager.Load<Texture2D>("button");
 
             keyboard = new KeyboardDriver<SpectrumKeySymbol>
-                                (SharedGraphicsDeviceManager.Current.GraphicsDevice,
-                                keyboardImage,
-                                new Vector2(0, 470),
-                                Shared.SpectrumKeyboard);
+                                (inputManager,
+                                 SharedGraphicsDeviceManager.Current.GraphicsDevice,
+                                 keyboardImage,
+                                 new Vector2(0, 490),
+                                 Shared.SpectrumKeyboard);
 
             keyboard.KeyEvent += HandleKeyboardEvent;
-            keyboard.TouchEvent += HandleTouchEvent;
+
+            control = new KeyboardDriver<ControlKeySymbol>
+                                (inputManager,
+                                 SharedGraphicsDeviceManager.Current.GraphicsDevice,
+                                 controlImage,
+                                 new Vector2(0, 450),
+                                 Shared.ControlKeyboard);
+
+            control.KeyEvent += HandleControlEvent;
+
+            joystick = new JoystickDriver(inputManager,
+                                          SharedGraphicsDeviceManager.Current.GraphicsDevice,
+                                          JoystickType.Digital,
+                                          joystickBackgroundImage, joystickImage, joystickButtonImage,
+                                          new Vector2(0, 490),
+                                          new Vector2(125),
+                                          new Vector2[2] {new Vector2(280, 100), new Vector2(380, 125)},
+                                          20, 75);
+
+            joystick.DigitalEvent += HandleJoystickEvent;
+
+            if (Shared.IsJoystickDisplayed)
+            {
+                keyboard.StopKeyboardUpdates();
+            }
+            else
+            {
+                joystick.StopJoystickUpdates();
+            }
+
+            inputManager.TouchEvent += HandleTouchEvent;
 
             // Initialise the sound manager
             //
@@ -136,11 +192,21 @@ namespace wpspec
             screen.Dispose();
             screen = null;
 
-            // Remove the keyboard
+            // Remove the inputs
             //
+            keyboard.StopKeyboardUpdates();
+            joystick.StopJoystickUpdates();
+            control.StopKeyboardUpdates();
+
             keyboard.KeyEvent -= HandleKeyboardEvent;
-            keyboard.TouchEvent -= HandleTouchEvent;
+            control.KeyEvent -= HandleControlEvent;
+            joystick.DigitalEvent -= HandleJoystickEvent;
+            inputManager.TouchEvent -= HandleTouchEvent;
+
             keyboard = null;
+            control = null;
+            joystick = null;
+            inputManager = null;
 
             // Stop the sound manager
             //
@@ -158,7 +224,7 @@ namespace wpspec
         /// </summary>
         private void OnUpdate(object sender, GameTimerEventArgs e)
         {
-            keyboard.Update();
+            inputManager.Update();
             Shared.Spectrum.Run();
         }
 
@@ -191,11 +257,11 @@ namespace wpspec
 
         #region Keyboard driver events
 
-        private void HandleKeyboardEvent(object sender, KeyboardDriver<SpectrumKeySymbol>.KeyPressEventArgs e)
+        private void HandleControlEvent(object sender, KeyboardDriver<ControlKeySymbol>.KeyPressEventArgs e)
         {
             switch (e.Key)
             {
-                case SpectrumKeySymbol.KeyReset:
+                case ControlKeySymbol.KeyReset:
                     if (!e.Pressed)
                     {
                         if (resetPressed)
@@ -210,36 +276,65 @@ namespace wpspec
                     }
                     break;
 
-                case SpectrumKeySymbol.KeyJoystick:
+                case ControlKeySymbol.KeyTurbo:
+                    resetPressed = false;
+                    turbo = e.Pressed;
                     break;
 
-                case SpectrumKeySymbol.KeyKeypad:
-                    break;
+                case ControlKeySymbol.KeyJoystick:
+                    resetPressed = false;
 
-                case SpectrumKeySymbol.KeyKeyboard:
-                    break;
-
-                default:
-                    if (e.Pressed)
+                    if (!Shared.IsJoystickDisplayed)
                     {
-                        Shared.Spectrum.KeyPressed(e.Key);
+                        joystick.StartJoystickUpdates();
+                        keyboard.StopKeyboardUpdates();
+                        Shared.IsJoystickDisplayed = true;
                     }
-                    else
+                    break;
+
+                case ControlKeySymbol.KeyKeyboard:
+                    resetPressed = false;
+
+                    if (Shared.IsJoystickDisplayed)
                     {
-                        Shared.Spectrum.KeyReleased(e.Key);
+                        joystick.StopJoystickUpdates();
+                        keyboard.StartKeyboardUpdates();
+                        Shared.IsJoystickDisplayed = false;
                     }
                     break;
                 }
         }
 
-        private void HandleTouchEvent(object sender, KeyboardDriver<SpectrumKeySymbol>.TouchLocationEventArgs e)
+        private void HandleKeyboardEvent(object sender, KeyboardDriver<SpectrumKeySymbol>.KeyPressEventArgs e)
+        {
+            resetPressed = false;
+
+            if (e.Pressed)
+            {
+                Shared.Spectrum.KeyPressed(e.Key);
+            }
+            else
+            {
+                Shared.Spectrum.KeyReleased(e.Key);
+            }
+        }
+
+        private void HandleTouchEvent(object sender, InputManager.TouchLocationEventArgs e)
         {
+            resetPressed = false;
+
             if (e.Location.State == TouchLocationState.Released && e.Location.Position.Y < 400)
             {
+                e.Handled = true;
                 Shared.IsZoomed = !Shared.IsZoomed;
             }
         }
 
+        private void HandleJoystickEvent(object sender, JoystickDriver.DigitalJoystickEventArgs e)
+        {
+            resetPressed = false;
+        }
+
         #endregion
     }
 }
\ No newline at end of file
diff --git a/wpspec/wpspec/Resources/Spectrum.keyboard b/wpspec/wpspec/Resources/Spectrum.keyboard
index 7dcd95a..edc5d06 100644
Binary files a/wpspec/wpspec/Resources/Spectrum.keyboard and b/wpspec/wpspec/Resources/Spectrum.keyboard differ
diff --git a/wpspec/wpspec/Resources/Strings.Designer.cs b/wpspec/wpspec/Resources/Strings.Designer.cs
index 36ad103..17a18c4 100644
--- a/wpspec/wpspec/Resources/Strings.Designer.cs
+++ b/wpspec/wpspec/Resources/Strings.Designer.cs
@@ -379,6 +379,24 @@ namespace wpspec.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Cursor Joystick Enabled.
+        /// </summary>
+        public static string SettingsCursorJoystick {
+            get {
+                return ResourceManager.GetString("SettingsCursorJoystick", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Fuller Joystick Enabled.
+        /// </summary>
+        public static string SettingsFullerJoystick {
+            get {
+                return ResourceManager.GetString("SettingsFullerJoystick", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Ignore MIME type of tape files.
         /// </summary>
@@ -388,6 +406,33 @@ namespace wpspec.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Joystick Lock.
+        /// </summary>
+        public static string SettingsJoystickLock {
+            get {
+                return ResourceManager.GetString("SettingsJoystickLock", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Kempston Joystick Enabled.
+        /// </summary>
+        public static string SettingsKempstonJoystick {
+            get {
+                return ResourceManager.GetString("SettingsKempstonJoystick", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Miner Willy Joystick.
+        /// </summary>
+        public static string SettingsManicJoystick {
+            get {
+                return ResourceManager.GetString("SettingsManicJoystick", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Settings.
         /// </summary>
diff --git a/wpspec/wpspec/Resources/Strings.resx b/wpspec/wpspec/Resources/Strings.resx
index fab77d2..edd0bd0 100644
--- a/wpspec/wpspec/Resources/Strings.resx
+++ b/wpspec/wpspec/Resources/Strings.resx
@@ -250,4 +250,19 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
   <data name="DisplayTapeTitleText" xml:space="preserve">
     <value>Tape Contents</value>
   </data>
+  <data name="SettingsCursorJoystick" xml:space="preserve">
+    <value>Cursor Joystick Enabled</value>
+  </data>
+  <data name="SettingsFullerJoystick" xml:space="preserve">
+    <value>Fuller Joystick Enabled</value>
+  </data>
+  <data name="SettingsJoystickLock" xml:space="preserve">
+    <value>Joystick Lock</value>
+  </data>
+  <data name="SettingsManicJoystick" xml:space="preserve">
+    <value>Miner Willy Joystick</value>
+  </data>
+  <data name="SettingsKempstonJoystick" xml:space="preserve">
+    <value>Kempston Joystick Enabled</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/wpspec/wpspec/Resources/controls.keyboard b/wpspec/wpspec/Resources/controls.keyboard
new file mode 100644
index 0000000..5777cc5
Binary files /dev/null and b/wpspec/wpspec/Resources/controls.keyboard differ
diff --git a/wpspec/wpspec/Settings.cs b/wpspec/wpspec/Settings.cs
index 1ddecbf..5c7f5ef 100644
--- a/wpspec/wpspec/Settings.cs
+++ b/wpspec/wpspec/Settings.cs
@@ -10,74 +10,151 @@ using System.Windows.Media.Animation;
 using System.Windows.Shapes;
 using System.IO.IsolatedStorage;
 using System.Collections.Generic;
+using Noddybox.Emulation.Xna.Input.Joystick;
 
 namespace wpspec
 {
     /// <summary>
     /// Holds global settings
     /// </summary>
-    public static class Settings
+    public class Settings
     {
         /// <summary>
         /// Get/set whether sound is enabled.
         /// </summary>
-        public static bool Sound
+        public bool Sound
         {
-            get {return sound;}
-            set
-            {
-                if (sound != value)
-                {
-                    sound = value;
-                    Save("sound", sound);
-                }
-            }
+            get {return sound.Setting;}
+            set {sound.Setting = value;}
         }
 
         /// <summary>
         /// Get/set URL to load tapes from.
         /// </summary>
-        public static string TapeUrl
+        public string TapeUrl
         {
-            get {return tapeUrl;}
-            set
-            {
-                if (tapeUrl != value)
-                {
-                    tapeUrl = value;
-                    Save("tapeurl", tapeUrl);
-                }
-            }
+            get {return tapeUrl.Setting;}
+            set {tapeUrl.Setting = value;}
+        }
+
+        /// <summary>
+        /// Get/set whether Kempston joystick emulation is enabled.
+        /// </summary>
+        public bool KempstonJoystick
+        {
+            get {return kempstonJoystick.Setting;}
+            set {kempstonJoystick.Setting = value;}
+        }
+
+        /// <summary>
+        /// Get/set whether Fuller joystick emulation is enabled.
+        /// </summary>
+        public bool FullerJoystick
+        {
+            get {return fullerJoystick.Setting;}
+            set {fullerJoystick.Setting = value;}
+        }
+
+        /// <summary>
+        /// Get/set whether cursor joystick emulation is enabled.
+        /// </summary>
+        public bool CursorJoystick
+        {
+            get {return cursorJoystick.Setting;}
+            set {cursorJoystick.Setting = value;}
+        }
+
+        /// <summary>
+        /// Get/set whether joystick emulation for pressing Manic Miner-style keys is enabled.
+        /// </summary>
+        public bool ManicJoystick
+        {
+            get {return manicJoystick.Setting;}
+            set {manicJoystick.Setting = value;}
         }
 
+        /// <summary>
+        /// Get/set the joystick lock type.
+        /// </summary>
+        public JoystickLock JoystickLock
+        {
+            get {return joystickLock.Setting;}
+            set {joystickLock.Setting = value;}
+        }
+
+        /// <summary>
+        /// Get singleton instance of this class.
+        /// </summary>
+        public static Settings Instance
+        {
+            get {return instance;}
+        }
 
         #region Private code
 
-        private static bool sound = Load("sound", true);
+        private static Settings instance = new Settings();
+
+        private Settings()
+        {
+            sound = new SettingValue<bool>("sound", true);
+            kempstonJoystick = new SettingValue<bool>("kempstonJoystick", true);
+            fullerJoystick = new SettingValue<bool>("fullerJoystick", true);
+            cursorJoystick = new SettingValue<bool>("cursorJoystick", false);
+            manicJoystick = new SettingValue<bool>("manicJoystick", false);
+            joystickLock = new SettingValue<JoystickLock>("joystickLock", JoystickLock.EightWay);
+
 #if DEBUG
-        private static string tapeUrl = Load("tapeurl", "http://noddybox.co.uk/spec");
+            tapeUrl = new SettingValue<string>("tapeurl", "http://noddybox.co.uk/spec");
 #else
-        private static string tapeUrl = Load("tapeurl", "http://enter-a-url.here/possible-path");
+            tapeUrl = new SettingValue<string>("tapeurl", "http://enter-a-url.here/possible-path");
 #endif
+        }
 
-        private static T Load<T>(string name, T defaultValue)
+        private class SettingValue<T>
         {
-            try
+            private string name;
+            private T setting;
+
+            public T Setting
             {
-                return (T)IsolatedStorageSettings.ApplicationSettings[name];
+                get
+                {
+                    return setting;
+                }
+
+                set
+                {
+                    if (!value.Equals(setting))
+                    {
+                        setting = value;
+                        IsolatedStorageSettings.ApplicationSettings.Add(name, setting);
+                        IsolatedStorageSettings.ApplicationSettings.Save();
+                    }
+                }
             }
-            catch (KeyNotFoundException)
+
+            public SettingValue(string name, T defaultValue)
             {
-            }
+                this.name = name;
 
-            return defaultValue;
+                try
+                {
+                    setting = (T)IsolatedStorageSettings.ApplicationSettings[name];
+                }
+                catch (KeyNotFoundException)
+                {
+                    setting = defaultValue;
+                }
+            }
         }
 
-        private static void Save<T>(string name, T value)
-        {
-            IsolatedStorageSettings.ApplicationSettings.Add(name, value);
-            IsolatedStorageSettings.ApplicationSettings.Save();
-        }
+        private SettingValue<bool> sound;
+        private SettingValue<bool> kempstonJoystick;
+        private SettingValue<bool> fullerJoystick;
+        private SettingValue<bool> cursorJoystick;
+        private SettingValue<bool> manicJoystick;
+        private SettingValue<JoystickLock> joystickLock;
+        private SettingValue<string> tapeUrl;
 
         #endregion
     }
diff --git a/wpspec/wpspec/SettingsPage.xaml b/wpspec/wpspec/SettingsPage.xaml
index 296efa3..8f3ab9c 100644
--- a/wpspec/wpspec/SettingsPage.xaml
+++ b/wpspec/wpspec/SettingsPage.xaml
@@ -7,6 +7,7 @@
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:res="clr-namespace:wpspec.Resources"
+    xmlns:conv="clr-namespace:wpspec.Converters"
     FontFamily="{StaticResource PhoneFontFamilyNormal}"
     FontSize="{StaticResource PhoneFontSizeNormal}"
     Foreground="{StaticResource PhoneForegroundBrush}"
@@ -16,6 +17,7 @@
 
     <phone:PhoneApplicationPage.Resources>
         <res:Strings x:Key="Strings" />
+        <conv:ConvertCamelToHuman x:Key="CamelToHuman" />
     </phone:PhoneApplicationPage.Resources>
 
     <!--LayoutRoot is the root grid where all page content is placed-->
@@ -38,18 +40,45 @@
         <!--ContentPanel - place additional content here-->
         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
             <Grid.RowDefinitions>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
+                <RowDefinition Height="Auto"/>
                 <RowDefinition Height="Auto"/>
                 <RowDefinition Height="Auto"/>
                 <RowDefinition Height="Auto"/>
                 <RowDefinition Height="*"/>
             </Grid.RowDefinitions>
-            <CheckBox IsChecked="{Binding SoundSetting, Mode=TwoWay}"
+            <CheckBox IsChecked="{Binding Sound, Mode=TwoWay}"
                       Content="{Binding SettingsSoundText, Source={StaticResource Strings}}"
                       Grid.Row="0" />
             <TextBlock Text="{Binding SettingsTapeUrlText, Source={StaticResource Strings}}"
                        Grid.Row="1" />
-            <TextBox Text="{Binding TapeUrlSetting, Mode=TwoWay}"
+            <TextBox Text="{Binding TapeUrl, Mode=TwoWay}"
                      Grid.Row="2" />
+            <CheckBox IsChecked="{Binding KempstonJoystick, Mode=TwoWay}"
+                      Content="{Binding SettingsKempstonJoystick, Source={StaticResource Strings}}"
+                      Grid.Row="3" />
+            <CheckBox IsChecked="{Binding FullerJoystick, Mode=TwoWay}"
+                      Content="{Binding SettingsFullerJoystick, Source={StaticResource Strings}}"
+                      Grid.Row="4" />
+            <CheckBox IsChecked="{Binding CursorJoystick, Mode=TwoWay}"
+                      Content="{Binding SettingsCursorJoystick, Source={StaticResource Strings}}"
+                      Grid.Row="5" />
+            <CheckBox IsChecked="{Binding ManicJoystick, Mode=TwoWay}"
+                      Content="{Binding SettingsManicJoystick, Source={StaticResource Strings}}"
+                      Grid.Row="6" />
+            <TextBlock Text="{Binding SettingsJoystickLock, Source={StaticResource Strings}}"
+                       Grid.Row="7" />
+            <ListBox SelectedItem="{Binding JoystickLock, Mode=TwoWay, Converter={StaticResource CamelToHuman}}"
+                     Grid.Row="8">
+                <ListBoxItem>Eight Way</ListBoxItem>
+                <ListBoxItem>Four Way</ListBoxItem>
+                <ListBoxItem>Two Way Horizontal</ListBoxItem>
+                <ListBoxItem>Two Way Vertical</ListBoxItem>
+            </ListBox>
         </Grid>
     </Grid>
 </phone:PhoneApplicationPage>
diff --git a/wpspec/wpspec/SettingsPage.xaml.cs b/wpspec/wpspec/SettingsPage.xaml.cs
index b64c3a9..301bcf8 100644
--- a/wpspec/wpspec/SettingsPage.xaml.cs
+++ b/wpspec/wpspec/SettingsPage.xaml.cs
@@ -18,19 +18,7 @@ namespace wpspec
         public SettingsPage()
         {
             InitializeComponent();
-            ContentPanel.DataContext = this;
-        }
-
-        public bool SoundSetting
-        {
-            get {return Settings.Sound;}
-            set {Settings.Sound = value;}
-        }
-
-        public string TapeUrlSetting
-        {
-            get {return Settings.TapeUrl;}
-            set {Settings.TapeUrl = value;}
+            DataContext = Settings.Instance;
         }
     }
 }
\ No newline at end of file
diff --git a/wpspec/wpspec/Shared.cs b/wpspec/wpspec/Shared.cs
index 193d977..38c391b 100644
--- a/wpspec/wpspec/Shared.cs
+++ b/wpspec/wpspec/Shared.cs
@@ -19,9 +19,19 @@ namespace wpspec
         /// </summary>
         public static bool IsZoomed {get; set;}
 
+        /// <summary>
+        /// Whether the joystick is displayed.
+        /// </summary>
+        public static bool IsJoystickDisplayed {get; set;}
+
         /// <summary>
         /// The layout for the spectrum keyboard.
         /// </summary>
         public static KeyboardDefinition SpectrumKeyboard {get; set;}
+
+        /// <summary>
+        /// The layout for the control keyboard.
+        /// </summary>
+        public static KeyboardDefinition ControlKeyboard {get; set;}
     }
 }
diff --git a/wpspec/wpspec/Spectrum/Emulation.cs b/wpspec/wpspec/Spectrum/Emulation.cs
index b6fa03a..c436aab 100644
--- a/wpspec/wpspec/Spectrum/Emulation.cs
+++ b/wpspec/wpspec/Spectrum/Emulation.cs
@@ -158,7 +158,7 @@ namespace wpspec.Spectrum
             {
                 if (soundPlayed)
                 {
-                    if (Settings.Sound)
+                    if (Settings.Instance.Sound)
                     {
                         SoundManager.Play(soundBuffer);
                     }
diff --git a/wpspec/wpspec/ViewModels/OpenRemoteTapeViewModel.cs b/wpspec/wpspec/ViewModels/OpenRemoteTapeViewModel.cs
index 0066f00..f842cc0 100644
--- a/wpspec/wpspec/ViewModels/OpenRemoteTapeViewModel.cs
+++ b/wpspec/wpspec/ViewModels/OpenRemoteTapeViewModel.cs
@@ -53,13 +53,13 @@ namespace wpspec.ViewModels
 
         private Uri FormatUrl()
         {
-            if (Settings.TapeUrl.EndsWith("/"))
+            if (Settings.Instance.TapeUrl.EndsWith("/"))
             {
-                return new Uri(String.Format("{0}{1}", Settings.TapeUrl, TapeName));
+                return new Uri(String.Format("{0}{1}", Settings.Instance.TapeUrl, TapeName));
             }
             else
             {
-                return new Uri(String.Format("{0}/{1}", Settings.TapeUrl, TapeName));
+                return new Uri(String.Format("{0}/{1}", Settings.Instance.TapeUrl, TapeName));
             }
         }
 
diff --git a/wpspec/wpspec/wpspec.csproj b/wpspec/wpspec/wpspec.csproj
index 870d5c3..30498a9 100644
--- a/wpspec/wpspec/wpspec.csproj
+++ b/wpspec/wpspec/wpspec.csproj
@@ -184,6 +184,7 @@
       <SubType>Designer</SubType>
     </None>
     <Content Include="Resources\Spectrum.keyboard" />
+    <Content Include="Resources\controls.keyboard" />
   </ItemGroup>
   <ItemGroup>
     <Content Include="ApplicationIcon.png">
@@ -200,9 +201,9 @@
       <Project>{0F5AA96A-9253-4E06-A3F9-5517A2A9C558}</Project>
       <Name>Noddybox.Emulation.Keyboard.Schema</Name>
     </ProjectReference>
-    <ProjectReference Include="..\..\..\EmulationKeyboard\WindowsPhone\Noddybox.Emulation.Xna.Keyboard\Noddybox.Emulation.Xna.Keyboard.csproj">
-      <Project>{3B034AAA-A9FD-4307-95F2-D87BE0A51990}</Project>
-      <Name>Noddybox.Emulation.Xna.Keyboard</Name>
+    <ProjectReference Include="..\..\..\EmulationKeyboard\WindowsPhone\Noddybox.Emulation.Xna.Input\Noddybox.Emulation.Xna.Input.csproj">
+      <Project>{0F95CAB2-B79A-45B5-B5F2-14F4CED2EF20}</Project>
+      <Name>Noddybox.Emulation.Xna.Input</Name>
     </ProjectReference>
     <ProjectReference Include="..\..\..\Noddybox.Emulation\WindowsPhone\Noddybox.Emulation.EightBit.Z80.Disassembler\Noddybox.Emulation.EightBit.Z80.Disassembler.csproj">
       <Project>{08D7120E-3D84-49D2-B73D-255E5DB9655C}</Project>
diff --git a/wpspec/wpspecLibContent/button.png b/wpspec/wpspecLibContent/button.png
new file mode 100644
index 0000000..5b500e9
Binary files /dev/null and b/wpspec/wpspecLibContent/button.png differ
diff --git a/wpspec/wpspecLibContent/controls.png b/wpspec/wpspecLibContent/controls.png
new file mode 100644
index 0000000..f71f97a
Binary files /dev/null and b/wpspec/wpspecLibContent/controls.png differ
diff --git a/wpspec/wpspecLibContent/joystick.png b/wpspec/wpspecLibContent/joystick.png
new file mode 100644
index 0000000..be34d29
Binary files /dev/null and b/wpspec/wpspecLibContent/joystick.png differ
diff --git a/wpspec/wpspecLibContent/joystick_background.png b/wpspec/wpspecLibContent/joystick_background.png
new file mode 100644
index 0000000..1b73485
Binary files /dev/null and b/wpspec/wpspecLibContent/joystick_background.png differ
diff --git a/wpspec/wpspecLibContent/keyboard.png b/wpspec/wpspecLibContent/keyboard.png
index 7657f3d..3ee2be2 100644
Binary files a/wpspec/wpspecLibContent/keyboard.png and b/wpspec/wpspecLibContent/keyboard.png differ
diff --git a/wpspec/wpspecLibContent/wpspecLibContent.contentproj b/wpspec/wpspecLibContent/wpspecLibContent.contentproj
index c0e0454..d911f03 100644
--- a/wpspec/wpspecLibContent/wpspecLibContent.contentproj
+++ b/wpspec/wpspecLibContent/wpspecLibContent.contentproj
@@ -48,6 +48,28 @@
       <Processor>TextureProcessor</Processor>
     </Compile>
   </ItemGroup>
+  <ItemGroup>
+    <Compile Include="button.png">
+      <Name>button</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="controls.png">
+      <Name>controls</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="joystick.png">
+      <Name>joystick</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+    <Compile Include="joystick_background.png">
+      <Name>joystick_background</Name>
+      <Importer>TextureImporter</Importer>
+      <Processor>TextureProcessor</Processor>
+    </Compile>
+  </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
   <!--  To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
-- 
cgit v1.2.3