diff --git a/FSFlightLogger/App.config b/FSFlightLogger/App.config
index 56efbc7..082f7de 100644
--- a/FSFlightLogger/App.config
+++ b/FSFlightLogger/App.config
@@ -1,6 +1,18 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FSFlightLogger/FSFlightLogger.csproj b/FSFlightLogger/FSFlightLogger.csproj
index 3d74b4a..6f7d610 100644
--- a/FSFlightLogger/FSFlightLogger.csproj
+++ b/FSFlightLogger/FSFlightLogger.csproj
@@ -55,16 +55,46 @@
..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
+ ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\Nito.AsyncEx.Context.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Context.dll
+
+
+ ..\packages\Nito.AsyncEx.Coordination.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Coordination.dll
+
+
+ ..\packages\Nito.AsyncEx.Interop.WaitHandles.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Interop.WaitHandles.dll
+
+
+ ..\packages\Nito.AsyncEx.Oop.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Oop.dll
+
+
+ ..\packages\Nito.AsyncEx.Tasks.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Tasks.dll
+
+
+ ..\packages\Nito.Cancellation.1.0.5\lib\netstandard2.0\Nito.Cancellation.dll
+
+
+ ..\packages\Nito.Collections.Deque.1.0.4\lib\netstandard2.0\Nito.Collections.Deque.dll
+
+
+ ..\packages\Nito.Disposables.2.0.0\lib\netstandard2.0\Nito.Disposables.dll
+
..\packages\SimpleInjector.5.0.3\lib\net461\SimpleInjector.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+ ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll
-
- ..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
@@ -77,6 +107,8 @@
+
+
Form
@@ -85,6 +117,11 @@
+
+ True
+ True
+ i18n.resx
+
MainForm.cs
@@ -97,6 +134,10 @@
True
Resources.resx
+
+ ResXFileCodeGenerator
+ i18n.Designer.cs
+
SettingsSingleFileGenerator
@@ -115,6 +156,10 @@
+
+ {D85BCC97-F653-4286-98D9-073A33A55857}
+ FlightLoggerLib
+
{f160bb6a-7620-41e5-a99c-948c208875e4}
SimConnect
diff --git a/FSFlightLogger/MainForm.Designer.cs b/FSFlightLogger/MainForm.Designer.cs
index 683db9f..768a6fc 100644
--- a/FSFlightLogger/MainForm.Designer.cs
+++ b/FSFlightLogger/MainForm.Designer.cs
@@ -18,30 +18,42 @@
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.OutputGroupbox = new System.Windows.Forms.GroupBox();
+ this.OutputKMLPathBrowseButton = new System.Windows.Forms.Button();
+ this.OutputKMLPathLabel = new System.Windows.Forms.Label();
+ this.OutputKMLPathTextbox = new System.Windows.Forms.TextBox();
+ this.OutputCSVPathBrowseButton = new System.Windows.Forms.Button();
+ this.OutputCSVPathLabel = new System.Windows.Forms.Label();
+ this.OutputCSVPathTextbox = new System.Windows.Forms.TextBox();
+ this.OutputKMLCheckbox = new System.Windows.Forms.CheckBox();
+ this.OutputCSVCheckbox = new System.Windows.Forms.CheckBox();
this.StatusImageList = new System.Windows.Forms.ImageList(this.components);
this.FlightSimulatorStatusIcon = new System.Windows.Forms.PictureBox();
this.FlightSimulatorLabel = new System.Windows.Forms.Label();
this.FlightSimulatorStatusLabel = new System.Windows.Forms.Label();
this.RecordButton = new System.Windows.Forms.Button();
this.RecordingStatusIcon = new System.Windows.Forms.PictureBox();
- this.OutputCSVCheckbox = new System.Windows.Forms.CheckBox();
- this.OutputKMLCheckbox = new System.Windows.Forms.CheckBox();
- this.OutputCSVPathTextbox = new System.Windows.Forms.TextBox();
- this.OutputCSVPathLabel = new System.Windows.Forms.Label();
- this.OutputCSVPathBrowseButton = new System.Windows.Forms.Button();
- this.OutputKMLPathBrowseButton = new System.Windows.Forms.Button();
- this.OutputKMLPathLabel = new System.Windows.Forms.Label();
- this.OutputKMLPathTextbox = new System.Windows.Forms.TextBox();
this.TriggersGroupbox = new System.Windows.Forms.GroupBox();
- this.TriggerWaitForMovementCheckbox = new System.Windows.Forms.CheckBox();
- this.TriggerNewLogStationaryCheckbox = new System.Windows.Forms.CheckBox();
- this.TriggerNewLogStationaryTimeEdit = new System.Windows.Forms.NumericUpDown();
+ this.TriggerConnectedCheckbox = new System.Windows.Forms.CheckBox();
this.TriggerNewLogStationaryUnitsLabel = new System.Windows.Forms.Label();
+ this.TriggerNewLogStationaryTimeEdit = new System.Windows.Forms.NumericUpDown();
+ this.TriggerNewLogStationaryCheckbox = new System.Windows.Forms.CheckBox();
+ this.TriggerWaitForMovementCheckbox = new System.Windows.Forms.CheckBox();
+ this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
+ this.KMLLiveGroupbox = new System.Windows.Forms.GroupBox();
+ this.KMLLiveHelpLabel = new System.Windows.Forms.Label();
+ this.KMLLiveLinkEdit = new System.Windows.Forms.TextBox();
+ this.KMLLivePortEdit = new System.Windows.Forms.NumericUpDown();
+ this.KMLLiveLinkLabel = new System.Windows.Forms.Label();
+ this.KMLLiveCheckbox = new System.Windows.Forms.CheckBox();
+ this.TryConnectTimer = new System.Windows.Forms.Timer(this.components);
+ this.ChangesWhileRecordingLabel = new System.Windows.Forms.Label();
this.OutputGroupbox.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.FlightSimulatorStatusIcon)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.RecordingStatusIcon)).BeginInit();
this.TriggersGroupbox.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.TriggerNewLogStationaryTimeEdit)).BeginInit();
+ this.KMLLiveGroupbox.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.KMLLivePortEdit)).BeginInit();
this.SuspendLayout();
//
// OutputGroupbox
@@ -59,10 +71,96 @@
this.OutputGroupbox.Location = new System.Drawing.Point(12, 59);
this.OutputGroupbox.Name = "OutputGroupbox";
this.OutputGroupbox.Size = new System.Drawing.Size(482, 136);
- this.OutputGroupbox.TabIndex = 0;
+ this.OutputGroupbox.TabIndex = 1;
this.OutputGroupbox.TabStop = false;
this.OutputGroupbox.Text = " Output ";
//
+ // OutputKMLPathBrowseButton
+ //
+ this.OutputKMLPathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.OutputKMLPathBrowseButton.Enabled = false;
+ this.OutputKMLPathBrowseButton.Location = new System.Drawing.Point(448, 102);
+ this.OutputKMLPathBrowseButton.Name = "OutputKMLPathBrowseButton";
+ this.OutputKMLPathBrowseButton.Size = new System.Drawing.Size(28, 20);
+ this.OutputKMLPathBrowseButton.TabIndex = 7;
+ this.OutputKMLPathBrowseButton.Text = "...";
+ this.OutputKMLPathBrowseButton.UseVisualStyleBackColor = true;
+ this.OutputKMLPathBrowseButton.Click += new System.EventHandler(this.OutputKMLPathBrowseButton_Click);
+ //
+ // OutputKMLPathLabel
+ //
+ this.OutputKMLPathLabel.AutoSize = true;
+ this.OutputKMLPathLabel.Location = new System.Drawing.Point(26, 105);
+ this.OutputKMLPathLabel.Name = "OutputKMLPathLabel";
+ this.OutputKMLPathLabel.Size = new System.Drawing.Size(32, 13);
+ this.OutputKMLPathLabel.TabIndex = 6;
+ this.OutputKMLPathLabel.Text = "Path:";
+ //
+ // OutputKMLPathTextbox
+ //
+ this.OutputKMLPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.OutputKMLPathTextbox.Enabled = false;
+ this.OutputKMLPathTextbox.Location = new System.Drawing.Point(71, 102);
+ this.OutputKMLPathTextbox.Name = "OutputKMLPathTextbox";
+ this.OutputKMLPathTextbox.Size = new System.Drawing.Size(373, 20);
+ this.OutputKMLPathTextbox.TabIndex = 5;
+ this.OutputKMLPathTextbox.TextChanged += new System.EventHandler(this.Configuration_Changed);
+ //
+ // OutputCSVPathBrowseButton
+ //
+ this.OutputCSVPathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.OutputCSVPathBrowseButton.Enabled = false;
+ this.OutputCSVPathBrowseButton.Location = new System.Drawing.Point(448, 46);
+ this.OutputCSVPathBrowseButton.Name = "OutputCSVPathBrowseButton";
+ this.OutputCSVPathBrowseButton.Size = new System.Drawing.Size(28, 20);
+ this.OutputCSVPathBrowseButton.TabIndex = 4;
+ this.OutputCSVPathBrowseButton.Text = "...";
+ this.OutputCSVPathBrowseButton.UseVisualStyleBackColor = true;
+ this.OutputCSVPathBrowseButton.Click += new System.EventHandler(this.OutputCSVPathBrowseButton_Click);
+ //
+ // OutputCSVPathLabel
+ //
+ this.OutputCSVPathLabel.AutoSize = true;
+ this.OutputCSVPathLabel.Location = new System.Drawing.Point(26, 49);
+ this.OutputCSVPathLabel.Name = "OutputCSVPathLabel";
+ this.OutputCSVPathLabel.Size = new System.Drawing.Size(32, 13);
+ this.OutputCSVPathLabel.TabIndex = 3;
+ this.OutputCSVPathLabel.Text = "Path:";
+ //
+ // OutputCSVPathTextbox
+ //
+ this.OutputCSVPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.OutputCSVPathTextbox.Enabled = false;
+ this.OutputCSVPathTextbox.Location = new System.Drawing.Point(71, 46);
+ this.OutputCSVPathTextbox.Name = "OutputCSVPathTextbox";
+ this.OutputCSVPathTextbox.Size = new System.Drawing.Size(373, 20);
+ this.OutputCSVPathTextbox.TabIndex = 2;
+ this.OutputCSVPathTextbox.TextChanged += new System.EventHandler(this.Configuration_Changed);
+ //
+ // OutputKMLCheckbox
+ //
+ this.OutputKMLCheckbox.AutoSize = true;
+ this.OutputKMLCheckbox.Location = new System.Drawing.Point(10, 79);
+ this.OutputKMLCheckbox.Name = "OutputKMLCheckbox";
+ this.OutputKMLCheckbox.Size = new System.Drawing.Size(119, 17);
+ this.OutputKMLCheckbox.TabIndex = 1;
+ this.OutputKMLCheckbox.Text = "Google Earth (KML)";
+ this.OutputKMLCheckbox.UseVisualStyleBackColor = true;
+ this.OutputKMLCheckbox.CheckedChanged += new System.EventHandler(this.OutputKMLCheckbox_CheckedChanged);
+ //
+ // OutputCSVCheckbox
+ //
+ this.OutputCSVCheckbox.AutoSize = true;
+ this.OutputCSVCheckbox.Location = new System.Drawing.Point(10, 23);
+ this.OutputCSVCheckbox.Name = "OutputCSVCheckbox";
+ this.OutputCSVCheckbox.Size = new System.Drawing.Size(141, 17);
+ this.OutputCSVCheckbox.TabIndex = 0;
+ this.OutputCSVCheckbox.Text = "Comma-separated (CSV)";
+ this.OutputCSVCheckbox.UseVisualStyleBackColor = true;
+ this.OutputCSVCheckbox.CheckedChanged += new System.EventHandler(this.OutputCSVCheckbox_CheckedChanged);
+ //
// StatusImageList
//
this.StatusImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("StatusImageList.ImageStream")));
@@ -106,9 +204,10 @@
this.RecordButton.Location = new System.Drawing.Point(348, 17);
this.RecordButton.Name = "RecordButton";
this.RecordButton.Size = new System.Drawing.Size(108, 23);
- this.RecordButton.TabIndex = 4;
- this.RecordButton.Text = "&Start recording";
+ this.RecordButton.TabIndex = 0;
+ this.RecordButton.Text = "";
this.RecordButton.UseVisualStyleBackColor = true;
+ this.RecordButton.Click += new System.EventHandler(this.RecordButton_Click);
//
// RecordingStatusIcon
//
@@ -119,151 +218,180 @@
this.RecordingStatusIcon.TabIndex = 5;
this.RecordingStatusIcon.TabStop = false;
//
- // OutputCSVCheckbox
- //
- this.OutputCSVCheckbox.AutoSize = true;
- this.OutputCSVCheckbox.Location = new System.Drawing.Point(10, 23);
- this.OutputCSVCheckbox.Name = "OutputCSVCheckbox";
- this.OutputCSVCheckbox.Size = new System.Drawing.Size(141, 17);
- this.OutputCSVCheckbox.TabIndex = 0;
- this.OutputCSVCheckbox.Text = "Comma-separated (CSV)";
- this.OutputCSVCheckbox.UseVisualStyleBackColor = true;
- //
- // OutputKMLCheckbox
- //
- this.OutputKMLCheckbox.AutoSize = true;
- this.OutputKMLCheckbox.Location = new System.Drawing.Point(10, 79);
- this.OutputKMLCheckbox.Name = "OutputKMLCheckbox";
- this.OutputKMLCheckbox.Size = new System.Drawing.Size(119, 17);
- this.OutputKMLCheckbox.TabIndex = 1;
- this.OutputKMLCheckbox.Text = "Google Earth (KML)";
- this.OutputKMLCheckbox.UseVisualStyleBackColor = true;
- //
- // OutputCSVPathTextbox
- //
- this.OutputCSVPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.OutputCSVPathTextbox.Enabled = false;
- this.OutputCSVPathTextbox.Location = new System.Drawing.Point(71, 46);
- this.OutputCSVPathTextbox.Name = "OutputCSVPathTextbox";
- this.OutputCSVPathTextbox.Size = new System.Drawing.Size(373, 20);
- this.OutputCSVPathTextbox.TabIndex = 2;
- //
- // OutputCSVPathLabel
- //
- this.OutputCSVPathLabel.AutoSize = true;
- this.OutputCSVPathLabel.Location = new System.Drawing.Point(26, 49);
- this.OutputCSVPathLabel.Name = "OutputCSVPathLabel";
- this.OutputCSVPathLabel.Size = new System.Drawing.Size(32, 13);
- this.OutputCSVPathLabel.TabIndex = 3;
- this.OutputCSVPathLabel.Text = "Path:";
- //
- // OutputCSVPathBrowseButton
- //
- this.OutputCSVPathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.OutputCSVPathBrowseButton.Enabled = false;
- this.OutputCSVPathBrowseButton.Location = new System.Drawing.Point(448, 46);
- this.OutputCSVPathBrowseButton.Name = "OutputCSVPathBrowseButton";
- this.OutputCSVPathBrowseButton.Size = new System.Drawing.Size(28, 20);
- this.OutputCSVPathBrowseButton.TabIndex = 4;
- this.OutputCSVPathBrowseButton.Text = "...";
- this.OutputCSVPathBrowseButton.UseVisualStyleBackColor = true;
- //
- // OutputKMLPathBrowseButton
- //
- this.OutputKMLPathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
- this.OutputKMLPathBrowseButton.Enabled = false;
- this.OutputKMLPathBrowseButton.Location = new System.Drawing.Point(448, 102);
- this.OutputKMLPathBrowseButton.Name = "OutputKMLPathBrowseButton";
- this.OutputKMLPathBrowseButton.Size = new System.Drawing.Size(28, 20);
- this.OutputKMLPathBrowseButton.TabIndex = 7;
- this.OutputKMLPathBrowseButton.Text = "...";
- this.OutputKMLPathBrowseButton.UseVisualStyleBackColor = true;
- //
- // OutputKMLPathLabel
- //
- this.OutputKMLPathLabel.AutoSize = true;
- this.OutputKMLPathLabel.Location = new System.Drawing.Point(26, 105);
- this.OutputKMLPathLabel.Name = "OutputKMLPathLabel";
- this.OutputKMLPathLabel.Size = new System.Drawing.Size(32, 13);
- this.OutputKMLPathLabel.TabIndex = 6;
- this.OutputKMLPathLabel.Text = "Path:";
- //
- // OutputKMLPathTextbox
- //
- this.OutputKMLPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.OutputKMLPathTextbox.Enabled = false;
- this.OutputKMLPathTextbox.Location = new System.Drawing.Point(71, 102);
- this.OutputKMLPathTextbox.Name = "OutputKMLPathTextbox";
- this.OutputKMLPathTextbox.Size = new System.Drawing.Size(373, 20);
- this.OutputKMLPathTextbox.TabIndex = 5;
- //
// TriggersGroupbox
//
this.TriggersGroupbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
+ this.TriggersGroupbox.Controls.Add(this.TriggerConnectedCheckbox);
this.TriggersGroupbox.Controls.Add(this.TriggerNewLogStationaryUnitsLabel);
this.TriggersGroupbox.Controls.Add(this.TriggerNewLogStationaryTimeEdit);
this.TriggersGroupbox.Controls.Add(this.TriggerNewLogStationaryCheckbox);
this.TriggersGroupbox.Controls.Add(this.TriggerWaitForMovementCheckbox);
this.TriggersGroupbox.Location = new System.Drawing.Point(12, 201);
this.TriggersGroupbox.Name = "TriggersGroupbox";
- this.TriggersGroupbox.Size = new System.Drawing.Size(482, 99);
- this.TriggersGroupbox.TabIndex = 6;
+ this.TriggersGroupbox.Size = new System.Drawing.Size(482, 124);
+ this.TriggersGroupbox.TabIndex = 2;
this.TriggersGroupbox.TabStop = false;
this.TriggersGroupbox.Text = " Triggers ";
//
+ // TriggerConnectedCheckbox
+ //
+ this.TriggerConnectedCheckbox.AutoSize = true;
+ this.TriggerConnectedCheckbox.Location = new System.Drawing.Point(10, 23);
+ this.TriggerConnectedCheckbox.Name = "TriggerConnectedCheckbox";
+ this.TriggerConnectedCheckbox.Size = new System.Drawing.Size(324, 17);
+ this.TriggerConnectedCheckbox.TabIndex = 0;
+ this.TriggerConnectedCheckbox.Text = " Start recording immediately when connected to Flight Simulator";
+ this.TriggerConnectedCheckbox.UseVisualStyleBackColor = true;
+ //
+ // TriggerNewLogStationaryUnitsLabel
+ //
+ this.TriggerNewLogStationaryUnitsLabel.AutoSize = true;
+ this.TriggerNewLogStationaryUnitsLabel.Location = new System.Drawing.Point(117, 94);
+ this.TriggerNewLogStationaryUnitsLabel.Name = "TriggerNewLogStationaryUnitsLabel";
+ this.TriggerNewLogStationaryUnitsLabel.Size = new System.Drawing.Size(47, 13);
+ this.TriggerNewLogStationaryUnitsLabel.TabIndex = 3;
+ this.TriggerNewLogStationaryUnitsLabel.Text = "seconds";
+ //
+ // TriggerNewLogStationaryTimeEdit
+ //
+ this.TriggerNewLogStationaryTimeEdit.Enabled = false;
+ this.TriggerNewLogStationaryTimeEdit.Location = new System.Drawing.Point(33, 92);
+ this.TriggerNewLogStationaryTimeEdit.Name = "TriggerNewLogStationaryTimeEdit";
+ this.TriggerNewLogStationaryTimeEdit.Size = new System.Drawing.Size(78, 20);
+ this.TriggerNewLogStationaryTimeEdit.TabIndex = 3;
+ this.TriggerNewLogStationaryTimeEdit.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
+ this.TriggerNewLogStationaryTimeEdit.Value = new decimal(new int[] {
+ 30,
+ 0,
+ 0,
+ 0});
+ this.TriggerNewLogStationaryTimeEdit.ValueChanged += new System.EventHandler(this.Configuration_Changed);
+ //
+ // TriggerNewLogStationaryCheckbox
+ //
+ this.TriggerNewLogStationaryCheckbox.AutoSize = true;
+ this.TriggerNewLogStationaryCheckbox.Location = new System.Drawing.Point(10, 69);
+ this.TriggerNewLogStationaryCheckbox.Name = "TriggerNewLogStationaryCheckbox";
+ this.TriggerNewLogStationaryCheckbox.Size = new System.Drawing.Size(353, 17);
+ this.TriggerNewLogStationaryCheckbox.TabIndex = 2;
+ this.TriggerNewLogStationaryCheckbox.Text = " Start a new log when stationary for at least (excluding when paused):";
+ this.TriggerNewLogStationaryCheckbox.UseVisualStyleBackColor = true;
+ this.TriggerNewLogStationaryCheckbox.CheckedChanged += new System.EventHandler(this.TriggerNewLogStationaryCheckbox_CheckedChanged);
+ //
// TriggerWaitForMovementCheckbox
//
this.TriggerWaitForMovementCheckbox.AutoSize = true;
this.TriggerWaitForMovementCheckbox.Checked = true;
this.TriggerWaitForMovementCheckbox.CheckState = System.Windows.Forms.CheckState.Checked;
- this.TriggerWaitForMovementCheckbox.Location = new System.Drawing.Point(10, 23);
+ this.TriggerWaitForMovementCheckbox.Location = new System.Drawing.Point(10, 46);
this.TriggerWaitForMovementCheckbox.Name = "TriggerWaitForMovementCheckbox";
this.TriggerWaitForMovementCheckbox.Size = new System.Drawing.Size(454, 17);
- this.TriggerWaitForMovementCheckbox.TabIndex = 0;
+ this.TriggerWaitForMovementCheckbox.TabIndex = 1;
this.TriggerWaitForMovementCheckbox.Text = " Wait for movement before logging the starting point (recommended, ignores initia" +
"l teleports)";
this.TriggerWaitForMovementCheckbox.UseVisualStyleBackColor = true;
+ this.TriggerWaitForMovementCheckbox.CheckedChanged += new System.EventHandler(this.Configuration_Changed);
//
- // TriggerNewLogStationaryCheckbox
+ // KMLLiveGroupbox
//
- this.TriggerNewLogStationaryCheckbox.AutoSize = true;
- this.TriggerNewLogStationaryCheckbox.Location = new System.Drawing.Point(10, 46);
- this.TriggerNewLogStationaryCheckbox.Name = "TriggerNewLogStationaryCheckbox";
- this.TriggerNewLogStationaryCheckbox.Size = new System.Drawing.Size(353, 17);
- this.TriggerNewLogStationaryCheckbox.TabIndex = 1;
- this.TriggerNewLogStationaryCheckbox.Text = " Start a new log when stationary for at least (excluding when paused):";
- this.TriggerNewLogStationaryCheckbox.UseVisualStyleBackColor = true;
+ this.KMLLiveGroupbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.KMLLiveGroupbox.Controls.Add(this.KMLLiveHelpLabel);
+ this.KMLLiveGroupbox.Controls.Add(this.KMLLiveLinkEdit);
+ this.KMLLiveGroupbox.Controls.Add(this.KMLLivePortEdit);
+ this.KMLLiveGroupbox.Controls.Add(this.KMLLiveLinkLabel);
+ this.KMLLiveGroupbox.Controls.Add(this.KMLLiveCheckbox);
+ this.KMLLiveGroupbox.Location = new System.Drawing.Point(12, 331);
+ this.KMLLiveGroupbox.Name = "KMLLiveGroupbox";
+ this.KMLLiveGroupbox.Size = new System.Drawing.Size(482, 97);
+ this.KMLLiveGroupbox.TabIndex = 3;
+ this.KMLLiveGroupbox.TabStop = false;
+ this.KMLLiveGroupbox.Text = " Google Earth live location ";
//
- // TriggerNewLogStationaryTimeEdit
+ // KMLLiveHelpLabel
//
- this.TriggerNewLogStationaryTimeEdit.Enabled = false;
- this.TriggerNewLogStationaryTimeEdit.Location = new System.Drawing.Point(33, 69);
- this.TriggerNewLogStationaryTimeEdit.Name = "TriggerNewLogStationaryTimeEdit";
- this.TriggerNewLogStationaryTimeEdit.Size = new System.Drawing.Size(78, 20);
- this.TriggerNewLogStationaryTimeEdit.TabIndex = 2;
- this.TriggerNewLogStationaryTimeEdit.Value = new decimal(new int[] {
- 30,
+ this.KMLLiveHelpLabel.AutoSize = true;
+ this.KMLLiveHelpLabel.ForeColor = System.Drawing.SystemColors.GrayText;
+ this.KMLLiveHelpLabel.Location = new System.Drawing.Point(124, 72);
+ this.KMLLiveHelpLabel.Name = "KMLLiveHelpLabel";
+ this.KMLLiveHelpLabel.Size = new System.Drawing.Size(352, 13);
+ this.KMLLiveHelpLabel.TabIndex = 5;
+ this.KMLLiveHelpLabel.Text = "Copy this link and use it in the Add > Network Link option in Google Earth";
+ //
+ // KMLLiveLinkEdit
+ //
+ this.KMLLiveLinkEdit.BackColor = System.Drawing.SystemColors.ButtonFace;
+ this.KMLLiveLinkEdit.Location = new System.Drawing.Point(123, 49);
+ this.KMLLiveLinkEdit.Name = "KMLLiveLinkEdit";
+ this.KMLLiveLinkEdit.ReadOnly = true;
+ this.KMLLiveLinkEdit.Size = new System.Drawing.Size(353, 20);
+ this.KMLLiveLinkEdit.TabIndex = 4;
+ //
+ // KMLLivePortEdit
+ //
+ this.KMLLivePortEdit.Enabled = false;
+ this.KMLLivePortEdit.Location = new System.Drawing.Point(123, 22);
+ this.KMLLivePortEdit.Maximum = new decimal(new int[] {
+ 65535,
0,
0,
0});
+ this.KMLLivePortEdit.Name = "KMLLivePortEdit";
+ this.KMLLivePortEdit.Size = new System.Drawing.Size(78, 20);
+ this.KMLLivePortEdit.TabIndex = 3;
+ this.KMLLivePortEdit.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
+ this.KMLLivePortEdit.Value = new decimal(new int[] {
+ 2020,
+ 0,
+ 0,
+ 0});
+ this.KMLLivePortEdit.ValueChanged += new System.EventHandler(this.KMLLivePortEdit_ValueChanged);
//
- // TriggerNewLogStationaryUnitsLabel
+ // KMLLiveLinkLabel
//
- this.TriggerNewLogStationaryUnitsLabel.AutoSize = true;
- this.TriggerNewLogStationaryUnitsLabel.Location = new System.Drawing.Point(117, 71);
- this.TriggerNewLogStationaryUnitsLabel.Name = "TriggerNewLogStationaryUnitsLabel";
- this.TriggerNewLogStationaryUnitsLabel.Size = new System.Drawing.Size(47, 13);
- this.TriggerNewLogStationaryUnitsLabel.TabIndex = 3;
- this.TriggerNewLogStationaryUnitsLabel.Text = "seconds";
+ this.KMLLiveLinkLabel.AutoSize = true;
+ this.KMLLiveLinkLabel.Location = new System.Drawing.Point(30, 52);
+ this.KMLLiveLinkLabel.Name = "KMLLiveLinkLabel";
+ this.KMLLiveLinkLabel.Size = new System.Drawing.Size(30, 13);
+ this.KMLLiveLinkLabel.TabIndex = 1;
+ this.KMLLiveLinkLabel.Text = "Link:";
+ //
+ // KMLLiveCheckbox
+ //
+ this.KMLLiveCheckbox.AutoSize = true;
+ this.KMLLiveCheckbox.Location = new System.Drawing.Point(10, 23);
+ this.KMLLiveCheckbox.Name = "KMLLiveCheckbox";
+ this.KMLLiveCheckbox.Size = new System.Drawing.Size(107, 17);
+ this.KMLLiveCheckbox.TabIndex = 0;
+ this.KMLLiveCheckbox.Text = " Enabled on port:";
+ this.KMLLiveCheckbox.UseVisualStyleBackColor = true;
+ this.KMLLiveCheckbox.CheckedChanged += new System.EventHandler(this.KMLLiveEnabled_CheckedChanged);
+ //
+ // TryConnectTimer
+ //
+ this.TryConnectTimer.Interval = 5000;
+ this.TryConnectTimer.Tick += new System.EventHandler(this.TryConnectTimer_Tick);
+ //
+ // ChangesWhileRecordingLabel
+ //
+ this.ChangesWhileRecordingLabel.AutoSize = true;
+ this.ChangesWhileRecordingLabel.ForeColor = System.Drawing.Color.Red;
+ this.ChangesWhileRecordingLabel.Location = new System.Drawing.Point(191, 43);
+ this.ChangesWhileRecordingLabel.Name = "ChangesWhileRecordingLabel";
+ this.ChangesWhileRecordingLabel.Size = new System.Drawing.Size(265, 13);
+ this.ChangesWhileRecordingLabel.TabIndex = 8;
+ this.ChangesWhileRecordingLabel.Text = "changes will take effect once the recording is restarted";
+ this.ChangesWhileRecordingLabel.TextAlign = System.Drawing.ContentAlignment.TopRight;
+ this.ChangesWhileRecordingLabel.Visible = false;
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(506, 309);
+ this.ClientSize = new System.Drawing.Size(506, 440);
+ this.Controls.Add(this.ChangesWhileRecordingLabel);
+ this.Controls.Add(this.KMLLiveGroupbox);
this.Controls.Add(this.TriggersGroupbox);
this.Controls.Add(this.RecordingStatusIcon);
this.Controls.Add(this.RecordButton);
@@ -271,6 +399,8 @@
this.Controls.Add(this.FlightSimulatorLabel);
this.Controls.Add(this.FlightSimulatorStatusIcon);
this.Controls.Add(this.OutputGroupbox);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "FS Flight Logger";
@@ -281,6 +411,9 @@
this.TriggersGroupbox.ResumeLayout(false);
this.TriggersGroupbox.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.TriggerNewLogStationaryTimeEdit)).EndInit();
+ this.KMLLiveGroupbox.ResumeLayout(false);
+ this.KMLLiveGroupbox.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.KMLLivePortEdit)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -308,6 +441,16 @@
private System.Windows.Forms.NumericUpDown TriggerNewLogStationaryTimeEdit;
private System.Windows.Forms.CheckBox TriggerNewLogStationaryCheckbox;
private System.Windows.Forms.CheckBox TriggerWaitForMovementCheckbox;
+ private System.Windows.Forms.FolderBrowserDialog FolderBrowserDialog;
+ private System.Windows.Forms.GroupBox KMLLiveGroupbox;
+ private System.Windows.Forms.Label KMLLiveHelpLabel;
+ private System.Windows.Forms.TextBox KMLLiveLinkEdit;
+ private System.Windows.Forms.NumericUpDown KMLLivePortEdit;
+ private System.Windows.Forms.Label KMLLiveLinkLabel;
+ private System.Windows.Forms.CheckBox KMLLiveCheckbox;
+ private System.Windows.Forms.Timer TryConnectTimer;
+ private System.Windows.Forms.Label ChangesWhileRecordingLabel;
+ private System.Windows.Forms.CheckBox TriggerConnectedCheckbox;
}
}
diff --git a/FSFlightLogger/MainForm.cs b/FSFlightLogger/MainForm.cs
index cfcdd8d..65f4434 100644
--- a/FSFlightLogger/MainForm.cs
+++ b/FSFlightLogger/MainForm.cs
@@ -1,12 +1,39 @@
-using System.Windows.Forms;
-using System.Windows.Forms.VisualStyles;
+using System;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using FSFlightLogger.Resources;
+using Newtonsoft.Json;
using SimConnect;
namespace FSFlightLogger
{
- public partial class MainForm : Form
+ public partial class MainForm : Form, ISimConnectClientObserver
{
private readonly ISimConnectClientFactory simConnectClientFactory;
+ private readonly CancellationTokenSource tryConnectCancellationTokenSource = new CancellationTokenSource();
+
+ private ISimConnectClient simConnectClient;
+ private SimConnectLogger logger;
+
+
+ private enum FlightSimulatorStateValue
+ {
+ Connecting,
+ Connected,
+ Disconnected,
+ Failed
+ }
+
+ private enum RecordingStateValue
+ {
+ Started,
+ Stopped
+ }
public MainForm(ISimConnectClientFactory simConnectClientFactory)
@@ -15,9 +42,11 @@ namespace FSFlightLogger
InitializeComponent();
- SetFlightSimulatorConnected(false);
- SetRecording(false);
- //var simConnectClient = simConnectClientFactory.TryConnect("FS Flight Logger");
+ FlightSimulatorState = FlightSimulatorStateValue.Connecting;
+ RecordingState = RecordingStateValue.Stopped;
+ LoadSettings();
+
+ TryConnect();
}
@@ -29,21 +58,333 @@ namespace FSFlightLogger
{
if (disposing)
{
+ SaveSettings();
+
components?.Dispose();
+ simConnectClient?.DisposeAsync();
}
base.Dispose(disposing);
}
- private void SetFlightSimulatorConnected(bool connected)
+ private string GetSettingsFilename()
{
- FlightSimulatorStatusIcon.Image = StatusImageList.Images[connected ? "FSConnected" : "FSDisconnected"];
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"FSFlightLogger", @"config.json");
}
- private void SetRecording(bool recording)
+
+ private void LoadSettings()
{
- RecordingStatusIcon.Image = StatusImageList.Images[recording ? "Recording" : "Idle"];
+ var defaultPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), i18n.DefaultFolderName);
+ var settings = new Settings
+ {
+ CSVPath = defaultPath,
+
+ KMLEnabled = true,
+ KMLPath = defaultPath,
+
+ KMLLivePort = 2020,
+
+ TriggerWaitForMovement = true
+ };
+
+ var filename = GetSettingsFilename();
+ if (File.Exists(filename))
+ using (var streamReader = new StreamReader(filename, Encoding.UTF8))
+ {
+ var serializer = new JsonSerializer();
+ serializer.Populate(streamReader, settings);
+ }
+
+
+ if (settings.MainFormLeft.HasValue && settings.MainFormTop.HasValue)
+ {
+ StartPosition = FormStartPosition.Manual;
+ Left = settings.MainFormLeft.Value;
+ Top = settings.MainFormTop.Value;
+
+ // Check if the user did not unplug the screen that last contained the form
+ var formRectangle = new Rectangle(Left, Top, Width, Height);
+ if (!Screen.AllScreens.Any(s => s.WorkingArea.IntersectsWith(formRectangle)))
+ StartPosition = FormStartPosition.CenterScreen;
+ }
+
+ OutputCSVCheckbox.Checked = settings.CSVEnabled;
+ OutputCSVPathTextbox.Text = settings.CSVPath;
+ OutputKMLCheckbox.Checked = settings.KMLEnabled;
+ OutputKMLPathTextbox.Text = settings.KMLPath;
+ KMLLiveCheckbox.Checked = settings.KMLLiveEnabled;
+ KMLLivePortEdit.Value = settings.KMLLivePort;
+
+ TriggerConnectedCheckbox.Checked = settings.TriggerConnected;
+ TriggerWaitForMovementCheckbox.Checked = settings.TriggerWaitForMovement;
+ TriggerNewLogStationaryCheckbox.Checked = settings.TriggerNewLogStationaryEnabled;
+ TriggerNewLogStationaryTimeEdit.Value = settings.TriggerNewLogStationarySeconds;
+
+ UpdateKMLLiveLink();
+ }
+
+
+ private void SaveSettings()
+ {
+ var settings = new Settings
+ {
+ MainFormTop = Top,
+ MainFormLeft = Left,
+
+ CSVEnabled = OutputCSVCheckbox.Checked,
+ CSVPath = OutputCSVPathTextbox.Text,
+ KMLEnabled = OutputKMLCheckbox.Checked,
+ KMLPath = OutputKMLPathTextbox.Text,
+ KMLLiveEnabled = KMLLiveCheckbox.Checked,
+ KMLLivePort = (int)KMLLivePortEdit.Value,
+
+ TriggerConnected = TriggerConnectedCheckbox.Checked,
+ TriggerWaitForMovement = TriggerWaitForMovementCheckbox.Checked,
+ TriggerNewLogStationaryEnabled = TriggerNewLogStationaryCheckbox.Checked,
+ TriggerNewLogStationarySeconds = (int)TriggerNewLogStationaryTimeEdit.Value
+ };
+
+ var filename = GetSettingsFilename();
+ Directory.CreateDirectory(Path.GetDirectoryName(filename));
+
+ using (var streamWriter = new StreamWriter(filename, false, Encoding.UTF8))
+ {
+ var serializer = new JsonSerializer();
+ serializer.Serialize(streamWriter, settings);
+ }
+ }
+
+
+
+ private void TryConnect()
+ {
+ FlightSimulatorState = FlightSimulatorStateValue.Connecting;
+
+ simConnectClientFactory.TryConnect(@"FS Flight Logger")
+ .ContinueWith((task, state) =>
+ {
+ if (task.Result == null)
+ {
+ FlightSimulatorState = FlightSimulatorStateValue.Failed;
+ TryConnectTimer.Enabled = true;
+ }
+ else
+ {
+ simConnectClient = task.Result;
+ simConnectClient.AttachObserver(this);
+
+ logger = new SimConnectLogger(simConnectClient);
+ if (TriggerConnectedCheckbox.Checked)
+ StartRecording();
+
+ FlightSimulatorState = FlightSimulatorStateValue.Connected;
+ }
+ },
+ null, tryConnectCancellationTokenSource.Token, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
+ }
+
+
+ public void OnQuit()
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(OnQuit));
+ return;
+ }
+
+ FlightSimulatorState = FlightSimulatorStateValue.Disconnected;
+ TryConnectTimer.Enabled = true;
+ }
+
+
+ private FlightSimulatorStateValue internalFlightSimulatorState;
+ private FlightSimulatorStateValue FlightSimulatorState
+ {
+ get => internalFlightSimulatorState;
+ set
+ {
+ if (value == internalFlightSimulatorState)
+ return;
+
+ internalFlightSimulatorState = value;
+ var isConnected = value == FlightSimulatorStateValue.Connected;
+
+ FlightSimulatorStatusIcon.Image = StatusImageList.Images[isConnected ? @"FSConnected" : @"FSDisconnected"];
+
+ switch (value)
+ {
+ case FlightSimulatorStateValue.Connecting:
+ FlightSimulatorStatusLabel.Text = i18n.FlightSimulatorConnecting;
+ break;
+
+ case FlightSimulatorStateValue.Connected:
+ FlightSimulatorStatusLabel.Text = i18n.FlightSimulatorConnected;
+ break;
+
+ case FlightSimulatorStateValue.Disconnected:
+ FlightSimulatorStatusLabel.Text = string.Format(i18n.FlightSimulatorQuit, TryConnectTimer.Interval / 1000);
+ break;
+
+ case FlightSimulatorStateValue.Failed:
+ FlightSimulatorStatusLabel.Text = string.Format(i18n.FlightSimulatorFailed, TryConnectTimer.Interval / 1000);
+ break;
+ }
+
+ CheckCanRecord();
+
+ if (!isConnected)
+ RecordingState = RecordingStateValue.Stopped;
+ }
+ }
+
+
+ private RecordingStateValue internalRecordingState;
+ private RecordingStateValue RecordingState
+ {
+ get => internalRecordingState;
+ set
+ {
+ if (value == internalRecordingState)
+ return;
+
+ internalRecordingState = value;
+ var isRecording = value == RecordingStateValue.Started;
+
+ RecordingStatusIcon.Image = StatusImageList.Images[isRecording ? @"Recording" : @"Idle"];
+ RecordButton.Text = isRecording ? i18n.RecordButtonStop : i18n.RecordButtonStart;
+
+ if (!isRecording)
+ ChangesWhileRecordingLabel.Visible = false;
+ }
+ }
+
+
+ private void StartRecording()
+ {
+ SaveSettings();
+
+ Task.Run(async () =>
+ {
+ await logger.Start(new SimConnectLogger.Config
+ {
+ CSVOutputPath = OutputCSVCheckbox.Checked ? OutputCSVPathTextbox.Text : null,
+ KMLOutputPath = OutputKMLCheckbox.Checked ? OutputKMLPathTextbox.Text : null,
+ KMLLivePort = KMLLiveCheckbox.Checked ? (int) KMLLivePortEdit.Value : (int?) null,
+
+ IntervalTime = TimeSpan.FromSeconds(1), // TODO configurable
+ IntervalDistance = 1, // TODO configurable
+
+ WaitForMovement = TriggerWaitForMovementCheckbox.Checked,
+ NewLogWhenIdleSeconds = TriggerNewLogStationaryCheckbox.Checked
+ ? TimeSpan.FromSeconds((double) TriggerNewLogStationaryTimeEdit.Value)
+ : (TimeSpan?) null
+ });
+ });
+
+ RecordingState = RecordingStateValue.Started;
+ }
+
+ private void StopRecording()
+ {
+ Task.Run(async () => await logger.Stop());
+ RecordingState = RecordingStateValue.Stopped;
+ }
+
+
+ private void UpdateKMLLiveLink()
+ {
+ KMLLiveLinkEdit.Text = $@"http://localhost:{KMLLivePortEdit.Value}/live";
+ }
+
+
+ private void CheckChangesWhileRecording()
+ {
+ if (RecordingState == RecordingStateValue.Started)
+ ChangesWhileRecordingLabel.Visible = true;
+ }
+
+
+ private void CheckCanRecord()
+ {
+ RecordButton.Enabled = FlightSimulatorState == FlightSimulatorStateValue.Connected &&
+ (OutputCSVCheckbox.Checked || OutputKMLCheckbox.Checked || KMLLiveCheckbox.Checked);
+ }
+
+
+ private void TryConnectTimer_Tick(object sender, EventArgs e)
+ {
+ TryConnectTimer.Enabled = false;
+ TryConnect();
+ }
+
+
+ private void OutputCSVCheckbox_CheckedChanged(object sender, EventArgs e)
+ {
+ OutputCSVPathTextbox.Enabled = OutputCSVCheckbox.Checked;
+ OutputCSVPathBrowseButton.Enabled = OutputCSVCheckbox.Checked;
+ CheckChangesWhileRecording();
+ CheckCanRecord();
+ }
+
+ private void OutputKMLCheckbox_CheckedChanged(object sender, EventArgs e)
+ {
+ OutputKMLPathTextbox.Enabled = OutputKMLCheckbox.Checked;
+ OutputKMLPathBrowseButton.Enabled = OutputKMLCheckbox.Checked;
+ CheckChangesWhileRecording();
+ CheckCanRecord();
+ }
+
+
+ private void TriggerNewLogStationaryCheckbox_CheckedChanged(object sender, EventArgs e)
+ {
+ TriggerNewLogStationaryTimeEdit.Enabled = TriggerNewLogStationaryCheckbox.Checked;
+ CheckChangesWhileRecording();
+ }
+
+ private void KMLLiveEnabled_CheckedChanged(object sender, EventArgs e)
+ {
+ KMLLivePortEdit.Enabled = KMLLiveCheckbox.Checked;
+ CheckChangesWhileRecording();
+ CheckCanRecord();
+ }
+
+ private void SelectPath(Control pathTextBox)
+ {
+ FolderBrowserDialog.SelectedPath = pathTextBox.Text;
+ if (FolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
+ pathTextBox.Text = FolderBrowserDialog.SelectedPath;
+ }
+
+ private void OutputCSVPathBrowseButton_Click(object sender, EventArgs e)
+ {
+ SelectPath(OutputCSVPathTextbox);
+ }
+
+ private void OutputKMLPathBrowseButton_Click(object sender, EventArgs e)
+ {
+ SelectPath(OutputKMLPathTextbox);
+ }
+
+
+ private void KMLLivePortEdit_ValueChanged(object sender, EventArgs e)
+ {
+ UpdateKMLLiveLink();
+ }
+
+ private void Configuration_Changed(object sender, EventArgs e)
+ {
+ CheckChangesWhileRecording();
+ }
+
+
+ private void RecordButton_Click(object sender, EventArgs e)
+ {
+ if (RecordingState == RecordingStateValue.Started)
+ StopRecording();
+ else
+ StartRecording();
}
}
}
diff --git a/FSFlightLogger/MainForm.resx b/FSFlightLogger/MainForm.resx
index a7ddf24..ff713ac 100644
--- a/FSFlightLogger/MainForm.resx
+++ b/FSFlightLogger/MainForm.resx
@@ -124,351 +124,349 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
- ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACO
- UAAAAk1TRnQBSQFMAgEBBAEAAQgBAAEIAQABIAEAASABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAGA
+ ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACe
+ TgAAAk1TRnQBSQFMAgEBBAEAAUwBAAFMAQABIAEAASABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAGA
AwABQAMAAQEBAAEgBgABgP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A
/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A
- /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AfgADPAFJA3QBlwOGAcIDiwHZA4sB3AOLAdwDjAHaA4YBwwN0
- AZgDPQFLAwEBAlQAAjwBOwFJAXMBcgFzAZcBfAF7AYgBwgF1AXQBlQHZAXIBcAGYAdwBcgFwAZgB3AF3
- AXYBlQHaAXwBewGIAcMCcgFzAZgCPQE8AUsDAQECMAADAQECAwQEBQEHAwUBBwMFAQcDBQEHAwUBBwMF
- AQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMF
- AQcDBQEHAwUBBwMFAQcDAwEEAwABARAAAwIBAwMHAQkDCgENAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEO
- AwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEO
- AwoBDgMKAQ0DBgEIAwEBAigAAw0BEANzAZYDhgHfA4cB/wOEAf8DhAH/A4sB/wOQAf8DkAH/A4sB/wOE
- Af8DgwH/A4YB/wOGAeQDdQGbAw8BE0AAAw0BEAJyAXABlgFoAWcBlQHfAUYBRQG0Af8COQG6Af8BMAEx
- AcMB/wExATMB0QH/ATUBNwHYAf8BNQE3AdgB/wExATMB0QH/AjABwgH/AjgBuQH/AUUBRAGzAf8BZwFm
- AZYB5AJ0AXEBmwMPARMnAAEBAwYBCAMQARUDGAEiAx4BKwMeASwDHgEsAx4BLAMeASwDHgEsAx4BLAMe
- ASwDHgEsAx4BLAMeASwDHgEsAx4BLAMeASwDHgEsAx4BLAMeASwDHgEsAx4BLAMeASwDHgEsAx4BKwMX
- ASADDgETAwQBBg8AAQEDCwEPAxwBKQErAikBRAEyAi8BVgEyAi8BVwEyAi8BVwEyATABLwFXATMBMAEv
- AVcBMwEwAS8BVwEzATABLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFX
- ATMCLwFXATMBMAEvAVcBMwEwAS8BVwEzATABLwFXATMBMAEvAVcBMgIvAVcBMgIvAVcBMgEwAS8BVgEp
- AScBKAFAARsCGgEmAwkBDCQAA14BdgOJAekDeQH/A4YB/wOdAf8DqgH/A7UB/wO6Af8DvQH/A70B/wO5
- Af8DtAH/A6oB/wOdAf8DhgH/A3cB/wOJAe8DYQF8OAACXwFcAXYCZwGcAekBLgEtAbAB/wEnASgBzwH/
- AUIBRQHnAf8BVgFcAfAB/wFlAXAB9wH/AW4BfgH6Af8BdAGGAfsB/wFzAYYB+wH/AW0BfAH6Af8BZAFu
- AfcB/wFVAVwB8AH/AUIBRQHnAf8BJwEoAc8B/wIsAa4B/wJlAZwB7wJiAV4BfCAAAwEBAgMQARUDNgFf
- A0EBfwNCAYADQgGAA0IBgANCAYADQgGAA0MBgANDAYADQwGAA0MBgANDAYADQwGAA0MBgANDAYADQwGA
- A0MBgANDAYADQwGAA0IBgANCAYADQgGAA0IBgANCAYADQQF+AzMBVwMLAQ8DAAEBCAADAgEDAR0CHAEp
- AU0BMQErAb4BWwEbAQoB/QFeARsBCAH/AV4BGwEIAf8BXwEcAQgB/wFhARwBCAH/AWEBGwEIAf8BYwEb
- AQkB/wFjARsBCQH/AWMBGwEJAf8BYwEbAQkB/wFjARsBCQH/AWMBGwEJAf8BYwEbAQkB/wFjARsBCQH/
- AWMBGwEJAf8BYwEbAQkB/wFjARsBCQH/AWMBGwEJAf8BYQEcAQgB/wFhARsBCAH/AV8BGwEIAf8BXgEb
- AQgB/wFeARsBCAH/AVsBHAELAfsBSwE0ATABrQMVAR0DAQECGAADBAEGA3gBpgN1Af8DgAH/A6IB/wOv
- Af8DtwH/A78B/wPGAf8DygH/A8wB/wPMAf8DygH/A8UB/wO9Af8DtgH/A68B/wOiAf8DgAH/A3IB/wN9
- AbEDDAEPKAADBAEGAnIBeAGmAjEBpAH/Ah4BywH/AUkBTAHqAf8BYgFoAe8B/wFrAXcB9gH/AXYBiQH8
- Af8BgwGbAv8BjQGpAv8BkQGtAv8BkAGsAv8BjAGnAv8BgQGYAv8BdAGGAfsB/wFqAXUB9QH/AWEBZwHv
- Af8BSQFMAesB/wIfAcwB/wEuAS0BoQH/AnYBfQGxAwwBDxsAAQEDNwFdA0cBgANFAYADRQGAA0YBgANG
+ /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AfgADLwFJA08BlwNZAcIDXwHZA2EB3ANhAdwDYgHaA1sBwwNO
+ AZgDMAFLAwEBAlQAAy8BSQNPAZcDWQHCAl4BYAHZAmEBYgHcAmEBYgHcAmEBYwHaA1sBwwNOAZgDMAFL
+ AwEBAjAAAwEBAgMEBAUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEH
+ AwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwMBBAMAAQEQAAMC
+ AQMDBwEJAwoBDQMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMK
+ AQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgENAwYBCAMBAQIoAAMMARADTgGW
+ A2EB3wOHAf8DhAH/A4QB/wOLAf8DkAH/A5AB/wOLAf8DhAH/A4MB/wOGAf8DZAHkA1ABmwMOARNAAAMM
+ ARADTgGWAlwBYgHfAT4BPQG0Af8CMQG6Af8BKAEpAcMB/wEpASsB0QH/AS0BLwHYAf8BLQEvAdgB/wEp
+ ASsB0QH/AigBwgH/AjABuQH/AT0BPAGzAf8CYQFnAeQDUAGbAw4BEycAAQEDBgEIAxABFQMYASIDHgEr
+ Ax8BLAMfASwDHwEsAx8BLAMfASwDHwEsAx8BLAMfASwDHwEsAx8BLAMfASwDHwEsAx8BLAMfASwDHwEs
+ Ax8BLAMfASwDHwEsAx8BLAMfASwDHgErAxcBIAMOARMDBAEGDwABAQMLAQ8DHQEpAywBRAM1AVYDNQFX
+ AzUBVwM1AVcDNQFXAzUBVwM1AVcDNQFXAzUBVwM1AVcDNQFXAzUBVwM1AVcDNQFXAzUBVwM1AVcDNQFX
+ AzUBVwM1AVcDNQFXAzUBVwM1AVYDKgFAAxsBJgMJAQwkAANCAXYDaQHpA3EB/wOGAf8DnQH/A6oB/wO1
+ Af8DugH/A70B/wO9Af8DuQH/A7QB/wOqAf8DnQH/A4YB/wNvAf8DawHvA0UBfDgAA0IBdgJiAW4B6QEm
+ ASUBsAH/AR8BIAHPAf8BOgE9AecB/wFOAVQB8AH/AV0BaAH3Af8BZgF2AfoB/wFsAYYB+wH/AWsBhgH7
+ Af8BZQF0AfoB/wFcAWYB9wH/AU0BVAHwAf8BOgE9AecB/wEfASABzwH/AiQBrgH/AmIBcwHvA0UBfCAA
+ AwEBAgMQARUDOQFfA0UBfwNGAYADRgGAA0YBgANGAYADRgGAA0YBgANGAYADRgGAA0YBgANGAYADRgGA
+ A0YBgANGAYADRgGAA0YBgANGAYADRgGAA0YBgANGAYADRgGAA0YBgANGAYADRQF+AzUBVwMLAQ8DAAEB
+ CAADAgEDAx0BKQNWAb4BUAEjARIB/QFWARMBAAH/AVYBEwEAAf8BVwEUAQAB/wFZARQBAAH/AVkBEwEA
+ Af8BWwETAQEB/wFbARMBAQH/AVsBEwEBAf8BWwETAQEB/wFbARMBAQH/AVsBEwEBAf8BWwETAQEB/wFb
+ ARMBAQH/AVsBEwEBAf8BWwETAQEB/wFbARMBAQH/AVsBEwEBAf8BWQEUAQAB/wFZARMBAAH/AVcBEwEA
+ Af8BVgETAQAB/wFWARMBAAH/AVsBIQEbAfsDUwGtAxUBHQMBAQIYAAMEAQYDVAGmA20B/wOAAf8DogH/
+ A68B/wO3Af8DvwH/A8YB/wPKAf8DzAH/A8wB/wPKAf8DxQH/A70B/wO2Af8DrwH/A6IB/wOAAf8DagH/
+ A1YBsQMLAQ8oAAMEAQYDVAGmAikBpAH/AhYBywH/AUEBRAHqAf8BWgFgAe8B/wFjAW8B9gH/AW4BiQH8
+ Af8BgwGbAv8BjQGpAv8BkQGtAv8BkAGsAv8BjAGnAv8BgQGYAv8BbAGGAfsB/wFiAW0B9QH/AVkBXwHv
+ Af8BQQFEAesB/wIXAcwB/wEmASUBoQH/A1YBsQMLAQ8bAAEBAzgBXQNHAYADRgGAA0YBgANGAYADRgGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANGAYADRgGAA0YBgANGAYADRwGAAzEBTgMAAQEIAAMBAQIBVwJVAboBgwEjAQUB/wFuAR0BAwH/
+ AW8BHgEEAf8BcwEgAQQB/wF2ASABBAH/AYIBIQEFAf8BhAEiAQUB/wGGASIBBQH/AYYBIwEFAf8BhgEj
+ AQUB/wGGASMBBQH/AYYBIwEFAf8BhgEjAQUB/wGGASMBBQH/AYYBIwEFAf8BhgEjAQUB/wGGASMBBQH/
+ AYYBIwEFAf8BhQEiAQUB/wGEASIBBQH/AYEBIQEFAf8BdQEhAQQB/wFzAR8BBAH/AW8BHQEEAf8BbgEe
+ AQMB/wGDASQBBQH/AVACTwGbAwABARQAAxMBGgNgAdcDZgH/A5MB/wOpAf8DsgH/A7kB/wO+Af8DwwH/
+ A8gB/wPLAf8DygH/A8oB/wPKAf8DxwH/A8EB/wO8Af8DuAH/A7EB/wOoAf8DlAH/A2UB/wNhAd4DFAEc
+ IAADEwEaAl4BYAHXAQ8BDgGwAf8BLgEvAd0B/wFSAVYB6QH/AV0BZQHxAf8BZgFzAfcB/wFvAYoB+AH/
+ AYMBmQH6Af8BjQGnAfsB/wGRAa0B/AH/AZABqgH8Af8BkAGrAfwB/wGRAa0B+wH/AYsBpQH6Af8BgAGW
+ AfgB/wFtAYcB9wH/AWUBcQH2Af8BXAFjAfAB/wFRAVUB6AH/AS8BMAHeAf8BDgENAa8B/wJgAWEB3gMU
+ ARwUAAMOARMDRgF/A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRQF9
+ AwQBBggAAxsBJgF/ATQBEAH+AaUBNQEJAf8BqgE3AQoB/wGwATkBCwH/AbYBOwEMAf8BuwE9AQwB/wHA
+ AUABDQH/AcMBQAENAf8BxAFBAQ0B/wHFAUIBDQH/AcUBQgENAf8BxQFCAQ0B/wHFAUIBDQH/AcUBQgEN
+ Af8BxQFCAQ0B/wHFAUIBDQH/AcUBQgENAf8BxQFCAQ0B/wHFAUIBDQH/AcQBQQENAf8BwgFAAQ0B/wG/
+ AUABDQH/AbsBPQEMAf8BtQE7AQwB/wGvATgBCwH/AaoBNwEKAf8BpAE0AQkB/wGAAUEBJAH5AwkBDBQA
+ A2AB1gNkAf8DmQH/A6cB/wOvAf8DswH/A7cB/wO8Af8DwgH/A8MB/wPEAf8DxQH/A8UB/wPEAf8DwwH/
+ A8AB/wO7Af8DtgH/A7IB/wOuAf8DpgH/A5kB/wNkAf8DZAHjAwcBCRwAA2AB1gIGAbIB/wE6ATsB3QH/
+ AVEBVQHmAf8BWgFiAe8B/wFhAW4B7wH/AWkBgwHwAf8BcgGPAfIB/wGEAZsB9QH/AYcBoAH1Af8BiQGj
+ AfYB/wGKAaUB9gH/AYoBpQH2Af8BiQGjAfUB/wGHAZ8B9QH/AYIBmAHzAf8BcAGNAfEB/wFnAYEB8AH/
+ AWABbAHvAf8BWQFgAe4B/wFQAVQB5QH/AToBOwHdAf8CBgGyAf8CXwFlAeMDBwEJEAADFwEgA0cBgANH
AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
- AYADRwGAA0YBgANGAYADRQGAA0UBgANHAYADMQFOAwABAQgAAwEBAgFaATcBLgG6AYMBKwENAf8BdgEl
- AQsB/wF3ASYBDAH/AXsBKAEMAf8BfgEoAQwB/wGCASkBDQH/AYQBKgENAf8BhgEqAQ0B/wGGASsBDQH/
- AYYBKwENAf8BhgErAQ0B/wGGASsBDQH/AYYBKwENAf8BhgErAQ0B/wGGASsBDQH/AYYBKwENAf8BhgEr
- AQ0B/wGGASsBDQH/AYUBKgENAf8BhAEqAQ0B/wGBASkBDQH/AX0BKQEMAf8BewEnAQwB/wF3ASUBDAH/
- AXYBJgELAf8BgwEsAQ0B/wFRAToBNQGbAwABARQAAxUBGgOIAdcDbgH/A5MB/wOpAf8DsgH/A7kB/wO+
- Af8DwwH/A8gB/wPLAf8DygH/A8oB/wPKAf8DxwH/A8EB/wO8Af8DuAH/A7EB/wOoAf8DlAH/A20B/wOE
- Ad4DFwEcIAADFQEaAnQBjwHXARcBFgGwAf8BNgE3Ad0B/wFaAV4B6QH/AWUBbQHxAf8BbgF7AfcB/wF3
- AYoB+AH/AYMBmQH6Af8BjQGnAfsB/wGRAa0B/AH/AZABqgH8Af8BkAGrAfwB/wGRAa0B+wH/AYsBpQH6
- Af8BgAGWAfgB/wF1AYcB9wH/AW0BeQH2Af8BZAFrAfAB/wFZAV0B6AH/ATcBOAHeAf8BFgEVAa8B/wJv
- AYwB3gMXARwUAAMOARMDSAF/A0wBgANMAYADTQGAA04BgANPAYADUAGAA1ABgANQAYADUAGAA1ABgANQ
- AYADUAGAA1ABgANQAYADUAGAA1ABgANQAYADUAGAA1ABgANQAYADTwGAA08BgANOAYADTQGAA0wBgANM
- AYADRwF9AwQBBggAAR0CGwEmAY8BNAEQAf4BpQE9AREB/wGqAT8BEgH/AbABQQETAf8BtgFDARQB/wG7
- AUUBFAH/AcABSAEVAf8BwwFIARUB/wHEAUkBFQH/AcUBSgEVAf8BxQFKARUB/wHFAUoBFQH/AcUBSgEV
- Af8BxQFKARUB/wHFAUoBFQH/AcUBSgEVAf8BxQFKARUB/wHFAUoBFQH/AcUBSgEVAf8BxAFJARUB/wHC
- AUgBFQH/Ab8BSAEVAf8BuwFFARQB/wG1AUMBFAH/Aa8BQAETAf8BqgE/ARIB/wGkATwBEQH/AYgBMgET
- AfkDCQEMFAADhwHWA2wB/wOZAf8DpwH/A68B/wOzAf8DtwH/A7wB/wPCAf8DwwH/A8QB/wPFAf8DxQH/
- A8QB/wPDAf8DwAH/A7sB/wO2Af8DsgH/A64B/wOmAf8DmQH/A2wB/wOFAeMDBwEJHAACcgGPAdYCDgGy
- Af8BQgFDAd0B/wFZAV0B5gH/AWIBagHvAf8BaQF2Ae8B/wFxAYMB8AH/AXoBjwHyAf8BhAGbAfUB/wGH
- AaAB9QH/AYkBowH2Af8BigGlAfYB/wGKAaUB9gH/AYkBowH1Af8BhwGfAfUB/wGCAZgB8wH/AXgBjQHx
- Af8BbwGBAfAB/wFoAXQB7wH/AWEBaAHuAf8BWAFcAeUB/wFCAUMB3QH/Ag4BsgH/Am0BjgHjAwcBCRAA
- AxgBIANLAYADUAGAA1EBgANTAYADVAGAA1UBgANWAYADYgGAA2ABgANXAYADVwGAA1cBgANXAYADVwGA
- A1cBgANXAYADVwGAA1cBgANXAYADZQGAA1cBgANVAYADVQGAA1QBgANSAYADUQGAA1ABgANKAYADDgET
- CAABLwEqASgBPwGiAT0BEQH/AcMBTAEVAf8BzQFQARYB/wHVAVUBGQH/AdoBWgEeAf8B3AFeASMB/wHd
- AWEBKAH/AeoBogGAAf8B5wGVAW4B/wHfAWcBLgH/Ad8BZwEuAf8B3wFnAS4B/wHfAWcBLgH/Ad8BZwEu
- Af8B3wFnAS4B/wHfAWcBLgH/Ad8BZwEuAf8B3wFnAS4B/wHfAWcBLgH/Ae0BswGXAf8B3wFqATMB/wHd
- AWEBJwH/AdsBXgEiAf8B2QFZAR4B/wHUAVQBGAH/AcwBTwEWAf8BwAFLARUB/wGaATcBEAH/AR0BHAEb
- ASYQAANzAZ0DaQH/A5QB/wOjAf8DqgH/A6wB/wOxAf8DtQH/A7kB/wO8Af8DvgH/A78B/wO/Af8DvwH/
- A78B/wO9Af8DuwH/A7kB/wO1Af8DsAH/A6wB/wOqAf8DowH/A5YB/wNoAf8DfAGyGAACbQF0AZ0CDwGq
- Af8BPgFAAdgB/wFVAVkB4gH/AVwBZAHpAf8BYgFuAegB/wFpAXoB6gH/AXIBhgHsAf8BeQGQAe0B/wF+
- AZcB7gH/AYIBnAHvAf8BgwGeAfAB/wGEAZ8B8AH/AYQBngHwAf8BgwGeAfAB/wGBAZsB7wH/AX0BlgHu
- Af8BeAGOAe0B/wFxAYQB6wH/AWgBeAHpAf8BYQFsAegB/wFcAWIB6AH/AVQBWAHiAf8CQAHZAf8CDwGp
- Af8CdAF9AbIQAAMYASADTAGAA1EBgANTAYADVQGAA1YBgANXAYADWAGAA3EBgANoAYADWQGAA1kBgANZ
- AYADWQGAA1kBgANZAYADWQGAA1kBgANZAYADWwGAA3QBgANoAYADWAGAA1cBgANWAYADVQGAA1MBgANR
- AYADSgGAAw8BFAgAATABKgEoAT8BqAE/ARIB/wHOAVEBFgH/AdkBVwEaAf8B3AFfASQB/wHeAWYBLQH/
- Ad8BawEyAf8B4QFvATgB/wH6AesB5AH/AfEBwQGpAf8B4gFzAT8B/wHiAXMBPwH/AeIBcwE/Af8B4gFz
- AT8B/wHiAXMBPwH/AeIBcwE/Af8B4gFzAT8B/wHiAXMBPwH/AeIBcwE/Af8B5AF9AU0B/wH9AfwB+wH/
- AfEBwAGpAf8B4QFuATcB/wHfAWoBMQH/Ad0BZQErAf8B3AFeASMB/wHYAVYBGQH/Ac0BUQEWAf8BnAE5
- ARAB/wEeARwBGwEnDAADRgFWA2wB/wOIAf8DnwH/A6UB/wOmAf8DqgH/A68B/wOzAf8DtgH/A7gB/wO6
- Af8DugH/A7sB/wO7Af8DugH/A7kB/wO4Af8DtgH/A7IB/wOuAf8DqQH/A6UB/wOkAf8DngH/A4oB/wNn
- Af8DTgFiEAACRgFFAVYCJAGdAf8BLgEvAc0B/wFQAVMB3AH/AVcBXAHjAf8BWwFlAeIB/wFhAW8B5AH/
- AWkBegHmAf8BcAGEAegB/wF1AYwB6gH/AXkBkgHsAf8BewGVAe0B/wF8AZcB7QH/AX0BmAHtAf8BfQGY
- Ae0B/wF8AZYB7QH/AXoBlQHtAf8BeAGRAewB/wF0AYoB6gH/AW8BgwHoAf8BZwF4AeYB/wFgAW4B4wH/
- AVkBYwHiAf8BVgFbAeMB/wFPAVIB2wH/ATABMQHQAf8BIAEfAZgB/wJOAUwBYgwAAxgBIANNAYADUgGA
- A1QBgANWAYADVwGAA1gBgANeAYADdAGAA2kBgANaAYADWgGAA1oBgANaAYADWgGAA1oBgANaAYADWgGA
- A1oBgANmAYADdQGAA3QBgANaAYADWAGAA1cBgANWAYADVAGAA1IBgANLAYADDwEUCAABMAEqASgBPwGr
- AUEBEgH/AdUBUwEXAf8B2wFbASAB/wHdAWUBKwH/Ad8BawEyAf8B4QFwAToB/wHnAYwBYAH/A/4B/wHz
- AcQBrgH/AeQBeQFHAf8B5AF5AUcB/wHkAXkBRwH/AeQBeQFHAf8B5AF5AUcB/wHkAXkBRwH/AeQBeQFH
- Af8B5AF5AUcB/wHkAXkBRwH/Ae8BtAGYBf8B/QH7AfoB/wHjAXYBQgH/AeEBbwE5Af8B3wFqATEB/wHd
- AWQBKQH/AdsBWwEeAf8B0wFSARcB/wGeATkBEQH/AR8BHAEbAScLAAEBA4YB6QNsAf8DmQH/A6AB/wOg
- Af8DpAH/A6kB/wOtAf8DsQH/A7QB/wO2Af8DuAH/A7kB/wO5Af8DuQH/A7gB/wO3Af8DtgH/A7QB/wOw
- Af8DrAH/A6gB/wOjAf8DoAH/A58B/wOYAf8DcAH/A4MB8QMBAQILAAEBAmEBnAHpAg0BtAH/AUsBTAHV
- Af8BUQFVAd4B/wFUAVsB3AH/AVoBZAHeAf8BYQFuAeIB/wFnAXcB5QH/AW0BgAHoAf8BcQGGAeoB/wF0
- AYsB7AH/AXYBjQHtAf8BeAGQAe0B/wF5AZEB7gH/AXgBkQHuAf8BdwGPAe0B/wF2AY0B7AH/AXMBigHr
- Af8BcAGFAeoB/wFsAX0B5wH/AWYBdgHkAf8BYAFsAeEB/wFZAWMB3QH/AVQBWwHbAf8BUAFUAd0B/wFK
- AUsB1AH/AhEBuAH/AloBmAHxAwEBAggAAxgBIANNAYADUwGAA1UBgANXAYADWAGAA1kBgANoAYADdQGA
- A2kBgANbAYADWwGAA1sBgANbAYADWwGAA1sBgANbAYADWwGAA1sBgANvAYADdQGAA28BgANaAYADWQGA
- A1gBgANWAYADVQGAA1MBgANLAYADDwEUCAABMAEqASgBPwGvAUIBEwH/AdgBVgEZAf8B3AFgASUB/wHf
- AWgBLwH/AeEBbwE4Af8B4wFzAUAB/wHxAb4BpgX/AfMByAGyAf8B5gF9AUwB/wHmAX4BTAH/AeYBfgFM
- Af8B5gF+AUwB/wHmAX4BTAH/AeYBfgFMAf8B5gF+AUwB/wHmAX4BTAH/AeYBfgFMAf8B+AHjAdkF/wH4
- AeIB2AH/AeQBdwFGAf8B4gFzAT8B/wHhAW4BNwH/Ad4BaAEuAf8B3AFeASMB/wHXAVUBGAH/AaIBOwER
- Af8BHwEcARsBJwgAA1oBcwNoAf8DiwH/A5kB/wOcAf8DngH/A6MB/wOnAf8DqwH/A64B/wOwAf8DsgH/
- A7MB/wO1Af8DtQH/A7UB/wO1Af8DswH/A7IB/wOwAf8DrQH/A6oB/wOmAf8DogH/A50B/wObAf8DmAH/
- A4wB/wNkAf8DZQGGCAABWQFYAVoBcwIYAZ8B/wE3ATgBywH/AUoBTQHWAf8BTwFUAdcB/wFTAVsB2AH/
- AVoBYwHbAf8BXwFrAd8B/wFlAXQB4gH/AWkBegHkAf8BbQGBAeYB/wFwAYQB5wH/AXEBhwHoAf8BcwGK
- AekB/wF0AYsB6QH/AXQBiwHpAf8BcwGKAekB/wFxAYYB5wH/AW8BhAHnAf8BbAGAAeYB/wFpAXkB4wH/
- AWQBcgHhAf8BXgFqAd4B/wFZAWIB2gH/AVMBWgHXAf8BTgFTAdYB/wFJAUwB1QH/ATgBOQHMAf8CFQGb
- Af8CYgFkAYYIAAMYASADTgGAA1MBgANWAYADVwGAA10BgANmAYADcgGAA3UBgANqAYADXAGAA1wBgANc
- AYADXAGAA1wBgANcAYADXAGAA1wBgANfAYADdAGAA3UBgANqAYADWwGAA1oBgANZAYADVwGAA1UBgANT
- AYADTAGAAw8BFAgAATABKgEoAT8BsgFEARMB/wHZAVgBGwH/Ad0BYwEoAf8B3wFrATIB/wHmAYoBXQH/
- Ae8BtgGcAf8B+wHxAewF/wH0AcoBtQH/AecBggFPAf8B5wGCAU8B/wHnAYIBTwH/AecBggFPAf8B5wGC
- AU8B/wHnAYIBTwH/AecBggFPAf8B5wGCAU8B/wHqAZQBagH/A/4F/wH0AcsBtwH/AeUBewFJAf8B4wF2
- AUQB/wHiAXEBPAH/Ad8BagExAf8B3AFiAScB/wHZAVcBGgH/AaUBPQERAf8BHwEcARsBJwgAA4AB3ANp
- Af8DkQH/A5YB/wOYAf8DmwH/A58B/wOjAf8DpwH/A6kB/wOrAf8DrQH/A64B/wOvAf8DrwH/A68B/wOv
- Af8DrgH/A60B/wOrAf8DqQH/A6YB/wOjAf8DnwH/A5oB/wOYAf8DlgH/A5EB/wNrAf8DgQHxCAACXAGU
- AdwCDQGtAf8BQwFFAcwB/wFIAUwB0gH/AUwBUQHRAf8BUQFZAdQB/wFXAWEB1wH/AVwBZwHaAf8BYgFv
- AdwB/wFlAXUB3gH/AWgBeQHgAf8BagF9AeEB/wFtAYAB4gH/AW4BgwHjAf8BbgGDAeMB/wFuAYMB4wH/
- AW4BgwHiAf8BbAGAAeIB/wFqAXwB4QH/AWgBeQHgAf8BZQF0Ad4B/wFhAW0B3AH/AVsBZwHaAf8BVgFf
- AdcB/wFQAVcB0wH/AUwBUQHRAf8BSAFMAdIB/wFDAUQBywH/ARABDwGvAf8CVgGYAfEIAAMYASADTgGA
- A1QBgANiAYADbgGAA3QBgAN1AYADdQGAA3UBgANxAYADXwGAA1wBgANcAYADXAGAA1wBgANcAYADXAGA
- A1wBgANpAYADdQGAA3UBgANlAYADWwGAA1oBgANZAYADVwGAA1YBgANUAYADTAGAAw8BFAgAATABKgEo
- AT8BtQFFARMB/wHaAVoBHQH/AekBoAF9Af8B9wHhAdYB/wH+Av0N/wH6Ae0B5wH/AeoBkgFmAf8B6AGF
- AVMB/wHoAYUBUwH/AegBhQFTAf8B6AGFAVMB/wHoAYUBUwH/AegBhQFTAf8B6AGFAVMB/wHzAcUBrwn/
- Ae8BsQGTAf8B5gF9AUsB/wHkAXcBRgH/AeIBcgE+Af8B4AFsATMB/wHdAWMBKQH/AdoBWQEcAf8BpwE9
- ARIB/wEfARwBGwEnBAADEgEWA3cB/wN5Af8DjwH/A5IB/wOVAf8DmAH/A5sB/wOfAf8DogH/A6UB/wOn
- Af8DqAH/A6kB/wOqAf8DqgH/A6oB/wOqAf8DqQH/A6gB/wOmAf8DpAH/A6IB/wOfAf8DmwH/A5gB/wOU
- Af8DkgH/A48B/wN8Af8DcQH/AygBMQMSARYBNQE0AaUB/wEkASUBugH/AUEBQwHKAf8BRQFJAcwB/wFJ
- AU8BzgH/AU4BVQHQAf8BUgFcAdMB/wFYAWIB1gH/AVwBaAHYAf8BYAFuAdoB/wFjAXIB2wH/AWQBdQHc
- Af8BZgF4Ad0B/wFnAXkB3gH/AWcBegHeAf8BZwF5Ad4B/wFnAXkB3gH/AWYBeAHdAf8BZAF0AdwB/wFi
- AXIB2wH/AV8BbQHZAf8BWwFnAdgB/wFXAWIB1gH/AVEBWwHTAf8BTQFUAdAB/wFJAU4BzQH/AUUBSQHM
- Af8BQQFDAckB/wInAbwB/wIuAZ4B/wMoATEEAAMYASADTgGAA1QBgANmAYADagGAA2sBgANsAYADbQGA
- A3QBgAN1AYADcQGAA2ABgANdAYADXQGAA10BgANdAYADXQGAA10BgANxAYADdQGAA3QBgANgAYADWwGA
- A1oBgANZAYADVwGAA1YBgANUAYADTAGAAw8BFAgAATABKgEoAT8BtwFEARMB/wHbAVoBHgH/Ae4BtQGa
- Af8B8wHLAbgB/wH0AdABvwH/AfYB1QHGAf8B9gHZAcsB/wH+AfwB+wX/AfsB7QHnAf8B6wGZAW8B/wHo
- AYcBVQH/AegBhwFVAf8B6AGHAVUB/wHoAYcBVQH/AegBhwFVAf8B6AGHAVUB/wH7Ae4B6AX/A/4B/wHq
- AZUBawH/AeYBfQFMAf8B5AF5AUcB/wHiAXMBPwH/AeABbQE0Af8B3QFkASsB/wHaAVoBHQH/AakBPQES
- Af8BHwEcARsBJwQAA0kBXANmAf8DgwH/A40B/wOOAf8DkQH/A5UB/wOYAf8DmwH/A54B/wOgAf8DogH/
- A6MB/wOkAf8DpQH/A6UB/wOlAf8DpAH/A6QB/wOjAf8DoQH/A6AB/wOdAf8DmgH/A5gB/wOUAf8DkQH/
- A44B/wONAf8DhAH/A2EB/wNnAYgCRwFKAVwCGQGbAf8BMgEzAb8B/wFAAUIBxwH/AUIBRwHHAf8BRgFM
- AcoB/wFLAVEBzQH/AU8BVwHPAf8BUwFeAdEB/wFYAWMB0wH/AVsBaAHVAf8BXQFsAdYB/wFfAW4B1wH/
- AWEBcAHYAf8BYgFyAdgB/wFiAXMB2QH/AWIBcwHZAf8BYQFxAdgB/wFgAXAB2AH/AV4BbgHXAf8BXAFq
- AdYB/wFaAWcB1QH/AVcBYgHTAf8BUgFdAdEB/wFOAVYBzwH/AUkBUQHNAf8BRQFKAckB/wFBAUUBxwH/
- AT8BQQHHAf8BMwE0Ab8B/wIUAZYB/wJjAWcBiAQAAxgBIANOAYADVAGAA1YBgANYAYADWQGAA1sBgANb
- AYADZAGAA3QBgAN1AYADcgGAA2EBgANdAYADXQGAA10BgANdAYADYwGAA3UBgAN1AYADcwGAA1wBgANb
- AYADWgGAA1kBgANXAYADVgGAA1QBgANMAYADDwEUCAABMAEqASgBPwG4AUQBEwH/AdsBWgEfAf8B3gFm
- ASwB/wHgAW0BNgH/AeMBcwFAAf8B5QF6AUgB/wHmAX4BTAH/Ae4BrAGMAf8C/gH9Bf8B/AHyAe4B/wHr
- AZ0BdQH/AegBhwFVAf8B6AGHAVUB/wHoAYcBVQH/AegBhwFVAf8B7QGmAYIJ/wH9AfgB9gH/AecBggFQ
- Af8B5gF+AUwB/wHkAXkBRwH/AeIBcwE/Af8B4AFtATQB/wHdAWUBKwH/AdoBWgEdAf8BqgE+ARIB/wEf
- ARwBGwEnBAADcwGiA1wB/wOFAf8DigH/A4sB/wOOAf8DkQH/A5QB/wOXAf8DmQH/A5sB/wOdAf8DngH/
- A58B/wOgAf8DoAH/A6AB/wOgAf8DnwH/A54B/wOdAf8DmwH/A5gB/wOWAf8DkwH/A5AB/wONAf8DigH/
- A4kB/wOFAf8DXgH/A3sBvgJmAXgBogEHAQYBmAH/ATcBOAG9Af8BPQE/AcMB/wE/AUMBwgH/AUMBSAHF
- Af8BRwFOAcgB/wFLAVQBygH/AU8BWgHMAf8BUwFeAc0B/wFWAWIBzwH/AVgBZgHQAf8BWgFpAdEB/wFb
- AWoB0QH/AVwBbAHSAf8BXQFtAdMB/wFdAWwB0wH/AVwBbAHSAf8BWwFqAdEB/wFZAWgB0QH/AVgBZQHQ
- Af8BVQFhAc8B/wFSAV4BzQH/AU8BWQHLAf8BSgFTAckB/wFGAU0BxwH/AUIBRwHEAf8BPQFBAcIB/wE8
- AT8BwgH/AjcBvQH/AQkBCAGbAf8CagGCAb4EAAMYASADTgGAA1QBgANWAYADWAGAA1kBgANbAYADWwGA
- A1wBgANlAYADdAGAA3UBgANzAYADYgGAA10BgANdAYADXQGAA2sBgAN1AYADdQGAA24BgANcAYADWwGA
- A1oBgANZAYADVwGAA1YBgANUAYADTQGAAw8BFAgAATABKgEoAT8BuQFFARMB/wHbAVoBHwH/Ad4BZgEs
- Af8B4AFtATYB/wHjAXMBQAH/AeUBegFIAf8B5gF+AUwB/wHnAYIBUAH/Ae8BsQGTAf8D/gX/AfwB9gH0
- Af8B7AGkAYAB/wHoAYcBVQH/AegBhwFVAf8B6AGHAVUB/wH1AdEBvwn/AfgB4AHUAf8B5wGCAU8B/wHm
- AX4BTAH/AeQBeQFHAf8B4gFzAT8B/wHgAW0BNAH/Ad0BZQErAf8B2gFaAR0B/wGrAT8BEgH/AR8BHAEb
- AScEAAN9AckDXwH/A4IB/wOGAf8DhwH/A4oB/wONAf8DjwH/A5EB/wOUAf8DlgH/A5cB/wOYAf8DmgH/
- A5oB/wOaAf8DmgH/A5oB/wOaAf8DmAH/A5cB/wOWAf8DkwH/A5EB/wOPAf8DjQH/A4kB/wOGAf8DhQH/
- A4IB/wNiAf8DfAHUAmIBiwHJAQgBBwGeAf8CNgG5Af8BOQE8Ab4B/wE7AT4BvgH/AT8BRAHBAf8BQgFJ
- AcMB/wFFAU4BxQH/AUkBUwHHAf8BTQFYAckB/wFQAVwBygH/AVIBXwHLAf8BUwFhAcwB/wFVAWMBzQH/
- AVYBZQHNAf8BVgFlAc0B/wFWAWUBzQH/AVYBZQHNAf8BVQFjAc0B/wFTAWABywH/AVIBXgHLAf8BTwFb
- AcoB/wFMAVgByAH/AUkBUgHGAf8BRQFNAcUB/wFCAUgBwwH/AT4BRAHAAf8BOwE+Ab0B/wE5ATsBvQH/
- AjUBuQH/AgsBoQH/Al0BiwHUBAADGAEgA08BgANUAYADVgGAA1gBgANZAYADWwGAA1sBgANcAYADXAGA
- A2gBgAN0AYADdQGAA3MBgANkAYADXQGAA2UBgAN0AYADdQGAA3UBgANoAYADXAGAA1sBgANaAYADWQGA
- A1cBgANWAYADVAGAA00BgAMPARQIAAEwASoBKAE/AboBRQETAf8B2wFbAR8B/wHeAWYBLAH/AeABbQE2
- Af8B4wFzAUAB/wHlAXoBSAH/AeYBfgFMAf8B5wGCAVAB/wHoAYYBVAH/AfIBwAGoAf8D/gX/Af0B+gH4
- Af8B7gGqAYgB/wHoAYcBVQH/Ae8BsAGRAf8B/gH9AfwJ/wHzAcIBqgH/AecBggFPAf8B5gF+AUwB/wHk
- AXkBRwH/AeIBcwE/Af8B4AFtATQB/wHdAWUBKwH/AdoBWgEdAf8BrAE/ARIB/wEfARwBGwEnBAADdgHH
- A2IB/wN+Af8DggH/A4MB/wOFAf8DiAH/A4sB/wONAf8DjgH/A5EB/wOSAf8DkwH/A5QB/wOUAf8DlAH/
- A5QB/wOUAf8DkwH/A5MB/wOSAf8DkAH/A44B/wOMAf8DigH/A4cB/wOFAf8DgwH/A4IB/wN9Af8DYwH/
- A3oB3QJZAYYBxwILAZ8B/wIzAbUB/wE2ATgBuAH/ATcBOgG5Af8BOgE/AbsB/wE+AUMBvgH/AUEBSAHA
- Af8BRAFMAcEB/wFGAVABwwH/AUkBVQHFAf8BSwFXAcUB/wFNAVkBxgH/AU8BXAHHAf8BTwFdAcYB/wFP
- AV0BxwH/AU8BXQHHAf8BTwFdAcYB/wFOAVwBxgH/AU0BWQHGAf8BSwFXAcUB/wFJAVQBxAH/AUcBUAHC
- Af8BQwFMAcEB/wFAAUcBvwH/AT0BQwG9Af8BOgE+AbsB/wE3ATsBuQH/ATYBNwG4Af8CMgG0Af8CDQGh
- Af8CVgGOAd0EAAMYASADTwGAA1QBgANWAYADWAGAA1kBgANbAYADXAGAA10BgANeAYADXwGAA2wBgAN0
- AYADdQGAA3QBgANrAYADdAGAA3UBgAN1AYADdQGAA2YBgANdAYADXAGAA1sBgANZAYADVwGAA1YBgANU
- AYADTQGAAw8BFAgAATABKgEoAT8BugFFARMB/wHbAVsBHwH/Ad4BZgEsAf8B4AFtATYB/wHjAXMBQQH/
- AeUBfQFNAf8B5gGFAVYB/wHnAYoBXQH/AegBjwFiAf8B6AGSAWcB/wH1AdYBxwH/A/4F/wH+AfwB+wH/
- AfMBzgG8Af8B/gL8Df8B7wG0AZcB/wHnAYkBXAH/AeYBhQFVAf8B5AF8AUsB/wHiAXMBPwH/AeABbQE0
- Af8B3QFlASsB/wHaAVoBHQH/AawBPwESAf8BHwEcARsBJwQAA3UByANeAf8DegH/A30B/wN+Af8DgQH/
- A4MB/wOEAf8DhgH/A4gB/wOKAf8DiwH/A40B/wONAf8DjQH/A40B/wONAf8DjQH/A40B/wOMAf8DiwH/
- A4kB/wOHAf8DhQH/A4QB/wODAf8DgQH/A30B/wN9Af8DegH/A2AB/wN4AdwCWAGGAcgBCQEIAZsB/wEv
- ATABsAH/ATIBNAGzAf8BNAE3AbQB/wE2ATsBtgH/ATkBPgG4Af8BOwFBAbkB/wE9AUUBuwH/AT8BSAG8
- Af8BQgFMAb0B/wFDAU4BvgH/AUYBUgHAAf8BRwFUAcAB/wFHAVQBwAH/AUcBVAHAAf8BRwFUAcAB/wFH
- AVQBwAH/AUcBUwHAAf8BRQFRAb8B/wFDAU4BvgH/AUEBSwG9Af8BPwFHAbsB/wE8AUUBugH/AToBQQG5
- Af8BOAE+AbcB/wE2AToBtQH/ATMBNgGzAf8BMgEzAbMB/wIvAbAB/wELAQoBnQH/AlQBjAHcBAADGAEg
- A08BgANUAYADWAGAA1wBgANeAYADXwGAA18BgANgAYADYAGAA2ABgANhAYADbgGAA3UBgAN1AYADdQGA
- A3UBgAN1AYADdQGAA3QBgANiAYADYAGAA18BgANfAYADXgGAA1sBgANYAYADVAGAA00BgAMPARQIAAEw
- ASoBKAE/AboBRQETAf8B2wFcASAB/wHfAXEBOwH/AeIBggFTAf8B5AGNAWIB/wHmAZIBaQH/AecBlAFt
- Af8B6AGWAW8B/wHoAZkBcQH/AegBmQFyAf8B6QGbAXUB/wH3Ad4B0xn/A/4B/wHqAaQBgwH/AegBlQFv
- Af8B5wGUAWwB/wHmAZEBaAH/AeQBjQFhAf8B4gGBAVEB/wHfAXABOgH/AdoBWwEeAf8BrAE/ARIB/wEf
- ARwBGwEnBAADdQHCA1cB/wN2Af8DeQH/A3oB/wN6Af8DfAH/A4EB/wOEAf8DhgH/A4gB/wOJAf8DiwH/
- A4wB/wOMAf8DjAH/A4wB/wOMAf8DjAH/A4sB/wOJAf8DiAH/A4YB/wOEAf8DgQH/A3sB/wN6Af8DeQH/
- A3gB/wN2Af8DWgH/A3YB0gJaAYMBwgIBAZIB/wIsAasB/wEvATABrwH/ATABMwGvAf8BMAE0Aa8B/wEy
- ATgBsQH/ATgBPgG0Af8BPAFDAbYB/wE/AUcBuAH/AUIBSwG6Af8BRAFNAboB/wFGAVABvAH/AUcBUgG9
- Af8BRwFTAb0B/wFIAVMBvAH/AUgBUwG9Af8BRwFSAb0B/wFHAVIBvQH/AUYBUAG8Af8BQwFMAboB/wFC
- AUoBuQH/AT8BRwG4Af8BPAFCAbYB/wE4AT4BtAH/ATIBNwGwAf8BMAEzAa8B/wEvATIBrgH/AS4BMAGu
- Af8CKwGrAf8CBAGVAf8CVgGGAdIEAAMYASADTwGAA1cBgANdAYADXgGAA18BgANfAYADYAGAA2EBgANh
- AYADYQGAA2EBgANhAYADcQGAA3UBgAN1AYADdQGAA3UBgAN1AYADcgGAA2EBgANgAYADYAGAA18BgANf
- AYADXgGAA1wBgANXAYADTQGAAw8BFAgAATABKgEoAT8BugFFARMB/wHcAWkBMwH/AeIBhwFcAf8B4wGO
- AWMB/wHlAZIBaQH/AeYBlQFtAf8B5wGYAXIB/wHoAZoBdQH/AegBnAF2Af8B6AGcAXcB/wHoAZwBdwH/
- AegBnwF7Af8B+gHtAecV/wH8AfQB8QH/AegBmwF3Af8B6AGZAXQB/wHnAZcBcQH/AeYBlQFsAf8B5QGS
- AWkB/wHjAY0BYgH/AeEBhwFbAf8B3AFpATIB/wGsAT8BEgH/AR8BHAEbAScEAANpAZoDUQH/A3IB/wN2
- Af8DdAH/A3gB/wOCAf8DhwH/A4kB/wOKAf8DiwH/A4wB/wONAf8DjgH/A44B/wOOAf8DjgH/A40B/wON
- Af8DjAH/A4wB/wOKAf8DiQH/A4gB/wOGAf8DgQH/A3cB/wNzAf8DdQH/A3EB/wNSAf8DdAG8AlwBbwGa
- AgABhwH/AigBpgH/ASwBLQGqAf8BKAErAakB/wEuATIBrAH/AT0BQQGyAf8BQwFHAbUB/wFFAUoBtwH/
- AUcBTQG4Af8BSQFPAbkB/wFKAVIBugH/AUsBUwG7Af8BTAFUAbwB/wFMAVQBvAH/AU0BVQG8Af8BTQFV
- AbwB/wFMAVQBuwH/AUwBUwG7Af8BSwFSAboB/wFKAVEBugH/AUgBTwG4Af8BRgFNAbcB/wFEAUkBtgH/
- AUIBRgG1Af8BPAFAAbEB/wEuATEBqwH/ASgBKgGoAf8CLAGpAf8BKAEnAaUB/wIAAYoB/wJgAXwBvAQA
- AxgBIANPAYADWAGAA14BgANfAYADXwGAA2ABgANhAYADYQGAA2IBgANiAYADYgGAA2kBgAN0AYADdQGA
- A3UBgAN1AYADdQGAA3UBgANvAYADYgGAA2EBgANhAYADYAGAA18BgANeAYADXQGAA1gBgANNAYADDwEU
- CAABMAEqASgBPwG6AUUBEwH/Ad0BbQE7Af8B4gGLAWMB/wHkAZEBaQH/AeUBlQFuAf8B5gGaAXQB/wHn
- AZwBeAH/AegBngF7Af8B6QGgAXwB/wHpAaABfQH/AekBoAF9Af8B8AHFAbAB/wP+Ff8B+AHkAdoB/wHp
- AaABfAH/AegBngF6Af8B5wGcAXcB/wHmAZoBcwH/AeUBlQFuAf8B5AGRAWgB/wHiAYsBYgH/Ad0BbQE5
- Af8BrAE/ARIB/wEfARwBGwEnBAADSAFfA1EB/wNrAf8DbwH/A3MB/wOFAf8DiAH/A4gB/wOJAf8DigH/
- A4sB/wOMAf8DjQH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wOMAf8DiwH/A4kB/wOIAf8DiAH/
- A4gB/wOEAf8DcwH/A28B/wNsAf8DTQH/A2MBhwJGAUkBXwIDAYMB/wEhASABoAH/AiUBowH/ASoBLAGm
- Af8BQwFFAbEB/wFIAUsBtAH/AUcBSwG0Af8BSQFNAbUB/wFKAU8BtgH/AUsBUQG3Af8BTQFTAbcB/wFO
- AVQBuAH/AU8BVQG5Af8BTwFWAbkB/wFQAVYBuQH/AVABVgG5Af8BTwFWAbkB/wFPAVUBuAH/AU4BUwG3
- Af8BTQFSAbcB/wFLAVEBtgH/AUkBTwG1Af8BSAFNAbQB/wFHAUoBtAH/AUgBSwG0Af8BQgFEAbAB/wEq
- ASwBpQH/AiUBowH/AiEBoAH/AgABfQH/Al0BZAGHBAADGAEgA04BgANZAYADXwGAA18BgANgAYADYQGA
- A2EBgANiAYADYgGAA2cBgANtAYADdAGAA3UBgAN1AYADdQGAA3UBgAN1AYADdQGAA28BgANiAYADYgGA
- A2EBgANhAYADYAGAA18BgANeAYADWAGAA00BgAMPARQIAAEwASoBKAE/AbkBRQETAf8B3gFyAUAB/wHj
- AZIBagH/AeQBlQFwAf8B5QGYAXYB/wHnAZwBegH/AecBnwF9Af8B6AGhAYAB/wHpAaIBgQH/Ae0BuQGh
- Af8B9QHbAc8B/wP+Gf8B+AHjAdoB/wHpAaQBgwH/AegBoAF+Af8B5wGfAXwB/wHmAZwBeQH/AeUBmAF1
- Af8B5AGVAW8B/wHjAZEBaQH/Ad0BcQE/Af8BqwE/ARIB/wEfARwBGwEnBAADDwETA2EB/wNdAf8DaQH/
- A3oB/wOMAf8DhwH/A4kB/wOLAf8DiwH/A4wB/wONAf8DjgH/A44B/wOOAf8DjwH/A48B/wOPAf8DjgH/
- A44B/wONAf8DjQH/A4sB/wOKAf8DiQH/A4cB/wOLAf8DegH/A2gB/wNfAf8DWAH/AyoBNAMPARMCHQGL
- Af8CDgGTAf8CHQGcAf8CNwGnAf8BUAFRAbMB/wFJAUsBsQH/AUsBTgGyAf8BTQFQAbQB/wFOAVIBtAH/
- AU8BVAG1Af8BUAFVAbUB/wFRAVYBtgH/AVIBVwG2Af8BUgFYAbcB/wFTAVgBtwH/AVMBWAG3Af8BUwFX
- AbcB/wFSAVgBtwH/AVEBVgG2Af8BUAFVAbUB/wFQAVQBtQH/AU4BUgG0Af8BTQFQAbIB/wFLAU4BsgH/
- AUkBSwGxAf8BUAFSAbIB/wI3AacB/wEcAR0BnAH/AhABlQH/AhQBggH/AyoBNAQAAxgBIANOAYADWQGA
- A18BgANgAYADYQGAA2IBgANkAYADagGAA3IBgAN0AYADdQGAA3UBgAN1AYADdQGAA3UBgAN1AYADdQGA
- A3UBgAN1AYADcAGAA2QBgANiAYADYQGAA2EBgANgAYADXwGAA1kBgANNAYADDwEUCAABMAEqASgBPwG5
- AUUBEwH/Ad4BdgFFAf8B4wGWAXEB/wHlAZoBdgH/AeYBngF7Af8B5wGhAYAB/wHpAaoBjQH/AfEBzQG8
- Af8B+gHwAewB/wP+Jf8B+AHmAd4B/wHpAakBjQH/AecBogGCAf8B5gGhAX4B/wHmAZ4BewH/AeQBmgF1
- Af8B4wGWAXAB/wHdAXUBRAH/AasBPgESAf8BHwEcARsBJwgAA2oB3QNPAf8DYwH/A3wB/wOPAf8DigH/
- A4wB/wOMAf8DjgH/A44B/wOPAf8DkAH/A5AB/wOQAf8DkAH/A5AB/wOQAf8DkAH/A5AB/wOPAf8DjgH/
- A44B/wOMAf8DiwH/A4oB/wOPAf8DfAH/A2IB/wNQAf8DaQHvCAACQAGCAd0CAAGDAf8CGAGVAf8BPQE8
- AacB/wJYAbMB/wFQAVEBsAH/AVEBUgGyAf8BUwFUAbIB/wFVAVYBswH/AVUBWAG0Af8BVgFZAbQB/wFX
- AVsBtQH/AVcBWwG1Af8BVwFbAbUB/wFYAVsBtQH/AVgBWwG1Af8BVwFbAbUB/wFXAVsBtQH/AVcBWwG1
- Af8BVgFZAbQB/wFVAVcBtAH/AVUBVgGzAf8BUwFUAbIB/wFRAVIBsQH/AVABUQGwAf8CWAGzAf8CPAGn
- Af8CFwGUAf8CAAGEAf8COQGEAe8IAAMYASADTgGAA1oBgANgAYADYgGAA2cBgANuAYADdAGAA3UBgAN1
- AYADdQGAA3UBgAN1AYADdQGAA3QBgAN0AYADcgGAA3MBgAN1AYADdQGAA3UBgANxAYADZAGAA2IBgANi
- AYADYQGAA2ABgANaAYADTQGAAw8BFAgAATABKgEoAT8BuAFGARMB/wHeAXoBSgH/AeQBmgF3Af8B5gGf
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgAMOARMIAAMpAT8BogE1
+ AQkB/wHDAUQBDQH/Ac0BSAEOAf8B1QFNAREB/wHaAVIBFgH/AdwBVgEbAf8B3QFZASAB/wHqAaIBgAH/
+ AecBlQFmAf8B3wFfASYB/wHfAV8BJgH/Ad8BXwEmAf8B3wFfASYB/wHfAV8BJgH/Ad8BXwEmAf8B3wFf
+ ASYB/wHfAV8BJgH/Ad8BXwEmAf8B3wFfASYB/wHtAbMBlwH/Ad8BYgErAf8B3QFZAR8B/wHbAVYBGgH/
+ AdkBUQEWAf8B1AFMARAB/wHMAUcBDgH/AcABQwENAf8BmgEvAQgB/wMbASYQAANQAZ0DYQH/A5QB/wOj
+ Af8DqgH/A6wB/wOxAf8DtQH/A7kB/wO8Af8DvgH/A78B/wO/Af8DvwH/A78B/wO9Af8DuwH/A7kB/wO1
+ Af8DsAH/A6wB/wOqAf8DowH/A5YB/wNgAf8DVQGyGAADUAGdAgcBqgH/ATYBOAHYAf8BTQFRAeIB/wFU
+ AVwB6QH/AVoBZgHoAf8BYQFyAeoB/wFqAYYB7AH/AXEBkAHtAf8BdgGXAe4B/wGCAZwB7wH/AYMBngHw
+ Af8BhAGfAfAB/wGEAZ4B8AH/AYMBngHwAf8BgQGbAe8B/wF1AZYB7gH/AXABjgHtAf8BaQGEAesB/wFg
+ AXAB6QH/AVkBZAHoAf8BVAFaAegB/wFMAVAB4gH/AjgB2QH/AgcBqQH/A1UBshAAAxcBIANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEUCAADKQE/AagBNwEK
+ Af8BzgFJAQ4B/wHZAU8BEgH/AdwBVwEcAf8B3gFeASUB/wHfAWMBKgH/AeEBZwEwAf8B+gHrAeQB/wHx
+ AcEBqQH/AeIBawE3Af8B4gFrATcB/wHiAWsBNwH/AeIBawE3Af8B4gFrATcB/wHiAWsBNwH/AeIBawE3
+ Af8B4gFrATcB/wHiAWsBNwH/AeQBdQFFAf8B/QH8AfsB/wHxAcABqQH/AeEBZgEvAf8B3wFiASkB/wHd
+ AV0BIwH/AdwBVgEbAf8B2AFOAREB/wHNAUkBDgH/AZwBMQEIAf8DHAEnDAADNQFWA2QB/wOIAf8DnwH/
+ A6UB/wOmAf8DqgH/A68B/wOzAf8DtgH/A7gB/wO6Af8DugH/A7sB/wO7Af8DugH/A7kB/wO4Af8DtgH/
+ A7IB/wOuAf8DqQH/A6UB/wOkAf8DngH/A4oB/wNfAf8DOgFiEAADNQFWAhwBnQH/ASYBJwHNAf8BSAFL
+ AdwB/wFPAVQB4wH/AVMBXQHiAf8BWQFnAeQB/wFhAXIB5gH/AWgBhAHoAf8BbQGMAeoB/wFxAZIB7AH/
+ AXMBlQHtAf8BdAGXAe0B/wF1AZgB7QH/AXUBmAHtAf8BdAGWAe0B/wFyAZUB7QH/AXABkQHsAf8BbAGK
+ AeoB/wFnAYMB6AH/AV8BcAHmAf8BWAFmAeMB/wFRAVsB4gH/AU4BUwHjAf8BRwFKAdsB/wEoASkB0AH/
+ ARgBFwGYAf8DOgFiDAADFwEgA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgAMPARQIAAMpAT8BqwE5AQoB/wHVAUsBDwH/AdsBUwEYAf8B3QFdASMB/wHfAWMBKgH/
+ AeEBaAEyAf8B5wGMAVgB/wP+Af8B8wHEAa4B/wHkAXEBPwH/AeQBcQE/Af8B5AFxAT8B/wHkAXEBPwH/
+ AeQBcQE/Af8B5AFxAT8B/wHkAXEBPwH/AeQBcQE/Af8B5AFxAT8B/wHvAbQBmAX/Af0B+wH6Af8B4wFu
+ AToB/wHhAWcBMQH/Ad8BYgEpAf8B3QFcASEB/wHbAVMBFgH/AdMBSgEPAf8BngExAQkB/wMcAScLAAEB
+ A2gB6QNkAf8DmQH/A6AB/wOgAf8DpAH/A6kB/wOtAf8DsQH/A7QB/wO2Af8DuAH/A7kB/wO5Af8DuQH/
+ A7gB/wO3Af8DtgH/A7QB/wOwAf8DrAH/A6gB/wOjAf8DoAH/A58B/wOYAf8DaAH/A28B8QMBAQILAAEB
+ AmEBbgHpAgUBtAH/AUMBRAHVAf8BSQFNAd4B/wFMAVMB3AH/AVIBXAHeAf8BWQFmAeIB/wFfAW8B5QH/
+ AWUBgAHoAf8BaQGGAeoB/wFsAYsB7AH/AW4BjQHtAf8BcAGQAe0B/wFxAZEB7gH/AXABkQHuAf8BbwGP
+ Ae0B/wFuAY0B7AH/AWsBigHrAf8BaAGFAeoB/wFkAXUB5wH/AV4BbgHkAf8BWAFkAeEB/wFRAVsB3QH/
+ AUwBUwHbAf8BSAFMAd0B/wFCAUMB1AH/AgkBuAH/AloBewHxAwEBAggAAxcBIANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEUCAADKQE/Aa8BOgELAf8B2AFO
+ AREB/wHcAVgBHQH/Ad8BYAEnAf8B4QFnATAB/wHjAWsBOAH/AfEBvgGmBf8B8wHIAbIB/wHmAXUBRAH/
+ AeYBdgFEAf8B5gF2AUQB/wHmAXYBRAH/AeYBdgFEAf8B5gF2AUQB/wHmAXYBRAH/AeYBdgFEAf8B5gF2
+ AUQB/wH4AeMB2QX/AfgB4gHYAf8B5AFvAT4B/wHiAWsBNwH/AeEBZgEvAf8B3gFgASYB/wHcAVYBGwH/
+ AdcBTQEQAf8BogEzAQkB/wMcAScIAANCAXMDYAH/A4sB/wOZAf8DnAH/A54B/wOjAf8DpwH/A6sB/wOu
+ Af8DsAH/A7IB/wOzAf8DtQH/A7UB/wO1Af8DtQH/A7MB/wOyAf8DsAH/A60B/wOqAf8DpgH/A6IB/wOd
+ Af8DmwH/A5gB/wOMAf8DXAH/A0gBhggAA0IBcwIQAZ8B/wEvATABywH/AUIBRQHWAf8BRwFMAdcB/wFL
+ AVMB2AH/AVIBWwHbAf8BVwFjAd8B/wFdAWwB4gH/AWEBcgHkAf8BZQGBAeYB/wFoAYQB5wH/AWkBhwHo
+ Af8BawGKAekB/wFsAYsB6QH/AWwBiwHpAf8BawGKAekB/wFpAYYB5wH/AWcBhAHnAf8BZAGAAeYB/wFh
+ AXEB4wH/AVwBagHhAf8BVgFiAd4B/wFRAVoB2gH/AUsBUgHXAf8BRgFLAdYB/wFBAUQB1QH/ATABMQHM
+ Af8CDQGbAf8DSAGGCAADFwEgA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgAMPARQIAAMpAT8BsgE8AQsB/wHZAVABEwH/Ad0BWwEgAf8B3wFjASoB/wHmAYoBVQH/
+ Ae8BtgGcAf8B+wHxAewF/wH0AcoBtQH/AecBggFHAf8B5wGCAUcB/wHnAYIBRwH/AecBggFHAf8B5wGC
+ AUcB/wHnAYIBRwH/AecBggFHAf8B5wGCAUcB/wHqAZQBYgH/A/4F/wH0AcsBtwH/AeUBcwFBAf8B4wFu
+ ATwB/wHiAWkBNAH/Ad8BYgEpAf8B3AFaAR8B/wHZAU8BEgH/AaUBNQEJAf8DHAEnCAADYQHcA2EB/wOR
+ Af8DlgH/A5gB/wObAf8DnwH/A6MB/wOnAf8DqQH/A6sB/wOtAf8DrgH/A68B/wOvAf8DrwH/A68B/wOu
+ Af8DrQH/A6sB/wOpAf8DpgH/A6MB/wOfAf8DmgH/A5gB/wOWAf8DkQH/A2MB/wNvAfEIAAJcAWIB3AIF
+ Aa0B/wE7AT0BzAH/AUABRAHSAf8BRAFJAdEB/wFJAVEB1AH/AU8BWQHXAf8BVAFfAdoB/wFaAWcB3AH/
+ AV0BbQHeAf8BYAFxAeAB/wFiAXUB4QH/AWUBgAHiAf8BZgGDAeMB/wFmAYMB4wH/AWYBgwHjAf8BZgGD
+ AeIB/wFkAYAB4gH/AWIBdAHhAf8BYAFxAeAB/wFdAWwB3gH/AVkBZQHcAf8BUwFfAdoB/wFOAVcB1wH/
+ AUgBTwHTAf8BRAFJAdEB/wFAAUQB0gH/ATsBPAHLAf8BCAEHAa8B/wJWAXsB8QgAAxcBIANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEUCAADKQE/AbUBPQEL
+ Af8B2gFSARUB/wHpAaABdQH/AfcB4QHWAf8B/gL9Df8B+gHtAecB/wHqAZIBXgH/AegBhQFLAf8B6AGF
+ AUsB/wHoAYUBSwH/AegBhQFLAf8B6AGFAUsB/wHoAYUBSwH/AegBhQFLAf8B8wHFAa8J/wHvAbEBkwH/
+ AeYBdQFDAf8B5AFvAT4B/wHiAWoBNgH/AeABZAErAf8B3QFbASEB/wHaAVEBFAH/AacBNQEKAf8DHAEn
+ BAADEAEWA28B/wNxAf8DjwH/A5IB/wOVAf8DmAH/A5sB/wOfAf8DogH/A6UB/wOnAf8DqAH/A6kB/wOq
+ Af8DqgH/A6oB/wOqAf8DqQH/A6gB/wOmAf8DpAH/A6IB/wOfAf8DmwH/A5gB/wOUAf8DkgH/A48B/wN0
+ Af8DaQH/AyIBMQMQARYBLQEsAaUB/wEcAR0BugH/ATkBOwHKAf8BPQFBAcwB/wFBAUcBzgH/AUYBTQHQ
+ Af8BSgFUAdMB/wFQAVoB1gH/AVQBYAHYAf8BWAFmAdoB/wFbAWoB2wH/AVwBbQHcAf8BXgFwAd0B/wFf
+ AXEB3gH/AV8BcgHeAf8BXwFxAd4B/wFfAXEB3gH/AV4BcAHdAf8BXAFsAdwB/wFaAWoB2wH/AVcBZQHZ
+ Af8BUwFfAdgB/wFPAVoB1gH/AUkBUwHTAf8BRQFMAdAB/wFBAUYBzQH/AT0BQQHMAf8BOQE7AckB/wIf
+ AbwB/wImAZ4B/wMiATEEAAMXASADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAAw8BFAgAAykBPwG3ATwBCwH/AdsBUgEWAf8B7gG1AZoB/wHzAcsBuAH/AfQB0AG/
+ Af8B9gHVAcYB/wH2AdkBywH/Af4B/AH7Bf8B+wHtAecB/wHrAZkBZwH/AegBhwFNAf8B6AGHAU0B/wHo
+ AYcBTQH/AegBhwFNAf8B6AGHAU0B/wHoAYcBTQH/AfsB7gHoBf8D/gH/AeoBlQFjAf8B5gF1AUQB/wHk
+ AXEBPwH/AeIBawE3Af8B4AFlASwB/wHdAVwBIwH/AdoBUgEVAf8BqQE1AQoB/wMcAScEAAM4AVwDXgH/
+ A4MB/wONAf8DjgH/A5EB/wOVAf8DmAH/A5sB/wOeAf8DoAH/A6IB/wOjAf8DpAH/A6UB/wOlAf8DpQH/
+ A6QB/wOkAf8DowH/A6EB/wOgAf8DnQH/A5oB/wOYAf8DlAH/A5EB/wOOAf8DjQH/A4QB/wNZAf8DSQGI
+ AzgBXAIRAZsB/wEqASsBvwH/ATgBOgHHAf8BOgE/AccB/wE+AUQBygH/AUMBSQHNAf8BRwFPAc8B/wFL
+ AVYB0QH/AVABWwHTAf8BUwFgAdUB/wFVAWQB1gH/AVcBZgHXAf8BWQFoAdgB/wFaAWoB2AH/AVoBawHZ
+ Af8BWgFrAdkB/wFZAWkB2AH/AVgBaAHYAf8BVgFmAdcB/wFUAWIB1gH/AVIBXwHVAf8BTwFaAdMB/wFK
+ AVUB0QH/AUYBTgHPAf8BQQFJAc0B/wE9AUIByQH/ATkBPQHHAf8BNwE5AccB/wErASwBvwH/AgwBlgH/
+ A0kBiAQAAxcBIANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADDwEUCAADKQE/AbgBPAELAf8B2wFSARcB/wHeAV4BJAH/AeABZQEuAf8B4wFrATgB/wHlAXIBQAH/
+ AeYBdgFEAf8B7gGsAYwB/wL+Af0F/wH8AfIB7gH/AesBnQFtAf8B6AGHAU0B/wHoAYcBTQH/AegBhwFN
+ Af8B6AGHAU0B/wHtAaYBggn/Af0B+AH2Af8B5wGCAUgB/wHmAXYBRAH/AeQBcQE/Af8B4gFrATcB/wHg
+ AWUBLAH/Ad0BXQEjAf8B2gFSARUB/wGqATYBCgH/AxwBJwQAA1EBogNUAf8DhQH/A4oB/wOLAf8DjgH/
+ A5EB/wOUAf8DlwH/A5kB/wObAf8DnQH/A54B/wOfAf8DoAH/A6AB/wOgAf8DoAH/A58B/wOeAf8DnQH/
+ A5sB/wOYAf8DlgH/A5MB/wOQAf8DjQH/A4oB/wOJAf8DhQH/A1YB/wNZAb4DUQGiAgABmAH/AS8BMAG9
+ Af8BNQE3AcMB/wE3ATsBwgH/ATsBQAHFAf8BPwFGAcgB/wFDAUwBygH/AUcBUgHMAf8BSwFWAc0B/wFO
+ AVoBzwH/AVABXgHQAf8BUgFhAdEB/wFTAWIB0QH/AVQBZAHSAf8BVQFlAdMB/wFVAWQB0wH/AVQBZAHS
+ Af8BUwFiAdEB/wFRAWAB0QH/AVABXQHQAf8BTQFZAc8B/wFKAVYBzQH/AUcBUQHLAf8BQgFLAckB/wE+
+ AUUBxwH/AToBPwHEAf8BNQE5AcIB/wE0ATcBwgH/Ai8BvQH/AQEBAAGbAf8DWQG+BAADFwEgA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgAMPARQIAAMpAT8BuQE9
+ AQsB/wHbAVIBFwH/Ad4BXgEkAf8B4AFlAS4B/wHjAWsBOAH/AeUBcgFAAf8B5gF2AUQB/wHnAYIBSAH/
+ Ae8BsQGTAf8D/gX/AfwB9gH0Af8B7AGkAYAB/wHoAYcBTQH/AegBhwFNAf8B6AGHAU0B/wH1AdEBvwn/
+ AfgB4AHUAf8B5wGCAUcB/wHmAXYBRAH/AeQBcQE/Af8B4gFrATcB/wHgAWUBLAH/Ad0BXQEjAf8B2gFS
+ ARUB/wGrATcBCgH/AxwBJwQAA1wByQNXAf8DggH/A4YB/wOHAf8DigH/A40B/wOPAf8DkQH/A5QB/wOW
+ Af8DlwH/A5gB/wOaAf8DmgH/A5oB/wOaAf8DmgH/A5oB/wOYAf8DlwH/A5YB/wOTAf8DkQH/A48B/wON
+ Af8DiQH/A4YB/wOFAf8DggH/A1oB/wNgAdQDXAHJAgABngH/Ai4BuQH/ATEBNAG+Af8BMwE2Ab4B/wE3
+ ATwBwQH/AToBQQHDAf8BPQFGAcUB/wFBAUsBxwH/AUUBUAHJAf8BSAFUAcoB/wFKAVcBywH/AUsBWQHM
+ Af8BTQFbAc0B/wFOAV0BzQH/AU4BXQHNAf8BTgFdAc0B/wFOAV0BzQH/AU0BWwHNAf8BSwFYAcsB/wFK
+ AVYBywH/AUcBUwHKAf8BRAFQAcgB/wFBAUoBxgH/AT0BRQHFAf8BOgFAAcMB/wE2ATwBwAH/ATMBNgG9
+ Af8BMQEzAb0B/wItAbkB/wIDAaEB/wJcAWAB1AQAAxcBIANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEUCAADKQE/AboBPQELAf8B2wFTARcB/wHeAV4BJAH/
+ AeABZQEuAf8B4wFrATgB/wHlAXIBQAH/AeYBdgFEAf8B5wGCAUgB/wHoAYYBTAH/AfIBwAGoAf8D/gX/
+ Af0B+gH4Af8B7gGqAYgB/wHoAYcBTQH/Ae8BsAGRAf8B/gH9AfwJ/wHzAcIBqgH/AecBggFHAf8B5gF2
+ AUQB/wHkAXEBPwH/AeIBawE3Af8B4AFlASwB/wHdAV0BIwH/AdoBUgEVAf8BrAE3AQoB/wMcAScEAANZ
+ AccDWgH/A3YB/wOCAf8DgwH/A4UB/wOIAf8DiwH/A40B/wOOAf8DkQH/A5IB/wOTAf8DlAH/A5QB/wOU
+ Af8DlAH/A5QB/wOTAf8DkwH/A5IB/wOQAf8DjgH/A4wB/wOKAf8DhwH/A4UB/wODAf8DggH/A3UB/wNb
+ Af8DYgHdAlkBWgHHAgMBnwH/AisBtQH/AS4BMAG4Af8BLwEyAbkB/wEyATcBuwH/ATYBOwG+Af8BOQFA
+ AcAB/wE8AUQBwQH/AT4BSAHDAf8BQQFNAcUB/wFDAU8BxQH/AUUBUQHGAf8BRwFUAccB/wFHAVUBxgH/
+ AUcBVQHHAf8BRwFVAccB/wFHAVUBxgH/AUYBVAHGAf8BRQFRAcYB/wFDAU8BxQH/AUEBTAHEAf8BPwFI
+ AcIB/wE7AUQBwQH/ATgBPwG/Af8BNQE7Ab0B/wEyATYBuwH/AS8BMwG5Af8BLgEvAbgB/wIqAbQB/wIF
+ AaEB/wJYAWQB3QQAAxcBIANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADDwEUCAADKQE/AboBPQELAf8B2wFTARcB/wHeAV4BJAH/AeABZQEuAf8B4wFrATkB/wHl
+ AXUBRQH/AeYBhQFOAf8B5wGKAVUB/wHoAY8BWgH/AegBkgFfAf8B9QHWAccB/wP+Bf8B/gH8AfsB/wHz
+ Ac4BvAH/Af4C/A3/Ae8BtAGXAf8B5wGJAVQB/wHmAYUBTQH/AeQBdAFDAf8B4gFrATcB/wHgAWUBLAH/
+ Ad0BXQEjAf8B2gFSARUB/wGsATcBCgH/AxwBJwQAA1sByANWAf8DcgH/A3UB/wN2Af8DgQH/A4MB/wOE
+ Af8DhgH/A4gB/wOKAf8DiwH/A40B/wONAf8DjQH/A40B/wONAf8DjQH/A40B/wOMAf8DiwH/A4kB/wOH
+ Af8DhQH/A4QB/wODAf8DgQH/A3UB/wN1Af8DcgH/A1gB/wNhAdwCWwFcAcgBAQEAAZsB/wEnASgBsAH/
+ ASoBLAGzAf8BLAEvAbQB/wEuATMBtgH/ATEBNgG4Af8BMwE5AbkB/wE1AT0BuwH/ATcBQAG8Af8BOgFE
+ Ab0B/wE7AUYBvgH/AT4BSgHAAf8BPwFMAcAB/wE/AUwBwAH/AT8BTAHAAf8BPwFMAcAB/wE/AUwBwAH/
+ AT8BSwHAAf8BPQFJAb8B/wE7AUYBvgH/ATkBQwG9Af8BNwE/AbsB/wE0AT0BugH/ATIBOQG5Af8BMAE2
+ AbcB/wEuATIBtQH/ASsBLgGzAf8BKgErAbMB/wInAbAB/wEDAQIBnQH/AlwBYQHcBAADFwEgA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgAMPARQIAAMpAT8BugE9
+ AQsB/wHbAVQBGAH/Ad8BaQEzAf8B4gGCAUsB/wHkAY0BWgH/AeYBkgFhAf8B5wGUAWUB/wHoAZYBZwH/
+ AegBmQFpAf8B6AGZAWoB/wHpAZsBbQH/AfcB3gHTGf8D/gH/AeoBpAGDAf8B6AGVAWcB/wHnAZQBZAH/
+ AeYBkQFgAf8B5AGNAVkB/wHiAYEBSQH/Ad8BaAEyAf8B2gFTARYB/wGsATcBCgH/AxwBJwQAA1kBwgNP
+ Af8DbgH/A3EB/wNyAf8DcgH/A3QB/wOBAf8DhAH/A4YB/wOIAf8DiQH/A4sB/wOMAf8DjAH/A4wB/wOM
+ Af8DjAH/A4wB/wOLAf8DiQH/A4gB/wOGAf8DhAH/A4EB/wNzAf8DcgH/A3EB/wNwAf8DbgH/A1IB/wNd
+ AdIDWQHCAgABkgH/AiQBqwH/AScBKAGvAf8BKAErAa8B/wEoASwBrwH/ASoBMAGxAf8BMAE2AbQB/wE0
+ ATsBtgH/ATcBPwG4Af8BOgFDAboB/wE8AUUBugH/AT4BSAG8Af8BPwFKAb0B/wE/AUsBvQH/AUABSwG8
+ Af8BQAFLAb0B/wE/AUoBvQH/AT8BSgG9Af8BPgFIAbwB/wE7AUQBugH/AToBQgG5Af8BNwE/AbgB/wE0
+ AToBtgH/ATABNgG0Af8BKgEvAbAB/wEoASsBrwH/AScBKgGuAf8BJgEoAa4B/wIjAasB/wIAAZUB/wJZ
+ AV0B0gQAAxcBIANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADDwEUCAADKQE/AboBPQELAf8B3AFhASsB/wHiAYcBVAH/AeMBjgFbAf8B5QGSAWEB/wHmAZUBZQH/
+ AecBmAFqAf8B6AGaAW0B/wHoAZwBbgH/AegBnAFvAf8B6AGcAW8B/wHoAZ8BcwH/AfoB7QHnFf8B/AH0
+ AfEB/wHoAZsBbwH/AegBmQFsAf8B5wGXAWkB/wHmAZUBZAH/AeUBkgFhAf8B4wGNAVoB/wHhAYcBUwH/
+ AdwBYQEqAf8BrAE3AQoB/wMcAScEAANQAZoDSQH/A2oB/wNuAf8DbAH/A3AB/wOCAf8DhwH/A4kB/wOK
+ Af8DiwH/A4wB/wONAf8DjgH/A44B/wOOAf8DjgH/A40B/wONAf8DjAH/A4wB/wOKAf8DiQH/A4gB/wOG
+ Af8DgQH/A28B/wNrAf8DbQH/A2kB/wNKAf8DWAG8A1ABmgIAAYcB/wIgAaYB/wEkASUBqgH/ASABIwGp
+ Af8BJgEqAawB/wE1ATkBsgH/ATsBPwG1Af8BPQFCAbcB/wE/AUUBuAH/AUEBRwG5Af8BQgFKAboB/wFD
+ AUsBuwH/AUQBTAG8Af8BRAFMAbwB/wFFAU0BvAH/AUUBTQG8Af8BRAFMAbsB/wFEAUsBuwH/AUMBSgG6
+ Af8BQgFJAboB/wFAAUcBuAH/AT4BRQG3Af8BPAFBAbYB/wE6AT4BtQH/ATQBOAGxAf8BJgEpAasB/wEg
+ ASIBqAH/AiQBqQH/ASABHwGlAf8CAAGKAf8DWAG8BAADFwEgA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgAMPARQIAAMpAT8BugE9AQsB/wHdAWUBMwH/AeIBiwFb
+ Af8B5AGRAWEB/wHlAZUBZgH/AeYBmgFsAf8B5wGcAXAB/wHoAZ4BcwH/AekBoAF0Af8B6QGgAXUB/wHp
+ AaABdQH/AfABxQGwAf8D/hX/AfgB5AHaAf8B6QGgAXQB/wHoAZ4BcgH/AecBnAFvAf8B5gGaAWsB/wHl
+ AZUBZgH/AeQBkQFgAf8B4gGLAVoB/wHdAWUBMQH/AawBNwEKAf8DHAEnBAADOQFfA0kB/wNjAf8DZwH/
+ A2sB/wOFAf8DiAH/A4gB/wOJAf8DigH/A4sB/wOMAf8DjQH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/
+ A40B/wOMAf8DiwH/A4kB/wOIAf8DiAH/A4gB/wOEAf8DawH/A2cB/wNkAf8DRQH/A0kBhwM5AV8CAAGD
+ Af8BGQEYAaAB/wIdAaMB/wEiASQBpgH/ATsBPQGxAf8BQAFDAbQB/wE/AUMBtAH/AUEBRQG1Af8BQgFH
+ AbYB/wFDAUkBtwH/AUUBSwG3Af8BRgFMAbgB/wFHAU0BuQH/AUcBTgG5Af8BSAFOAbkB/wFIAU4BuQH/
+ AUcBTgG5Af8BRwFNAbgB/wFGAUsBtwH/AUUBSgG3Af8BQwFJAbYB/wFBAUcBtQH/AUABRQG0Af8BPwFC
+ AbQB/wFAAUMBtAH/AToBPAGwAf8BIgEkAaUB/wIdAaMB/wIZAaAB/wIAAXUB/wNJAYcEAAMXASADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAAw8BFAgAAykBPwG5
+ AT0BCwH/Ad4BagE4Af8B4wGSAWIB/wHkAZUBaAH/AeUBmAFuAf8B5wGcAXIB/wHnAZ8BdQH/AegBoQGA
+ Af8B6QGiAYEB/wHtAbkBoQH/AfUB2wHPAf8D/hn/AfgB4wHaAf8B6QGkAYMB/wHoAaABdgH/AecBnwF0
+ Af8B5gGcAXEB/wHlAZgBbQH/AeQBlQFnAf8B4wGRAWEB/wHdAWkBNwH/AasBNwEKAf8DHAEnBAADDgET
+ A1kB/wNVAf8DYQH/A3IB/wOMAf8DhwH/A4kB/wOLAf8DiwH/A4wB/wONAf8DjgH/A44B/wOOAf8DjwH/
+ A48B/wOPAf8DjgH/A44B/wONAf8DjQH/A4sB/wOKAf8DiQH/A4cB/wOLAf8DcgH/A2AB/wNXAf8DUAH/
+ AyMBNAMOARMCFQGLAf8CBgGTAf8CFQGcAf8CLwGnAf8BSAFJAbMB/wFBAUMBsQH/AUMBRgGyAf8BRQFI
+ AbQB/wFGAUoBtAH/AUcBTAG1Af8BSAFNAbUB/wFJAU4BtgH/AUoBTwG2Af8BSgFQAbcB/wFLAVABtwH/
+ AUsBUAG3Af8BSwFPAbcB/wFKAVABtwH/AUkBTgG2Af8BSAFNAbUB/wFIAUwBtQH/AUYBSgG0Af8BRQFI
+ AbIB/wFDAUYBsgH/AUEBQwGxAf8BSAFKAbIB/wIvAacB/wEUARUBnAH/AggBlQH/AgwBggH/AyMBNAQA
+ AxcBIANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEU
+ CAADKQE/AbkBPQELAf8B3gFuAT0B/wHjAZYBaQH/AeUBmgFuAf8B5gGeAXMB/wHnAaEBgAH/AekBqgGN
+ Af8B8QHNAbwB/wH6AfAB7AH/A/4l/wH4AeYB3gH/AekBqQGNAf8B5wGiAYIB/wHmAaEBdgH/AeYBngFz
+ Af8B5AGaAW0B/wHjAZYBaAH/Ad0BbQE8Af8BqwE2AQoB/wMcAScIAANeAd0DRwH/A1sB/wN0Af8DjwH/
+ A4oB/wOMAf8DjAH/A44B/wOOAf8DjwH/A5AB/wOQAf8DkAH/A5AB/wOQAf8DkAH/A5AB/wOQAf8DjwH/
+ A44B/wOOAf8DjAH/A4sB/wOKAf8DjwH/A3QB/wNaAf8DSAH/A2IB7wgAAlgBYwHdAgABgwH/AhABlQH/
+ ATUBNAGnAf8CUAGzAf8BSAFJAbAB/wFJAUoBsgH/AUsBTAGyAf8BTQFOAbMB/wFNAVABtAH/AU4BUQG0
+ Af8BTwFTAbUB/wFPAVMBtQH/AU8BUwG1Af8BUAFTAbUB/wFQAVMBtQH/AU8BUwG1Af8BTwFTAbUB/wFP
+ AVMBtQH/AU4BUQG0Af8BTQFPAbQB/wFNAU4BswH/AUsBTAGyAf8BSQFKAbEB/wFIAUkBsAH/AlABswH/
+ AjQBpwH/Ag8BlAH/AgABhAH/AkwBaQHvCAADFwEgA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgAMPARQIAAMpAT8BuAE+AQsB/wHeAXIBQgH/AeQBmgFvAf8B5gGf
AYAB/wHsAb0BpgH/AfYB4AHVAf8B/gL8Gf8D/gH/Af4B/QH8Af8B+gHyAe4B/wH8AfcB9A3/AfkB7gHp
- Af8B6QGuAZIB/wHnAaMBhQH/AeYBogGBAf8B5QGdAXwB/wHkAZkBdgH/Ad4BegFKAf8BqwE+ARIB/wEf
- ARwBGwEnCAADUgFwA0cB/wNZAf8DeQH/A5UB/wOMAf8DjgH/A48B/wOQAf8DkAH/A5IB/wOSAf8DkgH/
- A5IB/wOTAf8DkwH/A5IB/wOSAf8DkgH/A5EB/wOQAf8DjwH/A48B/wOOAf8DjAH/A5QB/wN5Af8DWgH/
- A0YB/wNiAYoIAAJNAVQBcAIAAXEB/wIKAY0B/wI6AaQB/wJhAbYB/wJWAa8B/wJXAbEB/wFYAVkBsgH/
- AVkBWgGzAf8BWgFbAbMB/wFcAV0BtAH/AVwBXgG0Af8BXAFeAbUB/wFcAV8BtAH/AV0BXwG1Af8BXQFf
- AbUB/wFcAV8BtAH/AVwBXgG0Af8BXAFfAbQB/wFbAV0BtAH/AVoBWwGzAf8BWQFaAbIB/wJYAbIB/wJX
- AbEB/wFXAVYBrwH/AmEBtQH/AjoBpAH/AgsBjgH/AgABbgH/AlsBZAGKCAADGAEgA04BgANbAYADaAGA
- A3IBgAN0AYADdQGAA3UBgAN1AYADdQGAA3QBgANzAYADbwGAA2wBgANoAYADZQGAA2QBgANmAYADcgGA
- A3UBgAN1AYADdQGAA3EBgANjAYADYgGAA2IBgANhAYADWwGAA0wBgAMPARQIAAEwASoBKAE/AbYBRAET
- Af8B3wF9AVAB/wHtAcIBrwH/AfsB9AHxAf8D/hH/Af4C/QH/AfwB9wH1Af8B9gHhAdkB/wHzAdMBxwH/
- Ae0BvgGrAf8B6gGvAZcB/wHoAakBjwH/AesBtQGfAf8B+wH0AfEN/wH5AewB5gH/AegBqgGNAf8B5gGl
- AYYB/wHlAaEBgwH/AeQBngF9Af8B3gF7AU8B/wGpAT0BEgH/AR8BHAEbAScMAANlAegDSAH/A3EB/wOb
- Af8DkAH/A5EB/wORAf8DkgH/A5MB/wOUAf8DlAH/A5QB/wOUAf8DlQH/A5UB/wOUAf8DlAH/A5QB/wOU
- Af8DkwH/A5IB/wORAf8DkQH/A5AB/wObAf8DcQH/A0kB/wNfAfMDBAEGDAACNQF/AegCAAFyAf8CMAGc
- Af8CawG5Af8CXQGwAf8CXQGxAf8CXwGxAf8CXwGyAf8BYAFhAbMB/wFhAWIBtAH/AWEBYgG0Af8BYQFj
- AbQB/wFiAWMBtAH/AWIBZAG1Af8BYgFkAbUB/wFiAWQBtAH/AWEBYgG0Af8BYQFiAbQB/wFhAWIBtAH/
- AWABYQGzAf8CXwGyAf8CXwGxAf8BXgFdAbEB/wJdAbAB/wJrAbgB/wIwAZwB/wIAAXQB/wIsAX0B8wME
- AQYIAAMYASADTgGAA1sBgANkAYADcQGAA3QBgAN0AYADcAGAA20BgANqAYADZgGAA2QBgANkAYADZAGA
- A2QBgANkAYADZAGAA2QBgANmAYADcgGAA3UBgAN1AYADdQGAA28BgANjAYADYwGAA2IBgANbAYADTAGA
- Aw8BFAgAATABKgEoAT8BswFEARMB/wHeAYIBVgH/AecBrAGRAf8B+QHtAegB/wP+Af8B/QH6AfkB/wH4
- AeoB5AH/AfQB2QHNAf8B7wHKAbgB/wHqAbYBngH/AekBsAGUAf8B6AGuAZIB/wHoAa4BkgH/AegBrgGS
- Af8B6AGuAZIB/wHoAa4BkgH/AegBrgGSAf8B6wG4AaAB/wH6AfIB7g3/AfYB4QHYAf8B5gGnAYsB/wHm
- AaUBiAH/AeUBowGEAf8B3gGBAVUB/wGmAT0BEQH/AR8CHAEnDAADQgFVA0IB/wNVAf8DjgH/A54B/wOT
- Af8DlQH/A5YB/wOWAf8DlwH/A5cB/wOXAf8DmAH/A5gB/wOYAf8DlwH/A5cB/wOXAf8DlgH/A5YB/wOV
- Af8DlQH/A5MB/wOdAf8DjwH/A1YB/wM/Af8DUAFoEAACQAFDAVUCAAFmAf8CDAGCAf8CWwGuAf8CcQG6
- Af8CYgGyAf8CZQGzAf8CZgGzAf8BZgFnAbMB/wJnAbQB/wJnAbQB/wFnAWgBtAH/AWgBaQG1Af8BaAFp
- AbUB/wFoAWkBtQH/AWcBaQG1Af8BZwFoAbQB/wJnAbQB/wFmAWcBswH/AWYBZwGzAf8CZgGyAf8CZQGy
- Af8CYgGxAf8CcQG5Af8CXAGvAf8CDAGEAf8CAAFgAf8CTQFQAWgMAAMYASADTQGAA1wBgANjAYADZAGA
- A2YBgANkAYADZAGAA2QBgANkAYADZAGAA2QBgANkAYADZAGAA2QBgANkAYADZAGAA2QBgANkAYADZgGA
- A3EBgAN1AYADdQGAA3UBgANtAYADYwGAA2MBgANcAYADSwGAAw8BFAgAATABKgEoAT8BsAFDARMB/wHd
- AYQBXAH/AeUBpAGKAf8B5wGsAZQB/wHpAbkBogH/AecBsAGVAf8B5wGvAZIB/wHoAa8BkwH/AegBsQGV
- Af8B6AGwAZUB/wHoAbABlQH/AegBsAGVAf8B6AGwAZUB/wHoAbABlQH/AegBsAGVAf8B6AGwAZUB/wHo
- AbABlQH/AegBsAGVAf8B6gG3AZ8B/wH6AfAB6w3/AfMB2QHNAf8B5gGmAYwB/wHlAaQBiQH/Ad0BhAFb
- Af8BowE9AREB/wEfAhwBJxAAA2cBoQNAAf8DYgH/A6gB/wOhAf8DmQH/A5oB/wOaAf8DmwH/A5sB/wOb
- Af8DmwH/A5sB/wObAf8DmwH/A5sB/wOaAf8DmgH/A5oB/wOaAf8DmQH/A6AB/wOoAf8DZAH/Az4B/wNt
- AbYYAAJYAW4BoQIAAWEB/wIgAYwB/wKCAcAB/wJ3AboB/wJrAbQB/wJuAbUB/wJuAbUB/wJuAbYB/wFv
- AW4BtgH/Am8BtgH/Am8BtgH/Am8BtgH/Am8BtgH/Am8BtgH/Am8BtgH/AW8BbgG1Af8CbgG1Af8CbgG1
- Af8CbgG1Af8CawG0Af8CdgG5Af8CggHAAf8CIQGOAf8CAAFcAf8CWQF2AbYQAAMYASADTQGAA10BgANj
- AYADZAGAA2QBgANkAYADZAGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANl
- AYADZQGAA2YBgANxAYADdQGAA3UBgAN0AYADZQGAA2MBgANdAYADSwGAAw8BFAgAATABKgEoAT8BrgFD
- ARMB/wHdAYoBZAH/AeUBqAGOAf8B5gGqAZAB/wHmAa0BkgH/AecBrgGUAf8B5wGuAZYB/wHoAa8BlwH/
- AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/AegBrwGYAf8B6AGv
- AZgB/wHoAa8BmAH/AegBrwGYAf8B6AGvAZgB/wHpAbUBoAH/AfgB7AHnCf8D/gH/AecBrwGYAf8B5QGo
- AY0B/wHcAYoBYwH/AaIBPQERAf8BHwIcAScTAAEBA10B1QNDAf8DbAH/A7IB/wOuAf8DnwH/A6EB/wOh
- Af8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A58B/wOrAf8DswH/A28B/wM9
- Af8DXwHjAwYBCBsAAQECNwFwAdUCAAFnAf8CLwGRAf8CkQHHAf8CiwHDAf8CdQG3Af8CeAG5Af8CeQG5
- Af8CeQG6Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeAG5
- Af8CdQG3Af8CiAHBAf8CkgHIAf8CMwGVAf8CAAFbAf8CNwFzAeMDBgEIEAADGAEgA00BgANcAYADZAGA
- A2QBgANkAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGA
- A2UBgANlAYADZgGAA3ABgAN0AYADcgGAA2UBgANkAYADXAGAA0sBgAMPARQIAAEwASoBKAE/AawBQgES
- Af8B2wGJAWEB/wHlAakBkgH/AeYBrAGUAf8B5gGvAZYB/wHmAbABmAH/AecBsQGZAf8B5wGwAZoB/wHn
- AbIBmgH/AecBswGbAf8B5wGzAZsB/wHnAbMBmwH/AecBswGbAf8B5wGzAZsB/wHnAbMBmwH/AecBswGb
- Af8B5wGzAZsB/wHnAbMBmwH/AecBswGbAf8B5wGyAZoB/wHoAbQBngH/AfgB6gHjAf8D/gH/AfoB8gHv
- Af8B5gGvAZgB/wHlAakBkQH/AdoBigFhAf8BoQE8AREB/wEfARwBGwEnFAADDgESA1AB0QM/Af8DZAH/
- A6kB/wPCAf8DsQH/A6cB/wOoAf8DqQH/A6kB/wOpAf8DqQH/A6kB/wOpAf8DqAH/A6cB/wOvAf8DwQH/
- A64B/wNnAf8DOQH/A10B3AMWARsgAAMOARICKAFhAdECAAFfAf8CJwGJAf8ChQG/Af8CqAHSAf8CkQHE
- Af8CgwG8Af8ChQG9Af8ChQG+Af8ChgG+Af8ChgG+Af8ChgG+Af8ChgG+Af8ChgG+Af8ChQG9Af8CgwG8
- Af8CjgHCAf8CpwHRAf8CiwHDAf8CLAGLAf8CAAFSAf8COQFuAdwDFgEbFAADEQEXA0sBgANXAYADZAGA
- A2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGA
- A2UBgANlAYADZQGAA2UBgANnAYADZgGAA2UBgANkAYADVwGAA0kBfgMGAQgIAAEjASABHwEtAZ4BOwER
- Af8B0QFuAT4B/wHmAasBlwH/AeYBsAGZAf8B5gGzAZoB/wHnAbQBmwH/AecBtQGcAf8B5wG2AZwB/wHn
- AbYBnAH/AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/AecBuQGd
- Af8B5wG5AZ0B/wHnAbkBnQH/AecBuQGdAf8B5wG1AZwB/wHnAbYBnAH/AecBtwGeAf8B6QG/AagB/wHn
- AbcBnwH/AeYBsAGZAf8B5gGrAZcB/wHRAW8BPgH/AZIBNgERAfwBDQIMARAYAAMEAQUDZQGlAzkB/wNF
- Af8DhAH/A7MB/wPKAf8DxAH/A70B/wO1Af8DswH/A7MB/wO0Af8DuwH/A8QB/wPKAf8DugH/A4wB/wNF
- Af8DNQH/A2kBsQMKAQ0oAAMEAQUCVQFtAaUCAAFTAf8CAAFsAf8CVAGgAf8ClQHFAf8CtAHXAf8CrQHT
- Af8CogHNAf8CmAHHAf8ClAHFAf8ClAHFAf8ClwHGAf8CoAHLAf8CrAHSAf8CtQHXAf8CngHKAf8CXgGm
- Af8CAAFtAf8CAAFJAf8CVwFxAbEDCgENHAADPQFkA08BgANXAYADXgGAA2ABgANgAYADYAGAA2EBgANh
- AYADYQGAA2EBgANhAYADYQGAA2EBgANhAYADYQGAA2EBgANhAYADYQGAA2EBgANhAYADYAGAA2ABgANg
- AYADXgGAA1cBgANPAYADNwFVEAABcgE+ASwBxwG/AU0BFQH/AdIBcAFBAf8B3AGRAXAB/wHfAZwBeQH/
- AeABmwF6Af8B4gGdAXoB/wHjAZ8BewH/AeMBnwF8Af8B4wGgAXwB/wHjAZ8BfAH/AeMBnwF8Af8B4wGf
- AXwB/wHjAZ8BfAH/AeMBnwF8Af8B4wGfAXwB/wHjAZ8BfAH/AeMBnwF8Af8B4wGgAXwB/wHjAZ8BewH/
- AeMBnwF7Af8B4gGcAXoB/wHgAZsBegH/Ad4BnAF5Af8B3AGRAXAB/wHSAXABQQH/AbsBSgEUAf8BZwFD
- ATYBqSQAA00BdANPAeYDOAH/A0oB/wN9Af8DlwH/A6wB/wO/Af8DxgH/A8cB/wPBAf8DsQH/A5oB/wOD
- Af8DSgH/AzQB/wNJAewDWAF7OAACRgFQAXQCHgFnAeYCAAFQAf8CBAFyAf8CSwGaAf8CbwGuAf8CjAG+
- Af8CpgHNAf8CsAHTAf8CsQHUAf8CqQHPAf8CkgHCAf8CcgGwAf8CUgGeAf8CBAFyAf8CAAFIAf8CGAFg
- AewCUwFaAXskAAMMAQ8DPQFkA0sBgANNAYADTQGAA00BgANOAYADTgGAA04BgANOAYADTgGAA04BgANO
- AYADTgGAA04BgANOAYADTgGAA04BgANOAYADTgGAA04BgANOAYADTQGAA00BgANNAYADSgF/AzkBWwMG
- AQgQAAEXARYBFQEdAXIBPgEsAccBngE7AREB/wGsAUIBEgH/Aa0BQwETAf8BsAFDARMB/wGzAUQBEwH/
- AbYBRAETAf8BuAFFARMB/wG5AUUBEwH/AbkBRQETAf8BuQFFARMB/wG5AUUBEwH/AbkBRQETAf8BuQFF
- ARMB/wG5AUUBEwH/AbkBRQETAf8BuQFFARMB/wG5AUUBEwH/AbgBRQETAf8BtgFEARMB/wGzAUQBEwH/
- AbABQwETAf8BrQFDARMB/wGsAUIBEgH/AZoBOgERAf4BbAFBATIBtQENAgwBECgAAwoBDQNbAZADSQHe
- AzwB/wM5Af8DQAH/A04B/wNiAf8DZAH/A1AB/wNAAf8DOgH/AzoB/wNPAeADYQGWAw4BEkAAAwoBDQJQ
- AWABkAIcAV8B3gIAAVkB/wIAAVMB/wIAAWIB/wIJAXUB/wIlAYYB/wIoAYgB/wIMAXYB/wIAAWEB/wIA
- AVQB/wIAAVQB/wIjAWMB4AJXAWUBlgMOARIwAAMSARcDGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEg
- AxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEg
- Aw4BEyAAASMBIAEfAS0BMAErASgBPwEwASsBKQE/ATABKwEpAT8BMQErASkBPwExASsBKQE/ATEBKwEp
- AT8BMQErASkBPwExASsBKQE/ATEBKwEpAT8BMQErASkBPwExASsBKQE/ATEBKwEpAT8BMQErASkBPwEx
- ASsBKQE/ATEBKwEpAT8BMQErASkBPwExASsBKQE/ATEBKwEpAT8BMQErASkBPwEwASsBKQE/ATABKwEp
- AT8BMAErASgBPwEeARwBGwEmPAADMAFAA1UBigNYAb4DTAHsA0EB8gNAAfIDTQHuA1wBwANVAY0DNQFE
- WAACLgEwAUACSgFaAYoCPAFlAb4CGgFkAewCCgFZAfICCgFZAfICGgFkAe4CQQFpAcACSgFZAY0CMwE1
- AUT/AC0AAUIBTQE+BwABPgMAASgDAAGAAwABQAMAAQEBAAEBBgABBBYAA///AP8AAwAB/wHgAQMC/wHg
- AQMB/wHAAgABAwHAAgABAwH/AgAC/wIAAf8BgAIAAQMBgAIAAQMB/gIAAX8B/gIAAX8BgAIAAQEBgAIA
- AQEB+AIAAR8B+AIAAR8BgAIAAQEBgAIAAQEB8AIAAQ8B8AIAAQ8BgAIAAQEBgAIAAQEB8AIAAQcB8AIA
- AQcBgAIAAQEBgAIAAQEB4AIAAQcB4AIAAQcBgAIAAQEBgAIAAQEBwAIAAQMBwAIAAQMBgAIAAQEBgAIA
- AQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIA
- AQEBgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQgAAYACAAEBAYACAAEBCAABgAIAAQEBgAIAAQEIAAGA
- AgABAQGAAgABAQgAAYACAAEBAYACAAEBCAABgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQgAAYACAAEB
- AYACAAEBCAABgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGA
- AgABAQGAAgABAQGAAgABAQGAAgABAQHAAgABAQHAAgABAQGAAgABAQGAAgABAQHAAgABAwHAAgABAwGA
- AgABAQGAAgABAQHgAgABBwHgAgABBwGAAgABAQGAAgABAQHgAgABBwHgAgABBwGAAgABAQGAAgABAQHw
- AgABDwHwAgABDwGAAgABAQGAAgABAQH4AgABHwH4AgABHwHAAgABAwHAAgABAwH+AgABfwH+AgABfwHA
- AgABAwHAAgABAwH/AgAC/wIAAf8B8AIAAQ8B8AIAAQ8B/wHgAQcC/wHgAQcJ/ws=
+ Af8B6QGuAZIB/wHnAaMBhQH/AeYBogGBAf8B5QGdAXQB/wHkAZkBbgH/Ad4BcgFCAf8BqwE2AQoB/wMc
+ AScIAANAAXADPwH/A1EB/wNxAf8DlQH/A4wB/wOOAf8DjwH/A5AB/wOQAf8DkgH/A5IB/wOSAf8DkgH/
+ A5MB/wOTAf8DkgH/A5IB/wOSAf8DkQH/A5AB/wOPAf8DjwH/A44B/wOMAf8DlAH/A3EB/wNSAf8DPgH/
+ A0oBiggAA0ABcAIAAWkB/wICAY0B/wIyAaQB/wJZAbYB/wJOAa8B/wJPAbEB/wFQAVEBsgH/AVEBUgGz
+ Af8BUgFTAbMB/wFUAVUBtAH/AVQBVgG0Af8BVAFWAbUB/wFUAVcBtAH/AVUBVwG1Af8BVQFXAbUB/wFU
+ AVcBtAH/AVQBVgG0Af8BVAFXAbQB/wFTAVUBtAH/AVIBUwGzAf8BUQFSAbIB/wJQAbIB/wJPAbEB/wFP
+ AU4BrwH/AlkBtQH/AjIBpAH/AgMBjgH/AgABZgH/A0oBiggAAxcBIANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEUCAADKQE/AbYBPAELAf8B3wF1AUgB/wHt
+ AcIBrwH/AfsB9AHxAf8D/hH/Af4C/QH/AfwB9wH1Af8B9gHhAdkB/wHzAdMBxwH/Ae0BvgGrAf8B6gGv
+ AZcB/wHoAakBjwH/AesBtQGfAf8B+wH0AfEN/wH5AewB5gH/AegBqgGNAf8B5gGlAYYB/wHlAaEBgwH/
+ AeQBngF1Af8B3gFzAUcB/wGpATUBCgH/AxwBJwwAA18B6ANAAf8DaQH/A5sB/wOQAf8DkQH/A5EB/wOS
+ Af8DkwH/A5QB/wOUAf8DlAH/A5QB/wOVAf8DlQH/A5QB/wOUAf8DlAH/A5QB/wOTAf8DkgH/A5EB/wOR
+ Af8DkAH/A5sB/wNpAf8DQQH/A18B8wMEAQYMAAJVAWcB6AIAAWoB/wIoAZwB/wJjAbkB/wJVAbAB/wJV
+ AbEB/wJXAbEB/wJXAbIB/wFYAVkBswH/AVkBWgG0Af8BWQFaAbQB/wFZAVsBtAH/AVoBWwG0Af8BWgFc
+ AbUB/wFaAVwBtQH/AVoBXAG0Af8BWQFaAbQB/wFZAVoBtAH/AVkBWgG0Af8BWAFZAbMB/wJXAbIB/wJX
+ AbEB/wFWAVUBsQH/AlUBsAH/AmMBuAH/AigBnAH/AgABbAH/AkQBbAHzAwQBBggAAxcBIANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGA
+ A0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADDwEUCAADKQE/AbMBPAEL
+ Af8B3gGCAU4B/wHnAawBkQH/AfkB7QHoAf8D/gH/Af0B+gH5Af8B+AHqAeQB/wH0AdkBzQH/Ae8BygG4
+ Af8B6gG2AZ4B/wHpAbABlAH/AegBrgGSAf8B6AGuAZIB/wHoAa4BkgH/AegBrgGSAf8B6AGuAZIB/wHo
+ Aa4BkgH/AesBuAGgAf8B+gHyAe4N/wH2AeEB2AH/AeYBpwGLAf8B5gGlAYgB/wHlAaMBhAH/Ad4BgQFN
+ Af8BpgE1AQkB/wMcAScMAAM1AVUDOgH/A00B/wOOAf8DngH/A5MB/wOVAf8DlgH/A5YB/wOXAf8DlwH/
+ A5cB/wOYAf8DmAH/A5gB/wOXAf8DlwH/A5cB/wOWAf8DlgH/A5UB/wOVAf8DkwH/A50B/wOPAf8DTgH/
+ AzcB/wM9AWgQAAM1AVUCAAFeAf8CBAGCAf8CUwGuAf8CaQG6Af8CWgGyAf8CXQGzAf8CXgGzAf8BXgFf
+ AbMB/wJfAbQB/wJfAbQB/wFfAWABtAH/AWABYQG1Af8BYAFhAbUB/wFgAWEBtQH/AV8BYQG1Af8BXwFg
+ AbQB/wJfAbQB/wFeAV8BswH/AV4BXwGzAf8CXgGyAf8CXQGyAf8CWgGxAf8CaQG5Af8CVAGvAf8CBAGE
+ Af8CAAFYAf8DPQFoDAADFwEgA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgAMPARQIAAMpAT8BsAE7AQsB/wHdAYQBVAH/AeUBpAGKAf8B5wGsAZQB/wHpAbkBogH/
+ AecBsAGVAf8B5wGvAZIB/wHoAa8BkwH/AegBsQGVAf8B6AGwAZUB/wHoAbABlQH/AegBsAGVAf8B6AGw
+ AZUB/wHoAbABlQH/AegBsAGVAf8B6AGwAZUB/wHoAbABlQH/AegBsAGVAf8B6gG3AZ8B/wH6AfAB6w3/
+ AfMB2QHNAf8B5gGmAYwB/wHlAaQBiQH/Ad0BhAFTAf8BowE1AQkB/wMcAScQAANSAaEDOAH/A1oB/wOo
+ Af8DoQH/A5kB/wOaAf8DmgH/A5sB/wObAf8DmwH/A5sB/wObAf8DmwH/A5sB/wObAf8DmgH/A5oB/wOa
+ Af8DmgH/A5kB/wOgAf8DqAH/A1wB/wM2Af8DVgG2GAADUgGhAgABWQH/AhgBjAH/AoIBwAH/Am8BugH/
+ AmMBtAH/AmYBtQH/AmYBtQH/AmYBtgH/AWcBZgG2Af8CZwG2Af8CZwG2Af8CZwG2Af8CZwG2Af8CZwG2
+ Af8CZwG2Af8BZwFmAbUB/wJmAbUB/wJmAbUB/wJmAbUB/wJjAbQB/wJuAbkB/wKCAcAB/wIZAY4B/wIA
+ AVQB/wNWAbYQAAMXASADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAAw8BFAgAAykBPwGuATsBCwH/Ad0BigFcAf8B5QGoAY4B/wHmAaoBkAH/AeYBrQGSAf8B5wGu
+ AZQB/wHnAa4BlgH/AegBrwGXAf8B6AGvAZgB/wHoAa8BmAH/AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/
+ AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/AekBtQGgAf8B+AHs
+ AecJ/wP+Af8B5wGvAZgB/wHlAagBjQH/AdwBigFbAf8BogE1AQkB/wMcAScTAAEBA14B1QM7Af8DZAH/
+ A7IB/wOuAf8DnwH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/
+ A58B/wOrAf8DswH/A2cB/wM1Af8DXwHjAwYBCBsAAQECWgFeAdUCAAFfAf8CJwGRAf8CkQHHAf8CiwHD
+ Af8CbQG3Af8CcAG5Af8CcQG5Af8CcQG6Af8CcQG5Af8CcQG5Af8CcQG5Af8CcQG5Af8CcQG5Af8CcQG5
+ Af8CcQG5Af8CcQG5Af8CcAG5Af8CbQG3Af8CiAHBAf8CkgHIAf8CKwGVAf8CAAFTAf8CWAFfAeMDBgEI
+ EAADFwEgA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgAMP
+ ARQIAAMpAT8BrAE6AQoB/wHbAYkBWQH/AeUBqQGSAf8B5gGsAZQB/wHmAa8BlgH/AeYBsAGYAf8B5wGx
+ AZkB/wHnAbABmgH/AecBsgGaAf8B5wGzAZsB/wHnAbMBmwH/AecBswGbAf8B5wGzAZsB/wHnAbMBmwH/
+ AecBswGbAf8B5wGzAZsB/wHnAbMBmwH/AecBswGbAf8B5wGzAZsB/wHnAbIBmgH/AegBtAGeAf8B+AHq
+ AeMB/wP+Af8B+gHyAe8B/wHmAa8BmAH/AeUBqQGRAf8B2gGKAVkB/wGhATQBCQH/AxwBJxQAAw0BEgNY
+ AdEDNwH/A1wB/wOpAf8DwgH/A7EB/wOnAf8DqAH/A6kB/wOpAf8DqQH/A6kB/wOpAf8DqQH/A6gB/wOn
+ Af8DrwH/A8EB/wOuAf8DXwH/AzEB/wNcAdwDFAEbIAADDQESAlgBXAHRAgABVwH/Ah8BiQH/AoUBvwH/
+ AqgB0gH/ApEBxAH/AoMBvAH/AoUBvQH/AoUBvgH/AoYBvgH/AoYBvgH/AoYBvgH/AoYBvgH/AoYBvgH/
+ AoUBvQH/AoMBvAH/Ao4BwgH/AqcB0QH/AosBwwH/AiQBiwH/AgABSgH/AlgBYQHcAxQBGxQAAxEBFwNH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANGAX4DBgEICAADHwEt
+ AZ4BMwEJAf8B0QFmATYB/wHmAasBlwH/AeYBsAGZAf8B5gGzAZoB/wHnAbQBmwH/AecBtQGcAf8B5wG2
+ AZwB/wHnAbYBnAH/AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/
+ AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/AecBuQGdAf8B5wG1AZwB/wHnAbYBnAH/AecBtwGeAf8B6QG/
+ AagB/wHnAbcBnwH/AeYBsAGZAf8B5gGrAZcB/wHRAWcBNgH/AX0BLgEZAfwDDAEQGAADBAEFA1MBpQMx
+ Af8DPQH/A4QB/wOzAf8DygH/A8QB/wO9Af8DtQH/A7MB/wOzAf8DtAH/A7sB/wPEAf8DygH/A7oB/wOM
+ Af8DPQH/Ay0B/wNWAbEDCgENKAADBAEFA1MBpQIAAUsB/wIAAWQB/wJMAaAB/wKVAcUB/wK0AdcB/wKt
+ AdMB/wKiAc0B/wKYAccB/wKUAcUB/wKUAcUB/wKXAcYB/wKgAcsB/wKsAdIB/wK1AdcB/wKeAcoB/wJW
+ AaYB/wIAAWUB/wIAAUEB/wNWAbEDCgENHAADOwFkA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADNQFVEAADWQHHAb8BRQENAf8B0gFoATkB/wHcAZEBaAH/Ad8BnAFxAf8B4AGb
+ AXIB/wHiAZ0BcgH/AeMBnwFzAf8B4wGfAXQB/wHjAaABdAH/AeMBnwF0Af8B4wGfAXQB/wHjAZ8BdAH/
+ AeMBnwF0Af8B4wGfAXQB/wHjAZ8BdAH/AeMBnwF0Af8B4wGfAXQB/wHjAaABdAH/AeMBnwFzAf8B4wGf
+ AXMB/wHiAZwBcgH/AeABmwFyAf8B3gGcAXEB/wHcAZEBaAH/AdIBaAE5Af8BuwFCAQwB/wNSAakkAANC
+ AXQDWAHmAzAB/wNCAf8DdQH/A5cB/wOsAf8DvwH/A8YB/wPHAf8DwQH/A7EB/wOaAf8DgwH/A0IB/wMs
+ Af8DWQHsA0QBezgAA0IBdAJSAWEB5gIAAUgB/wIAAWoB/wJDAZoB/wJnAa4B/wKMAb4B/wKmAc0B/wKw
+ AdMB/wKxAdQB/wKpAc8B/wKSAcIB/wJqAbAB/wJKAZ4B/wIAAWoB/wIAAUAB/wJKAWAB7ANEAXskAAML
+ AQ8DOwFkA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH
+ AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRgF/AzcBWwMGAQgQAAMVAR0DWQHH
+ AZ4BMwEJAf8BrAE6AQoB/wGtATsBCwH/AbABOwELAf8BswE8AQsB/wG2ATwBCwH/AbgBPQELAf8BuQE9
+ AQsB/wG5AT0BCwH/AbkBPQELAf8BuQE9AQsB/wG5AT0BCwH/AbkBPQELAf8BuQE9AQsB/wG5AT0BCwH/
+ AbkBPQELAf8BuQE9AQsB/wG4AT0BCwH/AbYBPAELAf8BswE8AQsB/wGwATsBCwH/Aa0BOwELAf8BrAE6
+ AQoB/wGKAToBEQH+A1UBtQMMARAoAAMKAQ0DTAGQA1oB3gM0Af8DMQH/AzgB/wNGAf8DWgH/A1wB/wNI
+ Af8DOAH/AzIB/wMyAf8DXgHgA04BlgMNARJAAAMKAQ0DTAGQAlUBXwHeAgABUQH/AgABSwH/AgABWgH/
+ AgEBbQH/Ah0BhgH/AiABiAH/AgQBbgH/AgABWQH/AgABTAH/AgABTAH/AlkBXwHgA04BlgMNARIwAAMR
+ BBcBIAMXASADFwEgAxcBIAMXASADFwEgAxcBIAMXASADFwEgAxcBIAMXASADFwEgAxcBIAMXASADFwEg
+ AxcBIAMXASADFwEgAxcBIAMXASADFwEgAxcBIAMOARMgAAMfAS0DKQE/AykBPwMpAT8DKQE/AykBPwMp
+ AT8DKQE/AykBPwMpAT8DKQE/AykBPwMpAT8DKQE/AykBPwMpAT8DKQE/AykBPwMpAT8DKQE/AykBPwMp
+ AT8DKQE/AxsBJjwAAyoBQANKAYoDWQG+A1kB7ANNAfIDTQHyA1QB7gNaAcADSgGNAywBRFgAAyoBQANK
+ AYoCVgFZAb4CSgFkAewCOQFZAfICOQFZAfICRgFhAe4CWAFaAcADSgGNAywBRP8ALQABQgFNAT4HAAE+
+ AwABKAMAAYADAAFAAwABAQEAAQEGAAEEFgAD//8A/wADAAH/AeABAwL/AeABAwH/AcACAAEDAcACAAED
+ Af8CAAL/AgAB/wGAAgABAwGAAgABAwH+AgABfwH+AgABfwGAAgABAQGAAgABAQH4AgABHwH4AgABHwGA
+ AgABAQGAAgABAQHwAgABDwHwAgABDwGAAgABAQGAAgABAQHwAgABBwHwAgABBwGAAgABAQGAAgABAQHg
+ AgABBwHgAgABBwGAAgABAQGAAgABAQHAAgABAwHAAgABAwGAAgABAQGAAgABAQGAAgABAQGAAgABAQGA
+ AgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQgA
+ AYACAAEBAYACAAEBCAABgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQgAAYACAAEBAYACAAEBCAABgAIA
+ AQEBgAIAAQEIAAGAAgABAQGAAgABAQgAAYACAAEBAYACAAEBCAABgAIAAQEBgAIAAQEIAAGAAgABAQGA
+ AgABAQgAAYACAAEBAYACAAEBAYACAAEBAYACAAEBAYACAAEBAYACAAEBAYACAAEBAYACAAEBAYACAAEB
+ AYACAAEBAcACAAEBAcACAAEBAYACAAEBAYACAAEBAcACAAEDAcACAAEDAYACAAEBAYACAAEBAeACAAEH
+ AeACAAEHAYACAAEBAYACAAEBAeACAAEHAeACAAEHAYACAAEBAYACAAEBAfACAAEPAfACAAEPAYACAAEB
+ AYACAAEBAfgCAAEfAfgCAAEfAcACAAEDAcACAAEDAf4CAAF/Af4CAAF/AcACAAEDAcACAAEDAf8CAAL/
+ AgAB/wHwAgABDwHwAgABDwH/AeABBwL/AeABBwn/Cw==
+
+ 154, 17
+
+
+ 319, 17
+
\ No newline at end of file
diff --git a/FSFlightLogger/Program.cs b/FSFlightLogger/Program.cs
index 4b76b3c..a5fb59d 100644
--- a/FSFlightLogger/Program.cs
+++ b/FSFlightLogger/Program.cs
@@ -3,7 +3,6 @@ using System.Windows.Forms;
using SimConnect;
using SimConnect.Concrete;
using SimpleInjector;
-using SimpleInjector.Diagnostics;
namespace FSFlightLogger
{
diff --git a/FSFlightLogger/Resources/i18n.Designer.cs b/FSFlightLogger/Resources/i18n.Designer.cs
new file mode 100644
index 0000000..eef71b7
--- /dev/null
+++ b/FSFlightLogger/Resources/i18n.Designer.cs
@@ -0,0 +1,126 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace FSFlightLogger.Resources {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class i18n {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal i18n() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FSFlightLogger.Resources.i18n", typeof(i18n).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Flight logs.
+ ///
+ internal static string DefaultFolderName {
+ get {
+ return ResourceManager.GetString("DefaultFolderName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Connected.
+ ///
+ internal static string FlightSimulatorConnected {
+ get {
+ return ResourceManager.GetString("FlightSimulatorConnected", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Connecting....
+ ///
+ internal static string FlightSimulatorConnecting {
+ get {
+ return ResourceManager.GetString("FlightSimulatorConnecting", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Failed, will retry in {0} seconds....
+ ///
+ internal static string FlightSimulatorFailed {
+ get {
+ return ResourceManager.GetString("FlightSimulatorFailed", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Closed, will retry in {0} seconds....
+ ///
+ internal static string FlightSimulatorQuit {
+ get {
+ return ResourceManager.GetString("FlightSimulatorQuit", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to &Start recording.
+ ///
+ internal static string RecordButtonStart {
+ get {
+ return ResourceManager.GetString("RecordButtonStart", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to &Stop recording.
+ ///
+ internal static string RecordButtonStop {
+ get {
+ return ResourceManager.GetString("RecordButtonStop", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/FSFlightLogger/Resources/i18n.resx b/FSFlightLogger/Resources/i18n.resx
new file mode 100644
index 0000000..5d38302
--- /dev/null
+++ b/FSFlightLogger/Resources/i18n.resx
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Flight logs
+
+
+ Connected
+
+
+ Connecting...
+
+
+ Failed, will retry in {0} seconds...
+
+
+ Closed, will retry in {0} seconds...
+
+
+ &Start recording
+
+
+ &Stop recording
+
+
\ No newline at end of file
diff --git a/FSFlightLogger/Settings.cs b/FSFlightLogger/Settings.cs
new file mode 100644
index 0000000..ad3203c
--- /dev/null
+++ b/FSFlightLogger/Settings.cs
@@ -0,0 +1,20 @@
+namespace FSFlightLogger
+{
+ public class Settings
+ {
+ public int? MainFormLeft { get; set; }
+ public int? MainFormTop { get; set; }
+
+ public bool CSVEnabled { get; set; }
+ public string CSVPath { get; set; }
+ public bool KMLEnabled { get; set; }
+ public string KMLPath { get; set; }
+ public bool KMLLiveEnabled { get; set; }
+ public int KMLLivePort { get; set; }
+
+ public bool TriggerConnected { get; set; }
+ public bool TriggerWaitForMovement { get; set; }
+ public bool TriggerNewLogStationaryEnabled { get; set; }
+ public int TriggerNewLogStationarySeconds { get; set; }
+ }
+}
diff --git a/FSFlightLogger/SimConnectLogger.cs b/FSFlightLogger/SimConnectLogger.cs
new file mode 100644
index 0000000..a50f814
--- /dev/null
+++ b/FSFlightLogger/SimConnectLogger.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using FlightLoggerLib;
+using FlightLoggerLib.Concrete;
+using Nito.AsyncEx;
+using SimConnect;
+using SimConnect.Attribute;
+using SimConnect.Lib;
+
+namespace FSFlightLogger
+{
+ public class SimConnectLogger : IAsyncDisposable
+ {
+ private readonly ISimConnectClient client;
+ private SimConnectLoggerWorker worker;
+
+ public class Config
+ {
+ public string CSVOutputPath { get; set; }
+ public string KMLOutputPath { get; set; }
+ public int? KMLLivePort { get; set; }
+
+ public TimeSpan IntervalTime { get; set; }
+ public int IntervalDistance { get; set; }
+
+ public bool WaitForMovement { get; set; }
+ public TimeSpan? NewLogWhenIdleSeconds { get; set; }
+ }
+
+
+ public SimConnectLogger(ISimConnectClient client)
+ {
+ this.client = client;
+ }
+
+
+ public async ValueTask DisposeAsync()
+ {
+ // Do not dispose of client, it is managed by the caller
+ await Stop();
+ }
+
+
+ public async Task Start(Config config)
+ {
+ await Stop();
+ worker = new SimConnectLoggerWorker(client, config);
+ }
+
+
+ public async Task Stop()
+ {
+ if (worker != null)
+ {
+ await worker.DisposeAsync();
+ worker = null;
+ }
+ }
+
+
+
+ private class SimConnectLoggerWorker : IAsyncDisposable
+ {
+ private readonly Config config;
+ private readonly List loggers = new List();
+ private IAsyncDisposable definition;
+ private IAsyncDisposable simEvent;
+ private IAsyncDisposable pauseEvent;
+
+ private DateTime lastTime;
+ private PositionData lastData = PositionData.Empty();
+ private bool waitingForMovement;
+ private DateTime lastStopped;
+
+ private readonly AsyncLock simStateLock = new AsyncLock();
+ private volatile bool paused = true;
+ private volatile bool running = false;
+
+
+ public SimConnectLoggerWorker(ISimConnectClient client, Config config)
+ {
+ this.config = config;
+
+ if (!string.IsNullOrEmpty(config.CSVOutputPath))
+ {
+ Directory.CreateDirectory(config.CSVOutputPath);
+ loggers.Add(new CSVFlightLogger(config.CSVOutputPath));
+ }
+
+ if (!string.IsNullOrEmpty(config.KMLOutputPath))
+ {
+ Directory.CreateDirectory(config.KMLOutputPath);
+ loggers.Add(new KMLFlightLogger(config.KMLOutputPath, TimeSpan.FromSeconds(5)));
+ }
+
+ if (config.KMLLivePort.HasValue)
+ loggers.Add(new KMLLiveFlightLogger(config.KMLLivePort.Value));
+
+
+ waitingForMovement = config.WaitForMovement;
+ lastStopped = DateTime.Now;
+
+
+ pauseEvent = client.SubscribeToSystemEvent(SimConnectSystemEvent.Pause, HandlePauseEvent);
+ simEvent = client.SubscribeToSystemEvent(SimConnectSystemEvent.Sim, HandleSimEvent);
+ definition = client.AddDefinition(HandlePositionData);
+ }
+
+
+ public async ValueTask DisposeAsync()
+ {
+ if (definition != null)
+ {
+ await definition.DisposeAsync();
+ definition = null;
+ }
+
+ if (pauseEvent != null)
+ {
+ await pauseEvent.DisposeAsync();
+ pauseEvent = null;
+ }
+
+ if (simEvent != null)
+ {
+ await simEvent.DisposeAsync();
+ simEvent = null;
+ }
+
+ foreach (var logger in loggers)
+ await logger.DisposeAsync();
+ }
+
+
+ private async Task HandlePauseEvent(SimConnectSystemEventArgs args)
+ {
+ using (await simStateLock.LockAsync())
+ {
+ paused = ((SimConnectPauseSystemEventArgs)args).Paused;
+ }
+ }
+
+
+ private async Task HandleSimEvent(SimConnectSystemEventArgs args)
+ {
+ using (await simStateLock.LockAsync())
+ {
+ running = ((SimConnectSimSystemEventArgs)args).SimRunning;
+ }
+ }
+
+
+ private async Task HandlePositionData(PositionData data)
+ {
+ if (paused || !running)
+ return;
+
+
+ var moving = data.Airspeed > 0;
+ if (!moving && waitingForMovement)
+ return;
+
+ waitingForMovement = false;
+
+
+ // TODO take vertical position into account when going straight up or down (not a common use case, but still)
+ var distanceMeters = LatLon.DistanceBetweenInMeters(lastData.Latitude, lastData.Longitude, data.Latitude, data.Longitude);
+ if (distanceMeters < config.IntervalDistance)
+ {
+ if (data.Airspeed < 0.1)
+ data.Airspeed = 0;
+
+ // Make an exception if we were last moving and have now stopped, so the 0 velocity record is logged as well
+ if (data.Airspeed > 0 || lastData.Airspeed == 0)
+ return;
+ }
+
+
+ var now = DateTime.Now;
+
+ if (config.NewLogWhenIdleSeconds.HasValue)
+ {
+ if (moving)
+ {
+ if (now - lastStopped >= config.NewLogWhenIdleSeconds)
+ {
+ await Task.WhenAll(loggers.Select(logger =>
+ logger.NewLog()));
+ }
+ }
+
+ lastStopped = now;
+ }
+
+
+ var time = now - lastTime;
+ if (time < config.IntervalTime)
+ return;
+
+ lastTime = now;
+ lastData = data;
+
+ // ReSharper disable once AccessToDisposedClosure - covered by disposing the client first
+ await Task.WhenAll(loggers.Select(logger =>
+ logger.LogPosition(now, new FlightPosition
+ {
+ Latitude = data.Latitude,
+ Longitude = data.Longitude,
+ Altitude = data.Altitude,
+ Airspeed = data.Airspeed
+ })));
+ }
+ }
+
+ // ReSharper disable once ClassNeverInstantiated.Local - it is, by the SimConnect client
+ public class PositionData
+ {
+ [SimConnectVariable("PLANE LATITUDE", "degrees")]
+ public float Latitude;
+
+ [SimConnectVariable("PLANE LONGITUDE", "degrees")]
+ public float Longitude;
+
+ [SimConnectVariable("PLANE ALTITUDE", "feet")]
+ public float Altitude;
+
+ [SimConnectVariable("AIRSPEED INDICATED", "knots")]
+ public float Airspeed;
+
+
+ public static PositionData Empty()
+ {
+ return new PositionData
+ {
+ Latitude = 0,
+ Longitude = 0,
+ Altitude = 0,
+ Airspeed = 0
+ };
+ }
+ }
+ }
+}
diff --git a/FSFlightLogger/Resources/Images/FSXConnected.png b/FSFlightLogger/_Resources/Images/FSXConnected.png
similarity index 100%
rename from FSFlightLogger/Resources/Images/FSXConnected.png
rename to FSFlightLogger/_Resources/Images/FSXConnected.png
diff --git a/FSFlightLogger/Resources/Images/FSXDisconnected.png b/FSFlightLogger/_Resources/Images/FSXDisconnected.png
similarity index 100%
rename from FSFlightLogger/Resources/Images/FSXDisconnected.png
rename to FSFlightLogger/_Resources/Images/FSXDisconnected.png
diff --git a/FSFlightLogger/Resources/Images/Idle.png b/FSFlightLogger/_Resources/Images/Idle.png
similarity index 100%
rename from FSFlightLogger/Resources/Images/Idle.png
rename to FSFlightLogger/_Resources/Images/Idle.png
diff --git a/FSFlightLogger/Resources/Images/P3DConnected.png b/FSFlightLogger/_Resources/Images/P3DConnected.png
similarity index 100%
rename from FSFlightLogger/Resources/Images/P3DConnected.png
rename to FSFlightLogger/_Resources/Images/P3DConnected.png
diff --git a/FSFlightLogger/Resources/Images/P3DDisconnected.png b/FSFlightLogger/_Resources/Images/P3DDisconnected.png
similarity index 100%
rename from FSFlightLogger/Resources/Images/P3DDisconnected.png
rename to FSFlightLogger/_Resources/Images/P3DDisconnected.png
diff --git a/FSFlightLogger/Resources/Images/Recording.png b/FSFlightLogger/_Resources/Images/Recording.png
similarity index 100%
rename from FSFlightLogger/Resources/Images/Recording.png
rename to FSFlightLogger/_Resources/Images/Recording.png
diff --git a/FSFlightLogger/_Resources/en-US.Designer.cs b/FSFlightLogger/_Resources/en-US.Designer.cs
new file mode 100644
index 0000000..d267cca
--- /dev/null
+++ b/FSFlightLogger/_Resources/en-US.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace FSFlightLogger {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class en_US {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal en_US() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FSFlightLogger.en-US", typeof(en_US).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/FSFlightLogger/_Resources/en-US.resx b/FSFlightLogger/_Resources/en-US.resx
new file mode 100644
index 0000000..4fdb1b6
--- /dev/null
+++ b/FSFlightLogger/_Resources/en-US.resx
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/FSFlightLogger/packages.config b/FSFlightLogger/packages.config
index bcba459..62fd1c9 100644
--- a/FSFlightLogger/packages.config
+++ b/FSFlightLogger/packages.config
@@ -1,7 +1,17 @@
+
+
+
+
+
+
+
+
+
-
-
+
+
+
\ No newline at end of file
diff --git a/FSFlightLoggerCmd/App.config b/FSFlightLoggerCmd/App.config
index 56efbc7..b1f4bcd 100644
--- a/FSFlightLoggerCmd/App.config
+++ b/FSFlightLoggerCmd/App.config
@@ -1,6 +1,18 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj b/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj
index 1288739..91728bb 100644
--- a/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj
+++ b/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj
@@ -55,8 +55,17 @@
..\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+
diff --git a/FSFlightLoggerCmd/Program.cs b/FSFlightLoggerCmd/Program.cs
index 6c7f9cb..0e7e04b 100644
--- a/FSFlightLoggerCmd/Program.cs
+++ b/FSFlightLoggerCmd/Program.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommandLine;
@@ -15,6 +16,7 @@ using SimConnect.Lib;
namespace FSFlightLoggerCmd
{
// TODO verb for converting CSV to KML
+ // TODO with the introduction of the GUI and SimConnectLogger, either port to SimConnectLogger for a single code base or remove this project
public class PositionData
@@ -115,7 +117,7 @@ namespace FSFlightLoggerCmd
};
- client.AddDefinition(data =>
+ client.AddDefinition(async data =>
{
// TODO take vertical position into account when going straight up or down (not a common use case, but still)
var distanceMeters = LatLon.DistanceBetweenInMeters(lastData.Latitude, lastData.Longitude, data.Latitude, data.Longitude);
@@ -140,14 +142,14 @@ namespace FSFlightLoggerCmd
lastData = data;
// ReSharper disable once AccessToDisposedClosure - covered by disposing the client first
- loggers.ForEach(logger =>
+ await Task.WhenAll(loggers.Select(logger =>
logger.LogPosition(now, new FlightPosition
{
Latitude = data.Latitude,
Longitude = data.Longitude,
Altitude = data.Altitude,
Airspeed = data.Airspeed
- }));
+ })));
});
@@ -163,8 +165,8 @@ namespace FSFlightLoggerCmd
stopEvent.Wait(Timeout.Infinite);
Console.WriteLine("Closing...");
- client.Dispose();
- loggers.ForEach(logger => logger.Dispose());
+ client.DisposeAsync();
+ loggers.ForEach(logger => logger.DisposeAsync());
if (!Debugger.IsAttached)
return;
diff --git a/FSFlightLoggerCmd/packages.config b/FSFlightLoggerCmd/packages.config
index 3eb2507..b48f72a 100644
--- a/FSFlightLoggerCmd/packages.config
+++ b/FSFlightLoggerCmd/packages.config
@@ -1,4 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/FlightLoggerLib/Concrete/CSVFlightLogger.cs b/FlightLoggerLib/Concrete/CSVFlightLogger.cs
index f564c3c..8ceb147 100644
--- a/FlightLoggerLib/Concrete/CSVFlightLogger.cs
+++ b/FlightLoggerLib/Concrete/CSVFlightLogger.cs
@@ -11,25 +11,30 @@ namespace FlightLoggerLib.Concrete
{
public class CSVFlightLogger : IFlightLogger
{
- private readonly CsvWriter output;
+ private readonly string path;
+ private CsvWriter output;
public CSVFlightLogger(string path)
{
- var filename = Path.Combine(path, DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss") + ".csv");
- var header = !File.Exists(filename);
-
- output = new CsvWriter(new StreamWriter(filename, true), new CsvConfiguration(CultureInfo.CurrentCulture)
- {
- SanitizeForInjection = false,
- HasHeaderRecord = header
- });
+ this.path = path;
}
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
- output?.Dispose();
+ if (output != null)
+ await output.DisposeAsync();
+ }
+
+
+ public async Task NewLog()
+ {
+ if (output != null)
+ {
+ await output.DisposeAsync();
+ output = null;
+ }
}
@@ -44,6 +49,18 @@ namespace FlightLoggerLib.Concrete
Airspeed = position.Airspeed
};
+ if (output == null)
+ {
+ var filename = Path.Combine(path, eventTime.ToString("yyyy-MM-dd HH.mm.ss") + ".csv");
+ var header = !File.Exists(filename);
+
+ output = new CsvWriter(new StreamWriter(filename, true), new CsvConfiguration(CultureInfo.CurrentCulture)
+ {
+ SanitizeForInjection = false,
+ HasHeaderRecord = header
+ });
+ }
+
await output.WriteRecordsAsync(Enumerable.Repeat(record, 1));
await output.FlushAsync();
}
diff --git a/FlightLoggerLib/Concrete/KMLFlightLogger.cs b/FlightLoggerLib/Concrete/KMLFlightLogger.cs
index 483bdc1..59bf3a1 100644
--- a/FlightLoggerLib/Concrete/KMLFlightLogger.cs
+++ b/FlightLoggerLib/Concrete/KMLFlightLogger.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Linq.Expressions;
using System.Threading.Tasks;
using SharpKml.Base;
using SharpKml.Dom;
@@ -9,11 +8,12 @@ namespace FlightLoggerLib.Concrete
{
public class KMLFlightLogger : IFlightLogger
{
- private readonly string filename;
+ private readonly string path;
+ private string filename;
private readonly System.TimeSpan flushInterval;
- private readonly Document output;
- private readonly Folder rootFolder;
- private readonly LineString positionPath;
+ private Document output;
+ private Folder rootFolder;
+ private LineString positionPath;
private DateTime lastFlush = DateTime.MinValue;
private Vector lastPosition;
@@ -23,11 +23,74 @@ namespace FlightLoggerLib.Concrete
public KMLFlightLogger(string path, System.TimeSpan flushInterval)
{
- var dateString = DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss");
- filename = Path.Combine(path, dateString + ".kml");
+ this.path = path;
this.flushInterval = flushInterval;
+ }
+ public async Task NewLog()
+ {
+ if (output == null)
+ return;
+
+ CheckEndPoint();
+ await Flush();
+ output = null;
+ }
+
+
+ public async ValueTask DisposeAsync()
+ {
+ if (output == null)
+ return;
+
+ CheckEndPoint();
+ await Flush();
+ }
+
+
+ protected void CheckEndPoint()
+ {
+ // TODO replace last "full stop" point if they match
+ if (lastPosition != null)
+ {
+ AddPoint(lastPosition, "End", "end");
+ lastPosition = null;
+ }
+ }
+
+ protected async Task AutoFlush()
+ {
+ var now = DateTime.Now;
+ var diff = now - lastFlush;
+
+ if (diff < flushInterval)
+ return;
+
+ await Flush();
+ lastFlush = now;
+ }
+
+ protected async Task Flush()
+ {
+ var serializer = new Serializer();
+ serializer.Serialize(output);
+
+ using (var writer = new StreamWriter(filename))
+ {
+ await writer.WriteAsync(serializer.Xml);
+ }
+ }
+
+
+ protected void EnsureOutput(DateTime eventTime)
+ {
+ if (output != null)
+ return;
+
+ var dateString = eventTime.ToString("yyyy-MM-dd HH.mm.ss");
+ filename = Path.Combine(path, dateString + ".kml");
+
// Create folder
rootFolder = new Folder
{
@@ -65,38 +128,6 @@ namespace FlightLoggerLib.Concrete
output.AddFeature(rootFolder);
}
- public void Dispose()
- {
- if (lastPosition != null)
- AddPoint(lastPosition, "End", "end");
-
- Flush().Wait();
- }
-
-
- protected async Task AutoFlush()
- {
- var now = DateTime.Now;
- var diff = now - lastFlush;
-
- if (diff < flushInterval)
- return;
-
- await Flush();
- lastFlush = now;
- }
-
- protected async Task Flush()
- {
- var serializer = new Serializer();
- serializer.Serialize(output);
-
- using (var writer = new StreamWriter(filename))
- {
- await writer.WriteAsync(serializer.Xml);
- }
- }
-
protected void AddFlightPathStyleMap()
{
@@ -185,6 +216,8 @@ namespace FlightLoggerLib.Concrete
public async Task LogPosition(DateTime eventTime, FlightPosition position)
{
+ EnsureOutput(eventTime);
+
var altitudeMeters = position.Altitude * MetersPerFoot;
var coordinate = new Vector(position.Latitude, position.Longitude, altitudeMeters);
diff --git a/FlightLoggerLib/Concrete/KMLLiveFlightLogger.cs b/FlightLoggerLib/Concrete/KMLLiveFlightLogger.cs
new file mode 100644
index 0000000..03923b1
--- /dev/null
+++ b/FlightLoggerLib/Concrete/KMLLiveFlightLogger.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Ionic.BZip2;
+using Nito.AsyncEx;
+using SharpKml.Base;
+using SharpKml.Dom;
+
+namespace FlightLoggerLib.Concrete
+{
+ public class KMLLiveFlightLogger : IFlightLogger
+ {
+ private readonly CancellationTokenSource listenerCancellationTokenSource = new CancellationTokenSource();
+ private readonly HttpListener listener;
+ private readonly Task listenerTask;
+ private readonly string baseUrl;
+
+ private FlightPosition currentPosition;
+ private readonly AsyncLock currentPositionLock = new AsyncLock();
+
+ private byte[] entryDocument;
+ private Document dynamicDocument;
+ private Placemark dynamicPlacemark;
+
+
+ public KMLLiveFlightLogger(int port)
+ {
+ baseUrl = $"http://127.0.0.1:{port}/";
+ PrepareEntryDocument();
+
+ listener = new HttpListener();
+ listener.Prefixes.Add(baseUrl);
+ listener.Start();
+
+ listenerTask = Task.Run(RunServer);
+ }
+
+
+ public async ValueTask DisposeAsync()
+ {
+ listener?.Stop();
+ listenerCancellationTokenSource.Cancel();
+
+ await listenerTask;
+ }
+
+
+ protected void PrepareEntryDocument()
+ {
+ var networkLink = new NetworkLink
+ {
+ Name = "Refreshes every 5 seconds",
+ Link = new Link
+ {
+ Href = new Uri($"{baseUrl}live/dynamic", UriKind.Absolute),
+ RefreshMode = RefreshMode.OnInterval,
+ RefreshInterval = 5
+ }
+ };
+
+ var output = new Document();
+ output.AddFeature(networkLink);
+
+ var serializer = new Serializer();
+ serializer.Serialize(output);
+ entryDocument = Encoding.UTF8.GetBytes(serializer.Xml);
+ }
+
+
+ protected void PrepareDynamicDocument(Vector coordinate, float altitudeInFeet)
+ {
+ var name = $"Live location ({altitudeInFeet:#} feet)";
+
+ if (dynamicPlacemark != null)
+ {
+ dynamicPlacemark.Name = name;
+ ((Point)dynamicPlacemark.Geometry).Coordinate = coordinate;
+ return;
+ }
+
+ var point = new Point { Coordinate = coordinate, AltitudeMode = AltitudeMode.Absolute };
+ dynamicPlacemark = new Placemark
+ {
+ Name = name,
+ //StyleUrl = new Uri("#" + styleMapId, UriKind.Relative),
+ Geometry = point,
+ Visibility = true
+ };
+
+ dynamicDocument = new Document();
+ dynamicDocument.AddFeature(dynamicPlacemark);
+ }
+
+
+ public Task NewLog()
+ {
+ return Task.CompletedTask;
+ }
+
+
+ public async Task LogPosition(DateTime eventTime, FlightPosition position)
+ {
+ using (await currentPositionLock.LockAsync())
+ {
+ currentPosition = position;
+ }
+ }
+
+
+ private const float MetersPerFoot = 0.3048f;
+
+
+ private async Task RunServer()
+ {
+ while (!listenerCancellationTokenSource.IsCancellationRequested)
+ {
+ var context = await listener.GetContextAsync();
+
+ switch (context.Request.Url.AbsolutePath)
+ {
+ case "/live":
+ context.Response.StatusCode = 200;
+ await context.Response.OutputStream.WriteAsync(entryDocument, 0, entryDocument.Length);
+ break;
+
+ case "/live/dynamic":
+ {
+ byte[] buffer;
+
+ using (await currentPositionLock.LockAsync())
+ {
+ var altitudeFeet = currentPosition?.Altitude ?? -10;
+ var altitudeMeters = altitudeFeet * MetersPerFoot;
+
+ PrepareDynamicDocument(new Vector
+ {
+ Latitude = currentPosition?.Latitude ?? 24.999979,
+ Longitude = currentPosition?.Longitude ?? -70.999997,
+ Altitude = altitudeMeters
+ },
+ altitudeFeet);
+
+ var serializer = new Serializer();
+ serializer.Serialize(dynamicDocument);
+ buffer = Encoding.UTF8.GetBytes(serializer.Xml);
+ }
+
+ context.Response.StatusCode = 200;
+ await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
+ break;
+ }
+
+ default:
+ context.Response.StatusCode = 404;
+ break;
+ }
+
+ context.Response.Close();
+ }
+ }
+ }
+}
diff --git a/FlightLoggerLib/FlightLoggerLib.csproj b/FlightLoggerLib/FlightLoggerLib.csproj
index 74a5da7..23b8795 100644
--- a/FlightLoggerLib/FlightLoggerLib.csproj
+++ b/FlightLoggerLib/FlightLoggerLib.csproj
@@ -61,10 +61,37 @@
..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
+ ..\packages\Nito.AsyncEx.Context.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Context.dll
+
+
+ ..\packages\Nito.AsyncEx.Coordination.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Coordination.dll
+
+
+ ..\packages\Nito.AsyncEx.Interop.WaitHandles.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Interop.WaitHandles.dll
+
+
+ ..\packages\Nito.AsyncEx.Oop.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Oop.dll
+
+
+ ..\packages\Nito.AsyncEx.Tasks.5.0.0\lib\netstandard2.0\Nito.AsyncEx.Tasks.dll
+
+
+ ..\packages\Nito.Cancellation.1.0.5\lib\netstandard2.0\Nito.Cancellation.dll
+
+
+ ..\packages\Nito.Collections.Deque.1.0.4\lib\netstandard2.0\Nito.Collections.Deque.dll
+
+
+ ..\packages\Nito.Disposables.2.0.0\lib\netstandard2.0\Nito.Disposables.dll
+
..\packages\SharpKml.Core.5.1.3\lib\net45\SharpKml.Core.dll
+
+ ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll
+
@@ -83,6 +110,7 @@
+
diff --git a/FlightLoggerLib/IFlightLogger.cs b/FlightLoggerLib/IFlightLogger.cs
index 9ad42c3..e485ed4 100644
--- a/FlightLoggerLib/IFlightLogger.cs
+++ b/FlightLoggerLib/IFlightLogger.cs
@@ -13,8 +13,9 @@ namespace FlightLoggerLib
- public interface IFlightLogger : IDisposable
+ public interface IFlightLogger : IAsyncDisposable
{
+ Task NewLog();
Task LogPosition(DateTime eventTime, FlightPosition position);
//void LogEvent
}
diff --git a/FlightLoggerLib/packages.config b/FlightLoggerLib/packages.config
index caf7147..0efa187 100644
--- a/FlightLoggerLib/packages.config
+++ b/FlightLoggerLib/packages.config
@@ -4,7 +4,17 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/SimConnect/Concrete/DefaultSimConnectClient.cs b/SimConnect/Concrete/DefaultSimConnectClient.cs
index 671c6ed..123bd2d 100644
--- a/SimConnect/Concrete/DefaultSimConnectClient.cs
+++ b/SimConnect/Concrete/DefaultSimConnectClient.cs
@@ -5,7 +5,6 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Win32.SafeHandles;
namespace SimConnect.Concrete
{
@@ -14,12 +13,16 @@ namespace SimConnect.Concrete
/// Requires the following DLL files to be present: FSX2020-SimConnect.dll, FSX-SE-SimConnect.dll, FSX-SE-SimConnect.dll, FSX-SimConnect.dll.
/// These are renamed versions of the SimConnect.dll from the various flight simulator installations.
///
- public class DefaultSimConnectClient : ISimConnectClient, IDisposable
+ public class DefaultSimConnectClient : ISimConnectClient
{
private readonly ISimConnectLibrary simConnectLibrary;
private SimConnectWorker worker;
private uint nextDefinitionID = 1;
+ private uint nextEventID = 1;
+
+ private readonly object observersLock = new object();
+ private readonly List observers = new List();
///
@@ -33,10 +36,12 @@ namespace SimConnect.Concrete
///
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
- worker?.Close().Wait();
- simConnectLibrary?.Dispose();
+ worker?.Close();
+
+ if (simConnectLibrary != null)
+ await simConnectLibrary.DisposeAsync();
}
///
@@ -46,9 +51,7 @@ namespace SimConnect.Concrete
///
public async Task TryOpen(string appName)
{
- if (worker != null)
- await worker.Close();
-
+ worker?.Close();
worker = new SimConnectWorker(simConnectLibrary, appName);
return await worker.Open();
}
@@ -57,12 +60,20 @@ namespace SimConnect.Concrete
///
public void AttachObserver(ISimConnectClientObserver observer)
{
- throw new NotImplementedException();
+ Monitor.Enter(observersLock);
+ try
+ {
+ observers.Add(observer);
+ }
+ finally
+ {
+ Monitor.Exit(observersLock);
+ }
}
///
- public IDisposable AddDefinition(SimConnectDataHandlerAction onData) where T : class
+ public IAsyncDisposable AddDefinition(SimConnectDataHandlerAction onData) where T : class
{
if (worker == null)
throw new InvalidOperationException("TryOpen must be called first");
@@ -81,7 +92,41 @@ namespace SimConnect.Concrete
}
- private class SimConnectDefinitionRegistration : IDisposable where T : class
+ ///
+ public IAsyncDisposable SubscribeToSystemEvent(SimConnectSystemEvent systemEvent, SimConnectSystemEventAction onEvent)
+ {
+ if (worker == null)
+ throw new InvalidOperationException("TryOpen must be called first");
+
+ void HandleData(SimConnectRecvEvent recvEvent)
+ {
+ SimConnectSystemEventArgs args;
+
+ switch (systemEvent)
+ {
+ case SimConnectSystemEvent.Pause:
+ args = new SimConnectPauseSystemEventArgs { Paused = recvEvent.dwData == 1 };
+ break;
+
+ case SimConnectSystemEvent.Sim:
+ args = new SimConnectSimSystemEventArgs { SimRunning = recvEvent.dwData == 1 };
+ break;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(systemEvent), systemEvent, null);
+ }
+
+ onEvent(args);
+ }
+
+ var eventRegistration = new SimConnectSystemEventRegistration(nextEventID, systemEvent, HandleData, worker);
+ nextEventID++;
+
+ return eventRegistration;
+ }
+
+
+ private class SimConnectDefinitionRegistration : IAsyncDisposable where T : class
{
private readonly uint definitionID;
private readonly SimConnectWorker worker;
@@ -96,9 +141,33 @@ namespace SimConnect.Concrete
}
- public void Dispose()
+ public ValueTask DisposeAsync()
{
worker.UnregisterDefinition(definitionID);
+ return default;
+ }
+ }
+
+
+ private class SimConnectSystemEventRegistration : IAsyncDisposable
+ {
+ private readonly uint eventID;
+ private readonly SimConnectWorker worker;
+
+
+ public SimConnectSystemEventRegistration(uint eventID, SimConnectSystemEvent systemEvent, Action onData, SimConnectWorker worker)
+ {
+ this.eventID = eventID;
+ this.worker = worker;
+
+ worker.SubscribeToSystemEvent(eventID, systemEvent, onData);
+ }
+
+
+ public ValueTask DisposeAsync()
+ {
+ worker.UnsubscribeFromSystemEvent(eventID);
+ return default;
}
}
@@ -117,6 +186,7 @@ namespace SimConnect.Concrete
private readonly TaskCompletionSource openResult = new TaskCompletionSource();
private readonly ConcurrentDictionary> definitionDataHandler = new ConcurrentDictionary>();
+ private readonly ConcurrentDictionary> eventDataHandler = new ConcurrentDictionary>();
public SimConnectWorker(ISimConnectLibrary simConnectLibrary, string appName)
@@ -135,12 +205,10 @@ namespace SimConnect.Concrete
}
- public async Task Close()
+ public void Close()
{
closed = true;
-
workerPulse.Set();
- await workerTask;
}
@@ -170,6 +238,29 @@ namespace SimConnect.Concrete
}
+ public void SubscribeToSystemEvent(uint eventID, SimConnectSystemEvent systemEvent, Action onData)
+ {
+ Enqueue(hSimConnect =>
+ {
+ eventDataHandler.AddOrUpdate(eventID, onData, (key, value) => onData);
+ var result = simConnectLibrary.SimConnect_SubscribeToSystemEvent(hSimConnect, eventID, systemEvent.ToString());
+
+ if (result == 0)
+ return;
+ });
+ }
+
+
+ public void UnsubscribeFromSystemEvent(uint eventID)
+ {
+ Enqueue(hSimConnect =>
+ {
+ eventDataHandler.TryRemove(eventID, out var unused);
+ simConnectLibrary.SimConnect_UnsubscribeFromSystemEvent(hSimConnect, eventID);
+ });
+ }
+
+
private void Enqueue(Action work)
{
lock(workerLock)
@@ -241,21 +332,30 @@ namespace SimConnect.Concrete
{
case SimConnectRecvID.Exception:
var recvException = Marshal.PtrToStructure(dataPtr);
+ // TODO provide a way to get insight into exceptions
if (recvException.dwException == 0)
break;
break;
+ case SimConnectRecvID.Event:
+ var recvEvent = Marshal.PtrToStructure(dataPtr);
+ if (!eventDataHandler.TryGetValue(recvEvent.uEventID, out var eventHandler))
+ break;
+
+ eventHandler(recvEvent);
+ break;
+
case SimConnectRecvID.SimobjectData:
case SimConnectRecvID.SimobjectDataByType:
var recvSimobjectData = Marshal.PtrToStructure(dataPtr);
- if (!definitionDataHandler.TryGetValue((uint)recvSimobjectData.dwDefineID, out var dataHandler))
+ if (!definitionDataHandler.TryGetValue(recvSimobjectData.dwDefineID, out var dataHandler))
break;
unsafe
{
var streamOffset = Marshal.OffsetOf("dwData").ToInt32();
- var stream = new UnmanagedMemoryStream((byte*)IntPtr.Add(dataPtr, streamOffset).ToPointer(), (long)dataSize - streamOffset);
+ var stream = new UnmanagedMemoryStream((byte*)IntPtr.Add(dataPtr, streamOffset).ToPointer(), dataSize - streamOffset);
dataHandler(stream);
}
break;
diff --git a/SimConnect/ISimConnectClient.cs b/SimConnect/ISimConnectClient.cs
index a521632..a5e2394 100644
--- a/SimConnect/ISimConnectClient.cs
+++ b/SimConnect/ISimConnectClient.cs
@@ -1,13 +1,50 @@
using System;
+using System.Threading.Tasks;
namespace SimConnect
{
+ public enum SimConnectSystemEvent
+ {
+ ///
+ /// Request notifications when the flight is paused or unpaused. args will be of type SimConnectPauseSystemEventArgs.
+ ///
+ Pause,
+
+ ///
+ /// Request notifications when the flight is running or not. args will be of type SimConnectSimSystemEventArgs.
+ ///
+ Sim
+ }
+
+
+ public abstract class SimConnectSystemEventArgs
+ {
+ }
+
+ public class SimConnectPauseSystemEventArgs : SimConnectSystemEventArgs
+ {
+ public bool Paused { get; set; }
+ }
+
+ public class SimConnectSimSystemEventArgs : SimConnectSystemEventArgs
+ {
+ public bool SimRunning { get; set; }
+ }
+
+
+
+ public delegate Task SimConnectSystemEventAction(SimConnectSystemEventArgs args);
+
+
///
/// Called when new data arrives from the SimConnect server.
///
/// An instance of the data class as passed to AddDefinition containing the variable values
/// The data class as passed to AddDefinition
- public delegate void SimConnectDataHandlerAction(T data) where T : class;
+ public delegate Task SimConnectDataHandlerAction(T data) where T : class;
+
+
+
///
@@ -26,7 +63,7 @@ namespace SimConnect
///
/// Provides access to the SimConnect library.
///
- public interface ISimConnectClient : IDisposable
+ public interface ISimConnectClient : IAsyncDisposable
{
///
/// Attaches the specified observer to receive status notifications.
@@ -40,7 +77,16 @@ namespace SimConnect
///
/// A callback method which is called whenever a data update is received
/// A class defining the variables to monitor annotated using the SimConnectVariable attribute
- /// An IDisposable which can be disposed to unregister the definition. Dispose is not required to be called when the client is disconnected, but will not throw an exception.
- IDisposable AddDefinition(SimConnectDataHandlerAction onData) where T : class;
+ /// An IAsyncDisposable which can be disposed to unregister the definition. Dispose is not required to be called when the client is disconnected, but will not throw an exception.
+ IAsyncDisposable AddDefinition(SimConnectDataHandlerAction onData) where T : class;
+
+
+ ///
+ /// Subscribes to a SimConnect system event.
+ ///
+ /// The type of event to subscribe to.
+ /// The callback method called whenever the system event occurs.
+ /// An IAsyncDisposable which can be disposed to unregister the definition. Dispose is not required to be called when the client is disconnected, but will not throw an exception.
+ IAsyncDisposable SubscribeToSystemEvent(SimConnectSystemEvent systemEvent, SimConnectSystemEventAction onEvent);
}
}
diff --git a/SimConnect/ISimConnectLibrary.cs b/SimConnect/ISimConnectLibrary.cs
index 8b9dbd2..8881a96 100644
--- a/SimConnect/ISimConnectLibrary.cs
+++ b/SimConnect/ISimConnectLibrary.cs
@@ -128,6 +128,17 @@ namespace SimConnect
}
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct SimConnectRecvEvent
+ {
+ public SimConnectRecv Recv;
+
+ public uint uGroupID;
+ public uint uEventID;
+ public uint dwData;
+ }
+
+
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct SimConnectRecvSimobjectData
{
@@ -148,7 +159,7 @@ namespace SimConnect
///
/// Provides a low-level interface to a compatible SimConnect.dll.
///
- public interface ISimConnectLibrary : IDisposable
+ public interface ISimConnectLibrary : IAsyncDisposable
{
uint SimConnect_Open(out IntPtr phSimConnect, string szName, IntPtr hwnd, uint userEventWin32, IntPtr hEventHandle, uint configIndex);
uint SimConnect_Close(IntPtr hSimConnect);
@@ -158,6 +169,9 @@ namespace SimConnect
uint SimConnect_RequestDataOnSimObject(IntPtr hSimConnect, uint requestID, uint defineID, uint objectID, SimConnectPeriod period, uint flags, uint origin = 0, uint interval = 0, uint limit = 0);
uint SimConnect_GetNextDispatch(IntPtr hSimConnect, out IntPtr ppData, out uint pcbData);
+
+ uint SimConnect_SubscribeToSystemEvent(IntPtr hSimConnect, uint eventID, string systemEventName);
+ uint SimConnect_UnsubscribeFromSystemEvent(IntPtr hSimConnect, uint eventID);
}
diff --git a/SimConnect/SimConnect.csproj b/SimConnect/SimConnect.csproj
index 84cdd03..01dc654 100644
--- a/SimConnect/SimConnect.csproj
+++ b/SimConnect/SimConnect.csproj
@@ -56,8 +56,17 @@
true
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+
@@ -94,5 +103,9 @@
Always
+
+
+
+
\ No newline at end of file
diff --git a/SimConnect/Unmanaged/SimConnectDLLLibrary.cs b/SimConnect/Unmanaged/SimConnectDLLLibrary.cs
index 0bea960..2809aa0 100644
--- a/SimConnect/Unmanaged/SimConnectDLLLibrary.cs
+++ b/SimConnect/Unmanaged/SimConnectDLLLibrary.cs
@@ -1,10 +1,11 @@
using System;
+using System.Threading.Tasks;
using SimConnect.Lib;
#pragma warning disable 1591
namespace SimConnect.Unmanaged
{
- public class SimConnectDLLLibrary : ISimConnectLibrary, IDisposable
+ public class SimConnectDLLLibrary : ISimConnectLibrary, IAsyncDisposable
{
private delegate uint SimConnectOpenProc(out IntPtr phSimConnect, string szName, IntPtr hwnd, uint userEventWin32, IntPtr hEventHandle, uint configIndex);
private delegate uint SimConnectCloseProc(IntPtr hSimConnect);
@@ -12,6 +13,9 @@ namespace SimConnect.Unmanaged
private delegate uint SimConnectClearDataDefinitionProc(IntPtr hSimConnect, uint defineID);
private delegate uint SimConnectRequestDataOnSimObjectProc(IntPtr hSimConnect, uint requestID, uint defineID, uint objectID, SimConnectPeriod period, uint flags, uint origin = 0, uint interval = 0, uint limit = 0);
private delegate uint SimConnectGetNextDispatchProc(IntPtr hSimConnect, out IntPtr ppData, out uint pcbData);
+ private delegate uint SimConnectSubscribeToSystemEventProc(IntPtr hSimConnect, uint eventID, string systemEventName);
+ private delegate uint SimConnectUnsubscribeFromSystemEventProc(IntPtr hSimConnect, uint eventID);
+
private readonly UnmanagedLibrary library;
@@ -21,6 +25,8 @@ namespace SimConnect.Unmanaged
private readonly SimConnectClearDataDefinitionProc simConnectClearDataDefinition;
private readonly SimConnectRequestDataOnSimObjectProc simConnectRequestDataOnSimObject;
private readonly SimConnectGetNextDispatchProc simConnectGetNextDispatch;
+ private readonly SimConnectSubscribeToSystemEventProc simConnectSubscribeToSystemEvent;
+ private readonly SimConnectUnsubscribeFromSystemEventProc simConnectUnsubscribeFromSystemEvent;
@@ -33,11 +39,14 @@ namespace SimConnect.Unmanaged
simConnectClearDataDefinition = library.GetUnmanagedFunction("SimConnect_ClearDataDefinition");
simConnectRequestDataOnSimObject = library.GetUnmanagedFunction("SimConnect_RequestDataOnSimObject");
simConnectGetNextDispatch = library.GetUnmanagedFunction("SimConnect_GetNextDispatch");
+ simConnectSubscribeToSystemEvent = library.GetUnmanagedFunction("SimConnect_SubscribeToSystemEvent");
+ simConnectUnsubscribeFromSystemEvent = library.GetUnmanagedFunction("SimConnect_UnsubscribeFromSystemEvent");
}
- public void Dispose()
+ public ValueTask DisposeAsync()
{
library?.Dispose();
+ return default;
}
@@ -72,6 +81,17 @@ namespace SimConnect.Unmanaged
{
return simConnectGetNextDispatch(hSimConnect, out ppData, out pcbData);
}
+
+
+ public uint SimConnect_SubscribeToSystemEvent(IntPtr hSimConnect, uint eventID, string systemEventName)
+ {
+ return simConnectSubscribeToSystemEvent(hSimConnect, eventID, systemEventName);
+ }
+
+ public uint SimConnect_UnsubscribeFromSystemEvent(IntPtr hSimConnect, uint eventID)
+ {
+ return simConnectUnsubscribeFromSystemEvent(hSimConnect, eventID);
+ }
}
}
#pragma warning restore 1591
diff --git a/SimConnect/app.config b/SimConnect/app.config
new file mode 100644
index 0000000..74312e4
--- /dev/null
+++ b/SimConnect/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimConnect/packages.config b/SimConnect/packages.config
new file mode 100644
index 0000000..dd0dcaa
--- /dev/null
+++ b/SimConnect/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimConnectUnitTests/SimConnectUnitTests.csproj b/SimConnectUnitTests/SimConnectUnitTests.csproj
index 9aa0dc5..49d330e 100644
--- a/SimConnectUnitTests/SimConnectUnitTests.csproj
+++ b/SimConnectUnitTests/SimConnectUnitTests.csproj
@@ -65,6 +65,12 @@
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+
@@ -89,6 +95,7 @@
+
diff --git a/SimConnectUnitTests/app.config b/SimConnectUnitTests/app.config
new file mode 100644
index 0000000..74312e4
--- /dev/null
+++ b/SimConnectUnitTests/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimConnectUnitTests/packages.config b/SimConnectUnitTests/packages.config
index 31768df..a3c1ee4 100644
--- a/SimConnectUnitTests/packages.config
+++ b/SimConnectUnitTests/packages.config
@@ -1,6 +1,8 @@
+
+