Initial commit.
Initial commit.

file:b/Icon.xcf (new)
 Binary files /dev/null and b/Icon.xcf differ
--- /dev/null
+++ b/ScreenshotManager.cs
@@ -1,1 +1,509 @@
-
+// ScreenshotManager
+//
+// ScreenshotManager.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using KSP;
+using System;
+using System.Text.RegularExpressions;
+using ToadicusTools;
+using UnityEngine;
+
+[KSPAddon(KSPAddon.Startup.EveryScene, false)]
+public class ScreenshotManager : MonoBehaviour
+{
+	#region Fields
+
+	private ApplicationLauncherButton appLauncherButton;
+	private KeyCode realScreenshotKey;
+	private ScreenShotFormat ssFormat;
+	private string ssPath;
+	private Regex ssRegex;
+	private IButton toolbarButton;
+	private bool showWindow;
+	private Rect windowPosition;
+	private bool windowVisible;
+
+	private uint zoomFactor;
+
+	private KSP.IO.PluginConfiguration _config;
+	private Vector2i _screenRes;
+
+	#endregion
+
+	#region Properties
+
+	private KSP.IO.PluginConfiguration Config
+	{
+		get
+		{
+			if (this._config == null)
+			{
+				this._config = KSP.IO.PluginConfiguration.CreateForType<ScreenshotManager>();
+			}
+
+			return this._config;
+		}
+	}
+
+	private Vector2i ScreenshotRes
+	{
+		get
+		{
+			if (this._screenRes == default(Vector2i))
+			{
+				this._screenRes = new Vector2i();
+			}
+			this._screenRes.x = Screen.width;
+			this._screenRes.y = Screen.height;
+
+			return this._screenRes * this.zoomFactor;
+		}
+	}
+
+	private TextureFormat TexFormat
+	{
+		get
+		{
+			return TextureFormat.RGB24;
+		}
+	}
+
+	#endregion
+
+	#region Monobehaviour Lifecycle Methods
+
+	private void Awake()
+	{
+		// TODO: Implement configurable zoom factor.  This might not really be plausible.
+		this.zoomFactor = 1;
+
+		this.realScreenshotKey = KeyCode.None;
+
+		this.ssPath = System.IO.Path.Combine(KSPUtil.ApplicationRootPath, "Screenshots");
+
+		this.ssRegex = new Regex(@"[sS]creenshot(?<num>[0-9]+)\..*");
+
+		GameEvents.onGameSceneLoadRequested.Add(this.OnSceneChangeRequested);
+
+		this.Config.load();
+
+		string formatString = this.Config.GetValue("Format", "PNG");
+
+		switch (formatString.ToLower())
+		{
+			case "jpg":
+				this.ssFormat = ScreenShotFormat.JPG;
+				break;
+			case "png":
+			default:
+				this.ssFormat = ScreenShotFormat.PNG;
+				break;
+		}
+
+		string keyCodeString = this.Config.GetValue("TAKE_SCREENSHOT", Enum.GetName(typeof(KeyCode), KeyCode.F1)).Trim();
+		this.realScreenshotKey = (KeyCode)Enum.Parse(typeof(KeyCode), keyCodeString);
+
+		this.windowPosition = this.Config.GetValue("windowPosition",
+			new Rect(Screen.width * 1f / 8f, Screen.height * 3f / 8f, 320, 10));
+
+		this.Config.SetValue("Format", Enum.GetName(typeof(ScreenShotFormat), this.ssFormat));
+		this.Config.SetValue("TAKE_SCREENSHOT", Enum.GetName(typeof(KeyCode), this.realScreenshotKey));
+		this.Config.SetValue("windowPosition", this.windowPosition);
+
+		this.Config.save();
+	}
+
+	private void Update()
+	{
+		if (this.toolbarButton == null && this.appLauncherButton == null)
+		{
+			if (ToolbarManager.ToolbarAvailable)
+			{
+				this.InitializeToolbarButton();
+			}
+			else if (ApplicationLauncher.Ready)
+			{
+				this.InitializeAppLauncherButton();
+			}
+		}
+
+		if (this.showWindow && !this.windowVisible)
+		{
+			RenderingManager.AddToPostDrawQueue(0, this.DrawGUI);
+			this.windowVisible = true;
+		}
+		if (this.windowVisible && !this.showWindow)
+		{
+			RenderingManager.RemoveFromPostDrawQueue(0, this.DrawGUI);
+			this.windowVisible = false;
+		}
+
+		if (GameSettings.TAKE_SCREENSHOT.primary != KeyCode.None)
+		{
+			this.realScreenshotKey = GameSettings.TAKE_SCREENSHOT.primary;
+			GameSettings.TAKE_SCREENSHOT.primary = KeyCode.None;
+		}
+		else
+		{
+			GameSettings.TAKE_SCREENSHOT.primary = this.realScreenshotKey;
+		}
+
+		if (Input.GetKeyUp(this.realScreenshotKey))
+		{
+			Texture2D screenshot;
+
+			screenshot = this.RenderScreenshot(this.TexFormat);
+
+			if (screenshot != null)
+			{
+				this.WriteTextureToFile(screenshot);
+			}
+
+			Debug.Log(string.Format("[{0}]: Screenshot!", this.GetType().Name));
+		}
+	}
+
+	private void OnDestroy()
+	{
+		GameEvents.onGameSceneLoadRequested.Remove(this.OnSceneChangeRequested);
+		GameEvents.onGUIApplicationLauncherReady.Remove(this.OnAppLauncherReady);
+
+		if (this.realScreenshotKey != KeyCode.None)
+		{
+			GameSettings.TAKE_SCREENSHOT.primary = this.realScreenshotKey;
+		}
+
+		if (this.toolbarButton != null)
+		{
+			this.toolbarButton.Destroy();
+			this.toolbarButton = null;
+		}
+
+		if (this.appLauncherButton != null)
+		{
+			ApplicationLauncher.Instance.RemoveModApplication(this.appLauncherButton);
+			this.appLauncherButton = null;
+		}
+
+		Debug.Log(string.Format("[{0}]: Destroyed.", this.GetType().Name));
+	}
+
+	#endregion
+
+	#region Utility Methods
+
+	private Texture2D RenderScreenshot(TextureFormat format)
+	{
+		Texture2D screenshot = new Texture2D(this.ScreenshotRes.x, this.ScreenshotRes.y, format, false);
+		screenshot.ReadPixels(new Rect(0, 0, this._screenRes.x, this._screenRes.y), 0, 0);
+
+		return screenshot;
+	}
+
+	private bool WriteTextureToFile(Texture2D tex)
+	{
+		System.IO.FileStream fs = null;
+		System.IO.BinaryWriter binWriter = null;
+
+		try
+		{
+			string[] files = System.IO.Directory.GetFiles(this.ssPath);
+
+			int lastSSNumber = -1;
+
+			foreach (string file in files)
+			{
+				Match match = this.ssRegex.Match(file.ToLower());
+
+				if (match.Success)
+				{
+					string ssNumString = match.Groups["num"].Value;
+					int ssNumber;
+
+					if (int.TryParse(ssNumString, out ssNumber))
+					{
+						lastSSNumber = Math.Max(lastSSNumber, ssNumber);
+					}
+				}
+			}
+
+			string filename = string.Format("screenshot{0}.{1}",
+				lastSSNumber + 1, Enum.GetName(typeof(ScreenShotFormat), this.ssFormat).ToLower());
+
+			fs = System.IO.File.Open(System.IO.Path.Combine(this.ssPath, filename),
+				System.IO.FileMode.CreateNew);
+
+			binWriter = new System.IO.BinaryWriter(fs);
+
+			switch (this.ssFormat)
+			{
+				case ScreenShotFormat.JPG:
+					binWriter.Write(tex.EncodeToJPG());
+					break;
+				case ScreenShotFormat.PNG:
+					binWriter.Write(tex.EncodeToPNG());
+					break;
+				default:
+					throw new NotImplementedException("[SSM]: Only JPG and PNG formats are implemented.");
+			}
+
+			binWriter.Close();
+		}
+		catch (Exception ex)
+		{
+			Debug.LogException(ex);
+			return false;
+		}
+		finally
+		{
+			if (binWriter != null)
+			{
+				binWriter.Close();
+			}
+			if (fs != null)
+			{
+				fs.Close();
+			}
+		}
+
+		return true;
+	}
+
+	#endregion
+
+	#region UI Methods
+
+	private void InitializeToolbarButton()
+	{
+		if (ToolbarManager.ToolbarAvailable)
+		{
+			this.toolbarButton = ToolbarManager.Instance.add("ScreenshotManager", "main");
+			this.toolbarButton.TexturePath = "SSM/ToolbarIcon";
+			this.toolbarButton.Text = "SSM";
+			this.toolbarButton.TextColor = Color.Lerp(Color.cyan, Color.blue, 0.5f);
+			this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.SPACECENTER);
+			this.toolbarButton.OnClick += ((ClickEvent e) => this.ButtonClickHandler());
+		}
+	}
+
+	private void InitializeAppLauncherButton()
+	{
+		if (HighLogic.LoadedScene != GameScenes.SPACECENTER)
+		{
+			return;
+		}
+
+		Debug.Log("[SSM]: Adding AppLauncher button.");
+
+		Texture2D appIcon;
+
+		if (IOTools.LoadTexture(out appIcon, "SSM/AppLauncherIcon.png", 38, 38))
+		{
+			this.appLauncherButton = ApplicationLauncher.Instance.AddModApplication(
+				this.ButtonClickHandler,
+				this.ButtonClickHandler,
+				ApplicationLauncher.AppScenes.SPACECENTER,
+				appIcon
+			);
+		}
+		else
+		{
+			Debug.LogError("[SSM]: Failed to load texture.");
+		}
+	}
+
+	private void ButtonClickHandler()
+	{
+		this.showWindow = !this.showWindow;
+	}
+
+
+	private void DrawGUI()
+	{
+		Rect newPos = GUILayout.Window(this.GetType().GetHashCode(),
+			this.windowPosition,
+			this.SettingsWindow,
+			"Screenshot Manager"
+		);
+
+		if (newPos != this.windowPosition)
+		{
+			this.windowPosition = newPos;
+			this.Config.SetValue("windowPosition", this.windowPosition);
+			this.Config.save();
+		}
+	}
+
+	private void SettingsWindow(int id)
+	{
+		GUILayout.BeginVertical();
+
+		GUILayout.BeginHorizontal();
+		GUILayout.Label("Format:");
+		GUILayout.FlexibleSpace();
+		if (GUILayout.Button(Enum.GetName(typeof(ScreenShotFormat), this.ssFormat)))
+		{
+			switch (this.ssFormat)
+			{
+				case ScreenShotFormat.JPG:
+					this.ssFormat = ScreenShotFormat.PNG;
+					break;
+				case ScreenShotFormat.PNG:
+					this.ssFormat = ScreenShotFormat.JPG;
+					break;
+			}
+
+			this.Config.SetValue("Format", Enum.GetName(typeof(ScreenShotFormat), this.ssFormat));
+
+			this.Config.save();
+		}
+		GUILayout.EndHorizontal();
+
+		GUILayout.EndVertical();
+
+		GUI.DragWindow();
+	}
+
+	#endregion
+
+	#region Event Handlers
+
+	private void OnSceneChangeRequested(GameScenes scene)
+	{
+		if (scene == GameScenes.SETTINGS && this.realScreenshotKey != KeyCode.None)
+		{
+			GameSettings.TAKE_SCREENSHOT.primary = this.realScreenshotKey;
+		}
+		else
+		{
+			GameSettings.TAKE_SCREENSHOT.primary = KeyCode.None;
+		}
+	}
+
+	private void OnAppLauncherReady()
+	{
+		this.InitializeAppLauncherButton();
+	}
+
+	#endregion
+
+	private enum ScreenShotFormat
+	{
+		PNG,
+		JPG
+	}
+}
+
+public struct Vector2i
+{
+	public static bool operator ==(Vector2i lhs, Vector2i rhs)
+	{
+		return (lhs.x == rhs.x) && (lhs.y == rhs.y);
+	}
+
+	public static bool operator !=(Vector2i lhs, Vector2i rhs)
+	{
+		return !(lhs == rhs);
+	}
+
+	public static Vector2i operator *(Vector2i lhs, int rhs)
+	{
+		return new Vector2i(lhs.x * rhs, lhs.y * rhs);
+	}
+
+	public static Vector2i operator *(int lhs, Vector2i rhs)
+	{
+		return rhs * lhs;
+	}
+
+	public static Vector2i operator *(Vector2i lhs, uint rhs)
+	{
+		return new Vector2i((int)(lhs.x * rhs), (int)(lhs.y * rhs));
+	}
+
+	public static Vector2i operator *(uint lhs, Vector2i rhs)
+	{
+		return rhs * lhs;
+	}
+
+	public static Vector2i operator *(Vector2i lhs, float rhs)
+	{
+		return new Vector2i((int)((float)lhs.x * rhs), (int)((float)lhs.y * rhs));
+	}
+
+	public static Vector2i operator *(float lhs, Vector2i rhs)
+	{
+		return rhs * lhs;
+	}
+
+	public static Vector2i operator *(Vector2i lhs, double rhs)
+	{
+		return new Vector2i((int)((double)lhs.x * rhs), (int)((double)lhs.y * rhs));
+	}
+
+	public static Vector2i operator *(double lhs, Vector2i rhs)
+	{
+		return rhs * lhs;
+	}
+
+	public int x;
+	public int y;
+
+	public Vector2i(int x, int y)
+	{
+		this.x = x;
+		this.y = y;
+	}
+
+	public Vector2i(Vector2 vec)
+	{
+		this.x = (int)vec.x;
+		this.y = (int)vec.y;
+	}
+
+	public Vector2i(Vector2d vec)
+	{
+		this.x = (int)vec.x;
+		this.y = (int)vec.y;
+	}
+
+	public override bool Equals(object obj)
+	{
+		if (obj is Vector2i)
+		{
+			return (Vector2i)obj == this;
+		}
+
+		return base.Equals(obj);
+	}
+
+	public override int GetHashCode()
+	{
+		return this.x.GetHashCode() ^ this.y.GetHashCode() << 2;
+	}
+}
+

