Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/hakchi2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Clovershell/ClovershellConnection.cs15
-rw-r--r--Clovershell/ShellConnection.cs1
-rw-r--r--FileBrowserForm.Designer.cs239
-rw-r--r--FileBrowserForm.cs19
-rw-r--r--FileBrowserForm.resx1271
-rw-r--r--FtpServer/DebugLogHandler.cs74
-rw-r--r--FtpServer/FileSystemHelper.cs66
-rw-r--r--FtpServer/IAuthHandler.cs45
-rw-r--r--FtpServer/IFileSystemHandler.cs202
-rw-r--r--FtpServer/ILogHandler.cs25
-rw-r--r--FtpServer/NesMiniAuthHandler.cs41
-rw-r--r--FtpServer/NesMiniFileSystemHandler.cs317
-rw-r--r--FtpServer/Server.cs190
-rw-r--r--FtpServer/Session.cs1045
-rw-r--r--ImagesForm.Designer.cs1
-rw-r--r--ImagesForm.cs5
-rw-r--r--MainForm.Designer.cs76
-rw-r--r--MainForm.cs183
-rw-r--r--MainForm.resx651
-rw-r--r--Properties/AssemblyInfo.cs4
-rw-r--r--SaveStateManager.Designer.cs112
-rw-r--r--SaveStateManager.cs196
-rw-r--r--SaveStateManager.resx190
-rw-r--r--hakchi_gui.csproj18
24 files changed, 4229 insertions, 757 deletions
diff --git a/Clovershell/ClovershellConnection.cs b/Clovershell/ClovershellConnection.cs
index 74ce75a5..875b009d 100644
--- a/Clovershell/ClovershellConnection.cs
+++ b/Clovershell/ClovershellConnection.cs
@@ -393,13 +393,14 @@ namespace com.clusterrr.clovershell
internal void writeUsb(ClovershellCommand cmd, byte arg, byte[] data = null, int l = -1)
{
+ if (!IsOnline) throw new ClovershellException("NES Mini is offline");
+ if (epWriter == null) return;
lock (epWriter)
{
var len = (l >= 0) ? l : ((data != null) ? data.Length : 0);
#if VERY_DEBUG
Debug.WriteLine(string.Format("->[CLV] cmd={0}, arg={1:X2}, len={2}, data={3}", cmd, arg, len, data != null ? BitConverter.ToString(data, 0, len) : ""));
#endif
- if (!online) throw new ClovershellException("NES Mini is offline");
var buff = new byte[len + 4];
buff[0] = (byte)cmd;
buff[1] = arg;
@@ -433,9 +434,9 @@ namespace com.clusterrr.clovershell
void shellListenerThreadLoop(object o)
{
+ var server = o as TcpListener;
try
{
- var server = o as TcpListener;
while (true)
{
while (!server.Pending()) Thread.Sleep(100);
@@ -474,6 +475,10 @@ namespace com.clusterrr.clovershell
{
Debug.WriteLine(ex.Message);
}
+ finally
+ {
+ server.Stop();
+ }
shellEnabled = false;
}
@@ -499,7 +504,7 @@ namespace com.clusterrr.clovershell
{
try
{
- var connection = (from c in pendingExecConnections where c.command == command select c).First();
+ var connection = (from c in pendingExecConnections where c.command == command select c).Last();
pendingExecConnections.Remove(connection);
//Debug.WriteLine("Executing: " + command);
connection.id = arg;
@@ -592,6 +597,7 @@ namespace com.clusterrr.clovershell
}
public int Ping()
{
+ if (!IsOnline) throw new ClovershellException("NES Mini is offline");
var rnd = new Random();
var data = new byte[4];
rnd.NextBytes(data);
@@ -618,13 +624,13 @@ namespace com.clusterrr.clovershell
public int Execute(string command, Stream stdin = null, Stream stdout = null, Stream stderr = null, int timeout = 0, bool throwOnNonZero = false)
{
+ if (!IsOnline) throw new ClovershellException("NES Mini is offline");
if (throwOnNonZero && stderr == null)
stderr = new MemoryStream();
using (var c = new ExecConnection(this, command, stdin, stdout, stderr))
{
try
{
- pendingExecConnections.RemoveAll(o => o.command == command);
pendingExecConnections.Add(c);
writeUsb(ClovershellCommand.CMD_EXEC_NEW_REQ, 0, Encoding.UTF8.GetBytes(command));
int t = 0;
@@ -657,7 +663,6 @@ namespace com.clusterrr.clovershell
}
finally
{
- pendingExecConnections.RemoveAll(o => o.command == command);
if (c.id >= 0)
execConnections[c.id] = null;
}
diff --git a/Clovershell/ShellConnection.cs b/Clovershell/ShellConnection.cs
index c1be436f..dc5ba30b 100644
--- a/Clovershell/ShellConnection.cs
+++ b/Clovershell/ShellConnection.cs
@@ -20,6 +20,7 @@ namespace com.clusterrr.clovershell
this.connection = connection;
this.socket = socket;
id = -1;
+ //socket.Send(new byte[] { 0xFF, 0xFD, 0x2D });
}
internal void shellConnectionLoop()
diff --git a/FileBrowserForm.Designer.cs b/FileBrowserForm.Designer.cs
new file mode 100644
index 00000000..a77616e1
--- /dev/null
+++ b/FileBrowserForm.Designer.cs
@@ -0,0 +1,239 @@
+namespace com.clusterrr.hakchi_gui
+{
+ partial class FileBrowserForm
+ {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FileBrowserForm));
+ this.splitContainer1 = new System.Windows.Forms.SplitContainer();
+ this.groupBoxLocal = new System.Windows.Forms.GroupBox();
+ this.textBoxLocalPath = new System.Windows.Forms.TextBox();
+ this.dataGridViewLocal = new System.Windows.Forms.DataGridView();
+ this.colIcon = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colSize = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.groupBoxRemote = new System.Windows.Forms.GroupBox();
+ this.textBox1 = new System.Windows.Forms.TextBox();
+ this.dataGridViewRemote = new System.Windows.Forms.DataGridView();
+ this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.splitContainer1.Panel1.SuspendLayout();
+ this.splitContainer1.Panel2.SuspendLayout();
+ this.splitContainer1.SuspendLayout();
+ this.groupBoxLocal.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.dataGridViewLocal)).BeginInit();
+ this.groupBoxRemote.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.dataGridViewRemote)).BeginInit();
+ this.SuspendLayout();
+ //
+ // splitContainer1
+ //
+ this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitContainer1.Location = new System.Drawing.Point(0, 0);
+ this.splitContainer1.Name = "splitContainer1";
+ //
+ // splitContainer1.Panel1
+ //
+ this.splitContainer1.Panel1.Controls.Add(this.groupBoxLocal);
+ //
+ // splitContainer1.Panel2
+ //
+ this.splitContainer1.Panel2.Controls.Add(this.groupBoxRemote);
+ this.splitContainer1.Size = new System.Drawing.Size(757, 519);
+ this.splitContainer1.SplitterDistance = 376;
+ this.splitContainer1.TabIndex = 4;
+ //
+ // groupBoxLocal
+ //
+ this.groupBoxLocal.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBoxLocal.Controls.Add(this.textBoxLocalPath);
+ this.groupBoxLocal.Controls.Add(this.dataGridViewLocal);
+ this.groupBoxLocal.Location = new System.Drawing.Point(3, 3);
+ this.groupBoxLocal.Name = "groupBoxLocal";
+ this.groupBoxLocal.Size = new System.Drawing.Size(370, 513);
+ this.groupBoxLocal.TabIndex = 5;
+ this.groupBoxLocal.TabStop = false;
+ this.groupBoxLocal.Text = "Local files";
+ //
+ // textBoxLocalPath
+ //
+ this.textBoxLocalPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textBoxLocalPath.Location = new System.Drawing.Point(6, 19);
+ this.textBoxLocalPath.Name = "textBoxLocalPath";
+ this.textBoxLocalPath.ReadOnly = true;
+ this.textBoxLocalPath.Size = new System.Drawing.Size(355, 20);
+ this.textBoxLocalPath.TabIndex = 3;
+ //
+ // dataGridViewLocal
+ //
+ this.dataGridViewLocal.AllowUserToAddRows = false;
+ this.dataGridViewLocal.AllowUserToDeleteRows = false;
+ this.dataGridViewLocal.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.dataGridViewLocal.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.dataGridViewLocal.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+ this.colIcon,
+ this.colName,
+ this.colSize});
+ this.dataGridViewLocal.Location = new System.Drawing.Point(6, 45);
+ this.dataGridViewLocal.Name = "dataGridViewLocal";
+ this.dataGridViewLocal.ReadOnly = true;
+ this.dataGridViewLocal.RowHeadersVisible = false;
+ this.dataGridViewLocal.Size = new System.Drawing.Size(355, 462);
+ this.dataGridViewLocal.TabIndex = 2;
+ //
+ // colIcon
+ //
+ this.colIcon.HeaderText = "";
+ this.colIcon.MinimumWidth = 10;
+ this.colIcon.Name = "colIcon";
+ this.colIcon.ReadOnly = true;
+ this.colIcon.Width = 20;
+ //
+ // colName
+ //
+ this.colName.HeaderText = "Name";
+ this.colName.Name = "colName";
+ this.colName.ReadOnly = true;
+ this.colName.Width = 200;
+ //
+ // colSize
+ //
+ this.colSize.HeaderText = "Size";
+ this.colSize.Name = "colSize";
+ this.colSize.ReadOnly = true;
+ //
+ // groupBoxRemote
+ //
+ this.groupBoxRemote.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBoxRemote.Controls.Add(this.textBox1);
+ this.groupBoxRemote.Controls.Add(this.dataGridViewRemote);
+ this.groupBoxRemote.Location = new System.Drawing.Point(3, 3);
+ this.groupBoxRemote.Name = "groupBoxRemote";
+ this.groupBoxRemote.Size = new System.Drawing.Size(371, 513);
+ this.groupBoxRemote.TabIndex = 6;
+ this.groupBoxRemote.TabStop = false;
+ this.groupBoxRemote.Text = "NES Mini\'s files";
+ //
+ // textBox1
+ //
+ this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textBox1.Location = new System.Drawing.Point(6, 19);
+ this.textBox1.Name = "textBox1";
+ this.textBox1.ReadOnly = true;
+ this.textBox1.Size = new System.Drawing.Size(356, 20);
+ this.textBox1.TabIndex = 3;
+ //
+ // dataGridViewRemote
+ //
+ this.dataGridViewRemote.AllowUserToAddRows = false;
+ this.dataGridViewRemote.AllowUserToDeleteRows = false;
+ this.dataGridViewRemote.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.dataGridViewRemote.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.dataGridViewRemote.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+ this.dataGridViewTextBoxColumn1,
+ this.dataGridViewTextBoxColumn2,
+ this.dataGridViewTextBoxColumn3});
+ this.dataGridViewRemote.Location = new System.Drawing.Point(6, 45);
+ this.dataGridViewRemote.Name = "dataGridViewRemote";
+ this.dataGridViewRemote.ReadOnly = true;
+ this.dataGridViewRemote.RowHeadersVisible = false;
+ this.dataGridViewRemote.Size = new System.Drawing.Size(356, 462);
+ this.dataGridViewRemote.TabIndex = 2;
+ //
+ // dataGridViewTextBoxColumn1
+ //
+ this.dataGridViewTextBoxColumn1.HeaderText = "";
+ this.dataGridViewTextBoxColumn1.MinimumWidth = 10;
+ this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
+ this.dataGridViewTextBoxColumn1.ReadOnly = true;
+ this.dataGridViewTextBoxColumn1.Width = 20;
+ //
+ // dataGridViewTextBoxColumn2
+ //
+ this.dataGridViewTextBoxColumn2.HeaderText = "Name";
+ this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
+ this.dataGridViewTextBoxColumn2.ReadOnly = true;
+ this.dataGridViewTextBoxColumn2.Width = 200;
+ //
+ // dataGridViewTextBoxColumn3
+ //
+ this.dataGridViewTextBoxColumn3.HeaderText = "Size";
+ this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
+ this.dataGridViewTextBoxColumn3.ReadOnly = true;
+ //
+ // FileBrowserForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(757, 519);
+ this.Controls.Add(this.splitContainer1);
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.Name = "FileBrowserForm";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "NES Mini file browser";
+ this.splitContainer1.Panel1.ResumeLayout(false);
+ this.splitContainer1.Panel2.ResumeLayout(false);
+ this.splitContainer1.ResumeLayout(false);
+ this.groupBoxLocal.ResumeLayout(false);
+ this.groupBoxLocal.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.dataGridViewLocal)).EndInit();
+ this.groupBoxRemote.ResumeLayout(false);
+ this.groupBoxRemote.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.dataGridViewRemote)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.SplitContainer splitContainer1;
+ private System.Windows.Forms.GroupBox groupBoxLocal;
+ private System.Windows.Forms.TextBox textBoxLocalPath;
+ private System.Windows.Forms.DataGridView dataGridViewLocal;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colIcon;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colName;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colSize;
+ private System.Windows.Forms.GroupBox groupBoxRemote;
+ private System.Windows.Forms.TextBox textBox1;
+ private System.Windows.Forms.DataGridView dataGridViewRemote;
+ private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
+ private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
+ private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
+
+ }
+} \ No newline at end of file
diff --git a/FileBrowserForm.cs b/FileBrowserForm.cs
new file mode 100644
index 00000000..ebae73b8
--- /dev/null
+++ b/FileBrowserForm.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+
+namespace com.clusterrr.hakchi_gui
+{
+ public partial class FileBrowserForm : Form
+ {
+ public FileBrowserForm()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/FileBrowserForm.resx b/FileBrowserForm.resx
new file mode 100644
index 00000000..a9a837b4
--- /dev/null
+++ b/FileBrowserForm.resx
@@ -0,0 +1,1271 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <metadata name="colIcon.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="colName.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="colSize.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="dataGridViewTextBoxColumn1.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="dataGridViewTextBoxColumn2.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <metadata name="dataGridViewTextBoxColumn3.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>True</value>
+ </metadata>
+ <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+ <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+ AAABAAEAgIAAAAEAIAAoCAEAFgAAACgAAACAAAAAAAEAAAEAIAAAAAAAAAABABILAAASCwAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAfQAA
+ AMIAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADCAAAAfQAAACIAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAOkAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA6QAAAGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AIEAAAAAAAAAAAAAAAAAAABdAAAA/wAAAP8AAAD/AAAA/wAAAP8GBgX/Dg0M/w4NDP8ODQz/Dg0M/w4N
+ DP8ODQz/Dg0M/w4NDP8ODQz/Dg0M/w4NDP8ODQz/Dg0M/w4NDP8ODQz/Dg0M/w4NDP8ODQ3/Dg0N/w4N
+ Df8ODQ3/Dg4N/w8ODf8PDg3/Dw4N/w8ODv8PDg7/Dw4O/w8ODv8PDg7/Dw8O/xAPDv8QDw7/EA8P/xAP
+ D/8QDw//EA8P/xAPD/8QDw//EBAP/xAQD/8REA//ERAQ/xEQEP8REBD/ERAQ/xEQEP8RERD/EREQ/xER
+ EP8SERH/EhER/xIREf8SERH/EhER/xIREf8SERH/EhER/xIREf8SERH/EhER/xIREf8SERH/ERER/xER
+ EP8RERD/EREQ/xEQEP8REBD/ERAQ/xEQEP8REBD/ERAQ/xEQEP8REA//EBAP/xAQD/8QEA//EA8P/xAP
+ D/8QDw//EA8P/xAPD/8QDw//EA8O/xAPDv8PDw7/Dw8O/w8ODv8PDg7/Dw4O/w8ODv8PDg7/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8ODg3/Dg0N/w4NDf8ODQ3/Dg0N/w4NDf8ODQ3/Dg0N/w4NDf8ODQ3/Dg0N/w4N
+ Df8ODQ3/Dg4N/wYGBv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAF0AAAAAAAAAEQAAAN0AAAD/AAAA/wAA
+ AP8ODQz/WVFN/4d9d/+PhH7/joR9/46Eff+OhH3/joR9/46Eff+OhH3/joR9/46Eff+OhH3/joR9/46E
+ ff+OhH3/joR9/4+Efv+QhX//kYeB/5OIgv+UiYT/lYuE/5aMhv+WjYf/mI6J/5mPiv+akIv/nJKN/52T
+ jf+elY//n5aQ/6CWkv+hmJP/opmV/6Oalv+lnJf/pp2Y/6eemf+ooJv/qaCd/6qhnv+ro5//rKSg/62m
+ of+vp6P/sKik/7Gppv+yqqf/s6yo/7Stqf+1rqv/trCs/7ixrv+5sq//urOw/7y0sv+8trL/vbe0/723
+ tP+8trP/vLWy/7q0sv+6s7D/ubKv/7iyrv+3sa3/tq+r/7Wuqv+0ran/s6yo/7KrqP+yqqf/sail/7Co
+ pP+vqKP/rqai/62lof+spKD/q6Of/6qinv+poZ3/qKCb/6ifmv+mnpn/pZ2Y/6Wcl/+jm5b/opqV/6GZ
+ lP+gl5P/oJaS/5+Wkf+elY//nZSO/5yTjf+bkoz/mpGL/5mQiv+Yjon/l42I/5aNhv+WjIX/lYuE/5SK
+ hP+TiYP/k4mD/5OJg/+TiYP/k4mD/5OJg/+TiYP/k4mD/5OJg/+TioP/jIJ8/1tVUf8ODQ3/AAAA/wAA
+ AP8AAAD/AAAA3QAAABEAAABmAAAA/wAAAP8AAAD/CwsK/3lxa/+mmZL/oJOM/5+Si/+fkov/n5KL/5+S
+ i/+fkov/n5KL/5+Si/+fkov/n5KL/5+Si/+fkov/n5KL/5+Si/+fkov/oJOM/6GUjf+ilo//pJeQ/6WY
+ kv+mm5P/p5yV/6idl/+qn5n/q6Ca/6yhm/+uo53/r6Se/7CmoP+xp6H/sqij/7SqpP+1q6b/tqyn/7iu
+ qf+6r6r/u7Cr/7yyrf++s6//v7Sw/8C3sf/BuLT/wrq1/8S7t//Fvbj/xr66/8i/u//Jwb3/ysK+/8vD
+ wP/MxcH/zsbD/8/IxP/QycX/0srH/9PMyP/Uzcr/1M3K/9PMyf/Sy8j/0crH/9DJxf/PyMT/zsfD/83G
+ wv/MxMD/y8O//8rCvv/Jwb3/yMC8/8e/u//Gvbn/xb24/8S8t//Durb/wrm1/8G4tP/At7H/v7Ww/760
+ r/+8sq3/vLGs/7qwq/+4r6r/uK6p/7atp/+1rKb/tKul/7OppP+yqKP/saei/7CmoP+vpZ//rqSe/62j
+ nP+sopv/q6Ga/6qfmf+pnpj/qJ2V/6eclP+mm5P/pZqS/6SYkf+kmJH/pJiR/6SYkf+kmJH/pJiR/6SY
+ kf+kmJH/pJiR/6SYkf+lmZL/q5+Y/351cP8LCwr/AAAA/wAAAP8AAAD/AAAAZgAAANsAAAD/AAAA/wAA
+ AP9JQ0D/pJeQ/6KWkP+vpZ7/r6Wf/6+lnv+vpZ7/r6We/6+lnv+vpZ7/r6We/6+lnv+vpZ7/r6We/6+l
+ nv+vpZ7/r6We/6+knv+wpZ//sKag/7Goof+zqKL/tKmk/7Wrpf+1rKb/tq2n/7iuqf+5r6n/urCq/7ux
+ rP+8sq3/vbSu/721r/++tbH/wLey/8G4s//CubT/w7q2/8S7tv/FvLf/xr65/8e+u//Iv7v/ycG8/8rC
+ vv/Kw7//zMTA/83Gwf/Ox8P/z8fD/9DJxf/Rysb/0srI/9PMyP/Uzcr/1c/L/9bPzP/X0M3/2NLO/9nT
+ 0P/Z09D/2NLP/9fRzv/X0M3/1s/L/9XPy//Uzsr/083J/9LLyP/Sysf/0crG/9DJxf/PyMT/z8fD/87G
+ wv/NxsH/zMXA/8vDv//Kwr//ysK+/8nBvP/IwLv/x7+6/8a9uf/Gvbj/xLy3/8O7tv/Durb/wrq0/8G5
+ s//AuLP/v7ay/761sf+9tbD/vbSu/7yzrv+7sq3/urGr/7qxqv+5sKn/uK6p/7etqP+2rab/taym/7Wr
+ pf+0qqT/s6mj/7Opo/+zqaP/s6mj/7Opo/+zqaP/s6mj/7Opo/+zqqP/tKqk/7OppP+nnJb/qZ2W/0tG
+ Q/8AAAD/AAAA/wAAAP8AAADYAAAA/wAAAP8AAAD/AAAA/3BoY/+5sKn/x763/8G3rv+5rqT/uK2j/7it
+ o/+4raP/uK2j/7ito/+4raP/uK2j/7mto/+5raP/ua2j/7mtpP+5rqT/ua6k/7mupP+5rqT/ua6k/7mu
+ pP+6rqX/uq+l/7qvpf+6r6X/uq+l/7qvpf+6r6X/u6+m/7uvpv+7sKb/u7Cm/7uwpv+7sKb/u7Cm/7uw
+ p/+8sKf/vLGn/7yxp/+8saf/vLGn/7yxp/+8saj/vLGo/7yyqP+8sqj/vbKo/72yqP+9sqj/vbKp/72y
+ qf+9sqn/vbOp/76zqf++s6n/vrOp/76zqv++s6r/vrOq/760qv++tKr/vrSq/760qv++tKr/vrOq/76z
+ qv++s6r/vrOp/76zqf++s6n/vrOp/72zqf+9sqn/vbKp/72yqf+9sqj/vbKo/72yqP+9sqj/vLGo/7yx
+ qP+8saf/vLGn/7yxp/+8saf/vLGn/7ywp/+7sKf/u7Cm/7uwpv+7sKb/u7Cm/7uwpv+7sKb/u7Cm/7uv
+ pv+6r6X/uq+l/7qvpf+6r6X/uq+l/7qvpf+6rqX/uq6k/7mupP+5rqT/ua6k/7mupP+5rqT/ua6k/7mu
+ pP+5rqT/ua2k/7mto/+5rqT/wriv/8nAuP+9tK3/dGxn/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/ioJ9/7yxpv+dkIb/mo6F/52QiP+ekYn/n5KK/6CUi/+hlYz/opaO/6KXj/+jl5D/pZmR/6aa
+ kv+nm5P/qJyV/6mdlv+pnpf/qp+Y/6ugmf+topr/rqOb/6+jnf+wpZ7/sKWf/7GnoP+yqKH/s6mi/7Sq
+ o/+1q6X/tqyl/7etpv+4rqj/ua+p/7qwqv+7saz/vLKs/72zrv++ta//vraw/8C3sf/BuLL/wriz/8O6
+ tP/Eu7X/xLy2/8W9uP/Gvrn/x7+6/8nAu//Jwbz/ysO9/8vEv//MxcD/zcbB/87Gwv/Px8P/0MnE/9HK
+ xf/Ry8f/08zI/9TNyf/Vzsr/1s/K/9XOyv/Uzcn/0szI/9LLxv/RycX/0MjE/8/Hw//NxsH/zMXB/8zE
+ wP/Lw77/ysK9/8nBvP/Iv7r/x7+6/8a+uP/FvLf/xLu2/8O6tf/CubP/wbiz/8C3sf+/trD/vrWv/720
+ rf+8s6z/u7Ks/7qxqv+5sKn/uK+o/7etpv+2rKX/taul/7Sqo/+zqaL/sqih/7GnoP+wpp//r6Se/66j
+ nP+to5v/rKGa/6ugmf+qn5j/qZ6X/6mdlv+onJT/ppuT/6Wakv+kmZH/o5eQ/6KWj/+glIv/oZSK/72x
+ qP+MhYD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wICAv+If3j/oJKJ/56Tjf+flI3/oJSN/6GV
+ j/+il5D/o5iR/6SZkv+lmpT/p5uV/6iclv+qn5n/q6Ca/6yhm/+to53/rqSe/6+ln/+wpqH/saei/7Op
+ o/+0qqT/taum/7asp/+3raj/uK+p/7mwq/+6saz/vLKu/721sP++trH/v7ey/8G4tP/CubX/w7q2/8S8
+ uP/Fvbn/xr66/8fAvP/Iwb3/ysK+/8vDv//MxMD/zcbC/87Hw//PyMT/0MnH/9HKyP/Sy8n/1M7L/9TP
+ zP/V0c3/2NLP/9nT0P/a1NH/29XT/9zW1P/d2NX/3tnW/9/a2P/h29n/4tza/+Pd2//k39z/497b/+Lc
+ 2v/g29n/39rX/97Y1v/d19X/3NbT/9rV0v/Z09H/2dLQ/9fRzv/W0M3/1c/L/9TNyv/TzMn/0svH/9DJ
+ xv/PyMX/zsfE/83Gwv/MxcH/y8O//8rCvv/Jwb3/xsC7/8W+uv/Evbn/w7y3/8K7tv/BurX/v7iz/763
+ sv+9trH/vLOv/7uyrv+6sa3/ubCr/7ivqv+2ran/taym/7Srpv+zqaX/sqik/7Gnof+wpqH/r6Wg/66k
+ nf+sopz/q6Gb/6qgmf+pnpj/qJ2X/6aclv+lm5T/pJiO/4mAef8BAQL/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AQEA/3huZ/+glI3/oJWO/62knv+1q6b/tayn/7Wtp/+2raj/t66o/7ivqv+4r6r/ubCr/7qx
+ rP+7sq3/u7Kt/7yzr/+9tK//vbWw/762sf+/trL/wLiz/8G4tP/CubX/wrq1/8O6tv/EvLf/xLy4/8W9
+ uf/Gvrr/xr+7/8fAvP/Iwb3/ycK+/8rDv//Lw8D/zMXB/8zGwv/NxsP/zsjE/8/Ixf/Qycb/0crH/9LL
+ yP/SzMn/083K/9TOy//Vz8z/1tDN/9fRzv/Y0s//2NPQ/9nU0f/b1dP/3NbT/93X1P/d2Nb/3tnX/9/b
+ 2P/g29n/4dza/+Pd2//k3tz/5N/d/+Xh3v/k4N3/497c/+Hd2//g3Nn/39rY/97Y1//d2NX/29bT/9rV
+ 0//a1NH/2NLQ/9fRzv/W0M3/1c7L/9TNyv/TzMn/0cvH/9DJxv/PyMX/zsfD/83Gwv/LxMD/ysO//8nC
+ vv/Iwbz/x7+7/8a+uv/Evbj/w7u2/8K6tf/AuLP/v7ey/762sf+9tK//vLOu/7uyrP+5sKv/uK+p/7at
+ qP+1rKf/tKul/7OppP+yqKP/sKeh/6+ln/+upJ7/raOc/6uhm/+qoJr/qZ+Y/6edl/+mnJX/pZqT/6Sa
+ k/+onZb/enFp/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/dGtl/7Opo//f3Nn/8vDv//Px
+ 8f/y8PD/8vDv//Hw7//w7+7/8O7t/+/t7f/w7e3/7+3s/+7s6//u7Ov/7evq/+3r6v/t6+r/7erp/+3q
+ 6f/s6ej/7Ono/+zp6P/r6Of/6+jn/+vo5//q6Of/6ujn/+ro5v/q5+b/6ufm/+rn5v/q5+X/6ufl/+rn
+ 5f/q5uX/6ubl/+rm5f/q5uX/6ebl/+rm5f/q5uX/6ufl/+rm5f/q5uX/6ufl/+rn5f/q5+b/6ufm/+vn
+ 5v/r5+b/6+jm/+vo5//r6Of/6+jn/+vp6P/s6ej/7Ono/+zp6P/t6en/7erp/+7q6f/u6+n/7uvq/+7r
+ 6f/t6en/6+no/+vo5v/q5ub/6ebl/+nm4//o5OP/5+Pi/+fj4f/l4uD/5eHf/+Tg3v/j393/4t7c/+Ld
+ 2//g3Nn/39rZ/9/Z2P/e2db/3djW/9vW1f/b1tP/2tXS/9nT0P/Y0tD/19HO/9XQzf/Vz8z/1M7L/9LN
+ yv/SzMj/0MrH/8/Jxv/PyMX/zsfE/8zGwv/MxcH/ysTA/8nCvv/Iwb3/x8C8/8a+u//Fvrn/xLy4/8O7
+ t//CurX/wLi0/7+3s/++trH/vbWw/7yzr/+4sKr/q6Gb/6eclf97c23/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP+De3f/7uvq//Tz8//x7+//8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du
+ 7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du
+ 7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du
+ 7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du
+ 7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du7v/w7u7/8O7u//Du
+ 7v/w7u7/8O7u//Du7v/w7u//8O7v//Dv7//w7+//8e/v//Hv7//x7+//8e/v//Hv7//x7+//8e/v//Hv
+ 7//x7+//8e/v//Hv7//x7+//8e/v//Hv7//x7+//8e/v//Hv7//x7+//8e/v//Hv7//x7+//8e/v//Hv
+ 7//x7/D/8e/w//Hv8P/x8PD/8fDw//Hw8P/x8PD/8fDw//Lw8P/y8PD/8vDw//Lw8P/y8PD/8vDw//Du
+ 7v/l4uD/wLey/3pybP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/6ajof/49/f/8O7u//Du
+ 7v/w7u7/8/Hx//j29v/49vb/+Pb2//j29v/49vb/+Pb2//j29v/49vb/+Pb2//j29v/49vb/+Pb2//j2
+ 9v/49vb/+Pb2//j29v/49vb/+Pb2//j29v/49vb/+Pb2//j29v/49vb/+Pb2//j29v/49vb/+Pb2//j2
+ 9v/49vb/+Pb2//j29v/49vb/+Pb2//j29v/49vb/+Pb2//f19f/08/P/9PPz//Tz8//08/P/9PPz//Tz
+ 8//08/P/9PPz//Tz8//18/P/9fPz//Xz8//18/P/9fPz//Xz8//18/P/9fPz//Xz8//18/P/9fPz//Xz
+ 8//18/P/9fPz//Xz8//18/P/9fPz//Xz8//18/P/9fPz//Xz8//18/P/9fP0//f19f/39fX/9/X1//f1
+ 9f/39fX/9/X1//f19f/39fX/9/X1//f19f/39fX/9/X1//f19f/39fX/9/X1//f19f/39fX/9/X1//f1
+ 9f/39fX/9/X1//f19f/39fX/9/X1//f19f/39fX/9/X1//f19f/39fX/9/X1//f19f/29fX/9vX1//b1
+ 9f/29PX/9vT1//b09f/29PX/9vT0//Lw8P/w7u7/8O7u//Lw8P/x7u3/i4WB/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/trS0//b09P/w7u7/8O7u//Tx8f/b2dn/oJ6e/5aUlP+WlZX/lpWV/5aV
+ lf+WlZX/lpWV/5aVlf+WlZX/lpWV/5aVlf+WlZX/lpWV/5aVlf+WlZX/lpWV/5aVlf+WlZX/lpWV/5aV
+ lf+WlZX/lpWV/5aVlf+XlZX/l5WV/5eWlv+Xlpb/l5aW/5iWlv+Ylpb/mJaW/5iXl/+Yl5f/mJeX/5mY
+ l/+Xlpb/paKh/7+5tv+/ubb/vrm1/765tv++uLX/vri1/764tf+9uLX/vbe1/723tP+8t7T/vLe0/7y3
+ tP+8t7T/vLaz/7u2s/+7trP/u7az/7u2s/+7trP/urWz/7q1sv+6tbL/urSy/7m0sv+5tLH/ubSx/7m0
+ sf+5tLH/ubSx/7m0sf+1sa7/oJ6e/6Cenv+gnp7/oZ+e/6Gfnv+hn57/oZ+f/6Gfn/+in5//oqCf/6Kg
+ n/+ioJ//o6Cf/6OhoP+joaD/o6Gg/6OhoP+koaH/pKGh/6Siof+koqH/pKKh/6Siof+loqL/paOi/6Wj
+ ov+lo6L/pqOi/6akov+mpKL/pqSj/6ako/+npKP/p6Sk/6elpP+npaT/p6Wk/6eko/+vrav/393c//Pw
+ 8f/w7u7/8O7u//j29v+pp6b/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+2tLT/9vT0//Du
+ 7v/z8fH/19XV/ykoKP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8iHhv/al5V/2hbU/9nXFP/Z1pS/2Za
+ Uv9mWVH/ZVlR/2RYUP9kWFD/Y1dP/2JWT/9iVk7/YVVO/2FVTf9gVE3/X1RM/19TTP9eU0v/XlJL/1xS
+ Sv9cUUr/W1FJ/1tQSf9aUEj/WU9I/1lOR/9YTkf/WE1G/1dNRv9XTUb/WE5H/01EPv8UEhD/FBIR/xUT
+ Ef8VExH/FhQS/xYUE/8XFRP/FxUT/xgWFP8YFhT/GRcV/xoYFf8aGBb/GxkW/xwZF/8cGRf/HRoY/x4b
+ GP8eGxn/HxwZ/x8cGv8fHRr/IB4b/yEeHP8hHxz/Ih8d/yIfHf8jIB7/JCEe/yQhHv8lIh//JiIf/yYj
+ IP8nIyH/JyQh/yckIf8oJSL/KSUi/yckIf9QTUv/3NnZ//Lw8P/w7u7/9vT0/7a0tP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/7a0tP/29PT/8O7u//b09P+hn5//AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wEBAf8BAQH/AgIC/wICAv8DAwL/BAMD/wQEA/8EBAT/BQUE/wUFBf8GBgb/BwYG/wcH
+ Bv8IBwf/BQQE/ykkIf9vYlr/bGFY/2xgWP9rX1f/al9X/2peVv9pXlb/aV1V/2hdVf9nXFT/Z1xU/2Zb
+ U/9mW1P/ZVpS/2RZUv9kWVH/Y1lR/2NYUP9iV1D/YldQ/2FWT/9gVk//YFVO/19VTf9fVE3/XlRN/11T
+ TP9dU0z/XFJL/1xSS/9dU0z/UklD/xsZGP8cGhj/HBoY/x0bGf8dGxn/Hhwa/x4cGv8fHBv/Hx0b/yAe
+ HP8hHhz/IR8d/yIfHf8iIB7/IyEb/yQhG/8kIR3/JSIg/yUiIP8lIyD/JiMh/yYkIf8nJSL/KCUi/ygl
+ I/8pJiT/KSYk/yonJP8rJyX/Kygk/y0pIf8tKiL/LSkn/y0qJ/8uKij/Liso/y8sKf8vLCn/MC0q/ykl
+ Iv+wraz/9fPz//Du7v/29PT/trS0/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/trS0//bz
+ 8//w7e3/9vPz/52bm/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQH/AQEB/wIBAf8CAgL/AwMC/wMD
+ A/8EBAP/BAQE/wUEBP8FBQX/BgYF/wcGBv8HBwb/CAcH/wgIB/8GBgX/IyAd/25iWv9uYln/bWFZ/2xg
+ WP9sYFj/a19X/2pfV/9qXlb/aV5W/2hdVf9oXVX/Z1xU/2dcVP9mW1T/ZVtT/2VaU/9kWlL/ZFlS/2NZ
+ Uf9jWFH/YldP/2FXUP9hV0//YFZO/2BVTf9fVU7/XlRM/15TTP9dUkv/XVJL/15UTP9TSkT/HBoY/xwa
+ GP8dGxn/HRsZ/x4cGv8eHBr/Hxwb/x8dG/8gHhz/IB4c/yEfHf8iHx3/IiAe/yQhG/8XFl7/ExF9/yAe
+ N/8mIx3/JSMg/yYjIf8mJCH/JyQi/yglIv8oJSP/KSYj/ykmJP8qJyT/Kycl/ywpIv8mIz3/HRtn/x0b
+ Z/8sKSz/Lion/y4rKP8vKyj/Lywp/zAtKv8wLSr/LCkl/66qqf/08vL/8O3t//bz8/+2tLT/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+joqL/9/X1/+/t7f/18/P/nJua/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wEBAf8CAQH/AgIC/wMDAv8DAwP/BAQD/wQEBP8FBAT/BQUF/wYGBf8GBgb/BwcG/wgH
+ B/8IBwf/CQgI/wgIB/8VExL/W1JL/2leVv9nXFT/Z1xU/2dbU/9mW1P/ZlpS/2VaU/9lWVL/ZFlS/2NY
+ Uf9jV1H/YldQ/2JXUP9hV0//YFZP/2BWTv9fVU7/X1VN/15UTf9dVEz/XVNM/11TTP9cUkv/XFJL/1tR
+ Sv9bUUr/WlBJ/1pQSf9ZT0n/WlBJ/0A5NP8bGRf/HRoZ/x0bGf8eHBr/Hhwa/x8cG/8fHRv/IB0b/yAe
+ HP8hHx3/Ih8d/yIgHv8jIB7/JCIZ/xAPif8IB7T/GxlS/yYkG/8mIyH/JiQh/yckIv8nJSL/KCUj/ykm
+ I/8pJiT/Kick/yonJf8rKCX/LCgk/yonL/8NDKn/FhSG/y4rI/8uKyj/Lyso/y8sKf8wLSr/MC0q/zEt
+ Kv8tKSb/raqo//Px8f/v7e3/9/X1/6Oiov8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/5ya
+ mv/39fX/7+3t//Xy8v+bmZj/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AgEB/wICAv8DAwL/BAMD/wQE
+ A/8EBAT/BQUE/wUFBf8GBgX/BwYG/wcHBv8IBwf/CAgH/wkICP8KCQj/CgkJ/woJCP8RDw7/GhgV/xoX
+ Fv8aGBb/GhgX/xsZF/8cGRf/HBoX/xwaGP8dGhj/HRoY/x4bGf8eGxn/HhwZ/x8cGv8fHBr/IBwa/x8d
+ G/8gHhv/IB4c/yEeHP8iHhz/IR8c/yIfHf8iIB3/IyAe/yMgHv8jIB7/JCEf/yQhH/8kIR7/HBoY/x0a
+ Gf8dGxn/Hhwa/x0bGf8YFRT/FxUT/xgVE/8YFhT/GRcV/xkXFf8aGBb/GxgW/xsYFv8cGRT/EhBM/w8O
+ bf8hHzT/JiQf/yYkIf8jIB7/IB0a/yAdG/8hHhz/IR4c/yIfHP8jHx3/IyAd/yQgHv8kIR7/JiMb/xgV
+ Wv8kIEb/Lywl/y8sKf8vLCn/MC0q/zEtKv8xLSr/Mi4r/y4qJ/+sqaf/8/Hw/+/t7f/39fX/nJqa/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD0AAAA/wAAAP8AAAD/nZub//f19f/v7e3/9PLx/52amf8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BgYG/yYlJf8qKir/KSkp/ykp
+ Kf8qKir/Kysr/xUUFP8AAAD/AwMC/wMDA/8EBAP/BAQE/wUEBP8FBQX/BgYF/wYGBv8HBwb/CAcH/wgI
+ B/8JCAj/CQkI/woJCf8LCgn/CwoJ/wsKCf8KCgn/CwoK/wsLCv8MCwv/DAwL/w0NDP8ODQz/Dg4N/w8O
+ Df8PDg3/EA8O/xEQD/8REA//EhEQ/xMREP8TEhD/FBMR/xQTEv8VExL/FRQT/xYUE/8WFRT/FxYU/xgW
+ FP8YFhX/GRcV/xkXFv8aGBf/GxkX/xsZF/8dGxn/HRsZ/x4cGv8dGxn/JiMi/1xaWf9nZWT/Z2Vk/2dm
+ ZP9oZmX/aGZl/2lnZf9pZ2b/aWdm/2poZv9raWP/UlBI/yUiHf8mJCH/JCEf/0lHRP9tamn/bGtp/21r
+ af9ta2n/bWtq/25sav9vbGr/b21r/29ta/9wbmz/bWtk/z06M/8sKSb/Lywp/zAtKv8wLSr/MS0q/zIu
+ K/8yLiv/Lisn/66qqP/z8PD/7+3t//f19f+dm5v/AAAA/wAAAP8AAAD/AAAA9AAAANIAAAD/AAAA/wAA
+ AP+dm5v/9/T0/+/s7P/z8PD/r6yr/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/ycmJv/DwsL/4uDg/9zZ2f/d2tr/3dra/9zZ2f/e29v/393d/11cXP8AAAD/BAQD/wQE
+ BP8FBAT/BQUF/wYGBf8GBgb/BwcG/wgHB/8IBwf/CQgI/wkJCP8KCQn/CwoJ/wsKCv8MCwr/BgUF/wQD
+ Av8EAwP/BQQE/wYFBP8GBgX/BwYF/wgHBv8IBwb/CQgH/woIB/8KCQj/CwoI/wsKCf8MCwn/DAsK/w0M
+ Cv8ODAv/Dg0M/w8NDP8QDg3/EA8O/xEPDv8SEA7/EhAP/xMRD/8TERD/FBIR/xUTEf8VExH/FhQS/xoY
+ F/8eHBr/Hhsa/xwZGP+ioqH/////////////////////////////////////////////////////////
+ ///39/f/aWdl/xwZFv9PTUr/7Ozs////////////////////////////////////////////////////
+ ////////w8HB/zUyL/8vKyj/MC0q/zEtKv8yLiv/Mi4r/zMvLP8wLCn/vLi2//Hv7v/v7Oz/9/T0/52b
+ m/8AAAD/AAAA/wAAAP8AAADSAAAA0gAAAP8AAAD/AAAA/52cm//29PT/7uzs//Px8P+xrqz/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/hYSE/97a2f98c27/d21m/4N5
+ cf+Ge3P/e3Bp/25mYP+yrqv/0M7O/wwMDP8DAgL/BQUE/wUFBf8GBgX/BwYG/wcHBv8IBwf/CAgH/wkI
+ CP8KCQj/CgkJ/wsKCf8LCgr/DAsK/wwLCv9RUE7/gH58/358e/9+fHr/fnt6/357ev9+e3n/fnp5/316
+ eP99enj/fXp4/315d/98eXf/fHl3/3x5dv98eHb/fHh2/3x4df98eHX/e3d1/3t3df97d3T/e3d0/3p2
+ dP97dnP/enZz/3p1c/96dXL/enZy/3x2c/92cm//OTY0/xwaGP8dGhn/LCop/93c2//+/f3/+fj4//v6
+ +P/6+fT/6+rz/+Xk9//l5fb/8fHz//z79f/7+vj/+fj4//////+YlpX/FhQQ/4F/ff//////+fj4//r5
+ +f/8+/b/9PPz/+bl9f/l5Pf/6en0//n48//8+/f/+vn4//v6+v/t7ez/SEVD/y0qJ/8xLSr/Mi4r/zIv
+ LP8zLyz/MzAs/zEtKf+9ubb/8e/u/+7s7P/29PT/nZyb/wAAAP8AAAD/AAAA/wAAANIAAADSAAAA/wAA
+ AP8AAAD/jIqK//f09P/u6+v/8/Dv/7Csq/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP+Miov/kIqF/1FHP/9VS0T/V01G/1hOR/9VS0P/VEpC/2BXUf/Jx8b/GhkZ/wIC
+ Av8FBQX/BgYF/wYGBv8HBwb/CAcH/wgHB/8JCAj/CQkI/woJCf8LCgn/CwoJ/wwLCv8GBQX/Q0NB/+Xh
+ 3v/t6Ob/7Ofl/+vm5P/q5eP/6uXi/+jk4P/o49//5+Lf/+bg3f/l39z/493a/+Ld2f/i3Nj/4dvX/+Da
+ 1v/f2dX/3tfT/93W0v/c1dH/29TQ/9rTz//Z0s7/2NHM/9fQzP/Wzsr/1c3J/9XNyP/Vzcj/1c3J/9bN
+ yf+vqKX/JSEg/xwaGP8wLiz/2tjY//r5+P/29fD/09Lg/4KC3f9eXt//bW3l/2hp5P9lZd//nJvd/+fl
+ 5f/39vT///79/5SSkf8XFBL/g4B+///+/v/39vX/7u3n/6mp3f9nZ97/Y2Pi/29v5v9gYOH/eHje/8fF
+ 3//08+3/+ff3/+jn5f9IREL/Lion/zIuK/8yLiv/My8s/zMwLP80MC3/MS0p/7y3tf/x7u7/7uvr//f0
+ 9P+Mior/AAAA/wAAAP8AAAD/AAAA0gAAANMAAAD/AAAA/wAAAP+CgYH/9/X1/+3r6//y7+//r6up/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/4iHhv+MhYD/HRkX/wkI
+ B/8JCAf/CQgH/wkIB/8PDgz/U0tF/726uP8bGhr/AwIC/wYGBf8GBgb/BwcG/wgHB/8IBwf/CQgH/wkJ
+ CP8KCQn/CwoJ/wsKCf8MCwr/DAsL/wQEA/9YV1b/4t3a/+zp5//t6un/7Onn/+/r6v/w7Or/7uvp/+3p
+ 5//s6Ob/6+fk/+rm4//p5eL/6eTi/+Xh3v/h3Nn/4NvY/9/b2P/i3dn/497a/+Lc2P/g2tf/39nW/97Y
+ 1f/d19P/3dbS/9zV0f/Z0s7/1c7K/9XOyv/Wz8v/zcXA/7y1sf8vLCr/HBkX/zAuLP/X1dX/9/Xx/768
+ zv87O8b/OjrG/1xcw/9cXL7/XV2//1dXxP8uLsf/YF/G/9za2f/9+/n/k5CP/xgVE/+Bfnz//v37/+bk
+ 3/96ecb/KSnH/1FRxf9dXcD/W1u+/11dwv9ERMb/MDDH/6emyv/z8ev/5uPj/0hEQf8uKyj/Mi4r/zMv
+ LP8zMCz/NDAt/zQwLf8yLiv/u7az//Du7v/t6+v/9/X1/4KBgf8AAAD/AAAA/wAAAP8AAADTAAAAygAA
+ AP8AAAD/AAAA/4OCgv/39PT/7erq//Lu7v+uqqj/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/iomI/4B6dv8AAAD/BQUF/wYFBf8FBQX/BgUF/wAAAP83Mi7/wr28/xsb
+ Gv8DAwP/BwYG/wcHBv8IBwf/CAgH/wkICP8JCQj/CgkJ/wsKCf8LCgr/DAsK/wwLC/8NDAv/BQQE/1dW
+ Vf/d2NX/7Onn/+3q6P/s6ef/wb69/6Kfnf+opKL/rKil/6+qp/+tqKX/qaSh/6OfnP+fm5n/x8PB/+Pe
+ 2//g29j/4NvY/7m1sv+alZP/oJqX/6Sdmv+noJz/pZ+b/6Gal/+blZL/lZCN/7mzsP/Wz8v/1c7K/9bP
+ zP/Lw77/ubKu/y8tKv8cGhj/MC4s/9fU0v/Y1tP/REO4/zMzwP9ERLr/Gxus/wsLpv8PD6f/Jyew/0pK
+ vf8iIsD/eHa4//Lv6f+TkI7/GRYU/4B+fP/49fD/lZO7/yAgvv9KSr//Ly+y/xERqP8KCqb/Fxeq/zw8
+ t/89PcD/MTG6/8bEyf/l4uD/SERC/y8sKf8zLyz/MzAs/zQwLf80MC3/NTEu/zIuK/+6tbL/8O3t/+3q
+ 6v/39PT/g4KC/wAAAP8AAAD/AAAA/wAAAMoAAACmAAAA/wAAAP8AAAD/g4KB//b08//s6un/8O7t/6yp
+ pv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+JiIj/gXt2/wUE
+ A/8LCgn/CwoJ/wsKCf8LCgn/BgYF/zUwLf/Bvbz/Gxsb/wQEBP8HBwb/CAcH/wgHB/8JCAj/CQkI/woJ
+ Cf8LCgn/CwoJ/wwLCv8MCwv/DAwL/w0MDP8GBQT/V1ZV/9/a1//t6uj/8e7s/87My/85Nzb/GBUT/yEd
+ G/8oJCD/LSgk/ywnI/8mIR//HhsY/xYTEf9bWFb/3dnW/+Xg3f/NyMX/QT89/xcUEv8gHRr/JyMg/ywo
+ JP8sJyT/JiIf/x8cGf8WFBH/SUZE/8vFwf/X0Mz/19DM/8nBvP+yq6f/KCYj/x4bGf8xLiz/09DM/56b
+ s/8jI73/LS29/wICr/8AAK3/AACu/wAArv8AAK3/DQ2y/zAwwP83NrP/z83K/5OQjv8aFxT/gX58/+He
+ 1/9LSq7/LCzC/xYWtf8AAK3/AACu/wAArv8AAK3/AACu/yYmuv8lJb//f36v/9za0/9JRUL/MCwp/zMw
+ LP80MC3/NDAt/zUxLv81MS7/My8s/7m0sf/v7ev/7Orp//b08/+DgoH/AAAA/wAAAP8AAAD/AAAApgAA
+ AKQAAAD/AAAA/wAAAP+EgoL/9vPy/+zp6P/w7ez/q6el/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/4aFhP+Be3f/CQgH/w4NDP8VExH/GhcV/w4NDP8KCgn/ODMv/8C7
+ uv8WFRX/AAAA/wEAAP8BAQD/AgEB/wMCAf8FBAT/CgkJ/wsKCv8MCwr/DAwL/w0MC/8NDAz/Dg0M/wYF
+ BP9ZV1b/3NbT/+vn5v/x7+3/ysjG/0dCPf81Lin/NzAs/zYwK/82Lyv/NjAr/zYwK/83MSz/Miwm/2Zh
+ Xv/b19X/5uHe/8vGw/9STEj/NC4p/zcxLP83MCz/NjAr/zYwK/83MCz/NzEs/zQuKf9TTkn/ysTA/9fQ
+ zP/X0Mz/x765/62mof8jIR//Hxwa/zIvLf/LyMH/dnSi/x4ew/8JCb3/AAC7/wAAu/8AALv/AAC7/wAA
+ u/8AALr/FBTC/ygotv+rqbP/ko+L/xsYFf+Cfnv/xsO+/zExsP8ZGcT/AAC7/wAAu/8AALv/AAC7/wAA
+ u/8AALv/BAS8/xwdxf9eXaP/zsvC/0tHRP8xLSr/NDAt/zQwLf81MS7/NTIu/zYyL/8zLyz/uLKv/+/r
+ 6v/s6ej/9vPy/4SCgv8AAAD/AAAA/wAAAP8AAACkAAAApAAAAP8AAAD/AAAA/3Rzc//28/L/6+jn/+7r
+ 6v+xrKn/AwMD/wAAAP8AAAD/AAAA/wAAAP8VFRX/Pj49/0JBQf9BQUD/QUBA/0FAQP9AQED/p6Wl/4F7
+ dv8NCwn/GBUT/0Q9OP9NRT//JSEe/w4MC/85NDD/ycbD/1ZWVf9FREP/R0ZG/0hHRv9JSEf/SUhH/zIx
+ Mf8JCAf/DAsK/wwLC/8NDAv/DQwM/w4NDP8ODQz/BwYF/1pZWP/W0M3/5+Pi/+/t6//r6Of/uLKu/46E
+ fv+KgXr/ioF6/4qBev+KgHr/ioB6/4l/eP+SiYP/xL+8/+Tf3P/i3dv/4NvY/7awrP+KgXv/h312/4d9
+ dv+HfXb/h312/4Z8dv+GfHX/jIN8/7avqv/W0Mz/1s/L/9fQzP/EvLb/q6Sg/yQiIP8fHRv/Mi8t/8rG
+ vv9oZpr/ERHI/wEByP8AAMf/AADH/wAAx/8AAMf/AADH/wAAx/8GBsr/HBy4/6Kfq/+Tj4v/HBkW/4F9
+ e/+/u7b/Jyav/wkJzP8AAMf/AADH/wAAx/8AAMf/AADH/wAAx/8AAMf/Dg7N/1NRnP/Lx77/SkdE/zEt
+ Kv80MC3/NTEu/zUyLv82Mi//NjIu/zg0Mf+7trP/7erp/+vo5//28/L/dHNz/wAAAP8AAAD/AAAA/wAA
+ AKQAAAClAAAA/wAAAP8AAAD/aWdn//by8f/q5+b/7ero/7eyr/8ICAj/AAAA/wAAAP8AAAD/GhkZ/8jG
+ xf/m4+P/wL29/8C9vP/DwL//xsLB/8jFxP/KxsX/bWZh/xQRDv8dGhj/NS8r/zcwLP8mIh//FBEP/zcy
+ Lv+2sa7/yMXE/8PAv//Avb3/vbq6/7q4t//PzMz/6ufm/1RSUv8FBQT/DAwL/w0MDP8ODQz/Dg0M/w8O
+ Df8IBwb/VlVU/9LMyP/p5uT/8/Hw//Hv7v/08vH/9PHw//Lv7v/y7uz/8e3r//Ds6v/u6+n/7ern/+3p
+ 5//q5uT/5uLg/+Xh3//l4N7/5+Lf/+bi3//m4N7/5N/c/+Te2//j3dr/4dzZ/+Hb1//g2db/3tjU/9rU
+ 0P/Z0s//2tTR/8S7t/+up6P/JCIg/yAdG/8yLy3/zcnD/316mv8IB8v/AQHV/wAA0/8AANP/AADT/wAA
+ 0/8AANP/AADT/wMD2P8ZGbL/t7Sz/5GNif8dGRf/f3t4/9DMxP8pKKT/BATY/wAA0/8AANP/AADT/wAA
+ 0/8AANP/AADT/wAA1P8FBdP/XFqW/9XRx/9JRUL/MS0q/zUxLv81MS7/NjIv/zYyL/82Mi7/Pjo2/8C6
+ t//s6ef/6ufm//by8f9pZ2f/AAAA/wAAAP8AAAD/AAAApQAAAJ8AAAD/AAAA/wAAAP9qaGj/9vHw/+rm
+ 5f/t6ef/t7Gu/wgICP8AAAD/AAAA/wAAAP9TUlH/0c7N/zQxMP8ZFhX/Hxwa/yklIv80Liv/PTcy/0lC
+ PP9JQjv/HBkX/xwZF/8aFxX/GhcV/xsZFv8aGBX/NC8r/0lCPP83Mi7/LCck/yAdG/8UEhD/Dg0M/xEQ
+ EP+amJf/npyb/wUFBP8NDAv/Dg0M/w4NDP8PDg3/EA8O/woJCP9JR0b/0MvH/762sf/HwLv/xb65/8W9
+ uP/Evbj/w7y3/8O7tv/CurX/wbm0/8C4s/+/t7L/vrax/761sf+8tK//vLSu/7uzrf+7sqz/urGs/7qw
+ q/+5r6r/uK6p/7etqP+2rab/taym/7Sqpf+0qqT/s6mj/7Koov+xp6H/qZ6Y/7Stqf8kIiD/IR4c/zIv
+ Lf/Mx8P/uLS0/xIStf8AAOT/AADf/wAA3/8AAN//AADf/wAA3/8AAN//AADi/z08n//f2tD/jIiF/x4b
+ GP97d3T/6eTb/1tYnP8AANv/AADg/wAA3/8AAN//AADf/wAA3/8AAN//AADj/wUFxf+alqb/2dTO/0hE
+ Qf8yLiv/NTIu/zYyL/83My//NzMw/zczL/8+Ojf/wLq3/+zo5v/q5uX/9vHw/2poaP8AAAD/AAAA/wAA
+ AP8AAACfAAAAewAAAP8AAAD/AAAA/2loZ//08e//6ebk/+zp5v+1sK3/CAgI/wAAAP8AAAD/AAAA/1lY
+ V/+Oioj/Lyok/zkyLf80Lyr/NjAr/zcxLP84Mi3/OjMu/zUwK/8hHRr/IR4b/yEeG/8hHhv/IR4b/yAd
+ Gv8rJyP/OjQv/zcxLP82MCv/NS8q/zQuKf80Lir/My0o/1xYVf+WlJP/CAcG/w0MDP8ODQz/Dw4N/w8O
+ Df8QDw7/Dg0M/x8eHf+opqT/wLq2/7iyrv+4sa3/uLGt/7ewrP+2r6v/tq+q/7Wuqf+0raj/tKyo/7Os
+ p/+zq6b/sqqm/7GppP+xqKT/sKij/6+nov+up6H/rqag/62loP+tpJ//rKOf/6yjnv+rop3/qqGb/6mg
+ m/+pn5r/qJ+a/6eemP+xqaT/cGto/x8dG/8hHx3/Mi8t/8XAvf/j3tX/dnSm/wAA2P8AAO//AADs/wAA
+ 7P8AAOz/AADt/wAA8P8TEsL/qqax/+nk3v+KhoP/IR4b/3t3c//p5N//wr27/ycntv8AAOz/AADt/wAA
+ 7P8AAOz/AADs/wAA7/8AAOP/VlSm/9zXzf/Szcn/SURB/zMvLP82Mi//NjIv/zczMP84NDD/NzMw/z87
+ N/+/ubX/6+jl/+nm5P/08e//aWhn/wAAAP8AAAD/AAAA/wAAAHsAAAB3AAAA/wAAAP8AAAD/amlo//Pw
+ 7v/o5eP/6+jl/7Svq/8ICAj/AAAA/wAAAP8AAAD/VlVV/5yWk/80Lin/IyAd/yQhHf8kIB3/JCAd/yQg
+ Hf8kIB3/JCAd/yYiHv8mIh7/JiIf/yYiH/8mIh//JiIe/yUhHv8kIB3/JCAd/yQgHf8lIR3/JSEd/yQg
+ Hf8pJSD/eHFt/5OQj/8IBwf/Dg0M/w8ODf8PDg3/EA8O/xEPDv8REA//Dw0M/xcWFf8vLi3/MS8v/zEv
+ Lv8xMC7/MTAv/zIwL/8yMC//MjEw/zMxMP8zMjH/NDIw/zQyMP80MjH/NDIx/zUzMf81MzH/NTMy/zUz
+ Mf82NDL/NjQy/zc1M/83NTP/ODY0/zk3Nf86NzX/Ozg2/zw6OP89Ojj/Pzw6/zk2NP8mIyH/KSYk/ykm
+ JP81MjD/wry5/9/Z1v/b1cv/gH22/xwb3P8AAPP/AAD2/wAA9v8CAu7/MS/M/6mkuf/g2tH/493Z/4uG
+ g/8iHxz/d3Nw/+Pd2f/f2dP/vLe7/0JAxv8FBez/AAD2/wAA9/8AAPX/ERDg/2hluv/Uzsb/3tnV/9DK
+ x/9JRUH/My8s/zYyL/83My//ODQw/zg0Mf84NDD/Pzs3/764tP/q5uT/6OXj//Pw7v9qaWj/AAAA/wAA
+ AP8AAAD/AAAAdwAAAHcAAAD/AAAA/wAAAP9cW1r/7+vq/+jk4v/q5uT/s62q/wgICP8AAAD/AAAA/wAA
+ AP9WVFT/nZiU/ywnI/8oJCH/KSUi/yomIv8pJSL/KSUi/yklIv8pJSL/KiUi/yYiH/8dGhf/GxgW/yMf
+ Hf8pJSH/KSUi/yklIv8pJSL/KSUi/yomIv8qJiL/KSYi/yUhHv9zbWn/lJGP/wkIB/8PDg3/EA8O/xAP
+ Dv8RDw7/ERAP/xIQD/8SERD/ERAO/w4MDP8ODQz/Dg4M/w8ODf8PDg3/EA8O/xEPDv8REA//EhEQ/xMS
+ EP8VExL/FhQT/xcWFP8YFxb/GhkX/xwaGf8eHBr/IB4c/yEgHv8jIiD/JSMh/yYkIv8nJSP/KCYk/ykn
+ Jf8pKCb/Kigm/yooJv8rKCb/Kykn/y4sKv8uLCr/Liwq/ywpJv+Mh4T/2tPP/93W0v/i29H/ysTK/5aR
+ 0P+Cftr/g3/Y/6ehzf/X0Mr/4drT/93W0v/QycX/XlpX/yklI/9MSEX/xb67/9zV0v/g2dP/3NXN/6+p
+ zP+Hgtf/gn7b/5CL0//Ausj/4drP/97X0v/c1dH/op2a/zk1Mv81Mi7/NzMw/zg0MP84NDH/OTUx/zg0
+ Mf9AOzj/vLay/+nk4//o5OL/7+vq/1xbWv8AAAD/AAAA/wAAAP8AAAB3AAAAdwAAAP8AAAD/AAAA/1JR
+ UP/s6Ob/5+Pi/+nl4/+yrKj/CAgH/wAAAP8AAAD/AAAA/1VUU/+Zk5D/Lykk/y0pJf8uKib/JCAd/yQg
+ Hf8vKib/Likl/y4pJf8vKib/JyIf/x0aGP8dGhj/IR4b/y4pJv8uKSX/Likl/y8qJv8pJSH/HhsZ/ywo
+ JP8uKiX/KiUh/3BqZv+Sj43/CQgH/w4NDP8ODQz/Dw4N/xAPDv8REA//EhEQ/xIREP8bGRf/KSQi/ygk
+ If8oJSH/Kici/y0oJP8vKyb/MS0o/zMvKv82MS3/NzMu/zg0L/85NTH/OjYy/zo2M/87NzT/Ozc0/zs3
+ NP88ODP/PDg0/zs3M/87NzP/Ozcz/zo2Mv85NjL/OjYy/zo2Mv85NTL/OjUy/zk1Mv84NDH/MC0r/y8s
+ Kv8uLSr/Lisp/zEvLf9VUU//XlpX/11ZVv9gXFf/ZWFX/2hjVv9nY1b/ZF9X/2BbV/9eWlf/YFtY/0tI
+ RP8vLCn/MS4r/y8sKf9FQT7/X1tX/19bWP9gW1j/ZWBY/2plWP9qZVf/aWRY/2RfWf9hXFn/Yl1a/15Z
+ Vv89OTb/NTEt/zczMP84NDD/ODQx/zk1Mf85NTL/OTQx/0A8N/+8tbH/6OPi/+fj4v/s6Ob/UlFQ/wAA
+ AP8AAAD/AAAA/wAAAHcAAAB1AAAA/wAAAP8AAAD/U1FR/+vn5f/m4uD/6OPh/7Grp/8JCQj/AAAA/wAA
+ AP8AAAD/VVNS/5WPjP8wKib/Mi0o/zkyLv82MCz/OjQw/zMtKf8zLSn/Mi0o/zgyLv87NTD/Miwq/zMt
+ Kv83MS3/PjYy/zItKP8zLSn/Mi0o/zgyLv83MS3/PDUw/zQvKv8uKCT/bWdi/5SRj/8UExL/HBsa/x8d
+ HP8hIB//JCMi/ycmJf8qKSf/LS0r/1tSS/9sYFj/al9W/2hdWP9jWF//Zltb/2JXX/9iWF3/YFVf/2BW
+ Xv9hV1r/XlRe/2JXVv9iWFX/ZFlQ/2JYUf9iV1D/YldQ/1tSV/9dU1T/XVNR/1xSUv9fVU3/WlBS/15U
+ TP9bUU7/WlBO/1xSSv9cUkv/XFJL/1xSS/9CPDj/Lywq/y8tK/8wLSv/Ly0q/ywqJ/8rKCb/Kygm/yso
+ Jv8rKCb/Kygm/yspJv8rKCb/LCkm/y0pJ/8sKSb/Liso/zEuLP8yLiz/Mi8s/zAsKf8tKif/LSon/y4q
+ J/8uKif/Lyso/y8rKP8vKyj/Lyso/zAsKf8wLCn/MS0q/zUxLv83MzD/ODQw/zg0Mf85NTH/OTUy/zo1
+ Mv85NTH/QT05/7u0sP/o4+H/5+Ph/+vn5f9TUVH/AAAA/wAAAP8AAAD/AAAAdQAAAFAAAAD/AAAA/wAA
+ AP9TUVD/6ubj/+Xh3//m4d//u7Sv/xIREf8AAAD/AAAA/wAAAP9UUlH/j4mG/zEsJ/83MS3/ODIt/0Q8
+ N/9LQz3/NzEs/zcxLf83MSz/OzQw/1ZNRv9TSkP/UEdC/1lPSf9DPDf/NjAs/zcxLf82MCz/QTs1/0tD
+ Pf87NC//NzEt/zMtKP9nYV3/nZmX/zAvLv80MzL/NTQz/zU0M/81MzL/NDMy/zMzMv84NTT/aV5W/21h
+ WP9uYlX/Vkxz/ygkrf8hHrX/MCuh/zcxmP87NZH/JyOs/zMum/8yLJz/RDyC/0A5h/9oXU3/Y1lR/2RZ
+ UP9eVFf/Kyah/y8qm/87NIj/MiyU/y0pm/8pJKH/NTCO/0I7eP8/OHz/X1VH/1xSS/9cUkv/XlNM/0lB
+ PP8vLSv/MC4s/zAuLP8xLiv/MS4s/zEuLP8xLiz/MS4r/zIuLP8xLiz/Mi4s/zEvLP8yLiz/Mi8s/zIv
+ LP8yLyz/Mi8t/zIvLf8zMC3/MzAt/zMvLP80MC3/NDAt/zQwLf80MS3/NTEu/zUxLf81MS7/NTIu/zYy
+ L/83My//NzMw/zg0MP84NDH/OTUx/zk1Mv86NjL/OjYy/zk1Mf9JRED/wrq1/+bh3//m4uD/6ubk/1NR
+ UP8AAAD/AAAA/wAAAP8AAABQAAAASQAAAP8AAAD/AAAA/1NSUf/p5OH/5N/d/+Xg3v+8tbH/FBMS/wAB
+ AP8DAwP/BAQE/1dVU/+jnZn/LSgj/zcxLf84Mi3/NzEs/zcxLP84Mi3/ODIt/zo0MP86NDD/Rj45/11T
+ TP9hVk//T0dA/zs1Mf87NTH/ODIu/zgyLf83Mi3/NjEs/zcxLf84Mi3/Ligj/25nY/+hnZr/MC8v/zQz
+ Mv80MzL/NDMy/zQzMf80MjL/NDIx/zY0M/9lWlP/bWFZ/25iVP9RSHj/IR22/yAbuP8iHrT/NzGX/1ZN
+ bf8bGbz/KCSp/zUwl/8xK5z/JSGt/15UXP9lWlD/ZFlQ/11TWf8jIKz/KSWj/yQgrP8sJ57/Hhuz/y4p
+ mf8dGrT/KSSh/ygkoP9cUk3/XFJK/1xSS/9eU0z/SEE8/zAuK/8xLyz/MS4s/zEuLP8yLyz/MS8t/zIv
+ LP8yLyz/Mi8s/zIvLP8yLyz/Mi8t/zIvLP8zLy3/MzAt/zMwLf8zMC3/MzAu/zMwLf80MC3/NDAt/zQw
+ Lf81MS7/NDEt/zUxLv81MS7/NjIu/zUxLv82Mi//NjIv/zczMP84NDD/ODQx/zk1Mf85NTL/OjUy/zo2
+ Mv87NzP/OjUy/0pGQf/Cu7b/5uHf/+bh3//p5OP/U1JR/wAAAP8AAAD/AAAA/wAAAEkAAABKAAAA/wAA
+ AP8AAAD/SUdG/+Xg3f/j3tz/5N/d/7u0sP8aGRj/LCss/zc3N/81NTX/UE9O/8S+uv99d3P/VlBL/1hT
+ Tv9YU07/WFNO/1lTTv9VUEv/Pjg0/z84M/8/ODP/Pjcy/z43Mv8+ODL/QDkz/zs1MP9NSEP/WVNO/1lT
+ Tv9YU07/WFNO/1dRTP9kXlr/urSw/3l2dP8wMC//NTQz/zU0M/81MzL/NDMz/zUzMv80MzL/NjUz/2NZ
+ Uv9tYln/bGBX/2dcXP9XTXH/WlFr/09Gef9aUGn/ZVpa/1BGdf9VTGz/UUlw/09Hcv9LQ3f/XlNc/2Va
+ Uf9kWVH/Y1hR/1JKZ/9QSGn/SUJz/05Fav9USmH/U0pg/0tDbP9LQ2z/SEBu/1tRUP9cUkv/XFJL/15T
+ TP9IQj3/MS4s/zMwLf8yLy3/Mi8t/zIvLf8yMC3/Mi8t/zMwLf8zLy3/My8t/zMvLf8zMC3/Mi8t/zMw
+ Lf80MC3/NDEu/zQxLv80MC7/NDAu/zUxLv81MS7/NTEu/zUyLv81MS7/NTEu/zYyL/82Mi//NjIv/zYy
+ L/83My//ODQw/zg0Mf85NTH/OTUy/zo1Mv86NjL/Ozcz/zs3M/86NjL/SkVB/8O7tv/l4d//5eHf/+bh
+ 3v9IR0b/AAAA/wAAAP8AAAD/AAAASgAAAEoAAAD/AAAA/wAAAP88Ojr/4dvZ/+Lc2v/j3dv/urKt/xwb
+ Gv85ODj/QEBA/z8/Pv87Ozv/WlhX/4OAfv+Cfnz/gX17/4F9e/+BfXv/fnt5/66ppf9hW1f/Pzgy/0Q9
+ N/9EPTf/RD03/0Q9N/9EPTf/Pjg0/6qkoP+JhYL/fnp4/397eP9/e3j/fnt4/4J+fP9mZGL/NjU0/zY1
+ NP82NTT/NjU0/zY0NP81NDP/NTQz/zU0Mv83NjT/ZFpT/3BkW/9uYlr/bmJZ/29jVv9uYlb/b2JU/21h
+ Vf9rX1b/bWFT/2tfU/9rX1P/al9R/2pfUf9oXFL/ZltT/2ZbUv9lWlL/ZltP/2ZaT/9lW03/ZVlN/2NZ
+ Tf9iWE3/Y1hL/2JXS/9iV0r/X1VN/15TTP9eU0z/X1RN/0ZBPP8xLy3/MzAu/zMwLv8zMC3/MzAt/zMw
+ Lv8zMS7/NDAu/zQwLv80MC7/MzAt/zQwLv8zMC7/NDEu/zQxLv81Mi//NTEu/zUxLv81MS7/NTIv/zYy
+ L/82Mi//NjIv/zYyL/82Mi//NjMv/zczMP83My//NzMw/zg0MP84NDH/OTUx/zk1Mv86NjL/OjYy/zs3
+ M/88NzT/PDg0/zs2M/9LRUL/wrm0/+Xg3v/l4N7/4tza/zw6Ov8AAAD/AAAA/wAAAP8AAABKAAAASQAA
+ AP8AAAD/AAAA/z07Ov/f2tf/4NvY/+Hc2f+4sav/HRsa/zg4OP9APz//Pz4+/z8+Pv87Ozv/ODg4/zg4
+ Of84ODj/ODg4/zc4N/8zMzP/j4qH/2NdWP9DPDb/SEE7/0lCO/9JQjz/SUE7/0lBO/9AOzX/oZuX/0RD
+ Qv8yMTD/MzMy/zQzMv8zMjL/MjIx/zIxMf83NjX/NzY1/zc2Nf83NTT/NjU0/zY1NP81NTP/NTUz/zU0
+ M/9HQz//WlJM/1lRS/9ZUEv/WFBK/1hQSv9XT0n/V09J/1ZOSf9WTkj/VU1I/1VNR/9UTUf/VE1H/1RM
+ Rv9UTEb/U0tG/1JLRf9SSkX/UkpF/1FKRP9RSUT/UElE/1FJQ/9QSUP/T0hC/09IQv9OR0H/T0dC/05H
+ Qv9KRD//ODQx/zMxLv8zMS7/MzEu/zQxLv8zMC7/NDEu/zQxL/80MS7/NDEv/zQxLv80MS7/NDEu/zQx
+ Lv80MS7/NTIv/zUyL/81Mi//NjIv/zUyL/82Mi//NjMv/zcyMP82Mi//NzMw/zYzL/83MzD/NzMw/zg0
+ MP84NDD/ODQx/zk1Mf85NTL/OjUy/zo2Mv87NzP/Ozcz/zw4NP89ODT/Ozcz/0tGQv/AuLP/5N/d/+Tf
+ 3f/g29j/PDs6/wAAAP8AAAD/AAAA/wAAAEoAAAA6AAAA+wAAAP8AAAD/PDs6/97Y1f/f2db/4NrX/7ev
+ qv8dHBv/Ojk5/0FAQP9AQD//Pz8//z4/Pv8+Pj7/PT09/z09Pf89Pjz/PTw9/zg5OP+PiYX/X1lV/0hA
+ Of9MRD3/RT44/0Q9N/9KQjz/TkY//0E7Nf+dl5L/SEdG/zc3Nv85ODj/OTg3/zk3N/84Nzf/OTc3/zg3
+ Nv84NzX/ODc2/zc2NP82NjT/NjY0/zY1NP82NTT/NzU0/zUzM/8zMjH/MjIx/zIxMP8yMTD/MjEw/zIx
+ MP8yMTD/MjEw/zEwL/8xMC//MTAv/zEwL/8yMC//MjAv/zEwLv8xMC7/MTAu/zEvLv8xLy7/MS8u/zEv
+ Lv8xLy7/MTAu/zIwLv8xLy3/MS8t/zEvLf8yMC7/MTAt/zEwLf80MTD/NTIw/zQyL/80Mi//NTIv/zUy
+ L/81MS//NTEv/zUyMP81Mi//NDEv/zUyL/81MS7/NTIv/zUyMP82Mi//NjIv/zczMP82Mi//NzMw/zcz
+ MP83MzD/NzMw/zczMP83NDD/ODQx/zg0MP84NDH/OTUx/zk1Mf85NTH/OjUy/zo2Mv86NjL/Ozcz/zw3
+ NP88ODT/PTg1/z05Nf88NzT/S0ZC/8C3sf/k39z/5N/c/9/Z1v88Ozr/AAAA/wAAAP8AAAD9AAAAOwAA
+ ADgAAAD7AAAA/wAAAP89Ozr/3dfT/93X1P/e2NX/ta2o/x0cGv86Ojr/QUFB/0BAQP9AQD//Pz8//z8/
+ P/8/Pj7/Pj49/z4+Pv8+PT3/Ojk5/4yGg/9cVlH/TEQ9/1BIQf9COzb/PDUy/01FPv9TSkP/QTs2/5mS
+ jf9JR0f/ODg3/zo5OP86ODj/Ojk4/zk4N/85ODf/OTg3/zk4N/84Nzb/ODc2/zg3Nf83NjX/NzY0/zc1
+ Nf83NTT/Ozk3/0RAPf9DPzz/Qz88/0M+O/9CPjv/Qj48/0I+O/9CPjr/Qj07/0E9Ov9APTr/QDw5/0E8
+ Ov9APTn/QDw5/0A8OP9APDj/Pzs3/0A7OP8/Ozf/Pzs4/z87OP8/Ojf/Pzo3/z46N/8+Ojb/Pjo2/z46
+ Nv8/Ozb/PTk1/zUyMP81MjD/NTIw/zUyMP81Mi//NTIv/zUyL/83Myz/NjMt/zczLf83NCz/NzQr/zc0
+ LP83NC3/ODMs/zczLP84NCz/ODUt/zk1Kv84NS3/OTQr/zg0Lv85NS3/OTUs/zo2Kv85NS3/OjUs/zs3
+ Kv86NjD/OjYy/zk1Mv86NjL/OjYy/zs3M/88NzT/PDg0/z04NP89OTX/Pjk2/zw4NP9LRkL/vrWw/+Pe
+ 2//j3tv/3tjU/z07Ov8AAAD/AAAA/wAAAPsAAAA4AAAAOAAAAPsAAAD/AAAA/zQzMv/Z08//3NbS/9zW
+ 0v+5sKv/IiAf/zs7O/9CQUH/QUBA/0BAQP9AQED/QEBA/z8/P/8/Pj7/Pz4+/z8+Pv86Ozr/ioSA/1lT
+ Tv9RSEH/VU1F/2BVTv9jWFH/V05G/1dOR/9CPDb/lIyI/0pIR/85OTj/Ojk5/zo5OP86OTj/Ojk4/zo5
+ OP86OTj/OTg3/zk4N/85ODb/ODc2/zg2Nv84NjX/ODY1/zg2Nf9cU03/bmJa/2xhWP9sYFj/a19X/2tf
+ V/9qXlb/aV5W/2ldVf9oXVX/Z1xU/2ZbVP9mW1P/ZltT/2VaUv9kWVL/ZFlS/2NYUf9iWFD/YldQ/2FX
+ T/9hVk//YFZO/19VTv9fVU3/XlRN/15TTP9dU0z/XVNM/11TTP9dU0z/Qj05/zUyL/82MjH/NTMw/zYz
+ MP82MzD/NjMv/yspVP8wLUX/LyxI/ysoVv8pJ1n/KylU/y4rS/8sKVT/LClS/y0pUf8uK0//JSNs/y8r
+ Tf8rKFr/MS5G/y0qVP8tKlX/JSJs/y8sUP8sKVj/JiNt/zYyPf87NjH/OjYy/zo2Mv87NzP/Ozcz/zw4
+ NP89ODT/PTg1/z45Nv8+Ojb/PTg0/09JRf/AuLH/4t3a/+Ld2v/a1ND/NDIy/wAAAP8AAAD/AAAA+wAA
+ ADgAAAA4AAAA+wAAAP8AAAD/JyUl/9TOyv/a1ND/2tTQ/8C2sP8qKCf/Ozs7/0JCQv9BQUH/QUFB/0FB
+ Qf9BQED/QEA//0BAP/9APz//QD8+/zw7O/+Ign3/UkxI/1VMRP9cU0v/XlNM/19VTf9cUkv/X1VN/z03
+ Mf+SioX/SkhH/zo6OP87Ojn/Ozo5/zs6Of87Ojn/Ozk5/zs5OP86OTj/Ojk4/zo4OP85ODf/OTg3/zk3
+ Nv84Nzb/OTc2/2FXUP9uYVn/bGBY/2tfV/9qX1f/al5W/2leVv9oXVX/aF1V/2dcVP9nW1T/ZltT/2Va
+ U/9lWlL/ZFlS/2RZUf9jWFH/YlhQ/2JXUP9hV0//YFZP/2BWTv9fVU7/X1RN/15UTf9dU0z/XVNM/1xS
+ S/9cUkv/XFJL/15TTP9HQTz/NTIw/zc0Mf83MzH/NzMx/zczMv84NSz/ERCp/xsaif8MC7v/EA+w/wsL
+ vv8QEKz/GhiP/xAPr/8UE6P/EA6v/wsLvv8UE6H/ExKk/wkIxv8iH3j/CwrB/woJw/8WFKD/DAy8/w4N
+ t/8UE6T/GReW/z04Lf87NzP/Ozcz/zw3NP88ODT/PTg1/z05Nf8+OTb/Pjo2/z86Nv88ODX/VE5K/8S7
+ tP/h29j/4t3Z/9XPy/8mJSX/AAAA/wAAAP8AAAD7AAAAOAAAADgAAAD7AAAA/wAAAP8nJiX/08zI/9nS
+ zv/Z0s7/v7aw/yglJP87Ozv/Q0ND/0JCQf9CQkL/QUFB/0FBQf9BQED/QUBA/0BAP/9APz//PDs7/4iB
+ fP9gWlT/KiYi/0Y/OP9EPTf/RD03/0U+OP86NC7/Mi4r/6Wak/9FQ0L/Ozs6/zw7Ov88Ozr/PDo5/zw6
+ Ov88Ojn/Ozo5/zs6Of87OTj/Ojk4/zo5OP86ODf/Ojg3/zk4Nv86ODb/YFZQ/25iWf9sYFj/a19X/2pf
+ V/9qXlb/aV5W/2ldVf9oXVX/Z1xU/2dcVP9mW1P/ZltT/2VaUv9kWVL/ZFlR/2NYUf9jWFD/YldQ/2FX
+ T/9hVk//YFZO/2BVTv9fVU3/XlRN/15TTP9dU0z/XVNM/1xSS/9cUkv/XlNM/0dBPf81MzD/ODQy/zg0
+ Mv84NDL/NzQy/zg1Lf8SEaf/CAfJ/xEQrf8REKv/EhGr/xcWmP8REKv/EBCt/xUUof8KCcL/EhCq/w8O
+ s/8ZF5X/Dg23/xMSpv8YFpn/FhSg/xAQrv8QD7L/FhSg/xQTpf8kIXf/PTku/zw4NP88NzT/PDg0/z04
+ NP89OTX/Pjk2/z46Nv8/Ojb/Pzs3/z05Nf9UTkr/xbu1/+Hb2P/g29j/1M3J/ycmJf8AAAD/AAAA/wAA
+ APsAAAA4AAAAKAAAAPEAAAD/AAAA/yclJf/Rysb/18/L/9fPy/+/ta//JSIh/zo6Ov9FRET/Q0JC/0JC
+ Qv9CQkL/QkFB/0JBQf9BQUD/QUBA/0FAQP8+PT3/W1dU/6CUjP9dVlH/S0VB/0xFQf9MRUH/S0VB/1BK
+ Rf+TiID/fHVw/zs6Of89PDv/PTw7/z07Ov89Ozr/PDs6/zw7Ov88Ozn/Ozo5/zs6Of87Ojj/Ozk4/zo5
+ OP86OTf/Ojk3/zo4N/9gV1H/bmJZ/2xgWP9rYFf/a19X/2pfV/9pXlb/aV1V/2hdVf9oXFT/Z1xU/2Zb
+ U/9mW1P/ZVpT/2VaUv9kWVL/Y1lR/2NYUf9iWFD/YldQ/2FWT/9gVk//YFVO/19VTv9fVE3/XlRN/11T
+ TP9dU0z/XFJL/1xSS/9eU0z/SEI9/zY0Mf84NTL/ODUy/zg1Mv84NDL/OTYu/xcWmP8VE6L/JyVp/xcV
+ m/8XFZz/LytS/zUyPv82Mjz/Kidi/x4ch/82Mj3/NDBF/zo2NP82Mj//NTFD/zo2NP87NzL/IyF5/xoZ
+ kv86Njj/NTBI/zw3Mv88ODT/PDg0/zw4NP89ODT/PTg1/z45Nv8+Ojb/Pzo2/z87N/9AOzf/Pjk2/1RO
+ Sv/Eu7X/4dvY/+Da1//Sy8f/JyUl/wAAAP8AAAD/AAAA8QAAACgAAAAlAAAA7wAAAP8AAAD/JyYl/9DI
+ xP/Z0s7/3NXR/7+1r/8lIiH/Ozs7/0ZFRf9EREP/REND/0NDQv9DQ0L/Q0JC/0JBQf9CQUH/QkFA/0FB
+ QP8/Pj7/TUtK/2VgXf9nYV7/ZmFd/2ZhXf9lYF3/ZWBd/1RQTf8+PDz/Pj08/z49PP8+PTz/Pjw7/z08
+ Ov89PDv/PTw7/z08O/88Ozr/PDs6/zw7Of88Ojn/PDo5/zs5OP87OTj/Ozk3/19WUP9wY1v/bmFZ/21h
+ Wf9tYFj/bGBY/2xfV/9rX1f/al5W/2peVv9oXVX/aFxV/2dcVP9nXFT/ZltT/2VaU/9lWlL/ZFlS/2RZ
+ Uf9jWFH/YlhQ/2JXUP9hV0//YFZP/2BWTv9fVU7/X1VN/15UTf9dU0z/XVNM/15UTP9GQDz/ODUy/zk2
+ M/85NjP/OTYz/zk2M/85NjP/NzQ5/zg1Nf85NTX/ODQ5/zczO/85NjX/Ozcx/zo3Mv87Ny//PDgs/zs3
+ Mf87NzH/Ojcz/zs4Mv88ODL/Ozcz/zw4M/86Nzb/OTY6/z04Mv89OTL/PDg0/z04Nf89OTX/Pjk1/z05
+ Nf8+OTb/Pjo2/z86Nv8/Ozf/QDs3/0A8OP8/Ojb/VE9K/8O5s//g29f/39rW/9HJxf8nJiX/AAAA/wAA
+ AP8AAADvAAAAJQAAACUAAADvAAAA/wAAAP8hIB//zMXB/97Y1P/h29j/v7Wv/yUiIf88Ozv/RkZF/0VE
+ RP9EREP/REND/0RDQ/9DQkL/Q0JC/0JCQf9CQkH/QkFB/0FBQf8/Pz//Pj09/z49Pf8+PTz/PT08/zw8
+ O/88Ozr/PTw7/z8+Pf8/Pj3/Pz48/z49PP8+PTz/Pj07/z49O/8+PDv/Pjw7/z08O/89Ozr/PTs6/zw7
+ Ov88Ozn/PDo5/zw6Of87OTj/RUE+/1lQS/9ZUUv/WFFL/1hQS/9YUEv/V09K/1dPSv9WT0n/Vk9J/1ZO
+ Sf9VTkj/VU1I/1VNR/9UTEf/VExH/1NMR/9SS0b/UktG/1JLRf9RSkX/UUpE/1BKRP9RSUT/UElD/1BI
+ Q/9QSEL/T0hC/09HQv9OR0L/SkM//zw4Nf86NjT/OTc0/zk2NP85NzT/OjYz/zk2M/86NzP/OjYz/zo2
+ M/86NzP/Ozcz/zs3NP86NzT/Ojc0/zo3M/87NzT/Ojcz/zs3NP87NzT/Ozg0/zw4NP88ODX/PDg0/zw4
+ NP88OTT/PDg0/z05Nf89OTX/PTk1/z45Nf8+Ojb/Pjk2/z46Nv8/Ojb/Pzs3/0A7N/9APDj/QTw4/z87
+ N/9VTkr/wrix/+Da1v/f2dX/zcbB/yEgH/8AAAD/AAAA/wAAAO8AAAAlAAAAJQAAAO8AAAD/AAAA/xIS
+ Ef/HwLv/3dfU/+Da1/+9tK3/JSMh/zw8PP9HRkb/RUVE/0VERP9FRET/RURD/0RDQ/9DQ0L/Q0NC/0NC
+ Qv9CQkL/QkFB/0JBQP9BQUD/QkFA/0FBQP9BQED/QD8//0A/Pv9APz7/QD8+/0A/Pf8/Pz3/Pz49/z49
+ PP8+PTz/Pj08/z89PP8+PTz/Pjw7/z48O/89PDv/Pjw6/z07Ov88Ozr/PDs5/zw6Of86OTf/Ojg3/zs6
+ OP87Ojj/Ozo4/zo5N/86OTf/Ojg2/zo5N/86ODf/Ojg2/zo4Nv86ODb/Ojc2/zk3Nv86NzX/Ojg2/zo3
+ Nf85NzX/OTc1/zk3Nf85NzT/OTY0/zk2NP86NzX/OTY1/zk2NP86NjT/OTc0/zk2NP84NTP/Ojc1/zo3
+ Nf86NzT/Ojc0/zo3NP86NzT/Ojc0/zo3NP86NjT/Ozc0/zs3NP87NzT/Ozg0/zs3NP87NzT/Ozg0/zs3
+ NP87NzT/Ozc0/zw4NP88OTX/PDg1/z05Nf89ODX/PTg1/zw5Nf89OTX/PTk1/z46Nv8+OTX/Pjk2/z86
+ N/8/Ojf/Pzo2/z87N/9AOzf/QDw4/0E8OP9BPTn/QDs3/1VPSv/Bt7D/39nW/97Y1P/IwLv/EhER/wAA
+ AP8AAAD/AAAA7wAAACUAAAAmAAAA7wAAAP8AAAD/ExIS/8W9uP/c1tL/39rW/76zrP8mJCH/PT08/0hH
+ R/9GRkb/RkVF/0ZFRf9FRUT/RURE/0VEQ/9EREP/RENC/0RDQv9DQkL/Q0JB/0NCQf9DQkH/QkFB/0JB
+ QP9BQUD/QUA//0FAP/9BQD7/QT8//0E/Pv9APz7/QD89/z8+Pf8/Pj3/Pz49/z8+PP8/PTz/Pj08/z49
+ O/8+PDv/Pjw7/z08Ov89PDr/PDs5/0tGQv9fVlD/YFdQ/19XUP9fVk//X1ZP/15VTv9eVU7/XVRO/11U
+ Tf9cU0z/W1NM/1tSS/9aUkz/WlFL/1pQSv9aUUr/WU9K/1hQSf9YT0n/V09I/1dOSP9WTkj/VU1H/1ZN
+ R/9VTEf/VEtG/1RMRv9UTEb/VExG/09IQv89Ojb/Ozg1/zs4Nf87ODX/Ozg1/zs4Nf87ODX/Ozc0/zs4
+ Nf87ODX/PDg1/zw5Nf88ODX/PDg1/zw4Nf88ODX/PDg1/zw4Nf88ODX/PTk1/z05Nv89Ojb/PTk2/z45
+ Nv89OTX/Pjk2/z45Nv8+Ojb/Pzo3/z86Nv8/Ojf/Pzs3/0A7N/8/Ozf/QDs3/0A8OP9BPDj/Qj05/0I9
+ Of9APDj/VU9K/8C2rv/e2dX/3NbS/8W9uP8TEhL/AAAA/wAAAP8AAADvAAAAJgAAABgAAADmAAAA/wAA
+ AP8TEhH/xLu2/9zV0f/e2NT/xbqz/y4rKf89PTz/SEhI/0dHRv9HRkb/RkZF/0ZFRf9GRUT/RURE/0VE
+ Q/9EQ0P/REND/0RDQv9EQ0L/Q0JC/0NCQf9DQkH/Q0JB/0NBQP9CQED/QkA//0FAP/9BQD//QUA//0BA
+ Pv9APz7/QD89/0A+Pf9APj3/QD89/z8+Pf8/Pjz/Pz08/z89PP8/PTz/Pjw7/z48O/89Ozr/XVRO/3Bj
+ Wv9tYVn/bGBY/2tgWP9rX1f/al5W/2leVv9pXlb/aF1V/2hcVP9nXFT/ZltT/2ZbU/9lWlL/ZFpS/2RZ
+ Uv9jWFH/Y1hQ/2JYUP9hV0//YVZP/2BWT/9fVU7/X1VN/15UTf9eU0z/XVNM/11TTP9dU0z/XlNM/0ZB
+ Pf87ODX/PDk2/zw4Nv88OTb/PDk2/zs4Nf88ODX/PDg1/zw4Nv88OTX/PDk2/zw5Nf89OTb/PDk1/z05
+ Nv88ODb/PTk2/z05Nv89OTb/Pjo2/z06N/8+Ojb/Pjo2/z46Nv8+Ojb/Pjo3/z86N/8/Ozf/QDs3/z87
+ N/9AOzf/QDw4/0A8OP9APDj/QTw4/0E9Of9CPTn/Qz46/0A8OP9bVE//xbmy/97Y1P/c1dH/xLu2/xMS
+ Ef8AAAD/AAAA/wAAAOYAAAAYAAAAEgAAAOMAAAD/AAAA/xMSEf/CubT/29TQ/97Y1P/FurL/Liso/z4+
+ Pf9KSUn/SEdH/0hHR/9HR0b/R0ZG/0ZGRf9GRkX/RUVF/0VERP9FRET/RUVE/0REQ/9EQ0L/RENC/0RD
+ Qv9EQ0L/REJC/0NCQf9DQUD/QkFA/0JBQP9CQT//QUA//0FAP/9BQD//QT8+/0E/Pv9BPz7/QD8+/0A+
+ Pf9APj3/QD49/0A+Pf8/Pjz/Pz08/z48O/9dVU//bmJZ/2xgWP9rX1f/al9X/2peVv9pXlb/aV1V/2hd
+ Vf9nXFT/Z1xU/2ZbU/9lWlP/ZVpS/2RZUv9kWVH/Y1hR/2JYUP9iV1D/YVdP/2FWT/9gVk7/X1VO/19U
+ Tf9eVE3/XVNM/11TTP9cUkv/XFJL/1xSS/9dUkv/R0E9/zs5Nv88Ojf/PTo3/z05N/89OTb/PTk2/zw5
+ Nv89OTb/PTk2/z05Nv89OTb/PTo3/z06Nv89OTb/Pjk2/z05N/89Ojb/PTo3/z46Nv8/Ojf/Pzo3/z46
+ N/8/Ozf/Pzs3/z87N/8/Ojf/Pzo4/0A7N/9APDj/QDs3/0A8OP9APDj/QD05/0E8OP9BPTn/Qj05/0M+
+ Ov9DPjr/QTw4/1tUT//FurL/3tjU/9vU0P/CubT/ExIR/wAAAP8AAAD/AAAA4wAAABIAAAATAAAA4wAA
+ AP8AAAD/ERAQ/7yzrf/a08//3tjV/8m9tf9BPDj/KCgo/0RERP9DQkL/QkJC/0JCQv9CQUL/QUFA/0FB
+ Qf9BQED/QUA//0BAP/9AQD//QD8+/z8/Pv8/Pj7/Pz49/z8+Pv8/Pj3/Pz49/z49PP89PDz/Pjw8/z08
+ PP89PDv/PTw7/z08O/88PDv/PDs6/zw7Ov88Ozr/PDs6/zw6Ov88Ojn/PDo5/zs6Of87OTj/OTg3/1tT
+ Tf9tYVj/al5W/2leVf9pXVX/aF1V/2hcVP9nXFT/ZltT/2ZaU/9lWlL/ZFlS/2RZUf9jWFH/YlhQ/2JX
+ UP9hV0//YVZP/2BVTv9gVU7/X1RN/15UTf9eU0z/XVNL/11SS/9cUkr/W1FK/1tRSv9aUEn/WlBJ/1tQ
+ Sf9FPzv/OTYz/zo3Nf86NzT/Ojc0/zo3NP86NzT/Ojc0/zo3NP86NzT/Ojcz/zs3NP87NzT/Ozc0/zs3
+ NP87NzT/PDg1/zs3Nf87ODX/Ozg0/zw4NP88ODX/PDg1/z05Nf89OTb/PTk1/z05Nv89OTb/PTk2/z46
+ Nv8+Ojb/Pjo2/z86N/8+Ozf/Pzs3/0A7N/9AOzf/QTw4/0I9Of8+OTX/aWFb/8e8tP/e2NX/29PP/7yz
+ rf8REBD/AAAA/wAAAP8AAADjAAAAEwAAABMAAADjAAAA/wAAAP8LCwr/q6Od/9rTzf/e2dT/yL21/5yP
+ hv8+OTb/MzAt/zUxL/81Mi//NTIw/zUyMP81MjD/NjMw/zYzMP83NDH/NzMx/zc0Mv84NDH/ODUy/zk1
+ Mv85NTL/OTUy/zo2M/86NTP/OjYz/zo3M/86NzT/OzY0/zs3Nf87NzX/PDg1/zw4Nf88ODX/PDg1/z05
+ Nf89OTb/PTk3/z46N/8+Ojb/Pzs3/z86N/8+Ojf/ZlxV/3xvZv95bWT/eG1k/3hsY/93bGP/eGtj/3dr
+ Y/92amL/dmpi/3VpYf91aGH/dGhg/3RoYP9zaF//c2df/3NnXv9yZl7/cWZe/3FlXv9wZV3/cGRd/29k
+ XP9uZFz/bmNb/25jW/9tYlr/bWJa/2xhWf9sYVn/bGFZ/1RNSP9JRED/SkVB/0tFQf9MRkH/TEZC/0xG
+ Qv9NR0L/TUdD/01HQ/9NSEP/TkhD/05IRP9OSET/T0lF/09KRf9QSkX/UEpF/1BKRv9RS0b/UUtG/1JM
+ R/9STEf/U01I/1NNSP9TTUj/VE1J/1ROSf9UTkn/VE5J/1VOSf9VT0r/Vk9K/1ZQS/9XUEv/V1FL/1dR
+ TP9YUkz/V1FM/2VeWP+kl47/x7y0/97Y1P/b1M//raOe/wsLCv8AAAD/AAAA/wAAAOMAAAATAAAAFAAA
+ AOQAAAD/AAAA/wsLCv+qoZr/2dLM/93X0v/Vzcj/vrGo/7aonv+toJf/rqGX/66hl/+uoZf/rqGX/66h
+ l/+uoZf/rqGX/66hl/+uoZj/rqGY/66hmP+uoZj/rqGY/66hmP+uoZj/rqGY/66hmP+uoZj/rqGY/66h
+ mP+uoZj/rqGY/66imP+vopj/r6KY/6+imP+vopj/r6KY/6+imP+vopj/r6KY/6+imP+vopj/r6KZ/6+i
+ mP+ypJv/s6ac/7OmnP+zppz/s6Wc/7OlnP+zpZz/s6Wc/7OlnP+zpZz/s6Wc/7OlnP+zpZz/s6Wc/7Ol
+ nP+zpZz/s6Wb/7Klm/+zpZv/s6Wb/7Klm/+ypZv/sqWb/7Klm/+ypZv/sqWb/7Klm/+ypZv/sqWb/7Kl
+ m/+ypZv/saSa/7Cjmv+wo5r/sKOa/7Cjmv+wo5r/sKOa/7Cjmv+wo5r/sKOa/7Cjmv+xo5r/saOa/7Gj
+ mv+xo5r/saSa/7Gkmv+xpJr/saSa/7Gkmv+xpJr/saSa/7Gkmv+xpJr/saSa/7Gkmv+xpJr/saSa/7Gk
+ mv+xpJr/saSa/7Gkm/+xpJv/saSa/7Gkm/+xpJv/saSb/7Gkm/+xpJr/tqif/72wp//Vzcj/3dfS/9nS
+ zP+qoJr/CwsK/wAAAP8AAAD/AAAA5AAAABQAAAAHAAAA2wAAAP8AAAD/CwsK/6iel//Z0Mv/3dbR/93W
+ 0v/a087/1czH/9bNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XN
+ x//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XN
+ x//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XMx//VzMf/1czH/9XMx//VzMf/1czH/9XM
+ x//VzMf/1czH/9XMx//VzMf/1czH/9XMx//VzMf/1czH/9XMx//VzMf/1czH/9XMx//VzMf/1czH/9XM
+ x//VzMf/1czH/9XMx//VzMf/1czH/9XMx//VzMf/1czH/9XMx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XN
+ x//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XN
+ x//Vzcf/1c3H/9XNx//Vzcf/1c3H/9XNx//VzMf/1czH/9XMx//VzMf/1czH/9XMx//VzMf/1czH/9XM
+ x//VzMf/1czH/9XMx//VzMf/2tPO/93W0v/d1tH/2dDL/6iel/8LCwr/AAAA/wAAAP8AAADbAAAABwAA
+ AAAAAADWAAAA/wAAAP8LCgr/ppyV/9jQyv/c1tH/29XQ/9zV0P/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW
+ 0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1tH/3NbR/9zW0f/c1dD/29XQ/9zW
+ 0f/Y0Mr/ppyV/wsKCv8AAAD/AAAA/wAAANYAAAAAAAAAAQAAANQAAAD/AAAA/wUFBP+WjIX/1s3H/9zW
+ 0f/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV
+ 0P/b1dD/29XQ/9vV0P/b1dD/29XQ/9vV0P/b1dD/3NbR/9bNx/+WjIX/BQUE/wAAAP8AAAD/AAAA1AAA
+ AAEAAAAAAAAAsAAAAP8AAAD/AAAA/19ZVP/MwLj/29TQ/9zW0v/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV
+ 0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zV0f/c1dH/3NXR/9zW
+ 0v/b1ND/zMC4/19ZVP8AAAD/AAAA/wAAAP8AAACwAAAAAAAAAAAAAABqAAAA/wAAAP8AAAD/EA8O/5CF
+ fv/Lv7f/187I/9nRzP/Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQ
+ y//Z0Mv/2dDL/9nQy//Z0Mv/2dDL/9nQy//Z0cz/187I/8u/t/+QhX7/EA8O/wAAAP8AAAD/AAAA/wAA
+ AGoAAAAAAAAAAAAAABgAAADjAAAA/wAAAP8AAAD/EA4O/2FaVP+dkYn/ppqS/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZ
+ kf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aZkf+mmZH/ppmR/6aa
+ kv+dkYn/YVpU/xAODv8AAAD/AAAA/wAAAP8AAADjAAAAGAAAAAAAAAAAAAAAAAAAAGAAAAD/AAAA/wAA
+ AP8AAAD/AAAA/wYFBf8QDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8O
+ Df8PDg3/Dw4N/w8ODf8PDg3/Dw4N/w8ODf8PDg3/EA4N/wYFBf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AGYAAADmAAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA
+ AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADmAAAAZgAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAB3AAAAwAAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAA
+ ANgAAADYAAAA2AAAANgAAADAAAAAdwAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAA
+ AA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ /////////////////////////AAAAAAAAAAAAAAAAAAAP/AAAAAAAAAAAAAAAAAAAA/gAAAAAAAAAAAA
+ AAAAAAAHwAAAAAAAAAAAAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAA
+ AAEAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAA
+ AAAAAAABwAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAP+AAAAAAA
+ AAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAP//////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////8=
+</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/FtpServer/DebugLogHandler.cs b/FtpServer/DebugLogHandler.cs
new file mode 100644
index 00000000..292d5eee
--- /dev/null
+++ b/FtpServer/DebugLogHandler.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// Default log handler.
+ ///
+ public class DebugLogHandler : ILogHandler
+ {
+ private IPEndPoint peer;
+
+ public DebugLogHandler()
+ {
+ }
+
+ private DebugLogHandler(IPEndPoint peer)
+ {
+ this.peer = peer;
+ }
+
+ public ILogHandler Clone(IPEndPoint peer)
+ {
+ return new DebugLogHandler(peer);
+ }
+
+ private void Write(string format, params object[] args)
+ {
+ Debug.WriteLine(String.Format("{0}: {1}", peer, String.Format(format, args)));
+ }
+
+ public void NewControlConnection()
+ {
+ Write("new control connection");
+ }
+
+ public void ClosedControlConnection()
+ {
+ Write("closed control connection");
+ }
+
+ public void ReceivedCommand(string verb, string arguments)
+ {
+#if VERY_DEBUG
+ string argtext = (arguments == null || arguments == "" ? "" : ' ' + arguments);
+ Write("received command: {0}{1}", verb, argtext);
+#endif
+ }
+
+ public void SentResponse(uint code, string description)
+ {
+#if VERY_DEBUG
+ Write("sent response: {0} {1}", code, description);
+#endif
+ }
+
+ public void NewDataConnection(IPEndPoint remote, IPEndPoint local, bool passive)
+ {
+#if VERY_DEBUG
+ Write("new data connection: {0} <-> {1} ({2})", remote, local, (passive ? "passive" : "active"));
+#endif
+ }
+
+ public void ClosedDataConnection(IPEndPoint remote, IPEndPoint local, bool passive)
+ {
+#if VERY_DEBUG
+ Write("closed data connection: {0} <-> {1} ({2})", remote, local, (passive ? "passive" : "active"));
+#endif
+ }
+ }
+}
+
diff --git a/FtpServer/FileSystemHelper.cs b/FtpServer/FileSystemHelper.cs
new file mode 100644
index 00000000..36cc4979
--- /dev/null
+++ b/FtpServer/FileSystemHelper.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+
+namespace mooftpserv
+{
+ public class FileSystemHelper
+ {
+ /// Handles TVFS path resolution, similar to Path.GetFullPath(Path.Combine(basePath, path))
+ public static string ResolvePath(string basePath, string path)
+ {
+ // CF is missing String.IsNullOrWhiteSpace
+ if (path == null || path.Trim() == "")
+ return null;
+
+ // first, make a complete unix path
+ string fullPath;
+ if (path[0] == '/') {
+ fullPath = path;
+ } else {
+ fullPath = basePath;
+ if (!fullPath.EndsWith("/"))
+ fullPath += "/";
+ fullPath += path;
+ }
+
+ // then, remove ".." and "."
+ List<string> tokens = new List<string>(fullPath.Split('/'));
+ for (int i = 0; i < tokens.Count; ++i) {
+ if (tokens[i] == "") {
+ if (i == 0 || i == tokens.Count - 1) {
+ continue; // ignore, start and end should be empty tokens
+ } else {
+ tokens.RemoveAt(i);
+ --i;
+ }
+ } else if (tokens[i] == "..") {
+ if (i < 2) {
+ // cannot go higher than root, just remove the token
+ tokens.RemoveAt(i);
+ --i;
+ } else {
+ tokens.RemoveRange(i - 1, 2);
+ i -= 2;
+ }
+ } else if (i < tokens.Count - 1 && tokens[i].EndsWith(@"\")) {
+ int slashes = 0;
+ for (int c = tokens[i].Length - 1; c >= 0 && tokens[i][c] == '\\'; --c)
+ ++slashes;
+
+ if (slashes % 2 != 0) {
+ // the slash was actually escaped, merge tokens
+ tokens[i] += ("/" + tokens[i + 1]);
+ ++i;
+ }
+ }
+ }
+
+ if (tokens.Count > 1)
+ return String.Join("/", tokens.ToArray());
+ else
+ return "/";
+ }
+
+ }
+}
+
diff --git a/FtpServer/IAuthHandler.cs b/FtpServer/IAuthHandler.cs
new file mode 100644
index 00000000..24621c87
--- /dev/null
+++ b/FtpServer/IAuthHandler.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Net;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// Interface for a class managing user authentication and allowing connections.
+ /// </summary>
+ public interface IAuthHandler
+ {
+ /// <summary>
+ /// Make a new instance for a new session with the given peer.
+ /// Each FTP session uses a separate, cloned instance.
+ /// </summary>
+ IAuthHandler Clone(IPEndPoint peer);
+
+ /// <summary>
+ /// Check the given login. Note that the method can be called in three ways:
+ /// - user and pass are null: anonymous authentication
+ /// - pass is null: login only with username (e.g. "anonymous")
+ /// - both are non-null: login with user and password
+ /// </summary>
+ /// <param name='user'>
+ /// The username, or null.
+ /// </param>
+ /// <param name='pass'>
+ /// The password, or null.
+ /// </param>
+ bool AllowLogin(string user, string pass);
+
+ /// <summary>
+ /// Check if a control connection from the peer should be allowed.
+ /// </summary>
+ bool AllowControlConnection();
+
+ /// <summary>
+ /// Check if the PORT command of the peer with the given
+ /// target endpoint should be allowed.
+ /// </summary>
+ /// The argument given by the peer in the PORT command.
+ /// </param>
+ bool AllowActiveDataConnection(IPEndPoint target);
+ }
+}
+
diff --git a/FtpServer/IFileSystemHandler.cs b/FtpServer/IFileSystemHandler.cs
new file mode 100644
index 00000000..c84e8426
--- /dev/null
+++ b/FtpServer/IFileSystemHandler.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Net;
+using System.IO;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// File system entry as returned by List.
+ /// </summary>
+ public struct FileSystemEntry
+ {
+ public string Name;
+ public bool IsDirectory;
+ public long Size;
+ public DateTime LastModifiedTimeUtc;
+ }
+
+ /// <summary>
+ /// Wrapper that either contains a value or an error string.
+ /// </summary>
+ public class ResultOrError<T>
+ {
+ private T result;
+ private string error;
+
+ private ResultOrError(T result, string error)
+ {
+ this.result = result;
+ this.error = error;
+ }
+
+ public static ResultOrError<T> MakeResult(T result)
+ {
+ return new ResultOrError<T>(result, null);
+ }
+
+ public static ResultOrError<T> MakeError(string error)
+ {
+ if (error == null)
+ throw new ArgumentNullException();
+ return new ResultOrError<T>(default(T), error.Replace(Environment.NewLine, " "));
+ }
+
+ public bool HasError
+ {
+ get { return error != null; }
+ }
+
+ public string Error
+ {
+ get { return error; }
+ }
+
+ public T Result
+ {
+ get
+ {
+ if (HasError)
+ throw new InvalidOperationException(String.Format("No result available, error: {0}", error));
+ return result;
+ }
+ }
+ };
+
+ /// <summary>
+ /// Interface for file system access from FTP.
+ /// </summary>
+ public interface IFileSystemHandler
+ {
+ /// <summary>
+ /// Make a new instance for a new session with the given peer.
+ /// Each FTP session uses a separate, cloned instance.
+ /// </summary>
+ IFileSystemHandler Clone(IPEndPoint peer);
+
+ /// <summary>
+ /// PWD: Returns the path of the current working directory.
+ /// </summary>
+ /// <returns>
+ /// The absolute path of the current directory or an error string.
+ /// </returns>
+ ResultOrError<string> GetCurrentDirectory();
+
+ /// <summary>
+ /// CWD: Changes the current directory.
+ /// CDUP: Changes to parent directory (called with "..")
+ /// </summary>
+ /// <returns>
+ /// The new absolute path or an error string.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path to which to change.
+ /// </param>
+ ResultOrError<string> ChangeDirectory(string path);
+
+ /// <summary>
+ /// MKD: Create a directory.
+ /// </summary>
+ /// <returns>
+ /// The absolute path of the created directory or an error string.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path for the new directory.
+ /// </param>
+ ResultOrError<string> CreateDirectory(string path);
+
+ /// <summary>
+ /// RMD: Remove a directory.
+ /// </summary>
+ /// <returns>
+ /// A bool or an error string. The bool is not actually used.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path for the directory.
+ /// </param>
+ ResultOrError<bool> RemoveDirectory(string path);
+
+ /// <summary>
+ /// RETR: Open a stream for reading the specified file.
+ /// </summary>
+ /// <returns>
+ /// An opened stream for reading from the file, or an error string.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path for the file.
+ /// </param>
+ ResultOrError<Stream> ReadFile(string path);
+
+ /// <summary>
+ /// STOR: Open a stream for writing to the specified file.
+ /// If the file exists, it should be overwritten.
+ /// </summary>
+ /// <returns>
+ /// An opened stream for writing to the file, or an error string.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path for the file.
+ /// </param>
+ ResultOrError<Stream> WriteFile(string path);
+ ResultOrError<bool> WriteFileFinalize(string path, Stream stream);
+
+ /// <summary>
+ /// DELE: Deletes a file.
+ /// </summary>
+ /// <returns>
+ /// A bool or an error string. The bool is not actually used.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path for the file.
+ /// </param>
+ ResultOrError<bool> RemoveFile(string path);
+
+ /// <summary>
+ /// RNFR, RNTO: Renames or moves a file or directory.
+ /// </summary>
+ /// <returns>
+ /// A bool or an error string. The bool is not actually used.
+ /// </returns>
+ /// <param name="fromPath">
+ /// The relative or absolute path of an existing file or directory.
+ /// </param>
+ /// <param name="toPath">
+ /// A relative or absolute non-existing path to which the file will be renamed or moved.
+ /// </param>
+ ResultOrError<bool> RenameFile(string fromPath, string toPath);
+
+ /// <summary>
+ /// LIST: Return a list of files and folders in a directory, or for a file (like 'ls').
+ /// </summary>
+ /// <param name="path">
+ /// The relative or absolute path of an existing directory or file.
+ /// Can be null or empty to return the current directory.
+ /// </para>
+ /// <return>
+ /// An array of file system entries or an error string.
+ /// </return>
+ ResultOrError<FileSystemEntry[]> ListEntries(string path);
+
+ /// <summary>
+ /// SIZE: Gets the size of a file in bytes.
+ /// </summary>
+ /// <returns>
+ /// The file size, or -1 on error.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path.
+ /// </param>
+ ResultOrError<long> GetFileSize(string path);
+
+ /// <summary>
+ /// MDTM: Gets the last modified timestamp of a file.
+ /// </summary>
+ /// <returns>
+ /// The last modified time in UTC, or an error string.
+ /// </returns>
+ /// <param name='path'>
+ /// A relative or absolute path.
+ /// </param>
+ ResultOrError<DateTime> GetLastModifiedTimeUtc(string path);
+ }
+}
+
diff --git a/FtpServer/ILogHandler.cs b/FtpServer/ILogHandler.cs
new file mode 100644
index 00000000..87b7c9ad
--- /dev/null
+++ b/FtpServer/ILogHandler.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Net;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// Interface for a logger. Methods should be self-explanatory.
+ /// </summary>
+ public interface ILogHandler
+ {
+ /// <summary>
+ /// Make a new instance for a new session with the given peer.
+ /// Each FTP session uses a separate, cloned instance.
+ /// </summary>
+ ILogHandler Clone(IPEndPoint peer);
+
+ void NewControlConnection();
+ void ClosedControlConnection();
+ void ReceivedCommand(string verb, string arguments);
+ void SentResponse(uint code, string description);
+ void NewDataConnection(IPEndPoint remote, IPEndPoint local, bool passive);
+ void ClosedDataConnection(IPEndPoint remote, IPEndPoint local, bool passive);
+ }
+}
+
diff --git a/FtpServer/NesMiniAuthHandler.cs b/FtpServer/NesMiniAuthHandler.cs
new file mode 100644
index 00000000..3da4c2b7
--- /dev/null
+++ b/FtpServer/NesMiniAuthHandler.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Net;
+
+namespace mooftpserv
+{
+ public class NesMiniAuthHandler : IAuthHandler
+ {
+ private IPEndPoint peer;
+
+ public NesMiniAuthHandler()
+ {
+ }
+
+ private NesMiniAuthHandler(IPEndPoint peer)
+ {
+ this.peer = peer;
+ }
+
+ public IAuthHandler Clone(IPEndPoint peer)
+ {
+ return new NesMiniAuthHandler(peer);
+ }
+
+ public bool AllowLogin(string user, string pass)
+ {
+ return (user == "root" && pass == "clover");
+ }
+
+ public bool AllowControlConnection()
+ {
+ return true;
+ }
+
+ public bool AllowActiveDataConnection(IPEndPoint port)
+ {
+ // only allow active connections to the same peer as the control connection
+ return peer.Address.Equals(port.Address);
+ }
+ }
+}
+
diff --git a/FtpServer/NesMiniFileSystemHandler.cs b/FtpServer/NesMiniFileSystemHandler.cs
new file mode 100644
index 00000000..63b8b1cb
--- /dev/null
+++ b/FtpServer/NesMiniFileSystemHandler.cs
@@ -0,0 +1,317 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.IO;
+using com.clusterrr.clovershell;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// Default file system handler. Allows access to the whole file system. Supports drives on Windows.
+ /// </summary>
+ public class NesMiniFileSystemHandler : IFileSystemHandler
+ {
+ // list of supported operating systems
+ private enum OS { WinNT, WinCE, Unix };
+
+ // currently used operating system
+ private OS os;
+ // current path as TVFS or unix-like
+ private string currentPath;
+ // clovershell
+ private ClovershellConnection clovershell;
+
+ public NesMiniFileSystemHandler(ClovershellConnection clovershell, string startPath)
+ {
+ os = OS.Unix;
+ this.currentPath = startPath;
+ this.clovershell = clovershell;
+ }
+
+ public NesMiniFileSystemHandler(ClovershellConnection clovershell)
+ : this(clovershell, "/")
+ {
+ }
+
+ private NesMiniFileSystemHandler(string path, OS os, ClovershellConnection clovershell)
+ {
+ this.currentPath = path;
+ this.os = os;
+ this.clovershell = clovershell;
+ }
+
+ public IFileSystemHandler Clone(IPEndPoint peer)
+ {
+ return new NesMiniFileSystemHandler(currentPath, os, clovershell);
+ }
+
+ public ResultOrError<string> GetCurrentDirectory()
+ {
+ return MakeResult<string>(currentPath);
+ }
+
+ public ResultOrError<string> ChangeDirectory(string path)
+ {
+ string newPath = ResolvePath(path);
+ currentPath = newPath;
+ return MakeResult<string>(newPath);
+ }
+
+ public ResultOrError<string> ChangeToParentDirectory()
+ {
+ return ChangeDirectory("..");
+ }
+
+ public ResultOrError<string> CreateDirectory(string path)
+ {
+ string newPath = ResolvePath(path);
+ try
+ {
+ foreach (var c in newPath)
+ if ((int)c > 255) throw new Exception("Invalid characters in directory name");
+ var newpath = DecodePath(newPath);
+ clovershell.ExecuteSimple("mkdir \"" + newpath + "\"");
+ }
+ catch (Exception ex)
+ {
+ return MakeError<string>(ex.Message);
+ }
+
+ return MakeResult<string>(newPath);
+ }
+
+ public ResultOrError<bool> RemoveDirectory(string path)
+ {
+ string newPath = ResolvePath(path);
+
+ try
+ {
+ var rpath = DecodePath(newPath);
+ clovershell.ExecuteSimple("rm -rf \"" + rpath + "\"");
+ }
+ catch (Exception ex)
+ {
+ return MakeError<bool>(ex.Message);
+ }
+
+ return MakeResult<bool>(true);
+ }
+
+ public ResultOrError<Stream> ReadFile(string path)
+ {
+ string newPath = ResolvePath(path);
+ try
+ {
+ var data = new MemoryStream();
+ clovershell.Execute("cat \"" + newPath + "\"", null, data, null, 1000, true);
+ data.Seek(0, SeekOrigin.Begin);
+ return MakeResult<Stream>(data);
+ }
+ catch (Exception ex)
+ {
+ return MakeError<Stream>(ex.Message);
+ }
+ }
+
+ public ResultOrError<Stream> WriteFile(string path)
+ {
+ string newPath = ResolvePath(path);
+ try
+ {
+ foreach (var c in newPath)
+ if ((int)c > 255) throw new Exception("Invalid characters in directory name");
+ return MakeResult<Stream>(new MemoryStream());
+ }
+ catch (Exception ex)
+ {
+ return MakeError<Stream>(ex.Message);
+ }
+ }
+
+ public ResultOrError<bool> WriteFileFinalize(string path, Stream str)
+ {
+ string newPath = ResolvePath(path);
+ try
+ {
+ str.Seek(0, SeekOrigin.Begin);
+ clovershell.Execute("cat > \"" + newPath + "\"", str, null, null, 1000, true);
+ str.Dispose();
+ return MakeResult<bool>(true);
+ }
+ catch (Exception ex)
+ {
+ return MakeError<bool>(ex.Message);
+ }
+ }
+
+ public ResultOrError<bool> RemoveFile(string path)
+ {
+ string newPath = ResolvePath(path);
+
+ try
+ {
+ clovershell.ExecuteSimple("rm -rf \"" + newPath + "\"", 1000, true);
+ }
+ catch (Exception ex)
+ {
+ return MakeError<bool>(ex.Message);
+ }
+
+ return MakeResult<bool>(true);
+ }
+
+ public ResultOrError<bool> RenameFile(string fromPath, string toPath)
+ {
+ fromPath = ResolvePath(fromPath);
+ toPath = ResolvePath(toPath);
+ try
+ {
+ clovershell.ExecuteSimple("mv \"" + fromPath + "\" \"" + toPath + "\"", 1000, true);
+ }
+ catch (Exception ex)
+ {
+ return MakeError<bool>(ex.Message);
+ }
+
+ return MakeResult<bool>(true);
+ }
+
+ public ResultOrError<FileSystemEntry[]> ListEntries(string path)
+ {
+ string newPath = ResolvePath(path);
+ List<FileSystemEntry> result = new List<FileSystemEntry>();
+ try
+ {
+ var lines = clovershell.ExecuteSimple("ls -lep \"" + newPath + "\"", 1000, true)
+ .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ if (line.StartsWith("total")) continue;
+ FileSystemEntry entry = new FileSystemEntry();
+ entry.Name = line.Substring(69).Trim();
+ entry.IsDirectory = entry.Name.EndsWith("/");
+ if (entry.IsDirectory) entry.Name = entry.Name.Substring(0, entry.Name.Length - 1);
+ entry.Size = long.Parse(line.Substring(29, 15).Trim());
+ // Who cares? There is no time source on NES Mini
+ //DateTime.Parse(line.Substring(44, 25).Trim());
+ entry.LastModifiedTimeUtc = DateTime.MinValue;
+ result.Add(entry);
+ }
+ }
+ catch (Exception ex)
+ {
+ return MakeError<FileSystemEntry[]>(ex.Message);
+ }
+
+ return MakeResult<FileSystemEntry[]>(result.ToArray());
+ }
+
+ public ResultOrError<long> GetFileSize(string path)
+ {
+ string newPath = ResolvePath(path);
+ List<FileSystemEntry> result = new List<FileSystemEntry>();
+ try
+ {
+ var lines = clovershell.ExecuteSimple("ls -le \"" + newPath + "\"", 1000, true)
+ .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ return MakeResult<long>(long.Parse(line.Substring(29, 15).Trim()));
+ }
+ }
+ catch (Exception ex)
+ {
+ return MakeError<long>(ex.Message);
+ }
+ return MakeResult<long>(0);
+ }
+
+ public ResultOrError<DateTime> GetLastModifiedTimeUtc(string path)
+ {
+ /*
+ string newPath = ResolvePath(path);
+ List<FileSystemEntry> result = new List<FileSystemEntry>();
+ try
+ {
+ var lines = clovershell.ExecuteSimple("ls -le \"" + newPath + "\"", 1000, true)
+ .Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var line in lines)
+ {
+ MakeResult<DateTime>(DateTime.Parse(line.Substring(45, 25).Trim()));
+ }
+ }
+ catch (Exception ex)
+ {
+ return MakeError<DateTime>(ex.Message);
+ }
+ */
+ return MakeResult<DateTime>(DateTime.MinValue);
+ }
+
+ private string ResolvePath(string path)
+ {
+ if (path == null) return currentPath;
+ if (path.Contains(" -> "))
+ path = path.Substring(path.IndexOf(" -> ") + 4);
+ return FileSystemHelper.ResolvePath(currentPath, path);
+ }
+
+ private string EncodePath(string path)
+ {
+ if (os == OS.WinNT)
+ return "/" + path[0] + (path.Length > 2 ? path.Substring(2).Replace(@"\", "/") : "");
+ else if (os == OS.WinCE)
+ return path.Replace(@"\", "/");
+ else
+ return path;
+ }
+
+ private string DecodePath(string path)
+ {
+ if (path == null || path == "" || path[0] != '/')
+ return null;
+
+ if (os == OS.WinNT)
+ {
+ // some error checking for the drive layer
+ if (path == "/")
+ return null; // should have been caught elsewhere
+
+ if (path.Length > 1 && path[1] == '/')
+ return null;
+
+ if (path.Length > 2 && path[2] != '/')
+ return null;
+
+ if (path.Length < 4) // e.g. "/C/"
+ return path[1] + @":\";
+ else
+ return path[1] + @":\" + path.Substring(3).Replace("/", @"\");
+ }
+ else if (os == OS.WinCE)
+ {
+ return path.Replace("/", @"\");
+ }
+ else
+ {
+ return path;
+ }
+ }
+
+ /// <summary>
+ /// Shortcut for ResultOrError<T>.MakeResult()
+ /// </summary>
+ private ResultOrError<T> MakeResult<T>(T result)
+ {
+ return ResultOrError<T>.MakeResult(result);
+ }
+
+ /// <summary>
+ /// Shortcut for ResultOrError<T>.MakeError()
+ /// </summary>
+ private ResultOrError<T> MakeError<T>(string error)
+ {
+ return ResultOrError<T>.MakeError(error);
+ }
+ }
+}
diff --git a/FtpServer/Server.cs b/FtpServer/Server.cs
new file mode 100644
index 00000000..b544f8e4
--- /dev/null
+++ b/FtpServer/Server.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// Main FTP server class. Manages the server socket, creates sessions.
+ /// Can be used to configure the server.
+ /// </summary>
+ public class Server
+ {
+ // default buffer size for send/receive buffers
+ private const int DEFAULT_BUFFER_SIZE = 64 * 1024;
+ // default port for the server socket
+ private const int DEFAULT_PORT = 21;
+
+ private IPEndPoint endpoint;
+ private int bufferSize = DEFAULT_BUFFER_SIZE;
+ private IAuthHandler authHandler = null;
+ private IFileSystemHandler fsHandler = null;
+ private ILogHandler logHandler = null;
+ private TcpListener socket = null;
+ private List<Session> sessions;
+
+ public Server()
+ {
+ this.endpoint = new IPEndPoint(GetDefaultAddress(), DEFAULT_PORT);
+ this.sessions = new List<Session>();
+ }
+
+ /// <summary>
+ /// Gets or sets the local end point on which the server will listen.
+ /// Has to be an IPv4 endpoint.
+ /// The default value is IPAdress.Any and port 21, except on WinCE,
+ /// where the first non-loopback IPv4 address will be used.
+ /// </summary>
+ public IPEndPoint LocalEndPoint
+ {
+ get { return endpoint; }
+ set { endpoint = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the local IP address on which the server will listen.
+ /// Has to be an IPv4 address.
+ /// If none is set, IPAddress.Any will be used, except on WinCE,
+ /// where the first non-loopback IPv4 address will be used.
+ /// </summary>
+ public IPAddress LocalAddress
+ {
+ get { return endpoint.Address; }
+ set { endpoint.Address = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the local port on which the server will listen.
+ /// The default value is 21. Note that on Linux, only root can open ports < 1024.
+ /// </summary>
+ public int LocalPort
+ {
+ get { return endpoint.Port; }
+ set { endpoint.Port = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the size of the send/receive buffer to be used by each session/connection.
+ /// The default value is 64k.
+ /// </summary>
+ public int BufferSize
+ {
+ get { return bufferSize; }
+ set { bufferSize = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the auth handler that is used to check user credentials.
+ /// If none is set, a DefaultAuthHandler will be created when the server starts.
+ /// </summary>
+ public IAuthHandler AuthHandler
+ {
+ get { return authHandler; }
+ set { authHandler = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the file system handler that implements file system access for FTP commands.
+ /// If none is set, a DefaultFileSystemHandler is created when the server starts.
+ /// </summary>
+ public IFileSystemHandler FileSystemHandler
+ {
+ get { return fsHandler; }
+ set { fsHandler = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the log handler. Can be null to disable logging.
+ /// The default value is null.
+ /// </summary>
+ public ILogHandler LogHandler
+ {
+ get { return logHandler; }
+ set { logHandler = value; }
+ }
+
+ /// <summary>
+ /// Run the server. The method will not return until Stop() is called.
+ /// </summary>
+ public void Run()
+ {
+ //if (authHandler == null)
+ // authHandler = new DefaultAuthHandler();
+
+ //if (fsHandler == null)
+ // fsHandler = new DefaultFileSystemHandler();
+
+ if (socket == null)
+ socket = new TcpListener(endpoint);
+
+ socket.Start();
+
+ // listen for new connections
+ try {
+ while (true)
+ {
+ Socket peer = socket.AcceptSocket();
+
+ IPEndPoint peerPort = (IPEndPoint) peer.RemoteEndPoint;
+ Session session = new Session(peer, bufferSize,
+ authHandler.Clone(peerPort),
+ fsHandler.Clone(peerPort),
+ logHandler.Clone(peerPort));
+
+ session.Start();
+ sessions.Add(session);
+
+ // purge old sessions
+ for (int i = sessions.Count - 1; i >= 0; --i)
+ {
+ if (!sessions[i].IsOpen) {
+ sessions.RemoveAt(i);
+ --i;
+ }
+ }
+ }
+ } catch (SocketException) {
+ // ignore, Stop() will probably cause this exception
+ } finally {
+ // close all running connections
+ foreach (Session s in sessions) {
+ s.Stop();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Stop the server.
+ /// </summary>
+ public void Stop()
+ {
+ if (socket == null) return;
+ socket.Stop();
+ }
+
+ /// <summary>
+ /// Get the default address, which is IPAddress.Any everywhere except on WinCE,
+ /// where all local addresses are enumerated and the first non-loopback IP is used.
+ /// </summary>
+ private IPAddress GetDefaultAddress()
+ {
+ // on WinCE, 0.0.0.0 does not work because for accepted sockets,
+ // LocalEndPoint would also say 0.0.0.0 instead of the real IP
+#if WindowsCE
+ IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
+ IPAddress bindIp = IPAddress.Loopback;
+ foreach (IPAddress ip in host.AddressList) {
+ if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) {
+ return ip;
+ }
+ }
+
+ return IPAddress.Loopback;
+#else
+ return IPAddress.Any;
+#endif
+ }
+ }
+}
diff --git a/FtpServer/Session.cs b/FtpServer/Session.cs
new file mode 100644
index 00000000..068156d5
--- /dev/null
+++ b/FtpServer/Session.cs
@@ -0,0 +1,1045 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+namespace mooftpserv
+{
+ /// <summary>
+ /// FTP session/connection. Does all the heavy lifting of the FTP protocol.
+ /// Reads commands, sends replies, manages data connections, and so on.
+ /// Each session creates its own thread.
+ /// </summary>
+ class Session
+ {
+ // transfer data type, ascii or binary
+ enum DataType { ASCII, IMAGE };
+
+ // buffer size to use for reading commands from the control connection
+ private static int CMD_BUFFER_SIZE = 4096;
+ // version from AssemblyInfo
+ private static string LIB_VERSION = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(2);
+ // monthnames for LIST command, since DateTime returns localized names
+ private static string[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ // response text for initial response. preceeded by application name and version number.
+ private static string[] HELLO_TEXT = { "What can I do for you?", "Good day, sir or madam.", "Hey ho let's go!", "The poor man's FTP server." };
+ // response text for general ok messages
+ private static string[] OK_TEXT = { "Sounds good.", "Success!", "Alright, I'll do it...", "Consider it done." };
+ // Result for FEAT command
+ private static string[] FEATURES = { "MDTM", "PASV", "SIZE", "TVFS", "UTF8" };
+
+ // local EOL flavor
+ private static byte[] localEolBytes = Encoding.ASCII.GetBytes(Environment.NewLine);
+ // FTP-mandated EOL flavor (= CRLF)
+ private static byte[] remoteEolBytes = Encoding.ASCII.GetBytes("\r\n");
+ // on Windows, no ASCII conversion is necessary (CRLF == CRLF)
+ private static bool noAsciiConv = (localEolBytes == remoteEolBytes);
+
+ // socket for the control connection
+ private Socket controlSocket;
+ // buffer size to use for sending/receiving with data connections
+ private int dataBufferSize;
+ // auth handler, checks user credentials
+ private IAuthHandler authHandler;
+ // file system handler, implements file system access for the FTP commands
+ private IFileSystemHandler fsHandler;
+ // log handler, used for diagnostic logging output. can be null.
+ private ILogHandler logHandler;
+ // Session thread, the control and data connections are processed in this thread
+ private Thread thread;
+
+ // .NET CF does not have Thread.IsAlive, so this flag replaces it
+ private bool threadAlive = false;
+ // Random Number Generator for OK and HELLO texts
+ private Random randomTextIndex;
+ // flag for whether the user has successfully logged in
+ private bool loggedIn = false;
+ // name of the logged in user, also used to remember the username when waiting for the PASS command
+ private string loggedInUser = null;
+ // argument of pending RNFR command, when waiting for an RNTO command
+ private string renameFromPath = null;
+
+ // remote data port. null when PASV is used.
+ private IPEndPoint dataPort = null;
+ // socket for data connections
+ private Socket dataSocket = null;
+ // .NET CF does not have Socket.Bound, so this flag replaces it
+ private bool dataSocketBound = false;
+ // buffer for reading from the control connection
+ private byte[] cmdRcvBuffer;
+ // number of bytes in the cmdRcvBuffer
+ private int cmdRcvBytes;
+ // buffer for sending/receiving with data connections
+ private byte[] dataBuffer;
+ // data type of the session, can be changed by the client
+ private DataType transferDataType = DataType.ASCII;
+
+ /// <summary>
+ /// Creates a new session, which can afterwards be started with Start().
+ /// </summary>
+ public Session(Socket socket, int bufferSize, IAuthHandler authHandler, IFileSystemHandler fileSystemHandler, ILogHandler logHandler)
+ {
+ this.controlSocket = socket;
+ this.dataBufferSize = bufferSize;
+ this.authHandler = authHandler;
+ this.fsHandler = fileSystemHandler;
+ this.logHandler = logHandler;
+
+ this.cmdRcvBuffer = new byte[CMD_BUFFER_SIZE];
+ this.cmdRcvBytes = 0;
+ this.dataBuffer = new byte[dataBufferSize + 1]; // +1 for partial EOL
+ this.randomTextIndex = new Random();
+
+ this.thread = new Thread(new ThreadStart(this.Work));
+ }
+
+ /// <summary>
+ /// Indicates whether the session is still open
+ /// </summary>
+ public bool IsOpen
+ {
+ get { return threadAlive; }
+ }
+
+ /// <summary>
+ /// Start the session in a new thread
+ /// </summary>
+ public void Start()
+ {
+ if (!threadAlive) {
+ this.thread.Start();
+ threadAlive = true;
+ }
+ }
+
+ /// <summary>
+ /// Stop the session
+ /// </summary>
+ public void Stop()
+ {
+ if (threadAlive) {
+ threadAlive = false;
+ thread.Abort();
+ }
+
+ if (controlSocket.Connected)
+ controlSocket.Close();
+
+ if (dataSocket != null && dataSocket.Connected)
+ dataSocket.Close();
+ }
+
+ /// <summary>
+ /// Main method of the session thread.
+ /// Reads commands and executes them.
+ /// </summary>
+ private void Work()
+ {
+ if (logHandler != null)
+ logHandler.NewControlConnection();
+
+ try {
+ if (!authHandler.AllowControlConnection()) {
+ Respond(421, "Control connection refused.");
+ // first flush, then close
+ controlSocket.Shutdown(SocketShutdown.Both);
+ controlSocket.Close();
+ return;
+ }
+
+ Respond(220, String.Format("This is mooftpserv v{0}. {1}", LIB_VERSION, GetRandomText(HELLO_TEXT)));
+
+ // allow anonymous login?
+ if (authHandler.AllowLogin(null, null)) {
+ loggedIn = true;
+ }
+
+ while (controlSocket.Connected) {
+ string verb;
+ string args;
+ if (!ReadCommand(out verb, out args)) {
+ if (controlSocket.Connected) {
+ // assume clean disconnect if there are no buffered bytes
+ if (cmdRcvBytes != 0)
+ Respond(500, "Failed to read command, closing connection.");
+ controlSocket.Close();
+ }
+ break;
+ } else if (verb.Trim() == "") {
+ // ignore empty lines
+ continue;
+ }
+
+ try {
+ if (loggedIn)
+ ProcessCommand(verb, args);
+ else if (verb == "QUIT") { // QUIT should always be allowed
+ Respond(221, "Bye.");
+ // first flush, then close
+ controlSocket.Shutdown(SocketShutdown.Both);
+ controlSocket.Close();
+ } else {
+ HandleAuth(verb, args);
+ }
+ } catch (Exception ex) {
+ Respond(500, ex);
+ }
+ }
+ } catch (Exception) {
+ // catch any uncaught stuff, the server should not throw anything
+ } finally {
+ if (controlSocket.Connected)
+ controlSocket.Close();
+
+ if (logHandler != null)
+ logHandler.ClosedControlConnection();
+
+ threadAlive = false;
+ }
+ }
+
+ /// <summary>
+ /// Process an FTP command.
+ /// </summary>
+ private void ProcessCommand(string verb, string arguments)
+ {
+ switch (verb) {
+ case "SYST":
+ {
+ Respond(215, "UNIX emulated by mooftpserv");
+ break;
+ }
+ case "QUIT":
+ {
+ Respond(221, "Bye.");
+ // first flush, then close
+ controlSocket.Shutdown(SocketShutdown.Both);
+ controlSocket.Close();
+ break;
+ }
+ case "USER":
+ {
+ Respond(230, "You are already logged in.");
+ break;
+ }
+ case "PASS":
+ {
+ Respond(230, "You are already logged in.");
+ break;
+ }
+ case "FEAT":
+ {
+ Respond(211, "Features:\r\n " + String.Join("\r\n ", FEATURES), true);
+ Respond(211, "Features done.");
+ break;
+ }
+ case "OPTS":
+ {
+ // Windows Explorer uses lowercase args
+ if (arguments != null && arguments.ToUpper() == "UTF8 ON")
+ Respond(200, "Always in UTF8 mode.");
+ else
+ Respond(504, "Unknown option.");
+ break;
+ }
+ case "TYPE":
+ {
+ if (arguments == "A" || arguments == "A N") {
+ transferDataType = DataType.ASCII;
+ Respond(200, "Switching to ASCII mode.");
+ } else if (arguments == "I") {
+ transferDataType = DataType.IMAGE;
+ Respond(200, "Switching to BINARY mode.");
+ } else {
+ Respond(500, "Unknown TYPE arguments.");
+ }
+ break;
+ }
+ case "PORT":
+ {
+ IPEndPoint port = ParseAddress(arguments);
+ if (port == null) {
+ Respond(500, "Invalid host-port format.");
+ break;
+ }
+
+ if (!authHandler.AllowActiveDataConnection(port)) {
+ Respond(500, "PORT arguments refused.");
+ break;
+ }
+
+ dataPort = port;
+ CreateDataSocket(false);
+ Respond(200, GetRandomText(OK_TEXT));
+ break;
+ }
+ case "PASV":
+ {
+ dataPort = null;
+
+ try {
+ CreateDataSocket(true);
+ } catch (Exception ex) {
+ Respond(500, ex);
+ break;
+ }
+
+ string port = FormatAddress((IPEndPoint) dataSocket.LocalEndPoint);
+ Respond(227, String.Format("Switched to passive mode ({0})", port));
+ break;
+ }
+ case "XPWD":
+ case "PWD":
+ {
+ ResultOrError<string> ret = fsHandler.GetCurrentDirectory();
+ if (ret.HasError)
+ Respond(500, ret.Error);
+ else
+ Respond(257, EscapePath(ret.Result));
+ break;
+ }
+ case "XCWD":
+ case "CWD":
+ {
+ ResultOrError<string> ret = fsHandler.ChangeDirectory(arguments);
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(200, GetRandomText(OK_TEXT));
+ break;
+ }
+ case "XCUP":
+ case "CDUP":
+ {
+ ResultOrError<string> ret = fsHandler.ChangeDirectory("..");
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(200, GetRandomText(OK_TEXT));
+ break;
+ }
+ case "XMKD":
+ case "MKD":
+ {
+ ResultOrError<string> ret = fsHandler.CreateDirectory(arguments);
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(257, EscapePath(ret.Result));
+ break;
+ }
+ case "XRMD":
+ case "RMD":
+ {
+ ResultOrError<bool> ret = fsHandler.RemoveDirectory(arguments);
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(250, GetRandomText(OK_TEXT));
+ break;
+ }
+ case "RETR":
+ {
+ ResultOrError<Stream> ret = fsHandler.ReadFile(arguments);
+ if (ret.HasError) {
+ Respond(550, ret.Error);
+ break;
+ }
+
+ SendData(ret.Result);
+ break;
+ }
+ case "STOR":
+ {
+ ResultOrError<Stream> ret = fsHandler.WriteFile(arguments);
+ if (ret.HasError) {
+ Respond(550, ret.Error);
+ break;
+ }
+ ReceiveData(ret.Result);
+ var ret2 = fsHandler.WriteFileFinalize(arguments, ret.Result);
+ if (ret2.HasError)
+ {
+ Respond(550, ret.Error);
+ break;
+ }
+ break;
+ }
+ case "DELE":
+ {
+ ResultOrError<bool> ret = fsHandler.RemoveFile(arguments);
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(250, GetRandomText(OK_TEXT));
+ break;
+ }
+ case "RNFR":
+ {
+ if (arguments == null || arguments.Trim() == "") {
+ Respond(500, "Empty path is invalid.");
+ break;
+ }
+
+ renameFromPath = arguments;
+ Respond(350, "Waiting for target path.");
+ break;
+ }
+ case "RNTO":
+ {
+ if (renameFromPath == null) {
+ Respond(503, "Use RNFR before RNTO.");
+ break;
+ }
+
+ ResultOrError<bool> ret = fsHandler.RenameFile(renameFromPath, arguments);
+ renameFromPath = null;
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(250, GetRandomText(OK_TEXT));
+ break;
+ }
+ case "MDTM":
+ {
+ ResultOrError<DateTime> ret = fsHandler.GetLastModifiedTimeUtc(arguments);
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(213, FormatTime(EnsureUnixTime(ret.Result)));
+ break;
+ }
+ case "SIZE":
+ {
+ ResultOrError<long> ret = fsHandler.GetFileSize(arguments);
+ if (ret.HasError)
+ Respond(550, ret.Error);
+ else
+ Respond(213, ret.Result.ToString());
+ break;
+ }
+ case "LIST":
+ {
+ // apparently browsers like to pass arguments to LIST
+ // assuming they are passed through to the UNIX ls command
+ arguments = RemoveLsArgs(arguments);
+
+ ResultOrError<FileSystemEntry[]> ret = fsHandler.ListEntries(arguments);
+ if (ret.HasError) {
+ Respond(500, ret.Error);
+ break;
+ }
+
+ SendData(MakeStream(FormatDirList(ret.Result)));
+ break;
+ }
+ case "STAT":
+ {
+ if (arguments == null || arguments.Trim() == "") {
+ Respond(504, "Not implemented for these arguments.");
+ break;
+ }
+
+ arguments = RemoveLsArgs(arguments);
+
+ ResultOrError<FileSystemEntry[]> ret = fsHandler.ListEntries(arguments);
+ if (ret.HasError) {
+ Respond(500, ret.Error);
+ break;
+ }
+
+ Respond(213, "Status:\r\n" + FormatDirList(ret.Result), true);
+ Respond(213, "Status done.");
+ break;
+ }
+ case "NLST":
+ {
+ // remove common arguments, we do not support any of them
+ arguments = RemoveLsArgs(arguments);
+
+ ResultOrError<FileSystemEntry[]> ret = fsHandler.ListEntries(arguments);
+ if (ret.HasError)
+ {
+ Respond(500, ret.Error);
+ break;
+ }
+
+ SendData(MakeStream(FormatNLST(ret.Result)));
+ break;
+ }
+ case "NOOP":
+ {
+ Respond(200, GetRandomText(OK_TEXT));
+ break;
+ }
+ default:
+ {
+ Respond(500, "Unknown command.");
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Read a command from the control connection.
+ /// </summary>
+ /// <returns>
+ /// True if a command was read.
+ /// </returns>
+ /// <param name='verb'>
+ /// Will receive the verb of the command.
+ /// </param>
+ /// <param name='args'>
+ /// Will receive the arguments of the command, or null.
+ /// </param>
+ private bool ReadCommand(out string verb, out string args)
+ {
+ verb = null;
+ args = null;
+
+ int endPos = -1;
+ // can there already be a command in the buffer?
+ if (cmdRcvBytes > 0)
+ Array.IndexOf(cmdRcvBuffer, (byte)'\n', 0, cmdRcvBytes);
+
+ try {
+ // read data until a newline is found
+ do {
+ int freeBytes = cmdRcvBuffer.Length - cmdRcvBytes;
+ int bytes = controlSocket.Receive(cmdRcvBuffer, cmdRcvBytes, freeBytes, SocketFlags.None);
+ if (bytes <= 0)
+ break;
+
+ cmdRcvBytes += bytes;
+
+ // search \r\n
+ endPos = Array.IndexOf(cmdRcvBuffer, (byte)'\r', 0, cmdRcvBytes);
+ if (endPos != -1 && (cmdRcvBytes <= endPos + 1 || cmdRcvBuffer[endPos + 1] != (byte)'\n'))
+ endPos = -1;
+ } while (endPos == -1 && cmdRcvBytes < cmdRcvBuffer.Length);
+ } catch (SocketException) {
+ // in case the socket is closed or has some other error while reading
+ return false;
+ }
+
+ if (endPos == -1)
+ return false;
+
+ string command = DecodeString(cmdRcvBuffer, endPos);
+
+ // remove the command from the buffer
+ cmdRcvBytes -= (endPos + 2);
+ Array.Copy(cmdRcvBuffer, endPos + 2, cmdRcvBuffer, 0, cmdRcvBytes);
+
+ // CF is missing a limited String.Split
+ string[] tokens = command.Split(' ');
+ verb = tokens[0].ToUpper(); // commands are case insensitive
+ args = (tokens.Length > 1 ? String.Join(" ", tokens, 1, tokens.Length - 1) : null);
+
+ if (logHandler != null)
+ logHandler.ReceivedCommand(verb, args);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Send a response on the control connection
+ /// </summary>
+ private void Respond(uint code, string desc, bool moreFollows)
+ {
+ string response = code.ToString();
+ if (desc != null)
+ response += (moreFollows ? '-' : ' ') + desc;
+
+ if (!response.EndsWith("\r\n"))
+ response += "\r\n";
+
+ byte[] sendBuffer = EncodeString(response);
+ controlSocket.Send(sendBuffer);
+
+ if (logHandler != null)
+ logHandler.SentResponse(code, desc);
+ }
+
+ /// <summary>
+ /// Send a response on the control connection
+ /// </summary>
+ private void Respond(uint code, string desc)
+ {
+ Respond(code, desc, false);
+ }
+
+ /// <summary>
+ /// Send a response on the control connection, with an exception as text
+ /// </summary>
+ private void Respond(uint code, Exception ex)
+ {
+ Respond(code, ex.Message.Replace(Environment.NewLine, " "));
+ }
+
+ /// <summary>
+ /// Process FTP commands when the user is not yet logged in.
+ /// Mostly handles the login commands USER and PASS.
+ /// </summary>
+ private void HandleAuth(string verb, string args)
+ {
+ if (verb == "USER" && args != null) {
+ if (authHandler.AllowLogin(args, null)) {
+ Respond(230, "Login successful.");
+ loggedIn = true;
+ } else {
+ loggedInUser = args;
+ Respond(331, "Password please.");
+ }
+ } else if (verb == "PASS") {
+ if (loggedInUser != null) {
+ if (authHandler.AllowLogin(loggedInUser, args)) {
+ Respond(230, "Login successful.");
+ loggedIn = true;
+ } else {
+ loggedInUser = null;
+ Respond(530, "Login failed, please try again.");
+ }
+ } else {
+ Respond(530, "No USER specified.");
+ }
+ } else {
+ Respond(530, "Please login first.");
+ }
+ }
+
+ /// <summary>
+ /// Read from the given stream and send the data over a data connection
+ /// </summary>
+ private void SendData(Stream stream)
+ {
+ try {
+ bool passive = (dataPort == null);
+ using (Socket socket = OpenDataConnection()) {
+ if (socket == null)
+ return;
+
+ IPEndPoint remote = (IPEndPoint) socket.RemoteEndPoint;
+ IPEndPoint local = (IPEndPoint) socket.LocalEndPoint;
+
+ if (logHandler != null)
+ logHandler.NewDataConnection(remote, local, passive);
+
+ try {
+ while (true) {
+ int bytes = stream.Read(dataBuffer, 0, dataBufferSize);
+ if (bytes <= 0) {
+ break;
+ }
+
+ if (transferDataType == DataType.IMAGE || noAsciiConv) {
+ // TYPE I -> just pass through
+ socket.Send(dataBuffer, bytes, SocketFlags.None);
+ } else {
+ // TYPE A -> convert local EOL style to CRLF
+
+ // if the buffer ends with a potential partial EOL,
+ // try to read the rest of the EOL
+ // (i assume that the EOL has max. two bytes)
+ if (localEolBytes.Length == 2 &&
+ dataBuffer[bytes - 1] == localEolBytes[0]) {
+ if (stream.Read(dataBuffer, bytes, 1) == 1)
+ ++bytes;
+ }
+
+ byte[] convBuffer = null;
+ int convBytes = ConvertAsciiBytes(dataBuffer, bytes, true, out convBuffer);
+ socket.Send(convBuffer, convBytes, SocketFlags.None);
+ }
+ }
+
+ // flush socket before closing (done by using-statement)
+ socket.Shutdown(SocketShutdown.Send);
+ Respond(226, "Transfer complete.");
+ } catch (Exception ex) {
+ Respond(500, ex);
+ return;
+ } finally {
+ if (logHandler != null)
+ logHandler.ClosedDataConnection(remote, local, passive);
+ }
+ }
+ } finally {
+ stream.Close();
+ }
+ }
+
+ /// <summary>
+ /// Read from a data connection and write to the given stream
+ /// </summary>
+ private void ReceiveData(Stream stream)
+ {
+ try {
+ bool passive = (dataPort == null);
+ using (Socket socket = OpenDataConnection()) {
+ if (socket == null)
+ return;
+
+ IPEndPoint remote = (IPEndPoint) socket.RemoteEndPoint;
+ IPEndPoint local = (IPEndPoint) socket.LocalEndPoint;
+
+ if (logHandler != null)
+ logHandler.NewDataConnection(remote, local, passive);
+
+ try {
+ while (true) {
+ // fill up the in-memory buffer before writing to disk
+ int totalBytes = 0;
+ while (totalBytes < dataBufferSize) {
+ int freeBytes = dataBufferSize - totalBytes;
+ int newBytes = socket.Receive(dataBuffer, totalBytes, freeBytes, SocketFlags.None);
+
+ if (newBytes > 0) {
+ totalBytes += newBytes;
+ } else if (newBytes < 0) {
+ Respond(500, String.Format("Transfer failed: Receive() returned {0}", newBytes));
+ return;
+ } else {
+ // end of data
+ break;
+ }
+ }
+
+ // end of data
+ if (totalBytes == 0)
+ break;
+
+ if (transferDataType == DataType.IMAGE || noAsciiConv) {
+ // TYPE I -> just pass through
+ stream.Write(dataBuffer, 0, totalBytes);
+ } else {
+ // TYPE A -> convert CRLF to local EOL style
+
+ // if the buffer ends with a potential partial CRLF,
+ // try to read the LF
+ if (dataBuffer[totalBytes - 1] == remoteEolBytes[0]) {
+ if (socket.Receive(dataBuffer, totalBytes, 1, SocketFlags.None) == 1)
+ ++totalBytes;
+ }
+
+ byte[] convBuffer = null;
+ int convBytes = ConvertAsciiBytes(dataBuffer, totalBytes, false, out convBuffer);
+ stream.Write(convBuffer, 0, convBytes);
+ }
+ }
+
+ socket.Shutdown(SocketShutdown.Receive);
+ Respond(226, "Transfer complete.");
+ } catch (Exception ex) {
+ Respond(500, ex);
+ return;
+ } finally {
+ if (logHandler != null)
+ logHandler.ClosedDataConnection(remote, local, passive);
+ }
+ }
+ } finally {
+ //stream.Close();
+ }
+ }
+
+ /// <summary>
+ /// Create a socket for a data connection.
+ /// </summary>
+ /// <param name='listen'>
+ /// If true, the socket will be bound to a local port for the PASV command.
+ /// Otherwise the socket can be used for connecting to the address given in a PORT command.
+ /// </param>
+ private void CreateDataSocket(bool listen)
+ {
+ if (dataSocket != null)
+ dataSocket.Close();
+
+ dataSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
+
+ if (listen) {
+ IPAddress serverIP = ((IPEndPoint) controlSocket.LocalEndPoint).Address;
+ dataSocket.Bind(new IPEndPoint(serverIP, 0));
+ dataSocketBound = true; // CF is missing Socket.IsBound
+ dataSocket.Listen(1);
+ }
+ }
+
+ /// <summary>
+ /// Opens an active or passive data connection and returns the socket
+ /// or null if there was no preceding PORT or PASV command or in case or error.
+ /// </summary>
+ private Socket OpenDataConnection()
+ {
+ if (dataPort == null && !dataSocketBound) {
+ Respond(425, "No data port configured, use PORT or PASV.");
+ return null;
+ }
+
+ Respond(150, "Opening data connection.");
+
+ try {
+ if (dataPort != null) {
+ // active mode
+ dataSocket.Connect(dataPort);
+ dataPort = null;
+ return dataSocket;
+ } else {
+ // passive mode
+ Socket socket = dataSocket.Accept();
+ dataSocket.Close();
+ dataSocketBound = false;
+ return socket;
+ }
+ } catch (Exception ex) {
+ Respond(500, String.Format("Failed to open data connection: {0}", ex.Message.Replace(Environment.NewLine, " ")));
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Convert between different EOL flavors.
+ /// </summary>
+ /// <returns>
+ /// The number of bytes in the resultBuffer.
+ /// </returns>
+ /// <param name='buffer'>
+ /// The input buffer whose data will be converted.
+ /// </param>
+ /// <param name='len'>
+ /// The number of bytes in the input buffer.
+ /// </param>
+ /// <param name='localToRemote'>
+ /// If true, the conversion will be made from local to FTP flavor,
+ /// otherwise from FTP to local flavor.
+ /// </param>
+ /// <param name='resultBuffer'>
+ /// The resulting buffer with the converted text.
+ /// Can be the same reference as the input buffer if there is nothing to convert.
+ /// </param>
+ private int ConvertAsciiBytes(byte[] buffer, int len, bool localToRemote, out byte[] resultBuffer)
+ {
+ byte[] fromBytes = (localToRemote ? localEolBytes : remoteEolBytes);
+ byte[] toBytes = (localToRemote ? remoteEolBytes : localEolBytes);
+ resultBuffer = null;
+
+ int startIndex = 0;
+ int resultLen = 0;
+ int searchLen;
+ while ((searchLen = len - startIndex) > 0) {
+ // search for the first byte of the EOL sequence
+ int eolIndex = Array.IndexOf(buffer, fromBytes[0], startIndex, searchLen);
+
+ // shortcut if there is no EOL in the whole buffer
+ if (eolIndex == -1 && startIndex == 0) {
+ resultBuffer = buffer;
+ return len;
+ }
+
+ // allocate to worst-case size
+ if (resultBuffer == null)
+ resultBuffer = new byte[len * 2];
+
+ if (eolIndex == -1) {
+ Array.Copy(buffer, startIndex, resultBuffer, resultLen, searchLen);
+ resultLen += searchLen;
+ break;
+ } else {
+ // compare the rest of the EOL
+ int matchBytes = 1;
+ for (int i = 1; i < fromBytes.Length && eolIndex + i < len; ++i) {
+ if (buffer[eolIndex + i] == fromBytes[i])
+ ++matchBytes;
+ }
+
+ if (matchBytes == fromBytes.Length) {
+ // found an EOL to convert
+ int copyLen = eolIndex - startIndex;
+ if (copyLen > 0) {
+ Array.Copy(buffer, startIndex, resultBuffer, resultLen, copyLen);
+ resultLen += copyLen;
+ }
+ Array.Copy(toBytes, 0, resultBuffer, resultLen, toBytes.Length);
+ resultLen += toBytes.Length;
+ startIndex += copyLen + fromBytes.Length;
+ } else {
+ int copyLen = (eolIndex - startIndex) + 1;
+ Array.Copy(buffer, startIndex, resultBuffer, resultLen, copyLen);
+ resultLen += copyLen;
+ startIndex += copyLen;
+ }
+ }
+ }
+
+ return resultLen;
+ }
+
+ /// <summary>
+ /// Parse the argument of a PORT command into an IPEndPoint
+ /// </summary>
+ private IPEndPoint ParseAddress(string address)
+ {
+ string[] tokens = address.Split(',');
+ byte[] bytes = new byte[tokens.Length];
+ for (int i = 0; i < tokens.Length; ++i) {
+ try {
+ // CF is missing TryParse
+ bytes[i] = byte.Parse(tokens[i]);
+ } catch (Exception) {
+ return null;
+ }
+ }
+
+ long ip = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24;
+ int port = bytes[4] << 8 | bytes[5];
+ return new IPEndPoint(ip, port);
+ }
+
+ /// <summary>
+ /// Format an IPEndPoint so that it can be used in a response for a PASV command
+ /// </summary>
+ private string FormatAddress(IPEndPoint address)
+ {
+ byte[] ip = address.Address.GetAddressBytes();
+ int port = address.Port;
+
+ return String.Format("{0},{1},{2},{3},{4},{5}",
+ ip[0], ip[1], ip[2], ip[3],
+ (port & 0xFF00) >> 8, port & 0x00FF);
+ }
+
+ /// <summary>
+ /// Formats a list of file system entries for a response to a LIST or STAT command
+ /// </summary>
+ private string FormatDirList(FileSystemEntry[] list)
+ {
+ int maxSizeChars = 0;
+ foreach (FileSystemEntry entry in list) {
+ maxSizeChars = Math.Max(maxSizeChars, entry.Size.ToString().Length);
+ }
+
+ DateTime sixMonthsAgo = EnsureUnixTime(DateTime.Now.ToUniversalTime().AddMonths(-6));
+
+ StringBuilder result = new StringBuilder();
+ foreach (FileSystemEntry entry in list) {
+ char dirflag = (entry.IsDirectory ? 'd' : '-');
+ string size = entry.Size.ToString().PadLeft(maxSizeChars);
+ DateTime time = EnsureUnixTime(entry.LastModifiedTimeUtc);
+ string timestr = MONTHS[time.Month - 1];
+ if (time < sixMonthsAgo)
+ timestr += time.ToString(" dd yyyy");
+ else
+ timestr += time.ToString(" dd hh:mm");
+
+ result.AppendFormat("{0}rwxr--r-- 1 owner group {1} {2} {3}\r\n",
+ dirflag, size, timestr, entry.Name);
+ }
+
+ return result.ToString();
+ }
+
+ /// <summary>
+ /// Formats a list of file system entries for a response to an NLST command
+ /// </summary>
+ private string FormatNLST(FileSystemEntry[] list)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (FileSystemEntry entry in list) {
+ sb.Append(entry.Name);
+ sb.Append("\r\n");
+ }
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// Format a timestamp for a reponse to a MDTM command
+ /// </summary>
+ private string FormatTime(DateTime time)
+ {
+ return time.ToString("yyyyMMddHHmmss");
+ }
+
+ /// <summary>
+ /// Restrict the year in a timestamp to >= 1970
+ /// </summary>
+ private DateTime EnsureUnixTime(DateTime time)
+ {
+ // the server claims to be UNIX, so there should be
+ // no timestamps before 1970.
+ // e.g. FileZilla does not handle them correctly.
+
+ int yearDiff = time.Year - 1970;
+ if (yearDiff < 0)
+ return time.AddYears(-yearDiff);
+ else
+ return time;
+ }
+
+ /// <summary>
+ /// Escape a path for a response to a PWD command
+ /// </summary>
+ private string EscapePath(string path)
+ {
+ // double-quotes in paths are escaped by doubling them
+ return '"' + path.Replace("\"", "\"\"") + '"';
+ }
+
+ /// <summary>
+ /// Remove "-a" or "-l" from the arguments for a LIST or STAT command
+ /// </summary>
+ private string RemoveLsArgs(string args)
+ {
+ if (args != null && (args.StartsWith("-a") || args.StartsWith("-l"))) {
+ if (args.Length == 2)
+ return null;
+ else if (args.Length > 3 && args[2] == ' ')
+ return args.Substring(3);
+ }
+
+ return args;
+ }
+
+ /// <summary>
+ /// Convert a string to a list of UTF8 bytes
+ /// </summary>
+ private byte[] EncodeString(string data)
+ {
+ return Encoding.UTF8.GetBytes(data);
+ }
+
+ /// <summary>
+ /// Convert a list of UTF8 bytes to a string
+ /// </summary>
+ private string DecodeString(byte[] data, int len)
+ {
+ return Encoding.UTF8.GetString(data, 0, len);
+ }
+
+ /// <summary>
+ /// Convert a list of UTF8 bytes to a string
+ /// </summary>
+ private string DecodeString(byte[] data)
+ {
+ return DecodeString(data, data.Length);
+ }
+
+ /// <summary>
+ /// Fill a stream with the given string as UTF8 bytes
+ /// </summary>
+ private Stream MakeStream(string data)
+ {
+ return new MemoryStream(EncodeString(data));
+ }
+
+ /// <summary>
+ /// Return a randomly selected text from the given list
+ /// </summary>
+ private string GetRandomText(string[] texts)
+ {
+ int index = randomTextIndex.Next(0, texts.Length);
+ return texts[index];
+ }
+ }
+}
diff --git a/ImagesForm.Designer.cs b/ImagesForm.Designer.cs
index a01627cd..65047c42 100644
--- a/ImagesForm.Designer.cs
+++ b/ImagesForm.Designer.cs
@@ -95,6 +95,7 @@
this.Text = "ImagesForm";
this.TopMost = true;
this.TransparencyKey = System.Drawing.Color.Transparent;
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ImagesForm_FormClosing);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pictureBox3)).EndInit();
diff --git a/ImagesForm.cs b/ImagesForm.cs
index bc1136e5..5a623cdf 100644
--- a/ImagesForm.cs
+++ b/ImagesForm.cs
@@ -44,5 +44,10 @@ namespace com.clusterrr.hakchi_gui
}
}
}
+
+ private void ImagesForm_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ e.Cancel = true;
+ }
}
}
diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs
index 232abece..a71c4133 100644
--- a/MainForm.Designer.cs
+++ b/MainForm.Designer.cs
@@ -92,6 +92,13 @@
this.globalCommandLineArgumentsexpertsOnluToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator();
this.saveSettingsToNESMiniNowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.saveStateManagerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator();
+ this.FTPToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.openFTPInExplorerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator();
+ this.shellToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.gitHubPageWithActualReleasesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fAQToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -136,8 +143,6 @@
this.checkedListBoxDefaultGames = new System.Windows.Forms.CheckedListBox();
this.timerCalculateGames = new System.Windows.Forms.Timer(this.components);
this.timerConnectionCheck = new System.Windows.Forms.Timer(this.components);
- this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.saveStateManagerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip.SuspendLayout();
this.groupBoxOptions.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBoxArt)).BeginInit();
@@ -600,6 +605,54 @@
this.saveSettingsToNESMiniNowToolStripMenuItem.Name = "saveSettingsToNESMiniNowToolStripMenuItem";
this.saveSettingsToNESMiniNowToolStripMenuItem.Click += new System.EventHandler(this.saveSettingsToNESMiniNowToolStripMenuItem_Click);
//
+ // toolsToolStripMenuItem
+ //
+ this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.saveStateManagerToolStripMenuItem,
+ this.toolStripMenuItem6,
+ this.FTPToolStripMenuItem,
+ this.openFTPInExplorerToolStripMenuItem,
+ this.toolStripMenuItem7,
+ this.shellToolStripMenuItem});
+ this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
+ resources.ApplyResources(this.toolsToolStripMenuItem, "toolsToolStripMenuItem");
+ //
+ // saveStateManagerToolStripMenuItem
+ //
+ this.saveStateManagerToolStripMenuItem.Name = "saveStateManagerToolStripMenuItem";
+ resources.ApplyResources(this.saveStateManagerToolStripMenuItem, "saveStateManagerToolStripMenuItem");
+ this.saveStateManagerToolStripMenuItem.Click += new System.EventHandler(this.saveStateManagerToolStripMenuItem_Click);
+ //
+ // toolStripMenuItem6
+ //
+ this.toolStripMenuItem6.Name = "toolStripMenuItem6";
+ resources.ApplyResources(this.toolStripMenuItem6, "toolStripMenuItem6");
+ //
+ // FTPToolStripMenuItem
+ //
+ this.FTPToolStripMenuItem.CheckOnClick = true;
+ this.FTPToolStripMenuItem.Name = "FTPToolStripMenuItem";
+ resources.ApplyResources(this.FTPToolStripMenuItem, "FTPToolStripMenuItem");
+ this.FTPToolStripMenuItem.Click += new System.EventHandler(this.FTPToolStripMenuItem_Click);
+ //
+ // openFTPInExplorerToolStripMenuItem
+ //
+ resources.ApplyResources(this.openFTPInExplorerToolStripMenuItem, "openFTPInExplorerToolStripMenuItem");
+ this.openFTPInExplorerToolStripMenuItem.Name = "openFTPInExplorerToolStripMenuItem";
+ this.openFTPInExplorerToolStripMenuItem.Click += new System.EventHandler(this.openFTPInExplorerToolStripMenuItem_Click);
+ //
+ // toolStripMenuItem7
+ //
+ this.toolStripMenuItem7.Name = "toolStripMenuItem7";
+ resources.ApplyResources(this.toolStripMenuItem7, "toolStripMenuItem7");
+ //
+ // shellToolStripMenuItem
+ //
+ this.shellToolStripMenuItem.CheckOnClick = true;
+ this.shellToolStripMenuItem.Name = "shellToolStripMenuItem";
+ resources.ApplyResources(this.shellToolStripMenuItem, "shellToolStripMenuItem");
+ this.shellToolStripMenuItem.Click += new System.EventHandler(this.shellToolStripMenuItem_Click);
+ //
// helpToolStripMenuItem
//
this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -901,19 +954,6 @@
this.timerConnectionCheck.Interval = 500;
this.timerConnectionCheck.Tick += new System.EventHandler(this.timerConnectionCheck_Tick);
//
- // toolsToolStripMenuItem
- //
- this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.saveStateManagerToolStripMenuItem});
- this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
- resources.ApplyResources(this.toolsToolStripMenuItem, "toolsToolStripMenuItem");
- //
- // saveStateManagerToolStripMenuItem
- //
- this.saveStateManagerToolStripMenuItem.Name = "saveStateManagerToolStripMenuItem";
- resources.ApplyResources(this.saveStateManagerToolStripMenuItem, "saveStateManagerToolStripMenuItem");
- this.saveStateManagerToolStripMenuItem.Click += new System.EventHandler(this.saveStateManagerToolStripMenuItem_Click);
- //
// MainForm
//
this.AllowDrop = true;
@@ -933,6 +973,7 @@
this.MaximizeBox = false;
this.Name = "MainForm";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed);
this.Shown += new System.EventHandler(this.MainForm_Shown);
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.checkedListBoxGames_DragDrop);
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.checkedListBoxGames_DragEnter);
@@ -1061,6 +1102,11 @@
private System.Windows.Forms.ToolStripMenuItem compressGamesIfPossibleToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem saveStateManagerToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem FTPToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem shellToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6;
+ private System.Windows.Forms.ToolStripMenuItem openFTPInExplorerToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7;
}
}
diff --git a/MainForm.cs b/MainForm.cs
index 5b5f0203..e6bcfae1 100644
--- a/MainForm.cs
+++ b/MainForm.cs
@@ -22,6 +22,7 @@ namespace com.clusterrr.hakchi_gui
public static ClovershellConnection Clovershell;
//readonly string UBootDump;
readonly string KernelDump;
+ mooftpserv.Server ftpServer;
NesDefaultGame[] defaultNesGames = new NesDefaultGame[] {
new NesDefaultGame { Code = "CLV-P-NAAAE", Name = "Super Mario Bros.", Size = 571031 },
@@ -164,16 +165,12 @@ namespace com.clusterrr.hakchi_gui
Clovershell = new ClovershellConnection() { AutoReconnect = true, Enabled = true };
Clovershell.OnConnected += Clovershell_OnConnected;
-#if DEBUG
- try
- {
- Clovershell.ShellEnabled = true;
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex.Message + ex.StackTrace);
- }
-#endif
+
+ ftpServer = new mooftpserv.Server();
+ ftpServer.AuthHandler = new mooftpserv.NesMiniAuthHandler();
+ ftpServer.FileSystemHandler = new mooftpserv.NesMiniFileSystemHandler(Clovershell);
+ ftpServer.LogHandler = new mooftpserv.DebugLogHandler();
+ ftpServer.LocalPort = 1021;
}
catch (Exception ex)
{
@@ -186,6 +183,7 @@ namespace com.clusterrr.hakchi_gui
{
try
{
+ ConfigIni.CustomFlashed = true; // Just in case of new installation
var region = Clovershell.ExecuteSimple("cat /etc/clover/REGION", 500, true);
Debug.WriteLine(string.Format("Detected region: {0}", region));
if (region == "JPN")
@@ -515,8 +513,14 @@ namespace com.clusterrr.hakchi_gui
{
Debug.WriteLine("Closing main form");
SaveConfig();
+ ftpServer.Stop();
Clovershell.Dispose();
}
+ private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ Process.GetCurrentProcess().Kill(); // Suicide! Just easy and dirty way to kill all threads.
+ }
+
struct CountResult
{
@@ -612,6 +616,39 @@ namespace com.clusterrr.hakchi_gui
}
}
+ DialogResult RequireKernelDump()
+ {
+ if (File.Exists(KernelDump)) return DialogResult.OK; // OK - already dumped
+ // Asking user to dump kernel
+ if (MessageBox.Show(Resources.NoKernelWarning, Resources.NoKernel, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
+ == System.Windows.Forms.DialogResult.Yes)
+ {
+ if (DoKernelDump())
+ return DialogResult.Yes; // Succesfully dumped
+ else
+ return DialogResult.No; // Not dumped for some other reason
+ }
+ else return DialogResult.No; // Kernel dump cancelled by user
+ }
+
+ DialogResult RequirePatchedKernel()
+ {
+ if (ConfigIni.CustomFlashed) return DialogResult.OK; // OK - already flashed
+ var kernelDump = RequireKernelDump(); // We need kernel dump first
+ if (kernelDump == System.Windows.Forms.DialogResult.No)
+ return DialogResult.No; // Abort if user has not dumped it
+ if (MessageBox.Show((kernelDump == DialogResult.Yes ? (Resources.KernelDumped + "\r\n") : "") +
+ Resources.CustomWarning, Resources.CustomKernel, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
+ == System.Windows.Forms.DialogResult.Yes)
+ {
+ if (FlashCustomKernel())
+ return DialogResult.Yes; // Succesfully flashed
+ else
+ return DialogResult.No; // Not flashed for some other reason
+ }
+ else return DialogResult.No;
+ }
+
private void buttonStart_Click(object sender, EventArgs e)
{
SaveConfig();
@@ -622,28 +659,10 @@ namespace com.clusterrr.hakchi_gui
MessageBox.Show(Resources.SelectAtLeast, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
- bool dumpedKernelNow = false;
- if (!File.Exists(KernelDump))
- {
- if (MessageBox.Show(Resources.NoKernelWarning, Resources.NoKernel, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
- == System.Windows.Forms.DialogResult.Yes)
- {
- if (!DoKernelDump()) return;
- dumpedKernelNow = true;
- }
- else return;
- }
- if (!ConfigIni.CustomFlashed)
- {
- if (MessageBox.Show((dumpedKernelNow ? (Resources.KernelDumped + "\r\n") : "") +
- Resources.CustomWarning, Resources.CustomKernel, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
- == System.Windows.Forms.DialogResult.Yes)
- {
- if (!FlashCustomKernel()) return;
- MessageBox.Show(Resources.DoneYouCanUpload + "\r\n" + Resources.PressOkToContinue, Resources.Congratulations, MessageBoxButtons.OK, MessageBoxIcon.Information);
- }
- else return;
- }
+ var kernel = RequirePatchedKernel();
+ if (kernel == DialogResult.No) return;
+ if (kernel == DialogResult.Yes) // Message for new user
+ MessageBox.Show(Resources.DoneYouCanUpload + "\r\n" + Resources.PressOkToContinue, Resources.Congratulations, MessageBoxButtons.OK, MessageBoxIcon.Information);
if (UploadGames())
{
MessageBox.Show(Resources.Done, Resources.Wow, MessageBoxButtons.OK, MessageBoxIcon.Information);
@@ -850,11 +869,7 @@ namespace com.clusterrr.hakchi_gui
private void flashCustomKernelToolStripMenuItem_Click(object sender, EventArgs e)
{
- if (!File.Exists(KernelDump))
- {
- MessageBox.Show(Resources.NoKernelYouNeed, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
+ if (RequireKernelDump() == DialogResult.No) return;
if (MessageBox.Show(Resources.CustomKernelQ, Resources.AreYouSure, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
== System.Windows.Forms.DialogResult.Yes)
{
@@ -1230,6 +1245,7 @@ namespace com.clusterrr.hakchi_gui
private void installModules(string[] add = null)
{
+ if (RequireKernelDump() == DialogResult.No) return;
var form = new SelectModsForm(false, true, add);
form.Text = Resources.SelectModsInstall;
if (form.ShowDialog() == DialogResult.OK)
@@ -1245,11 +1261,7 @@ namespace com.clusterrr.hakchi_gui
private void uninstallModulesToolStripMenuItem_Click(object sender, EventArgs e)
{
- if (!File.Exists(KernelDump))
- {
- MessageBox.Show(Resources.NoKernelYouNeed, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
+ if (RequireKernelDump() == DialogResult.No) return;
var form = new SelectModsForm(true, false);
form.Text = Resources.SelectModsUninstall;
if (form.ShowDialog() == DialogResult.OK)
@@ -1271,6 +1283,7 @@ namespace com.clusterrr.hakchi_gui
private void saveSettingsToNESMiniNowToolStripMenuItem_Click(object sender, EventArgs e)
{
+ if (RequirePatchedKernel() == DialogResult.No) return;
try
{
if (WaitingClovershellForm.WaitForDevice(this))
@@ -1288,11 +1301,7 @@ namespace com.clusterrr.hakchi_gui
private void saveStateManagerToolStripMenuItem_Click(object sender, EventArgs e)
{
- if (!ConfigIni.CustomFlashed)
- {
- MessageBox.Show(Resources.NoKernelYouNeed, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
- return;
- }
+ if (RequirePatchedKernel() == DialogResult.No) return;
var gameNames = new Dictionary<string, string>();
foreach (var game in defaultNesGames)
gameNames[game.Code] = game.Name;
@@ -1306,5 +1315,85 @@ namespace com.clusterrr.hakchi_gui
var form = new SaveStateManager(gameNames);
form.ShowDialog();
}
+
+ private void FTPToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ if (FTPToolStripMenuItem.Checked)
+ {
+ try
+ {
+ var ftpThread = new Thread(delegate()
+ {
+ try
+ {
+ ftpServer.Run();
+ }
+ catch (ThreadAbortException)
+ {
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+ ftpServer.Stop();
+ }
+ catch { }
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ Invoke(new Action(delegate()
+ {
+ MessageBox.Show(this, ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ FTPToolStripMenuItem.Checked = false;
+ }));
+ }
+ });
+ ftpThread.Start();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ MessageBox.Show(this, ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ FTPToolStripMenuItem.Checked = false;
+ }
+ }
+ else
+ {
+ ftpServer.Stop();
+ }
+ openFTPInExplorerToolStripMenuItem.Enabled = FTPToolStripMenuItem.Checked;
+ }
+
+ private void shellToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ Clovershell.ShellEnabled = shellToolStripMenuItem.Checked;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ MessageBox.Show(this, ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ shellToolStripMenuItem.Checked = false;
+ }
+ }
+
+ private void openFTPInExplorerToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ new Process()
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = "explorer.exe",
+ Arguments = "ftp://root:clover@127.0.0.1:1021/"
+ }
+ }.Start();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex.Message + ex.StackTrace);
+ MessageBox.Show(this, ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
}
}
diff --git a/MainForm.resx b/MainForm.resx
index 286a61e2..c05e9288 100644
--- a/MainForm.resx
+++ b/MainForm.resx
@@ -120,90 +120,17 @@
<metadata name="menuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
- <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <data name="fileToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>37, 20</value>
- </data>
- <data name="fileToolStripMenuItem.Text" xml:space="preserve">
- <value>&amp;File</value>
- </data>
- <data name="kernelToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>52, 20</value>
- </data>
- <data name="kernelToolStripMenuItem.Text" xml:space="preserve">
- <value>&amp;Kernel</value>
- </data>
- <data name="modulesToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>65, 20</value>
- </data>
- <data name="modulesToolStripMenuItem.Text" xml:space="preserve">
- <value>&amp;Modules</value>
- </data>
- <data name="settingsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>61, 20</value>
- </data>
- <data name="settingsToolStripMenuItem.Text" xml:space="preserve">
- <value>&amp;Settings</value>
- </data>
- <data name="saveStateManagerToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>179, 22</value>
- </data>
- <data name="saveStateManagerToolStripMenuItem.Text" xml:space="preserve">
- <value>Save-State Manager</value>
- </data>
- <data name="toolsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>48, 20</value>
- </data>
- <data name="toolsToolStripMenuItem.Text" xml:space="preserve">
- <value>&amp;Tools</value>
- </data>
- <data name="helpToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>44, 20</value>
- </data>
- <data name="helpToolStripMenuItem.Text" xml:space="preserve">
- <value>&amp;Help</value>
- </data>
- <data name="menuStrip.Location" type="System.Drawing.Point, System.Drawing">
- <value>0, 0</value>
- </data>
- <data name="menuStrip.Size" type="System.Drawing.Size, System.Drawing">
- <value>609, 24</value>
- </data>
- <assembly alias="mscorlib" name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
- <data name="menuStrip.TabIndex" type="System.Int32, mscorlib">
- <value>1</value>
- </data>
- <data name="menuStrip.Text" xml:space="preserve">
- <value>menuStrip</value>
- </data>
- <data name="&gt;&gt;menuStrip.Name" xml:space="preserve">
- <value>menuStrip</value>
- </data>
- <data name="&gt;&gt;menuStrip.Type" xml:space="preserve">
- <value>System.Windows.Forms.MenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;menuStrip.Parent" xml:space="preserve">
- <value>$this</value>
- </data>
- <data name="&gt;&gt;menuStrip.ZOrder" xml:space="preserve">
- <value>6</value>
- </data>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="addMoreGamesToolStripMenuItem.ShortcutKeys" type="System.Windows.Forms.Keys, System.Windows.Forms">
<value>Ctrl+O</value>
</data>
+ <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="addMoreGamesToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>238, 22</value>
</data>
<data name="addMoreGamesToolStripMenuItem.Text" xml:space="preserve">
<value>Add more &amp;games</value>
</data>
- <data name="presetsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>238, 22</value>
- </data>
- <data name="presetsToolStripMenuItem.Text" xml:space="preserve">
- <value>Selection presets</value>
- </data>
<data name="toolStripMenuItem2.Size" type="System.Drawing.Size, System.Drawing">
<value>139, 6</value>
</data>
@@ -219,6 +146,12 @@
<data name="deletePresetToolStripMenuItem.Text" xml:space="preserve">
<value>Delete preset</value>
</data>
+ <data name="presetsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>238, 22</value>
+ </data>
+ <data name="presetsToolStripMenuItem.Text" xml:space="preserve">
+ <value>Selection presets</value>
+ </data>
<data name="synchronizeToolStripMenuItem.ShortcutKeys" type="System.Windows.Forms.Keys, System.Windows.Forms">
<value>F5</value>
</data>
@@ -252,6 +185,12 @@
<data name="exitToolStripMenuItem.Text" xml:space="preserve">
<value>&amp;Exit</value>
</data>
+ <data name="fileToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>37, 20</value>
+ </data>
+ <data name="fileToolStripMenuItem.Text" xml:space="preserve">
+ <value>&amp;File</value>
+ </data>
<data name="dumpKernelToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>179, 22</value>
</data>
@@ -276,6 +215,12 @@
<data name="uninstallToolStripMenuItem.Text" xml:space="preserve">
<value>Uninstall</value>
</data>
+ <data name="kernelToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>52, 20</value>
+ </data>
+ <data name="kernelToolStripMenuItem.Text" xml:space="preserve">
+ <value>&amp;Kernel</value>
+ </data>
<data name="installModulesToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>197, 22</value>
</data>
@@ -288,11 +233,11 @@
<data name="uninstallModulesToolStripMenuItem.Text" xml:space="preserve">
<value>Uninstall extra modules</value>
</data>
- <data name="consoleTypeToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>376, 22</value>
+ <data name="modulesToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>65, 20</value>
</data>
- <data name="consoleTypeToolStripMenuItem.Text" xml:space="preserve">
- <value>Console type</value>
+ <data name="modulesToolStripMenuItem.Text" xml:space="preserve">
+ <value>&amp;Modules</value>
</data>
<data name="nESMiniToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>151, 22</value>
@@ -306,11 +251,11 @@
<data name="famicomMiniToolStripMenuItem.Text" xml:space="preserve">
<value>Famicom Mini</value>
</data>
- <data name="cloverconHackToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <data name="consoleTypeToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>376, 22</value>
</data>
- <data name="cloverconHackToolStripMenuItem.Text" xml:space="preserve">
- <value>Controller hacks</value>
+ <data name="consoleTypeToolStripMenuItem.Text" xml:space="preserve">
+ <value>Console type</value>
</data>
<data name="resetUsingCombinationOfButtonsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>308, 22</value>
@@ -342,6 +287,12 @@
<data name="upABStartOnSecondControllerToolStripMenuItem.Text" xml:space="preserve">
<value>"Up+A+B = Start" on 2nd controller</value>
</data>
+ <data name="cloverconHackToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>376, 22</value>
+ </data>
+ <data name="cloverconHackToolStripMenuItem.Text" xml:space="preserve">
+ <value>Controller hacks</value>
+ </data>
<data name="useExtendedFontToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>376, 22</value>
</data>
@@ -360,12 +311,6 @@
<data name="compressGamesIfPossibleToolStripMenuItem.Text" xml:space="preserve">
<value>Compress non-NES games (for additional emulator mod)</value>
</data>
- <data name="pagesfoldersTypeToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>376, 22</value>
- </data>
- <data name="pagesfoldersTypeToolStripMenuItem.Text" xml:space="preserve">
- <value>Pages/folders structure</value>
- </data>
<data name="disablePagefoldersToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>356, 22</value>
</data>
@@ -429,12 +374,6 @@
<data name="foldersSplitByFirstLetterOriginalToolStripMenuItem.Text" xml:space="preserve">
<value>Original games in root -&gt; Folders, split by first letter</value>
</data>
- <data name="maximumGamesPerFolderToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
- <value>356, 22</value>
- </data>
- <data name="maximumGamesPerFolderToolStripMenuItem.Text" xml:space="preserve">
- <value>Maximum games per page/folder</value>
- </data>
<data name="max20toolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>92, 22</value>
</data>
@@ -507,6 +446,12 @@
<data name="max100toolStripMenuItem.Text" xml:space="preserve">
<value>100</value>
</data>
+ <data name="maximumGamesPerFolderToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>356, 22</value>
+ </data>
+ <data name="maximumGamesPerFolderToolStripMenuItem.Text" xml:space="preserve">
+ <value>Maximum games per page/folder</value>
+ </data>
<data name="toolStripMenuItem4.Size" type="System.Drawing.Size, System.Drawing">
<value>353, 6</value>
</data>
@@ -516,6 +461,12 @@
<data name="customToolStripMenuItem.Text" xml:space="preserve">
<value>Custom - show Folders Manager every time</value>
</data>
+ <data name="pagesfoldersTypeToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>376, 22</value>
+ </data>
+ <data name="pagesfoldersTypeToolStripMenuItem.Text" xml:space="preserve">
+ <value>Pages/folders structure</value>
+ </data>
<data name="globalCommandLineArgumentsexpertsOnluToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>376, 22</value>
</data>
@@ -534,6 +485,52 @@
<data name="saveSettingsToNESMiniNowToolStripMenuItem.Text" xml:space="preserve">
<value>Save settings to NES Mini now</value>
</data>
+ <data name="settingsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>61, 20</value>
+ </data>
+ <data name="settingsToolStripMenuItem.Text" xml:space="preserve">
+ <value>&amp;Settings</value>
+ </data>
+ <data name="saveStateManagerToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>402, 22</value>
+ </data>
+ <data name="saveStateManagerToolStripMenuItem.Text" xml:space="preserve">
+ <value>Save-State Manager</value>
+ </data>
+ <data name="toolStripMenuItem6.Size" type="System.Drawing.Size, System.Drawing">
+ <value>399, 6</value>
+ </data>
+ <data name="FTPToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>402, 22</value>
+ </data>
+ <data name="FTPToolStripMenuItem.Text" xml:space="preserve">
+ <value>FTP server on ftp://root:clover@127.0.0.1:1021</value>
+ </data>
+ <assembly alias="mscorlib" name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="openFTPInExplorerToolStripMenuItem.Enabled" type="System.Boolean, mscorlib">
+ <value>False</value>
+ </data>
+ <data name="openFTPInExplorerToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>402, 22</value>
+ </data>
+ <data name="openFTPInExplorerToolStripMenuItem.Text" xml:space="preserve">
+ <value>Open FTP in explorer</value>
+ </data>
+ <data name="toolStripMenuItem7.Size" type="System.Drawing.Size, System.Drawing">
+ <value>399, 6</value>
+ </data>
+ <data name="shellToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>402, 22</value>
+ </data>
+ <data name="shellToolStripMenuItem.Text" xml:space="preserve">
+ <value>Shell server on telnet://127.0.0.1:1023 (raw, noecho, login:root)</value>
+ </data>
+ <data name="toolsToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>48, 20</value>
+ </data>
+ <data name="toolsToolStripMenuItem.Text" xml:space="preserve">
+ <value>&amp;Tools</value>
+ </data>
<data name="gitHubPageWithActualReleasesToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>246, 22</value>
</data>
@@ -552,6 +549,36 @@
<data name="aboutToolStripMenuItem.Text" xml:space="preserve">
<value>About...</value>
</data>
+ <data name="helpToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
+ <value>44, 20</value>
+ </data>
+ <data name="helpToolStripMenuItem.Text" xml:space="preserve">
+ <value>&amp;Help</value>
+ </data>
+ <data name="menuStrip.Location" type="System.Drawing.Point, System.Drawing">
+ <value>0, 0</value>
+ </data>
+ <data name="menuStrip.Size" type="System.Drawing.Size, System.Drawing">
+ <value>609, 24</value>
+ </data>
+ <data name="menuStrip.TabIndex" type="System.Int32, mscorlib">
+ <value>1</value>
+ </data>
+ <data name="menuStrip.Text" xml:space="preserve">
+ <value>menuStrip</value>
+ </data>
+ <data name="&gt;&gt;menuStrip.Name" xml:space="preserve">
+ <value>menuStrip</value>
+ </data>
+ <data name="&gt;&gt;menuStrip.Type" xml:space="preserve">
+ <value>System.Windows.Forms.MenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;menuStrip.Parent" xml:space="preserve">
+ <value>$this</value>
+ </data>
+ <data name="&gt;&gt;menuStrip.ZOrder" xml:space="preserve">
+ <value>6</value>
+ </data>
<data name="checkedListBoxGames.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>None</value>
</data>
@@ -579,270 +606,6 @@
<data name="groupBoxOptions.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>None</value>
</data>
- <data name="&gt;&gt;buttonShowGameGenieDatabase.Name" xml:space="preserve">
- <value>buttonShowGameGenieDatabase</value>
- </data>
- <data name="&gt;&gt;buttonShowGameGenieDatabase.Type" xml:space="preserve">
- <value>System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;buttonShowGameGenieDatabase.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;buttonShowGameGenieDatabase.ZOrder" xml:space="preserve">
- <value>0</value>
- </data>
- <data name="&gt;&gt;maskedTextBoxReleaseDate.Name" xml:space="preserve">
- <value>maskedTextBoxReleaseDate</value>
- </data>
- <data name="&gt;&gt;maskedTextBoxReleaseDate.Type" xml:space="preserve">
- <value>System.Windows.Forms.MaskedTextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;maskedTextBoxReleaseDate.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;maskedTextBoxReleaseDate.ZOrder" xml:space="preserve">
- <value>1</value>
- </data>
- <data name="&gt;&gt;label1.Name" xml:space="preserve">
- <value>label1</value>
- </data>
- <data name="&gt;&gt;label1.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;label1.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;label1.ZOrder" xml:space="preserve">
- <value>2</value>
- </data>
- <data name="&gt;&gt;textBoxGameGenie.Name" xml:space="preserve">
- <value>textBoxGameGenie</value>
- </data>
- <data name="&gt;&gt;textBoxGameGenie.Type" xml:space="preserve">
- <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;textBoxGameGenie.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;textBoxGameGenie.ZOrder" xml:space="preserve">
- <value>3</value>
- </data>
- <data name="&gt;&gt;label7.Name" xml:space="preserve">
- <value>label7</value>
- </data>
- <data name="&gt;&gt;label7.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;label7.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;label7.ZOrder" xml:space="preserve">
- <value>4</value>
- </data>
- <data name="&gt;&gt;label6.Name" xml:space="preserve">
- <value>label6</value>
- </data>
- <data name="&gt;&gt;label6.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;label6.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;label6.ZOrder" xml:space="preserve">
- <value>5</value>
- </data>
- <data name="&gt;&gt;radioButtonTwoSim.Name" xml:space="preserve">
- <value>radioButtonTwoSim</value>
- </data>
- <data name="&gt;&gt;radioButtonTwoSim.Type" xml:space="preserve">
- <value>System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;radioButtonTwoSim.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;radioButtonTwoSim.ZOrder" xml:space="preserve">
- <value>6</value>
- </data>
- <data name="&gt;&gt;buttonGoogle.Name" xml:space="preserve">
- <value>buttonGoogle</value>
- </data>
- <data name="&gt;&gt;buttonGoogle.Type" xml:space="preserve">
- <value>System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;buttonGoogle.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;buttonGoogle.ZOrder" xml:space="preserve">
- <value>7</value>
- </data>
- <data name="&gt;&gt;buttonBrowseImage.Name" xml:space="preserve">
- <value>buttonBrowseImage</value>
- </data>
- <data name="&gt;&gt;buttonBrowseImage.Type" xml:space="preserve">
- <value>System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;buttonBrowseImage.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;buttonBrowseImage.ZOrder" xml:space="preserve">
- <value>8</value>
- </data>
- <data name="&gt;&gt;pictureBoxArt.Name" xml:space="preserve">
- <value>pictureBoxArt</value>
- </data>
- <data name="&gt;&gt;pictureBoxArt.Type" xml:space="preserve">
- <value>System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;pictureBoxArt.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;pictureBoxArt.ZOrder" xml:space="preserve">
- <value>9</value>
- </data>
- <data name="&gt;&gt;label4.Name" xml:space="preserve">
- <value>label4</value>
- </data>
- <data name="&gt;&gt;label4.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;label4.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;label4.ZOrder" xml:space="preserve">
- <value>10</value>
- </data>
- <data name="&gt;&gt;textBoxArguments.Name" xml:space="preserve">
- <value>textBoxArguments</value>
- </data>
- <data name="&gt;&gt;textBoxArguments.Type" xml:space="preserve">
- <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;textBoxArguments.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;textBoxArguments.ZOrder" xml:space="preserve">
- <value>11</value>
- </data>
- <data name="&gt;&gt;label3.Name" xml:space="preserve">
- <value>label3</value>
- </data>
- <data name="&gt;&gt;label3.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;label3.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;label3.ZOrder" xml:space="preserve">
- <value>12</value>
- </data>
- <data name="&gt;&gt;textBoxPublisher.Name" xml:space="preserve">
- <value>textBoxPublisher</value>
- </data>
- <data name="&gt;&gt;textBoxPublisher.Type" xml:space="preserve">
- <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;textBoxPublisher.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;textBoxPublisher.ZOrder" xml:space="preserve">
- <value>13</value>
- </data>
- <data name="&gt;&gt;label2.Name" xml:space="preserve">
- <value>label2</value>
- </data>
- <data name="&gt;&gt;label2.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;label2.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;label2.ZOrder" xml:space="preserve">
- <value>14</value>
- </data>
- <data name="&gt;&gt;radioButtonTwo.Name" xml:space="preserve">
- <value>radioButtonTwo</value>
- </data>
- <data name="&gt;&gt;radioButtonTwo.Type" xml:space="preserve">
- <value>System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;radioButtonTwo.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;radioButtonTwo.ZOrder" xml:space="preserve">
- <value>15</value>
- </data>
- <data name="&gt;&gt;radioButtonOne.Name" xml:space="preserve">
- <value>radioButtonOne</value>
- </data>
- <data name="&gt;&gt;radioButtonOne.Type" xml:space="preserve">
- <value>System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;radioButtonOne.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;radioButtonOne.ZOrder" xml:space="preserve">
- <value>16</value>
- </data>
- <data name="&gt;&gt;textBoxName.Name" xml:space="preserve">
- <value>textBoxName</value>
- </data>
- <data name="&gt;&gt;textBoxName.Type" xml:space="preserve">
- <value>System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;textBoxName.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;textBoxName.ZOrder" xml:space="preserve">
- <value>17</value>
- </data>
- <data name="&gt;&gt;labelName.Name" xml:space="preserve">
- <value>labelName</value>
- </data>
- <data name="&gt;&gt;labelName.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;labelName.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;labelName.ZOrder" xml:space="preserve">
- <value>18</value>
- </data>
- <data name="&gt;&gt;labelID.Name" xml:space="preserve">
- <value>labelID</value>
- </data>
- <data name="&gt;&gt;labelID.Type" xml:space="preserve">
- <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;labelID.Parent" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;labelID.ZOrder" xml:space="preserve">
- <value>19</value>
- </data>
- <data name="groupBoxOptions.Location" type="System.Drawing.Point, System.Drawing">
- <value>306, 27</value>
- </data>
- <data name="groupBoxOptions.Size" type="System.Drawing.Size, System.Drawing">
- <value>293, 529</value>
- </data>
- <data name="groupBoxOptions.TabIndex" type="System.Int32, mscorlib">
- <value>4</value>
- </data>
- <data name="groupBoxOptions.Text" xml:space="preserve">
- <value>Game options</value>
- </data>
- <data name="&gt;&gt;groupBoxOptions.Name" xml:space="preserve">
- <value>groupBoxOptions</value>
- </data>
- <data name="&gt;&gt;groupBoxOptions.Type" xml:space="preserve">
- <value>System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;groupBoxOptions.Parent" xml:space="preserve">
- <value>$this</value>
- </data>
- <data name="&gt;&gt;groupBoxOptions.ZOrder" xml:space="preserve">
- <value>7</value>
- </data>
<data name="buttonShowGameGenieDatabase.Location" type="System.Drawing.Point, System.Drawing">
<value>252, 286</value>
</data>
@@ -1350,6 +1113,30 @@
<data name="&gt;&gt;labelID.ZOrder" xml:space="preserve">
<value>19</value>
</data>
+ <data name="groupBoxOptions.Location" type="System.Drawing.Point, System.Drawing">
+ <value>306, 27</value>
+ </data>
+ <data name="groupBoxOptions.Size" type="System.Drawing.Size, System.Drawing">
+ <value>293, 529</value>
+ </data>
+ <data name="groupBoxOptions.TabIndex" type="System.Int32, mscorlib">
+ <value>4</value>
+ </data>
+ <data name="groupBoxOptions.Text" xml:space="preserve">
+ <value>Game options</value>
+ </data>
+ <data name="&gt;&gt;groupBoxOptions.Name" xml:space="preserve">
+ <value>groupBoxOptions</value>
+ </data>
+ <data name="&gt;&gt;groupBoxOptions.Type" xml:space="preserve">
+ <value>System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;groupBoxOptions.Parent" xml:space="preserve">
+ <value>$this</value>
+ </data>
+ <data name="&gt;&gt;groupBoxOptions.ZOrder" xml:space="preserve">
+ <value>7</value>
+ </data>
<data name="label5.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>None</value>
</data>
@@ -1410,6 +1197,27 @@
<metadata name="statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>109, 17</value>
</metadata>
+ <data name="toolStripStatusConnectionIcon.AutoSize" type="System.Boolean, mscorlib">
+ <value>False</value>
+ </data>
+ <data name="toolStripStatusConnectionIcon.Size" type="System.Drawing.Size, System.Drawing">
+ <value>16, 17</value>
+ </data>
+ <data name="toolStripStatusLabelSelected.AutoSize" type="System.Boolean, mscorlib">
+ <value>False</value>
+ </data>
+ <data name="toolStripStatusLabelSelected.Size" type="System.Drawing.Size, System.Drawing">
+ <value>150, 17</value>
+ </data>
+ <data name="toolStripStatusLabelSize.AutoSize" type="System.Boolean, mscorlib">
+ <value>False</value>
+ </data>
+ <data name="toolStripStatusLabelSize.Size" type="System.Drawing.Size, System.Drawing">
+ <value>150, 17</value>
+ </data>
+ <data name="toolStripProgressBar.Size" type="System.Drawing.Size, System.Drawing">
+ <value>285, 16</value>
+ </data>
<data name="statusStrip.Location" type="System.Drawing.Point, System.Drawing">
<value>0, 615</value>
</data>
@@ -1434,27 +1242,6 @@
<data name="&gt;&gt;statusStrip.ZOrder" xml:space="preserve">
<value>2</value>
</data>
- <data name="toolStripStatusConnectionIcon.AutoSize" type="System.Boolean, mscorlib">
- <value>False</value>
- </data>
- <data name="toolStripStatusConnectionIcon.Size" type="System.Drawing.Size, System.Drawing">
- <value>16, 17</value>
- </data>
- <data name="toolStripStatusLabelSelected.AutoSize" type="System.Boolean, mscorlib">
- <value>False</value>
- </data>
- <data name="toolStripStatusLabelSelected.Size" type="System.Drawing.Size, System.Drawing">
- <value>150, 17</value>
- </data>
- <data name="toolStripStatusLabelSize.AutoSize" type="System.Boolean, mscorlib">
- <value>False</value>
- </data>
- <data name="toolStripStatusLabelSize.Size" type="System.Drawing.Size, System.Drawing">
- <value>150, 17</value>
- </data>
- <data name="toolStripProgressBar.Size" type="System.Drawing.Size, System.Drawing">
- <value>285, 16</value>
- </data>
<metadata name="openFileDialogNes.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>202, 17</value>
</metadata>
@@ -1464,15 +1251,6 @@
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>340, 17</value>
</metadata>
- <data name="contextMenuStrip.Size" type="System.Drawing.Size, System.Drawing">
- <value>141, 70</value>
- </data>
- <data name="&gt;&gt;contextMenuStrip.Name" xml:space="preserve">
- <value>contextMenuStrip</value>
- </data>
- <data name="&gt;&gt;contextMenuStrip.Type" xml:space="preserve">
- <value>System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
<data name="selectAllToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>140, 22</value>
</data>
@@ -1491,6 +1269,15 @@
<data name="deleteGameToolStripMenuItem.Text" xml:space="preserve">
<value>Delete game</value>
</data>
+ <data name="contextMenuStrip.Size" type="System.Drawing.Size, System.Drawing">
+ <value>141, 70</value>
+ </data>
+ <data name="&gt;&gt;contextMenuStrip.Name" xml:space="preserve">
+ <value>contextMenuStrip</value>
+ </data>
+ <data name="&gt;&gt;contextMenuStrip.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
<metadata name="openFileDialogImage.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>624, 17</value>
</metadata>
@@ -1530,6 +1317,18 @@
<data name="groupBoxDefaultGames.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>None</value>
</data>
+ <data name="checkedListBoxDefaultGames.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
+ <value>Top, Bottom, Left, Right</value>
+ </data>
+ <data name="checkedListBoxDefaultGames.Location" type="System.Drawing.Point, System.Drawing">
+ <value>5, 49</value>
+ </data>
+ <data name="checkedListBoxDefaultGames.Size" type="System.Drawing.Size, System.Drawing">
+ <value>282, 454</value>
+ </data>
+ <data name="checkedListBoxDefaultGames.TabIndex" type="System.Int32, mscorlib">
+ <value>3</value>
+ </data>
<data name="&gt;&gt;checkedListBoxDefaultGames.Name" xml:space="preserve">
<value>checkedListBoxDefaultGames</value>
</data>
@@ -1569,30 +1368,6 @@
<data name="&gt;&gt;groupBoxDefaultGames.ZOrder" xml:space="preserve">
<value>8</value>
</data>
- <data name="checkedListBoxDefaultGames.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
- <value>Top, Bottom, Left, Right</value>
- </data>
- <data name="checkedListBoxDefaultGames.Location" type="System.Drawing.Point, System.Drawing">
- <value>5, 49</value>
- </data>
- <data name="checkedListBoxDefaultGames.Size" type="System.Drawing.Size, System.Drawing">
- <value>282, 454</value>
- </data>
- <data name="checkedListBoxDefaultGames.TabIndex" type="System.Int32, mscorlib">
- <value>3</value>
- </data>
- <data name="&gt;&gt;checkedListBoxDefaultGames.Name" xml:space="preserve">
- <value>checkedListBoxDefaultGames</value>
- </data>
- <data name="&gt;&gt;checkedListBoxDefaultGames.Type" xml:space="preserve">
- <value>System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;checkedListBoxDefaultGames.Parent" xml:space="preserve">
- <value>groupBoxDefaultGames</value>
- </data>
- <data name="&gt;&gt;checkedListBoxDefaultGames.ZOrder" xml:space="preserve">
- <value>0</value>
- </data>
<metadata name="timerCalculateGames.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>473, 17</value>
</metadata>
@@ -3121,6 +2896,48 @@
<data name="&gt;&gt;saveSettingsToNESMiniNowToolStripMenuItem.Type" xml:space="preserve">
<value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
+ <data name="&gt;&gt;toolsToolStripMenuItem.Name" xml:space="preserve">
+ <value>toolsToolStripMenuItem</value>
+ </data>
+ <data name="&gt;&gt;toolsToolStripMenuItem.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;saveStateManagerToolStripMenuItem.Name" xml:space="preserve">
+ <value>saveStateManagerToolStripMenuItem</value>
+ </data>
+ <data name="&gt;&gt;saveStateManagerToolStripMenuItem.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;toolStripMenuItem6.Name" xml:space="preserve">
+ <value>toolStripMenuItem6</value>
+ </data>
+ <data name="&gt;&gt;toolStripMenuItem6.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;FTPToolStripMenuItem.Name" xml:space="preserve">
+ <value>FTPToolStripMenuItem</value>
+ </data>
+ <data name="&gt;&gt;FTPToolStripMenuItem.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;openFTPInExplorerToolStripMenuItem.Name" xml:space="preserve">
+ <value>openFTPInExplorerToolStripMenuItem</value>
+ </data>
+ <data name="&gt;&gt;openFTPInExplorerToolStripMenuItem.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;toolStripMenuItem7.Name" xml:space="preserve">
+ <value>toolStripMenuItem7</value>
+ </data>
+ <data name="&gt;&gt;toolStripMenuItem7.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;shellToolStripMenuItem.Name" xml:space="preserve">
+ <value>shellToolStripMenuItem</value>
+ </data>
+ <data name="&gt;&gt;shellToolStripMenuItem.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
<data name="&gt;&gt;helpToolStripMenuItem.Name" xml:space="preserve">
<value>helpToolStripMenuItem</value>
</data>
@@ -3211,18 +3028,6 @@
<data name="&gt;&gt;timerConnectionCheck.Type" xml:space="preserve">
<value>System.Windows.Forms.Timer, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
- <data name="&gt;&gt;toolsToolStripMenuItem.Name" xml:space="preserve">
- <value>toolsToolStripMenuItem</value>
- </data>
- <data name="&gt;&gt;toolsToolStripMenuItem.Type" xml:space="preserve">
- <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;saveStateManagerToolStripMenuItem.Name" xml:space="preserve">
- <value>saveStateManagerToolStripMenuItem</value>
- </data>
- <data name="&gt;&gt;saveStateManagerToolStripMenuItem.Type" xml:space="preserve">
- <value>System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
<data name="&gt;&gt;$this.Name" xml:space="preserve">
<value>MainForm</value>
</data>
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
index 383456c8..e8bf5f3d 100644
--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.15.12")]
-[assembly: AssemblyFileVersion("2.0.15.12")]
+[assembly: AssemblyVersion("2.0.16.1")]
+[assembly: AssemblyFileVersion("2.0.16.1")]
diff --git a/SaveStateManager.Designer.cs b/SaveStateManager.Designer.cs
index e02e89f7..5d9bffd9 100644
--- a/SaveStateManager.Designer.cs
+++ b/SaveStateManager.Designer.cs
@@ -30,11 +30,6 @@
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SaveStateManager));
- this.dataGridView = new System.Windows.Forms.DataGridView();
- this.colCode = new System.Windows.Forms.DataGridViewTextBoxColumn();
- this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn();
- this.colSize = new System.Windows.Forms.DataGridViewTextBoxColumn();
- this.colFlags = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -47,61 +42,15 @@
this.toolStripStatusLabelSize = new System.Windows.Forms.ToolStripStatusLabel();
this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
- this.timerCellHover = new System.Windows.Forms.Timer(this.components);
- ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
+ this.listViewSaves = new System.Windows.Forms.ListView();
+ this.colName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.colCode = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.colSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.colFlags = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.contextMenuStrip.SuspendLayout();
this.statusStrip.SuspendLayout();
this.SuspendLayout();
//
- // dataGridView
- //
- this.dataGridView.AllowUserToAddRows = false;
- this.dataGridView.AllowUserToDeleteRows = false;
- this.dataGridView.AllowUserToOrderColumns = true;
- this.dataGridView.AllowUserToResizeRows = false;
- resources.ApplyResources(this.dataGridView, "dataGridView");
- this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
- this.colCode,
- this.colName,
- this.colSize,
- this.colFlags});
- this.dataGridView.ContextMenuStrip = this.contextMenuStrip;
- this.dataGridView.Name = "dataGridView";
- this.dataGridView.ReadOnly = true;
- this.dataGridView.RowHeadersVisible = false;
- this.dataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
- this.dataGridView.CellMouseEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellMouseEnter);
- this.dataGridView.CellMouseLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellMouseLeave);
- this.dataGridView.CellMouseMove += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dataGridView_CellMouseMove);
- this.dataGridView.SelectionChanged += new System.EventHandler(this.dataGridView_SelectionChanged);
- //
- // colCode
- //
- resources.ApplyResources(this.colCode, "colCode");
- this.colCode.Name = "colCode";
- this.colCode.ReadOnly = true;
- //
- // colName
- //
- resources.ApplyResources(this.colName, "colName");
- this.colName.Name = "colName";
- this.colName.ReadOnly = true;
- //
- // colSize
- //
- this.colSize.FillWeight = 30F;
- resources.ApplyResources(this.colSize, "colSize");
- this.colSize.Name = "colSize";
- this.colSize.ReadOnly = true;
- //
- // colFlags
- //
- this.colFlags.FillWeight = 30F;
- resources.ApplyResources(this.colFlags, "colFlags");
- this.colFlags.Name = "colFlags";
- this.colFlags.ReadOnly = true;
- //
// contextMenuStrip
//
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -110,6 +59,9 @@
this.deleteToolStripMenuItem});
this.contextMenuStrip.Name = "contextMenuStrip";
resources.ApplyResources(this.contextMenuStrip, "contextMenuStrip");
+ this.contextMenuStrip.Closed += new System.Windows.Forms.ToolStripDropDownClosedEventHandler(this.contextMenuStrip_Closed);
+ this.contextMenuStrip.Opened += new System.EventHandler(this.contextMenuStrip_Opened);
+ this.contextMenuStrip.ItemClicked += new System.Windows.Forms.ToolStripItemClickedEventHandler(this.contextMenuStrip_ItemClicked);
//
// exportToolStripMenuItem
//
@@ -176,24 +128,51 @@
this.openFileDialog.DefaultExt = "clvs";
this.openFileDialog.Multiselect = true;
//
- // timerCellHover
+ // listViewSaves
+ //
+ resources.ApplyResources(this.listViewSaves, "listViewSaves");
+ this.listViewSaves.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.colName,
+ this.colCode,
+ this.colSize,
+ this.colFlags});
+ this.listViewSaves.ContextMenuStrip = this.contextMenuStrip;
+ this.listViewSaves.Name = "listViewSaves";
+ this.listViewSaves.UseCompatibleStateImageBehavior = false;
+ this.listViewSaves.View = System.Windows.Forms.View.Details;
+ this.listViewSaves.ItemMouseHover += new System.Windows.Forms.ListViewItemMouseHoverEventHandler(this.listViewSaves_ItemMouseHover);
+ this.listViewSaves.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.listViewSaves_ItemSelectionChanged);
+ this.listViewSaves.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listViewSaves_KeyDown);
+ this.listViewSaves.MouseMove += new System.Windows.Forms.MouseEventHandler(this.listViewSaves_MouseMove);
//
- this.timerCellHover.Interval = 300;
- this.timerCellHover.Tick += new System.EventHandler(this.timerCellHover_Tick);
+ // colName
+ //
+ resources.ApplyResources(this.colName, "colName");
+ //
+ // colCode
+ //
+ resources.ApplyResources(this.colCode, "colCode");
+ //
+ // colSize
+ //
+ resources.ApplyResources(this.colSize, "colSize");
+ //
+ // colFlags
+ //
+ resources.ApplyResources(this.colFlags, "colFlags");
//
// SaveStateManager
//
resources.ApplyResources(this, "$this");
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.listViewSaves);
this.Controls.Add(this.statusStrip);
this.Controls.Add(this.buttonDelete);
this.Controls.Add(this.buttonImport);
this.Controls.Add(this.buttonExport);
- this.Controls.Add(this.dataGridView);
this.Controls.Add(this.labelLoading);
this.Name = "SaveStateManager";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.SaveStateManager_FormClosed);
- ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
this.contextMenuStrip.ResumeLayout(false);
this.statusStrip.ResumeLayout(false);
this.statusStrip.PerformLayout();
@@ -204,7 +183,6 @@
#endregion
- private System.Windows.Forms.DataGridView dataGridView;
private System.Windows.Forms.Button buttonExport;
private System.Windows.Forms.Button buttonImport;
private System.Windows.Forms.Button buttonDelete;
@@ -217,10 +195,10 @@
private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem;
private System.Windows.Forms.SaveFileDialog saveFileDialog;
private System.Windows.Forms.OpenFileDialog openFileDialog;
- private System.Windows.Forms.DataGridViewTextBoxColumn colCode;
- private System.Windows.Forms.DataGridViewTextBoxColumn colName;
- private System.Windows.Forms.DataGridViewTextBoxColumn colSize;
- private System.Windows.Forms.DataGridViewTextBoxColumn colFlags;
- private System.Windows.Forms.Timer timerCellHover;
+ private System.Windows.Forms.ListView listViewSaves;
+ private System.Windows.Forms.ColumnHeader colCode;
+ private System.Windows.Forms.ColumnHeader colName;
+ private System.Windows.Forms.ColumnHeader colSize;
+ private System.Windows.Forms.ColumnHeader colFlags;
}
} \ No newline at end of file
diff --git a/SaveStateManager.cs b/SaveStateManager.cs
index 7a0dadef..bed7793d 100644
--- a/SaveStateManager.cs
+++ b/SaveStateManager.cs
@@ -43,14 +43,19 @@ namespace com.clusterrr.hakchi_gui
{
try
{
- Invoke(new Action(delegate
+ if (!(bool)Invoke(new Func<bool>(delegate
{
- dataGridView.Visible = false;
+ listViewSaves.Visible = false;
exportToolStripMenuItem.Enabled = importToolStripMenuItem.Enabled =
buttonExport.Enabled = buttonImport.Enabled = false;
if (!WaitingClovershellForm.WaitForDevice(this))
- Close();
- }));
+ return false;
+ return true;
+ })))
+ {
+ Close();
+ return;
+ }
var clovershell = MainForm.Clovershell;
WorkerForm.ShowSplashScreen();
@@ -82,13 +87,13 @@ namespace com.clusterrr.hakchi_gui
var lines = Encoding.UTF8.GetString(output.ToArray()).Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
Invoke(new Action(delegate
{
- dataGridView.Rows.Clear();
+ listViewSaves.Items.Clear();
foreach (var line in lines)
{
var l = line;
var code = l.Substring(0, l.IndexOf(' '));
l = l.Substring(l.IndexOf(' ') + 1);
- var size = int.Parse(l.Substring(0, l.IndexOf(' ')));
+ var size = l.Substring(0, l.IndexOf(' ')) + "KB";
l = l.Substring(l.IndexOf(' ') + 1);
var flags = l.Substring(0, l.IndexOf(' ')).Replace("F", "");
l = l.Substring(l.IndexOf(' ') + 1);
@@ -100,13 +105,16 @@ namespace com.clusterrr.hakchi_gui
else
name = Resources.UnknownGame;
}
- var r = dataGridView.Rows.Add();
- dataGridView.Rows[r].Cells["colCode"].Value = code;
- dataGridView.Rows[r].Cells["colName"].Value = name;
- dataGridView.Rows[r].Cells["colSize"].Value = size;
- dataGridView.Rows[r].Cells["colFlags"].Value = flags;
+ listViewSaves.Items.Add(new ListViewItem(new ListViewItem.ListViewSubItem[] {
+ new ListViewItem.ListViewSubItem() { Name = "colName", Text = name},
+ new ListViewItem.ListViewSubItem() { Name = "colCode", Text = code},
+ new ListViewItem.ListViewSubItem() { Name = "colSize", Text = size},
+ new ListViewItem.ListViewSubItem() { Name = "colFlags", Text = flags}
+ }, 0));
+ listViewSaves.Sorting = SortOrder.Ascending;
+ listViewSaves.Sort();
}
- dataGridView.Visible = true;
+ listViewSaves.Visible = true;
importToolStripMenuItem.Enabled = true;
buttonImport.Enabled = true;
}));
@@ -128,27 +136,14 @@ namespace com.clusterrr.hakchi_gui
}
}
- private void dataGridView_SelectionChanged(object sender, EventArgs e)
- {
- exportToolStripMenuItem.Enabled = deleteToolStripMenuItem.Enabled =
- buttonExport.Enabled = buttonDelete.Enabled = dataGridView.SelectedRows.Count > 0;
- if (dataGridView.SelectedRows.Count > 0)
- {
- var size = 0;
- foreach (DataGridViewRow game in dataGridView.SelectedRows)
- if (game.Cells["colSize"].Value != null)
- size += (int)game.Cells["colSize"].Value;
- toolStripStatusLabelSize.Text = string.Format(Resources.SizeOfSaves, size);
- }
- else toolStripStatusLabelSize.Text = "";
- }
-
private void buttonDelete_Click(object sender, EventArgs e)
{
if (MessageBox.Show(this, Resources.DeleteSavesQ, Resources.AreYouSure, MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
!= System.Windows.Forms.DialogResult.Yes)
return;
- var savesToDelete = dataGridView.SelectedRows;
+ var savesToDelete = new List<ListViewItem>();
+ foreach (ListViewItem item in listViewSaves.SelectedItems)
+ savesToDelete.Add(item);
new Thread(deleteRowsThread).Start(savesToDelete);
}
@@ -156,19 +151,20 @@ namespace com.clusterrr.hakchi_gui
{
try
{
- var savesToDelete = (DataGridViewSelectedRowCollection)o;
- Invoke(new Action(delegate
+ var savesToDelete = (IEnumerable<ListViewItem>)o;
+ if (!(bool)Invoke(new Func<bool>(delegate
{
if (!WaitingClovershellForm.WaitForDevice(this))
- return;
- }));
- foreach (DataGridViewRow game in savesToDelete)
+ return false;
+ return true;
+ }))) return;
+ foreach (ListViewItem game in savesToDelete)
{
var clovershell = MainForm.Clovershell;
- clovershell.ExecuteSimple("rm -rf /var/lib/clover/profiles/0/" + game.Cells["colCode"].Value, 3000, true);
+ clovershell.ExecuteSimple("rm -rf /var/lib/clover/profiles/0/" + game.SubItems["colCode"].Text, 3000, true);
Invoke(new Action(delegate
{
- dataGridView.Rows.Remove(game);
+ listViewSaves.Items.Remove(game);
}));
}
}
@@ -192,10 +188,10 @@ namespace com.clusterrr.hakchi_gui
{
try
{
- foreach (DataGridViewRow game in dataGridView.SelectedRows)
+ foreach (ListViewItem game in listViewSaves.SelectedItems)
{
- saveFileDialog.FileName = game.Cells["colName"].Value + ".clvs";
- var name = game.Cells["colName"].Value != null ? game.Cells["colName"].Value.ToString() : "save";
+ saveFileDialog.FileName = game.SubItems["colName"].Text + ".clvs";
+ var name = game.SubItems["colName"].Text != null ? game.SubItems["colName"].Text : "save";
saveFileDialog.Title = name;
if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
@@ -204,7 +200,7 @@ namespace com.clusterrr.hakchi_gui
var clovershell = MainForm.Clovershell;
using (var save = new MemoryStream())
{
- clovershell.Execute("cd /var/lib/clover/profiles/0 && tar -cz " + game.Cells["colCode"].Value, null, save, null, 10000, true);
+ clovershell.Execute("cd /var/lib/clover/profiles/0 && tar -cz " + game.SubItems["colCode"].Text, null, save, null, 10000, true);
var buffer = save.ToArray();
File.WriteAllBytes(saveFileDialog.FileName, buffer);
}
@@ -231,6 +227,9 @@ namespace com.clusterrr.hakchi_gui
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
var files = openFileDialog.FileNames;
+ listViewSaves.Visible = false;
+ buttonDelete.Enabled = buttonExport.Enabled = buttonImport.Enabled =
+ deleteToolStripMenuItem.Enabled = exportToolStripMenuItem.Enabled = importToolStripMenuItem.Enabled = false;
new Thread(importSaves).Start(files);
}
}
@@ -240,11 +239,12 @@ namespace com.clusterrr.hakchi_gui
try
{
var files = (string[])o;
- Invoke(new Action(delegate
+ if (!(bool)Invoke(new Func<bool>(delegate
{
if (!WaitingClovershellForm.WaitForDevice(this))
- return;
- }));
+ return false;
+ return true;
+ }))) return;
foreach (var file in files)
{
var clovershell = MainForm.Clovershell;
@@ -284,44 +284,6 @@ namespace com.clusterrr.hakchi_gui
catch { }
}
- private void dataGridView_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
- {
- timerCellHover.Enabled = true;
- }
-
- private void dataGridView_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
- {
- timerCellHover.Enabled = false;
- if (imagesForm != null)
- imagesForm.Hide();
- }
-
- private void dataGridView_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
- {
- if (lastPosition.Equals(Cursor.Position)) return;
- lastPosition = Cursor.Position;
- if (imagesForm != null)
- imagesForm.Hide();
- timerCellHover.Enabled = false;
- timerCellHover.Enabled = true;
- }
-
- private void timerCellHover_Tick(object sender, EventArgs e)
- {
- timerCellHover.Enabled = false;
- if (!MainForm.Clovershell.IsOnline) return;
- for (int i = 0; i < dataGridView.RowCount; i++)
- {
- var rect = dataGridView.RectangleToScreen(dataGridView.GetRowDisplayRectangle(i, true));
- if (Cursor.Position.Y >= rect.Top && Cursor.Position.Y <= rect.Bottom)
- {
- var row = dataGridView.Rows[i];
- if (row.Cells["colFlags"].Value.ToString().Equals("S")) return; // No images?
- new Thread(loadImagesThread).Start(row.Cells["colCode"].Value);
- }
- }
- }
-
// Mouse on cell? Show some screenshots to remember game!
void loadImagesThread(object s)
{
@@ -357,28 +319,21 @@ namespace com.clusterrr.hakchi_gui
}
Debug.WriteLine("Loaded " + images.Count + " imags");
+ if (images.Count == 0) return;
+ if (menuOpened) return; // Right click...
Invoke(new Action(delegate
{
// Maybe it's too late?
- string name = null;
- for (int i = 0; i < dataGridView.RowCount; i++)
- {
- var rect = dataGridView.RectangleToScreen(dataGridView.GetRowDisplayRectangle(i, true));
- if (Cursor.Position.Y >= rect.Top && Cursor.Position.Y <= rect.Bottom)
- {
- var row = dataGridView.Rows[i];
- if (row.Cells["colCode"].Value.ToString() != code)
- return;
- name = row.Cells["colName"].Value.ToString();
- }
- }
- if (name == null) return; // No rows at all
+ var p = listViewSaves.PointToClient(Cursor.Position);
+ var item = listViewSaves.GetItemAt(p.X, p.Y);
+ if (item == null) return; // No rows at all
+ if (item.SubItems["colCode"].Text != code) return; // Other item
if (imagesForm == null)
imagesForm = new ImagesForm();
imagesForm.Left = Cursor.Position.X + 5;
imagesForm.Top = Cursor.Position.Y + 5;
- imagesForm.Text = name;
+ imagesForm.Text = item.SubItems["colName"].Text;
imagesForm.ShowImages(images);
imagesForm.Show();
}));
@@ -388,5 +343,58 @@ namespace com.clusterrr.hakchi_gui
Debug.WriteLine(ex.Message + ex.StackTrace);
}
}
+
+ private void listViewSaves_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
+ {
+ exportToolStripMenuItem.Enabled = deleteToolStripMenuItem.Enabled =
+ buttonExport.Enabled = buttonDelete.Enabled = listViewSaves.SelectedItems.Count > 0;
+ if (listViewSaves.SelectedItems.Count > 0)
+ {
+ var size = 0;
+ foreach (ListViewItem game in listViewSaves.SelectedItems)
+ if (game.SubItems["colSize"].Text != null)
+ size += int.Parse(game.SubItems["colSize"].Text.Replace("KB", ""));
+ toolStripStatusLabelSize.Text = string.Format(Resources.SizeOfSaves, size);
+ }
+ else toolStripStatusLabelSize.Text = "";
+ }
+
+ private void listViewSaves_ItemMouseHover(object sender, ListViewItemMouseHoverEventArgs e)
+ {
+ new Thread(loadImagesThread).Start(e.Item.SubItems["colCode"].Text);
+ }
+
+ private void listViewSaves_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (lastPosition.Equals(Cursor.Position)) return;
+ lastPosition = Cursor.Position;
+ if (imagesForm != null)
+ imagesForm.Hide();
+ }
+
+ bool menuOpened = false;
+ private void contextMenuStrip_Opened(object sender, EventArgs e)
+ {
+ menuOpened = true;
+ }
+
+ private void contextMenuStrip_Closed(object sender, ToolStripDropDownClosedEventArgs e)
+ {
+ menuOpened = false;
+ }
+
+ private void contextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
+ {
+ if (imagesForm != null)
+ imagesForm.Hide();
+ }
+
+ private void listViewSaves_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode == Keys.Delete && buttonDelete.Enabled)
+ {
+ buttonDelete_Click(null, null);
+ }
+ }
}
}
diff --git a/SaveStateManager.resx b/SaveStateManager.resx
index 0fb65a07..2ae3e3f7 100644
--- a/SaveStateManager.resx
+++ b/SaveStateManager.resx
@@ -117,81 +117,14 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
- <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
- <data name="dataGridView.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
- <value>Top, Bottom, Left, Right</value>
- </data>
- <metadata name="colCode.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
- <value>True</value>
- </metadata>
- <data name="colCode.HeaderText" xml:space="preserve">
- <value>Code</value>
- </data>
- <metadata name="colName.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
- <value>True</value>
- </metadata>
- <data name="colName.HeaderText" xml:space="preserve">
- <value>Name</value>
- </data>
- <data name="colName.ToolTipText" xml:space="preserve">
- <value>wtf</value>
- </data>
- <assembly alias="mscorlib" name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
- <data name="colName.Width" type="System.Int32, mscorlib">
- <value>400</value>
- </data>
- <metadata name="colSize.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
- <value>True</value>
- </metadata>
- <data name="colSize.HeaderText" xml:space="preserve">
- <value>Size (KB)</value>
- </data>
- <metadata name="colFlags.UserAddedColumn" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
- <value>True</value>
- </metadata>
- <data name="colFlags.HeaderText" xml:space="preserve">
- <value>Save slots</value>
- </data>
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>126, 17</value>
</metadata>
- <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
- <data name="contextMenuStrip.Size" type="System.Drawing.Size, System.Drawing">
- <value>111, 70</value>
- </data>
- <data name="&gt;&gt;contextMenuStrip.Name" xml:space="preserve">
- <value>contextMenuStrip</value>
- </data>
- <data name="&gt;&gt;contextMenuStrip.Type" xml:space="preserve">
- <value>System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="dataGridView.Location" type="System.Drawing.Point, System.Drawing">
- <value>12, 12</value>
- </data>
- <data name="dataGridView.Size" type="System.Drawing.Size, System.Drawing">
- <value>710, 432</value>
- </data>
- <data name="dataGridView.TabIndex" type="System.Int32, mscorlib">
- <value>0</value>
- </data>
- <data name="dataGridView.Visible" type="System.Boolean, mscorlib">
- <value>False</value>
- </data>
- <data name="&gt;&gt;dataGridView.Name" xml:space="preserve">
- <value>dataGridView</value>
- </data>
- <data name="&gt;&gt;dataGridView.Type" xml:space="preserve">
- <value>System.Windows.Forms.DataGridView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;dataGridView.Parent" xml:space="preserve">
- <value>$this</value>
- </data>
- <data name="&gt;&gt;dataGridView.ZOrder" xml:space="preserve">
- <value>5</value>
- </data>
+ <assembly alias="mscorlib" name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="exportToolStripMenuItem.Enabled" type="System.Boolean, mscorlib">
<value>False</value>
</data>
+ <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="exportToolStripMenuItem.Size" type="System.Drawing.Size, System.Drawing">
<value>110, 22</value>
</data>
@@ -216,6 +149,16 @@
<data name="deleteToolStripMenuItem.Text" xml:space="preserve">
<value>Delete</value>
</data>
+ <data name="contextMenuStrip.Size" type="System.Drawing.Size, System.Drawing">
+ <value>111, 70</value>
+ </data>
+ <data name="&gt;&gt;contextMenuStrip.Name" xml:space="preserve">
+ <value>contextMenuStrip</value>
+ </data>
+ <data name="&gt;&gt;contextMenuStrip.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="buttonExport.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>Bottom, Right</value>
</data>
@@ -244,7 +187,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;buttonExport.ZOrder" xml:space="preserve">
- <value>4</value>
+ <value>5</value>
</data>
<data name="buttonImport.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>Bottom, Right</value>
@@ -277,7 +220,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;buttonImport.ZOrder" xml:space="preserve">
- <value>3</value>
+ <value>4</value>
</data>
<data name="buttonDelete.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
<value>Bottom, Right</value>
@@ -310,7 +253,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;buttonDelete.ZOrder" xml:space="preserve">
- <value>2</value>
+ <value>3</value>
</data>
<data name="labelLoading.Font" type="System.Drawing.Font, System.Drawing">
<value>Microsoft Sans Serif, 30pt</value>
@@ -367,7 +310,7 @@
<value>$this</value>
</data>
<data name="&gt;&gt;statusStrip.ZOrder" xml:space="preserve">
- <value>1</value>
+ <value>2</value>
</data>
<data name="toolStripStatusLabelSize.Size" type="System.Drawing.Size, System.Drawing">
<value>0, 17</value>
@@ -378,9 +321,54 @@
<metadata name="openFileDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>404, 17</value>
</metadata>
- <metadata name="timerCellHover.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
- <value>537, 17</value>
- </metadata>
+ <data name="listViewSaves.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
+ <value>Top, Bottom, Left, Right</value>
+ </data>
+ <data name="colName.Text" xml:space="preserve">
+ <value>Name</value>
+ </data>
+ <data name="colName.Width" type="System.Int32, mscorlib">
+ <value>400</value>
+ </data>
+ <data name="colCode.Text" xml:space="preserve">
+ <value>Code</value>
+ </data>
+ <data name="colCode.Width" type="System.Int32, mscorlib">
+ <value>100</value>
+ </data>
+ <data name="colSize.Text" xml:space="preserve">
+ <value>Size</value>
+ </data>
+ <data name="colSize.Width" type="System.Int32, mscorlib">
+ <value>100</value>
+ </data>
+ <data name="colFlags.Text" xml:space="preserve">
+ <value>Save slots</value>
+ </data>
+ <data name="colFlags.Width" type="System.Int32, mscorlib">
+ <value>100</value>
+ </data>
+ <data name="listViewSaves.Location" type="System.Drawing.Point, System.Drawing">
+ <value>12, 12</value>
+ </data>
+ <data name="listViewSaves.Size" type="System.Drawing.Size, System.Drawing">
+ <value>710, 433</value>
+ </data>
+ <data name="listViewSaves.TabIndex" type="System.Int32, mscorlib">
+ <value>6</value>
+ </data>
+ <data name="&gt;&gt;listViewSaves.Name" xml:space="preserve">
+ <value>listViewSaves</value>
+ </data>
+ <data name="&gt;&gt;listViewSaves.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;listViewSaves.Parent" xml:space="preserve">
+ <value>$this</value>
+ </data>
+ <data name="&gt;&gt;listViewSaves.ZOrder" xml:space="preserve">
+ <value>1</value>
+ </data>
<metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
@@ -1531,30 +1519,6 @@
<data name="$this.Text" xml:space="preserve">
<value>Save-State Manager</value>
</data>
- <data name="&gt;&gt;colCode.Name" xml:space="preserve">
- <value>colCode</value>
- </data>
- <data name="&gt;&gt;colCode.Type" xml:space="preserve">
- <value>System.Windows.Forms.DataGridViewTextBoxColumn, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;colName.Name" xml:space="preserve">
- <value>colName</value>
- </data>
- <data name="&gt;&gt;colName.Type" xml:space="preserve">
- <value>System.Windows.Forms.DataGridViewTextBoxColumn, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;colSize.Name" xml:space="preserve">
- <value>colSize</value>
- </data>
- <data name="&gt;&gt;colSize.Type" xml:space="preserve">
- <value>System.Windows.Forms.DataGridViewTextBoxColumn, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
- <data name="&gt;&gt;colFlags.Name" xml:space="preserve">
- <value>colFlags</value>
- </data>
- <data name="&gt;&gt;colFlags.Type" xml:space="preserve">
- <value>System.Windows.Forms.DataGridViewTextBoxColumn, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </data>
<data name="&gt;&gt;exportToolStripMenuItem.Name" xml:space="preserve">
<value>exportToolStripMenuItem</value>
</data>
@@ -1591,11 +1555,29 @@
<data name="&gt;&gt;openFileDialog.Type" xml:space="preserve">
<value>System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
- <data name="&gt;&gt;timerCellHover.Name" xml:space="preserve">
- <value>timerCellHover</value>
+ <data name="&gt;&gt;colName.Name" xml:space="preserve">
+ <value>colName</value>
+ </data>
+ <data name="&gt;&gt;colName.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;colCode.Name" xml:space="preserve">
+ <value>colCode</value>
+ </data>
+ <data name="&gt;&gt;colCode.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </data>
+ <data name="&gt;&gt;colSize.Name" xml:space="preserve">
+ <value>colSize</value>
+ </data>
+ <data name="&gt;&gt;colSize.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
- <data name="&gt;&gt;timerCellHover.Type" xml:space="preserve">
- <value>System.Windows.Forms.Timer, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <data name="&gt;&gt;colFlags.Name" xml:space="preserve">
+ <value>colFlags</value>
+ </data>
+ <data name="&gt;&gt;colFlags.Type" xml:space="preserve">
+ <value>System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="&gt;&gt;$this.Name" xml:space="preserve">
<value>SaveStateManager</value>
diff --git a/hakchi_gui.csproj b/hakchi_gui.csproj
index 40da219b..cafb5b9e 100644
--- a/hakchi_gui.csproj
+++ b/hakchi_gui.csproj
@@ -91,6 +91,21 @@
<Compile Include="Clovershell\ExecConnection.cs" />
<Compile Include="Clovershell\ShellConnection.cs" />
<Compile Include="ConfigIni.cs" />
+ <Compile Include="FileBrowserForm.cs">
+ <SubType>Form</SubType>
+ </Compile>
+ <Compile Include="FileBrowserForm.Designer.cs">
+ <DependentUpon>FileBrowserForm.cs</DependentUpon>
+ </Compile>
+ <Compile Include="FtpServer\NesMiniFileSystemHandler.cs" />
+ <Compile Include="FtpServer\NesMiniAuthHandler.cs" />
+ <Compile Include="FtpServer\DebugLogHandler.cs" />
+ <Compile Include="FtpServer\FileSystemHelper.cs" />
+ <Compile Include="FtpServer\IAuthHandler.cs" />
+ <Compile Include="FtpServer\IFileSystemHandler.cs" />
+ <Compile Include="FtpServer\ILogHandler.cs" />
+ <Compile Include="FtpServer\Server.cs" />
+ <Compile Include="FtpServer\Session.cs" />
<Compile Include="GameGenieCodeAddModForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -406,6 +421,9 @@
<EmbeddedResource Include="AboutBox.resx">
<DependentUpon>AboutBox.cs</DependentUpon>
</EmbeddedResource>
+ <EmbeddedResource Include="FileBrowserForm.resx">
+ <DependentUpon>FileBrowserForm.cs</DependentUpon>
+ </EmbeddedResource>
<EmbeddedResource Include="FoldersManagerForm.de-De.resx">
<DependentUpon>FoldersManagerForm.cs</DependentUpon>
</EmbeddedResource>