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 @@  + +