--- /dev/null
+++ b/ScreenshotManager.csproj
@@ -1,1 +1,75 @@
-
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{0EF93D20-B4BD-4BF1-9867-6D803ADB5CE5}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>ScreenshotManager</RootNamespace>
+    <AssemblyName>ScreenshotManager</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release (win)|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CustomCommands>
+      <CustomCommands>
+        <Command type="AfterBuild" command="xcopy /Y ${TargetFile} ..\..\..\Games\KSP_win\GameData\SSM\Plugins\" />
+      </CustomCommands>
+    </CustomCommands>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release (linux)|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CustomCommands>
+      <CustomCommands>
+        <Command type="AfterBuild" command="cp -fav ${TargetFile} /opt/games/KSP_testing/GameData/SSM/" />
+      </CustomCommands>
+    </CustomCommands>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ItemGroup>
+    <Reference Include="Assembly-CSharp">
+      <HintPath>..\_KSPAssemblies\Assembly-CSharp.dll</HintPath>
+    </Reference>
+    <Reference Include="System">
+      <HintPath>..\_KSPAssemblies\System.dll</HintPath>
+    </Reference>
+    <Reference Include="UnityEngine">
+      <HintPath>..\_KSPAssemblies\UnityEngine.dll</HintPath>
+    </Reference>
+    <Reference Include="Assembly-CSharp-firstpass">
+      <HintPath>..\_KSPAssemblies\Assembly-CSharp-firstpass.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ScreenshotManager.cs" />
+    <Compile Include="..\ToadicusTools\Wrapper\ToolbarWrapper.cs">
+      <Link>ToolbarWrapper.cs</Link>
+    </Compile>
+    <Compile Include="..\ToadicusTools\AppLauncherTools.cs">
+      <Link>AppLauncherTools.cs</Link>
+    </Compile>
+    <Compile Include="..\ToadicusTools\IOTools.cs">
+      <Link>IOTools.cs</Link>
+    </Compile>
+  </ItemGroup>
+</Project>