use Application.MainLook.Invoke(...) to refresh UI

--- abremir.AllMyBricks.DatabaseSeeder/Program.cs
+++ abremir.AllMyBricks.DatabaseSeeder/Program.cs
@@ -7,8 +7,8 @@
 using abremir.AllMyBricks.DataSynchronizer.Interfaces;
 using abremir.AllMyBricks.Device.Interfaces;
 using Microsoft.Extensions.Logging;
+using System;
 using System.Diagnostics;
-using System.Threading.Tasks;
 using Terminal.Gui;

 namespace abremir.AllMyBricks.DatabaseSeeder
@@ -93,6 +93,8 @@

                 SynchronizationProgressFrame.Clear();

+                Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
+
                 stopwatch = Stopwatch.StartNew();
             });
             dataSynchronizerEventHandler.Register<InsightsAcquired>(ev =>
@@ -100,21 +102,21 @@
                 lastUpdatedLabel.Text = $"Last Updated: {(ev.SynchronizationTimestamp.HasValue ? ev.SynchronizationTimestamp.Value.ToString("yyyy-MM-dd HH:mm:ss") : "Never")}";
                 synchronizeExtendedDataLabel.Text = $"Synchronize Extended Data: {(Settings.SynchronizeSetExtendedData ? "True" : "False")}";

-                Application.Refresh();
+                Application.MainLoop.Invoke(lastUpdatedLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(synchronizeExtendedDataLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<ThemeSynchronizerStart>(_ =>
             {
                 themeIndex = 0;
                 themeProgress.Fraction = 0;

-                Application.Refresh();
-            });
-            dataSynchronizerEventHandler.Register<ThemesAcquired>(ev =>
-            {
-                themeCount = ev.Count;
-
-                Application.Refresh();
-            });
+                Application.MainLoop.Invoke(themeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
+            });
+            dataSynchronizerEventHandler.Register<ThemesAcquired>(ev => themeCount = ev.Count);
             dataSynchronizerEventHandler.Register<SynchronizingTheme>(ev =>
             {
                 themeLabel.Text = $"Theme: {ev.Theme}";
@@ -122,7 +124,10 @@
                 totalUpdatedThemes++;
                 themeProgress.Fraction = themeIndex / themeCount;

-                Application.Refresh();
+                Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(themeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SynchronizedTheme>(_ =>
             {
@@ -130,7 +135,10 @@

                 totalUpdatedThemesLabel.Text = $"Total Updated Themes: {totalUpdatedThemes}";

-                Application.Refresh();
+                Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(totalUpdatedThemesLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<ThemeSynchronizerEnd>(_ =>
             {
@@ -138,13 +146,18 @@
                 themeIndex = 0f;
                 themeProgress.Fraction = 0;

-                Application.Refresh();
+                Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(themeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<ProcessingTheme>(ev =>
             {
                 themeLabel.Text = $"Theme: {ev.Name}";

-                Application.Refresh();
+                Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SubthemeSynchronizerStart>(_ =>
             {
@@ -152,14 +165,15 @@
                 subthemeIndex = 0;
                 subthemeProgress.Fraction = 0;

-                Application.Refresh();
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(subthemeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SubthemesAcquired>(ev =>
             {
                 subthemeCount = ev.Count;
                 subthemeIndex = 0;
-
-                Application.Refresh();
             });
             dataSynchronizerEventHandler.Register<SynchronizingSubtheme>(ev =>
             {
@@ -168,15 +182,21 @@
                 totalUpdatedSubthemes++;
                 subthemeProgress.Fraction = subthemeIndex / subthemeCount;

-                Application.Refresh();
-            });
-            dataSynchronizerEventHandler.Register<SynchronizedSubtheme>(ev =>
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(subthemeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
+            });
+            dataSynchronizerEventHandler.Register<SynchronizedSubtheme>(_ =>
             {
                 subthemeLabel.Text = string.Empty;

                 totalUpdatedSubthemesLabel.Text = $"Total Updated Subthemes: {totalUpdatedSubthemes}";

-                Application.Refresh();
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(totalUpdatedSubthemesLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SubthemeSynchronizerEnd>(_ =>
             {
@@ -184,21 +204,29 @@
                 subthemeIndex = 0f;
                 subthemeProgress.Fraction = 0;

-                Application.Refresh();
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(subthemeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<ProcessingSubtheme>(ev =>
             {
                 subthemeLabel.Text = $"Subtheme: {ev.Name}";

-                Application.Refresh();
-            });
-            dataSynchronizerEventHandler.Register<SetSynchronizerStart>(ev =>
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
+            });
+            dataSynchronizerEventHandler.Register<SetSynchronizerStart>(_ =>
             {
                 setLabel.Text = string.Empty;
                 setIndex = 0;
                 setProgress.Fraction = 0;

-                Application.Refresh();
+                Application.MainLoop.Invoke(setLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(setProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<AcquiringSets>(ev =>
             {
@@ -206,7 +234,10 @@
                 setProgress.Fraction = 0;
                 subthemeLabel.Text = $"Subtheme: {ev.Subtheme}, {ev.Year}";

-                Application.Refresh();
+                Application.MainLoop.Invoke(setProgress.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SetsAcquired>(ev =>
             {
@@ -216,9 +247,13 @@
                 if (ev.Year.HasValue)
                 {
                     subthemeLabel.Text = $"Subtheme: {ev.Subtheme}, {ev.Year.Value}";
+
+                    Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
                 }

-                Application.Refresh();
+                Application.MainLoop.Invoke(setProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SynchronizingSet>(ev =>
             {
@@ -229,7 +264,12 @@
                 totalUpdatedSets++;
                 setProgress.Fraction = setIndex / setCount;

-                Application.Refresh();
+                Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(setLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(setProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SynchronizedSet>(_ =>
             {
@@ -237,7 +277,10 @@

                 totalUpdatedSetsLabel.Text = $"Total Updated Sets: {totalUpdatedSets}";

-                Application.Refresh();
+                Application.MainLoop.Invoke(setLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(totalUpdatedSetsLabel.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<SetSynchronizerEnd>(ev =>
             {
@@ -245,12 +288,18 @@
                 {
                     themeLabel.Text = string.Empty;
                     subthemeLabel.Text = string.Empty;
+
+                    Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+                    Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
                 }
                 setLabel.Text = string.Empty;
                 setIndex = 0;
                 setProgress.Fraction = 0;

-                Application.Refresh();
+                Application.MainLoop.Invoke(setLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(setProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<ProcessedSubtheme>(_ =>
             {
@@ -258,7 +307,10 @@
                 subthemeIndex++;
                 subthemeProgress.Fraction = subthemeIndex / subthemeCount;

-                Application.Refresh();
+                Application.MainLoop.Invoke(subthemeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(subthemeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<ProcessedTheme>(_ =>
             {
@@ -270,9 +322,14 @@
                 {
                     themeProgress.Fraction = 0f;
                     subthemeProgress.Fraction = 0f;
+
+                    Application.MainLoop.Invoke(subthemeProgress.ChildNeedsDisplay);
                 }

-                Application.Refresh();
+                Application.MainLoop.Invoke(themeLabel.ChildNeedsDisplay);
+                Application.MainLoop.Invoke(themeProgress.ChildNeedsDisplay);
+
+                //Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);
             });
             dataSynchronizerEventHandler.Register<DataSynchronizationEnd>(_ =>
             {
@@ -296,7 +353,7 @@

                 SynchronizationProgressFrame.Clear();

-                Application.Refresh();
+                Application.MainLoop.Invoke(SynchronizationProgressFrame.ChildNeedsDisplay);

                 var dialog = new Dialog("Synchronization finished", 50, 8, new Button("Ok")
                 {
@@ -375,6 +432,9 @@
                     CanExit = false;

                     window.Add(SynchronizationProgressFrame);
+
+                    // HACK: since there is a bug in Application.MainLoop.Invoke(...) this is needed to force the UI to refresh!
+                    Application.MainLoop.AddTimeout(TimeSpan.FromMilliseconds(500), _ => true);

                     var dataSynchronizationService = IoC.IoCContainer.GetInstance<IDataSynchronizationService>();


Register number field with the integer serializer

--- core/Piranha/App.cs
+++ core/Piranha/App.cs
@@ -312,6 +312,7 @@
                         fields.Register<Extend.Fields.ImageField>();
                         fields.Register<Extend.Fields.MarkdownField>();
                         fields.Register<Extend.Fields.MediaField>();
+                        fields.Register<Extend.Fields.NumberField>();
                         fields.Register<Extend.Fields.PageField>();
                         fields.Register<Extend.Fields.PostField>();
                         fields.Register<Extend.Fields.StringField>();
@@ -332,6 +333,7 @@
                         serializers.Register<Extend.Fields.HtmlField>(new StringFieldSerializer<Extend.Fields.HtmlField>());
                         serializers.Register<Extend.Fields.MarkdownField>(new StringFieldSerializer<Extend.Fields.MarkdownField>());
                         serializers.Register<Extend.Fields.MediaField>(new MediaFieldSerializer());
+                        serializers.Register<Extend.Fields.NumberField>(new IntegerFieldSerializer<Extend.Fields.NumberField>());
                         serializers.Register<Extend.Fields.PageField>(new PageFieldSerializer());
                         serializers.Register<Extend.Fields.PostField>(new PostFieldSerializer());
                         serializers.Register<Extend.Fields.StringField>(new StringFieldSerializer<Extend.Fields.StringField>());


Minor refactoring

Minor refactoring

--- 6502.Net/Asm6502.cs
+++ 6502.Net/Asm6502.cs
@@ -456,10 +456,7 @@
                 }
                 else if (isRockwell)
                 {
-                    var bit = Assembler.Evaluator.Eval(firstElement);
-                    if (bit < 0 || bit > 7)
-                        throw new OverflowException(bit.ToString());
-                    fmt.Evaluations.Add(bit);
+                    var bit = Assembler.Evaluator.Eval(firstElement, 0, 7);
                     formatBuilder.Append(bit);
                 }
                 else
@@ -540,10 +537,6 @@
                 }
             }
             fmt.FormatString = finalFormat;
-
-            if (Reserved.IsOneOf("Rockwell", line.Instruction))
-                fmt.Evaluations.RemoveAt(0);
-
             return (fmt, instruction);
         }


Throttle HTTP POST requests, max X requests per second

--- Assets/PostToLog.cs
+++ Assets/PostToLog.cs
@@ -127,28 +127,59 @@
             Debug.Log("Post failed. Error: " + request.responseCode + " " + request.downloadHandler.text); // HTTP request performed, but backend responded with an HTTP error code
     }

+    /// <summary>
+    /// Max number of messages included in one HTTP POST request to the backend
+    /// This setting limits the size of each individual HTTP POST request body
+    /// </summary>
     private const int MaxMessagesPerPost = 10;
-    private const string url = "https://us-central1-unity-log-to-stackdriver.cloudfunctions.net/appendToLog";
+
+    /// <summary>
+    /// Backend endpoint
+    /// </summary>
+    private const string backendUrl = "https://us-central1-unity-log-to-stackdriver.cloudfunctions.net/appendToLog";
+
+    /// <summary>
+    /// Minimum interval between the start of one HTTP POST request and the start of the next
+    /// </summary>
+    private const float minIntervalBetweenPosts = 1.0f;
+
+    private float previousPostTimestamp = 0.0f;
+

     private void PostMessagesIfAvailable()
     {
         if (!postInProgress)
         {
-            UnityWebRequest request = null;
+            bool minimumDurationExceeded;

-            lock (jsonableMessages)
+            if (previousPostTimestamp == 0.0f)
+                minimumDurationExceeded = true;
+            else
             {
-                if (jsonableMessages.entries.Count > 0)
-                {
-                    extractJsonableMessages(jsonableMessages, jsonableMessagesInFlight, MaxMessagesPerPost);
-                    request = createPost(jsonableMessagesInFlight, url);
-                }
+                float delta = Time.time - previousPostTimestamp;
+                minimumDurationExceeded = (delta > minIntervalBetweenPosts);
             }

-            if (request != null)
+            if (minimumDurationExceeded)
             {
-                postInProgress = true;
-                StartCoroutine(PostRequest(request, PostRequestSucceeded, PostRequestFailed));
+                UnityWebRequest request = null;
+
+                lock (jsonableMessages)
+                {
+                    if (jsonableMessages.entries.Count > 0)
+                    {
+                        extractJsonableMessages(jsonableMessages, jsonableMessagesInFlight, MaxMessagesPerPost);
+                        request = createPost(jsonableMessagesInFlight, backendUrl);
+                    }
+                }
+
+                if (request != null)
+                {
+                    previousPostTimestamp = Time.time;
+
+                    postInProgress = true;
+                    StartCoroutine(PostRequest(request, PostRequestSucceeded, PostRequestFailed));
+                }
             }
         }
     }


BasketMessageAdapter. Fix order lookup child<->parent id mapping.

--- Algo/BasketMessageAdapter.cs
+++ Algo/BasketMessageAdapter.cs
@@ -1490,7 +1490,10 @@
 							break;

 						if (execMsg.TransactionId != default)
+						{
+							ApplyParentLookupId(execMsg);
 							_orderAdapters.TryAdd(execMsg.TransactionId, innerAdapter);
+						}

 						break;

@@ -1517,17 +1520,28 @@
 			if (msg == null)
 				throw new ArgumentNullException(nameof(msg));

-			var ids = msg.GetSubscriptionIds().ToArray();
+			var originIds = msg.GetSubscriptionIds();
+			var ids = originIds;
+			var changed = false;

 			for (var i = 0; i < ids.Length; i++)
 			{
 				var parentId = _parentChildMap.TryGetParent(ids[i]);

-				if (parentId != null)
-					ids[i] = parentId.Value;
-			}
-
-			msg.SetSubscriptionIds(ids);
+				if (parentId == null)
+					continue;
+
+				if (!changed)
+				{
+					ids = originIds.ToArray();
+					changed = true;
+				}
+
+				ids[i] = parentId.Value;
+			}
+
+			if (changed)
+				msg.SetSubscriptionIds(ids);
 		}

 		private void SendOutMarketDataNotSupported(long id)


リセットメニュー実装

リセットメニュー実装

--- Anne.Model/Git/Repository.cs
+++ Anne.Model/Git/Repository.cs
@@ -149,7 +149,6 @@
                 () =>
                 {
                     var opts = new CheckoutOptions { CheckoutModifiers = CheckoutModifiers.Force };
-
                     Internal.CheckoutPaths("HEAD", enumerable, opts);
                 });
         }
@@ -160,7 +159,9 @@
                 $"Reset: {mode} {sha}",
                 () =>
                 {
-                    Debug.WriteLine( $"Reset: {mode} {sha}" );
+                    var commit = Internal.Lookup<LibGit2Sharp.Commit>(sha);
+                    Debug.Assert(commit != null);
+                    Internal.Reset(mode, commit);
                 });
         }


(PFS): added "010" extension.

(PFS): added "010" extension.

--- ArcFormats/Artemis/ArcPFS.cs
+++ ArcFormats/Artemis/ArcPFS.cs
@@ -42,7 +42,7 @@

         public PfsOpener ()
         {
-            Extensions = new string[] { "pfs", "000", "001", "002", "003", "004", "005" };
+            Extensions = new string[] { "pfs", "000", "001", "002", "003", "004", "005", "010" };
             ContainedFormats = new string[] { "PNG", "JPEG", "IPT", "OGG", "TXT", "SCR" };
             Settings = new[] { PfsEncoding };
         }


(AkbReader.Unpack): simplified if/else branching.

--- ArcFormats/Silky/ImageAKB.cs
+++ ArcFormats/Silky/ImageAKB.cs
@@ -100,41 +100,32 @@

         public byte[] Unpack ()
         {
-            byte[] image;
-            if (m_info.InnerWidth != 0 && m_info.InnerHeight != 0)
+            if (0 == m_info.InnerWidth || 0 == m_info.InnerHeight)
+                return CreateBackground();
+
+            m_input.Position = 0x20;
+            int inner_stride = m_info.InnerWidth * m_pixel_size;
+            var pixels = new byte[m_info.InnerHeight * inner_stride];
+            using (var lz = new LzssStream (m_input, LzssMode.Decompress, true))
             {
-                m_input.Position = 0x20;
-                int inner_stride = m_info.InnerWidth * m_pixel_size;
-                var pixels = new byte[m_info.InnerHeight * inner_stride];
-                using (var lz = new LzssStream (m_input, LzssMode.Decompress, true))
+                for (int pos = pixels.Length - inner_stride; pos >= 0; pos -= inner_stride)
                 {
-                    for (int dst = pixels.Length - inner_stride; dst >= 0; dst -= inner_stride)
-                    {
-                        if (inner_stride != lz.Read (pixels, dst, inner_stride))
-                            throw new InvalidFormatException();
-                    }
-                }
-                RestoreDelta (pixels, inner_stride);
-                if (m_info.InnerWidth != m_info.Width || m_info.InnerHeight != m_info.Height)
-                {
-                    image = CreateBackground();
-                    int src = 0;
-                    int dst = m_info.OffsetY * Stride + m_info.OffsetX * m_pixel_size;
-                    for (int y = 0; y < m_info.InnerHeight; ++y)
-                    {
-                        Buffer.BlockCopy (pixels, src, image, dst, inner_stride);
-                        dst += Stride;
-                        src += inner_stride;
-                    }
-                }
-                else
-                {
-                    image = pixels;
+                    if (inner_stride != lz.Read (pixels, pos, inner_stride))
+                        throw new InvalidFormatException();
                 }
             }
-            else
+            RestoreDelta (pixels, inner_stride);
+            if (m_info.InnerWidth == m_info.Width && m_info.InnerHeight == m_info.Height)
+                return pixels;
+
+            var image = CreateBackground();
+            int src = 0;
+            int dst = m_info.OffsetY * Stride + m_info.OffsetX * m_pixel_size;
+            for (int y = 0; y < m_info.InnerHeight; ++y)
             {
-                image = CreateBackground();
+                Buffer.BlockCopy (pixels, src, image, dst, inner_stride);
+                dst += Stride;
+                src += inner_stride;
             }
             return image;
         }


Bump Version Numbers.

Bump Version Numbers.

--- ArchiSteamFarm/SharedInfo.cs
+++ ArchiSteamFarm/SharedInfo.cs
@@ -44,8 +44,8 @@
 		internal const string ServiceDescription = "ASF is an application that allows you to farm steam cards using multiple steam accounts simultaneously.";
 		internal const string ServiceName = "ArchiSteamFarm";
 		internal const string StatisticsServer = "https://asf.justarchi.net";
-		internal const string VersionNumber = "2.1.7.4";
-        internal const string MyVersionNumber = "2.1.6.1";
+		internal const string VersionNumber = "2.1.7.9";
+        internal const string MyVersionNumber = "2.1.6.2";

         internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
     	}


fix removeNewLine() bug …

fix removeNewLine() bug …

was removing 1 too much

--- Assets/CheckButtonControl.cs
+++ Assets/CheckButtonControl.cs
@@ -4,6 +4,12 @@
 using System.Linq; // for Where
 using System.Collections.Generic;
 using System; // for StringSplitOptions.RemoveEmptyEntries
+
+/*
+ * V0.1 2015/08/26
+ *   add dictionary export/import feature
+ *   add web search / dictionary search feature
+ */

 public class CheckButtonControl : MonoBehaviour {

@@ -116,7 +122,7 @@
 				return "";
 			}
 			int pos = src.IndexOf(System.Environment.NewLine);
-			return src.Substring(0, pos - 1);
+			return src.Substring(0, pos);
 		}
 		return src;
 	}
@@ -152,7 +158,7 @@
 			}
 			telbook.Add(key, value);
 		}
-		int nop = 1;
+//		int nop = 1; // TODO: remove
 	}

 }


Adjust initial placement of the 'table'

Adjust initial placement of the 'table'

--- Assets/Scripts/Initialize.cs
+++ Assets/Scripts/Initialize.cs
@@ -61,23 +61,25 @@
     protected void AddTerrain()
     {
         terrain = new GameObject("Terrain");
+        terrain.transform.position = new Vector3(0f, 0f, 2f);
+

         Vector3 pos = new Vector3(0f, 0f, 0f);

         #region table

         GameObject _table = Resources.Load<GameObject>("table");
-        terrain.transform.localScale = new Vector3(0.001f, 0.001f, 0.001f);
+        terrain.transform.localScale = new Vector3(0.002f, 0.002f, 0.002f);
         //GameObject table = (GameObject)Instantiate(_table, terrain.transform);
         //table.transform.localScale = new Vector3(200f, 200f, 200f);
         var _terrain = new GameObject("MapTerrain");
         _terrain.transform.parent = terrain.transform;
         //GameObject _terrain = table.transform.FindChild("Location Terrain").gameObject;
-        //_terrain.transform.localPosition = new Vector3(0.001875073f, 16.34f, 0.153019f);
-       // _terrain.transform.localScale = new Vector3(0.001f, 0.001f, 0.001f);
+        //_terrain.transform.localPosition = new Vector3(0, 1f, 0);
+        _terrain.transform.localScale = new Vector3(1.5f, 1.5f, 1.5f);

         world = new GameObject("World");
-        //world.transform.position = new Vector3(0f, 0.21f, 0f);
+        world.transform.position = new Vector3(0f, 0.5f, 0f);

         world.transform.parent = _terrain.transform;

@@ -148,9 +150,9 @@
         var poisFactory = pois.AddComponent<PoiFactory>();

-        var _symbolicInitHandler = world.AddComponent<SymbolFactory>();
-        _symbolicInitHandler.geojson = json;
-        _symbolicInitHandler.AddSymbols();
+        //var _symbolicInitHandler = world.AddComponent<SymbolFactory>();
+        //_symbolicInitHandler.geojson = json;
+        //_symbolicInitHandler.AddSymbols();
         #endregion

         #region TILE PLUGINS


sounds

sounds

--- Bialjam/Assets/Gra/Bullet.cs
+++ Bialjam/Assets/Gra/Bullet.cs
@@ -5,7 +5,7 @@
 {
 	public GameObject BulletBody;
 	public GameObject Shooter;
-    public AudioClip DeadSound;
+    public AudioClip DeadSound, rozszczepienieSound;
     void Start()
     {
     }
@@ -28,6 +28,7 @@
 		if (coll.gameObject) {

 			if (coll.gameObject.layer == 10) { // Player's layer
+                gameObject.AddComponent<AudioSource>().PlayOneShot(rozszczepienieSound);
 				gameObject.layer = 11;
 				GameObject Temporary_Bullet_Handler;
 				Temporary_Bullet_Handler = Instantiate (gameObject, GetComponent<Collider2D> ().transform.position, transform.rotation) as GameObject;
@@ -50,10 +51,10 @@
 				Temporary_Bullet_Handler.SendMessage ("SetShooter", Shooter);
 			} else if (coll.gameObject.layer == 9) { // enemy
 				if (coll.gameObject != Shooter) {
-					coll.gameObject.SendMessage ("OnDamage");
+                    gameObject.AddComponent<AudioSource>().PlayOneShot(DeadSound);
+                    coll.gameObject.SendMessage ("OnDamage");
 					GlobalVariable.Instance.score += 100;
 					GlobalVariable.Instance.shake = true;
-                    gameObject.AddComponent<AudioSource>().PlayOneShot(DeadSound);
 					Destroy (coll.gameObject); // enemy dies
 					GlobalVariable.Instance.enemies--;
 				}


what's wrong?

what's wrong?

--- CSharp.Tests/BuilderTests.cs
+++ CSharp.Tests/BuilderTests.cs
@@ -30,7 +30,8 @@
                    .Block("snd", @"
                             _   @V : data_rows2+
 ").Parse(section);
-            Assert.That(result, Is.EquivalentTo(new ParsedBlock[] {
+
+            Assert.That(result.ToArray(), Is.EquivalentTo(new ParsedBlock[] {
                 new ParsedBlock( "fst",new []{
                         new ParsedRow("header", new[] { "H" }),
                         new ParsedRow("data_rows", new[] { "D1" }),


corrected merge

corrected merge

--- dotFlip/dotFlip/MainWindow.xaml.cs
+++ dotFlip/dotFlip/MainWindow.xaml.cs
@@ -253,43 +253,38 @@
             toolThicknessSlider.Value = _flipbook.CurrentTool.Thickness;
         }

-        private void chkPlay_Click(object sender, RoutedEventArgs e)
-        {
-            _flipbook.IsPlaying = chkPlay.IsChecked.Value;
-            if (chkPlay.IsChecked.Value)
-            {
-                btnNext.IsEnabled = false;
-                btnPrev.IsEnabled = false;
-                btnUndo.IsEnabled = false;
-                btnUndo.IsEnabled = false;
-                btnCopy.IsEnabled = false;
-                btnRedo.IsEnabled = false;
-                btnDelete.IsEnabled = false;
-                txtNavigation.IsEnabled = false;
-                sldrNavigation.IsEnabled = false;
-                btnGhost.IsChecked = false;
-                btnGhost.IsEnabled = false;
-                _flipbook.IsShowingGhostStrokes = false;
-                flipbookHolder.IsHitTestVisible = false;
-            }
-            else
-            {
-                btnNext.IsEnabled = true;
-                btnPrev.IsEnabled = true;
-                btnUndo.IsEnabled = true;
-                btnUndo.IsEnabled = true;
-                btnCopy.IsEnabled = true;
-                btnDelete.IsEnabled = true;
-                txtNavigation.IsEnabled = true;
-                sldrNavigation.IsEnabled = true;
-                btnRedo.IsEnabled = true;
-                btnGhost.IsEnabled = true;
-                flipbookHolder.IsHitTestVisible = true;
-            }
-            _flipbook.PlayAnimation(Convert.ToInt32(animationSpeedSlider.Value));
+        private void EnableControls()
+        {
+            btnNext.IsEnabled = false;
+            btnPrev.IsEnabled = false;
+            btnUndo.IsEnabled = false;
+            btnUndo.IsEnabled = false;
+            btnCopy.IsEnabled = false;
+            btnRedo.IsEnabled = false;
+            btnDelete.IsEnabled = false;
+            txtNavigation.IsEnabled = false;
+            sldrNavigation.IsEnabled = false;
+            btnGhost.IsChecked = false;
+            btnGhost.IsEnabled = false;
+            _flipbook.IsShowingGhostStrokes = false;
+            flipbookHolder.IsHitTestVisible = false;
+        }
+
+        private void DisableControls()
+        {
+            btnNext.IsEnabled = true;
+            btnPrev.IsEnabled = true;
+            btnUndo.IsEnabled = true;
+            btnUndo.IsEnabled = true;
+            btnCopy.IsEnabled = true;
+            btnDelete.IsEnabled = true;
+            txtNavigation.IsEnabled = true;
+            sldrNavigation.IsEnabled = true;
+            btnRedo.IsEnabled = true;
+            btnGhost.IsEnabled = true;
+            flipbookHolder.IsHitTestVisible = true;
         }

-
         private void eraserButton_Click(object sender, RoutedEventArgs e)
         {
             _flipbook.UseTool("Eraser");


Update HowToUse.cs

Update HowToUse.cs

--- C-Sharp/HowToUse.cs
+++ C-Sharp/HowToUse.cs
@@ -3,8 +3,8 @@
 		{
 			CryptLib _crypt = new CryptLib ();
 			string plainText = "This is the text to be encrypted";
-			String iv = CryptLib.GenerateRandomIV (15); //do not exceed 15
-			string key = CryptLib.getHashSha256("my secret key", 31); //do not exceed 31
+			String iv = CryptLib.GenerateRandomIV (16); //16 bytes = 128 bits
+			string key = CryptLib.getHashSha256("my secret key", 31); //32 bytes = 256 bits
 			String cypherText = _crypt.encrypt (plainText, key, iv);
 			Console.WriteLine ("iv="+iv);
 			Console.WriteLine ("key=" + key);


ParseBlock() - what is after the block doesn't belong here

--- CobaltAHK/Parser.cs
+++ CobaltAHK/Parser.cs
@@ -169,6 +169,7 @@

 			AssertToken(token, Token.OpenBrace);
 			var expressions = ParseBlock(lexer, e => ValidateExpressionInDefinition(e));
+			AssertToken(lexer.GetToken(), Token.Newline, Token.EOF);

 			IEnumerable<FunctionDefinitionExpression> methods;
 			FilterClassBodyExpressions(expressions, out methods);
@@ -220,6 +221,8 @@
 			Expression result;
 			if (token == Token.OpenBrace) { // function definition
 				var body = ParseBlock(lexer, e => ValidateExpressionInDefinition(e));
+				AssertToken(lexer.GetToken(), Token.Newline, Token.EOF);
+
 				var prms = ValidateFunctionDefParams(parameters);
 				result = new FunctionDefinitionExpression(lexer.Position, func.Text, prms, body);

@@ -716,7 +719,6 @@
 				token = lexer.PeekToken();
 			}
 			lexer.GetToken(); // swallow the closing brace
-			AssertToken(lexer.GetToken(), Token.Newline, Token.EOF);

 			lexer.PopState();
 			return body.ToArray();


Tiny refactoring of logic

Tiny refactoring of logic

--- core/p5.webapp/Default.aspx.cs
+++ core/p5.webapp/Default.aspx.cs
@@ -58,11 +58,11 @@
             // Making sure our form gets the correct action attribute.
             Form.Action = HttpContext.Current.Request.RawUrl;

-            // Mapping up our Page_Load event for initial loading of web page.
-            Load += delegate {
+            // Checking if page is not postback.
+            if (!IsPostBack) {

-                // Checking if page is not postback.
-                if (!IsPostBack) {
+                // Mapping up our Page_Load event for initial loading of web page.
+                Load += delegate {

                     /*
                      * Raising our [p5.web.load-ui] Active Event, creating the node to pass in first,
@@ -102,8 +102,8 @@
                     var baseCtrl = new LiteralControl ();
                     baseCtrl.Text = string.Format (@"<base href= ""{0}""/>", baseUrl);
                     Page.Header.Controls.Add (baseCtrl);
-                }
-            };
+                };
+            }

             base.OnInit (e);
         }


Incremented Formulate version to 0.1.0.4.

--- src/formulate.meta/Constants.cs
+++ src/formulate.meta/Constants.cs
@@ -17,7 +17,7 @@
         /// Do not reformat this code. A grunt task reads this
         /// version number with a regular expression.
         /// </remarks>
-        public const string Version = "0.1.0.3";
+        public const string Version = "0.1.0.4";

         /// <summary>


Incremented the version number to 1.3.1.2

--- src/formulate.meta/Constants.cs
+++ src/formulate.meta/Constants.cs
@@ -17,7 +17,7 @@
         /// Do not reformat this code. A grunt task reads this
         /// version number with a regular expression.
         /// </remarks>
-        public const string Version = "1.3.1.0";
+        public const string Version = "1.3.1.2";

         /// <summary>


Update BigFont.cs

Update BigFont.cs

Removed the GenerateStyled method as it didn't really fit with the purpose of the class.  Cleaned up a few comments and finalized the header block.


install as automatic startup

install as automatic startup

--- demo_service/ProjectInstaller.cs
+++ demo_service/ProjectInstaller.cs
@@ -36,7 +36,7 @@
 			serviceProcessInstaller.Account = ServiceAccount.LocalSystem;

 			serviceInstaller.ServiceName = demo_service.MyServiceName;
-			//serviceInstaller.StartType = ServiceStartMode.Automatic;
+			serviceInstaller.StartType = ServiceStartMode.Automatic;
 			//serviceInstaller.DelayedAutoStart = true;

 			this.Installers.AddRange(new Installer[] { serviceProcessInstaller, serviceInstaller });


Improve performance of reading INI files.

--- src/SilDev/Ini.cs
+++ src/SilDev/Ini.cs
@@ -5,9 +5,9 @@
 // ==============================================
 //
 // Filename: Ini.cs
-// Version:  2017-08-10 17:55
+// Version:  2018-03-23 22:29
 //
-// Copyright (c) 2017, Si13n7 Developments (r)
+// Copyright (c) 2018, Si13n7 Developments (r)
 // All rights reserved.
 // ______________________________________________

@@ -18,6 +18,7 @@
     using System;
     using System.Collections.Generic;
     using System.ComponentModel;
+    using System.Diagnostics.CodeAnalysis;
     using System.Drawing;
     using System.IO;
     using System.Linq;
@@ -30,8 +31,9 @@
     public static class Ini
     {
         private const string NonSectionId = "\0\u0002(NON-SECTION)\u0003\0";
-        private static string _filePath;
-        private static string _tmpFileGuid;
+        private const string ObjectPrefix = "\u0001Object\u0002";
+        private const string ObjectSuffix = "\u0003";
+        private static string _filePath, _tmpFileGuid;
         private static Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>> CachedFiles { get; set; }

         /// <summary>
@@ -48,7 +50,7 @@
         {
             get
             {
-                if (!string.IsNullOrEmpty(_tmpFileGuid))
+                if (_tmpFileGuid != default(string))
                     return _tmpFileGuid;
                 _tmpFileGuid = Guid.NewGuid().ToString();
                 return _tmpFileGuid;
@@ -84,7 +86,7 @@

         private static void InitializeCache(int code, string section = null, string key = null)
         {
-            if (CachedFiles == null)
+            if (CachedFiles == default(Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>>))
                 CachedFiles = new Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>>();

             if (!CachedFiles.ContainsKey(code))
@@ -92,13 +94,11 @@

             if (string.IsNullOrEmpty(section))
                 return;
-
             if (!CachedFiles[code].ContainsKey(section))
                 CachedFiles[code][section] = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

             if (string.IsNullOrEmpty(key))
                 return;
-
             if (!CachedFiles[code][section].ContainsKey(key))
                 CachedFiles[code][section][key] = new List<string>();
         }
@@ -208,6 +208,21 @@
         }

         /// <summary>
+        ///     Gets the regular expression to convert the INI data into an accessible format.
+        /// </summary>
+        /// <param name="allowEmptySection">
+        ///     true to allow key value pairs without section; otherwise, false.
+        /// </param>
+        [SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
+        public static Regex GetRegex(bool allowEmptySection = true)
+        {
+            const RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline;
+            if (allowEmptySection)
+                return new Regex(@"^((?:\[)(?<Section>[^\]]*)(?:\])(?:[\r\n]{0,}|\Z))((?!\[)(?<Key>[^=]*?)(?:=)(?<Value>[^\r\n]*)(?:[\r\n]{0,4}))*", options);
+            return new Regex(@"^((?:\[)(?<Section>[^\]]*)(?:\])(?:[\r\n]{0,}|\Z))((?!\[)(?<Key>[^=]*?)(?:=)(?<Value>[^\r\n]*)(?:[\r\n]{0,4}))+", options);
+        }
+
+        /// <summary>
         ///     Gets the full path of the default INI file.
         /// </summary>
         public static string GetFile() =>
@@ -464,43 +479,9 @@
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));

-                // Enforce INI format rules
-                var lines = TextEx.FormatNewLine(source).SplitNewLine();
-                for (var i = 0; i < lines.Length; i++)
-                {
-                    var line = lines[i].Trim();
-                    if (line.StartsWith("[") && !line.EndsWith("]") && line.Contains("]") && line.IndexOf(']') > 1)
-                        lines[i] = line.Substring(0, line.IndexOf(']') + 1);
-                    else
-                        lines[i] = line;
-                }
-                source = lines.Where(LineHasIniFormat).Join(Environment.NewLine)?.TrimStart();
-                if (string.IsNullOrEmpty(source))
-                    throw new ArgumentNullException(nameof(source));
-                if (!source.StartsWith("["))
-                    source = $"[{NonSectionId}]{Environment.NewLine}{source}";
-                if (!source.EndsWith(Environment.NewLine))
-                    source += Environment.NewLine;
-
-                var matches = Regex.Matches(source,
-                                            @"^                    # Beginning of the line
-                                              ((?:\[)              # Section Start
-                                               (?<Section>[^\]]*)  # Actual Section text into Section Group
-                                               (?:\])              # Section End then EOL/EOB
-                                               (?:[\r\n]{0,}|\Z))  # Match but don't capture the CRLF or EOB
-                                              (                    # Begin capture groups (Key Value Pairs)
-                                               (?!\[)              # Stop capture groups if a [ is found; new section
-                                               (?<Key>[^=]*?)      # Any text before the =, matched few as possible
-                                               (?:=)               # Get the = now
-                                               (?<Value>[^\r\n]*)  # Get everything that is not an Line Changes
-                                               (?:[\r\n]{0,4})     # MBDC \r\n
-                                              )*                   # End Capture groups",
-                                            RegexOptions.IgnoreCase |
-                                            RegexOptions.Multiline |
-                                            RegexOptions.IgnorePatternWhitespace);
-
+                source = ForceFormat(source);
                 var content = new Dictionary<string, Dictionary<string, List<string>>>(StringComparer.OrdinalIgnoreCase);
-                foreach (Match match in matches)
+                foreach (var match in GetRegex().Matches(source).Cast<Match>())
                 {
                     var section = match.Groups["Section"]?.Value.Trim();
                     if (string.IsNullOrEmpty(section))
@@ -545,10 +526,10 @@
             return output;
         }

-        private static bool LineHasIniFormat(this string str)
+        private static bool LineIsValid(string str)
         {
             var s = str;
-            if (string.IsNullOrWhiteSpace(s))
+            if (string.IsNullOrWhiteSpace(s) || s.Length < 3)
                 return false;
             if (s.StartsWith("[") && s.EndsWith("]") && s.Count(x => x == '[') == 1 && s.Count(x => x == ']') == 1 && s.Any(char.IsLetterOrDigit))
                 return true;
@@ -556,7 +537,30 @@
             if (!char.IsLetterOrDigit(c) && !c.IsBetween('$', '/') && !c.IsBetween('<', '@') && !c.IsBetween('{', '~') && c != '!' && c != '"' && c != ':' && c != '^' && c != '_')
                 return false;
             var i = s.IndexOf('=');
-            return i > 0 && s.Substring(0, i).Any(char.IsLetterOrDigit) && s.Contains('=') && i + 1 < s.Length;
+            return i > 0 && s.Substring(0, i).Any(char.IsLetterOrDigit) && i + 1 < s.Length;
+        }
+
+        private static string ForceFormat(string str)
+        {
+            var builder = new StringBuilder();
+            foreach (var text in str.TrimStart().Split(TextEx.NewLineFormats.All))
+            {
+                var line = text.Trim();
+                if (line.StartsWith("[") && !line.EndsWith("]") && line.Contains(']') && line.IndexOf(']') > 1)
+                    line = line.Substring(0, line.IndexOf(']') + 1);
+                if (LineIsValid(line))
+                    builder.AppendLine(line);
+            }
+            if (builder.Length < 1)
+                throw new ArgumentNullException(nameof(builder));
+            var first = builder.ToString(0, 1).First();
+            if (first.Equals('['))
+                return builder.ToString();
+            builder.Insert(0, Environment.NewLine);
+            builder.Insert(0, ']');
+            builder.Insert(0, NonSectionId);
+            builder.Insert(0, '[');
+            return builder.ToString();
         }

         /// <summary>
@@ -600,7 +604,11 @@
                 if (!CodeExists(code))
                     throw new ArgumentNullException(nameof(fileOrContent));
                 if (string.IsNullOrEmpty(section))
-                    throw new ArgumentNullException(nameof(section));
+                {
+                    if (section != null)
+                        throw new ArgumentNullException(nameof(section));
+                    section = NonSectionId;
+                }
                 if (string.IsNullOrEmpty(key))
                     throw new ArgumentNullException(nameof(key));
                 if (KeyExists(code, section, key))
@@ -675,20 +683,20 @@
                 {
                     if (Log.DebugMode > 1)
                     {
-                        var lines = new[]
-                        {
-                            "The value is not defined.",
-                            $"   Section: '{section}'",
-                            $"   Key: '{key}'",
-                            $"   FileOrContent: '{(fileOrContent?.Any(TextEx.IsLineSeparator) == true ? fileOrContent.EncodeToBase85() : GetFile()) ?? "NULL"}'"
-                        };
-                        throw new WarningException(lines.Join(Environment.NewLine));
+                        var msg = new StringBuilder();
+                        msg.AppendLine("The value is not defined.");
+                        msg.AppendLine($"   Section: '{section}'");
+                        msg.AppendLine($"   Key: '{key}'");
+                        msg.Append($"   FileOrContent: '{(fileOrContent?.Any(TextEx.IsLineSeparator) == true ? fileOrContent.EncodeToBase85() : GetFile()) ?? "NULL"}'");
+                        throw new WarningException(msg.ToString());
                     }
                     newValue = (object)defValue ?? string.Empty;
                 }
-                else if (strValue.StartsWith("\u0001Object\u0002") && strValue.EndsWith("\u0003"))
-                {
-                    var bytes = strValue.Substring(8).TrimEnd('\u0003').DecodeBytesFromBase85(null, null);
+                else if (strValue.StartsWith(ObjectPrefix) && strValue.EndsWith(ObjectSuffix))
+                {
+                    var startIndex = ObjectPrefix.Length;
+                    var length = strValue.Length - ObjectPrefix.Length - ObjectSuffix.Length;
+                    var bytes = strValue.Substring(startIndex, length).DecodeBytesFromBase85(null, null);
                     var unzipped = bytes?.Unzip();
                     if (unzipped != null)
                         bytes = unzipped;
@@ -969,10 +977,14 @@
                 if (!CodeExists(code))
                     ReadAll(fileOrContent);

+                if (string.IsNullOrEmpty(section))
+                {
+                    if (section != null)
+                        throw new ArgumentNullException(nameof(section));
+                    section = NonSectionId;
+                }
                 if (section.Any(TextEx.IsLineSeparator))
                     throw new ArgumentOutOfRangeException(nameof(section));
-                if (string.IsNullOrEmpty(section))
-                    section = NonSectionId;

                 if (key.Any(TextEx.IsLineSeparator))
                     throw new ArgumentOutOfRangeException(nameof(key));
@@ -988,7 +1000,7 @@
                         var zipped = bytes?.Zip();
                         if (zipped?.Length < bytes?.Length)
                             bytes = zipped;
-                        val = "\u0001Object\u0002" + bytes.EncodeToBase85(null, null) + "\u0003";
+                        val = string.Concat(ObjectPrefix, bytes.EncodeToBase85(null, null), ObjectSuffix);
                     }
                     else
                         val = str;


Fix StringBuilder capacity for <Hash/> task (#5560)

I was profiling <Hash/> to see if I could improve it:

https://github.com/jonathanpeppers/Benchmarks/tree/msbuild-hash

Then I noticed a simple bug that calculates the `StringBuilder`
capacity:

    var separatorSize = itemsToHash.Length - 1;
    return totalItemSize + separatorSize;

The `- 1`, seems wrong because the separator is always appended:

    foreach (var item in ItemsToHash)
    {
        string itemSpec = item.ItemSpec;
        stringBuilder.Append(IgnoreCase ? itemSpec.ToUpperInvariant() : itemSpec);
        stringBuilder.Append(ItemSeparatorCharacter);
    }

This means the capacity is 1 less than it should be, and the
underlying `StringBuilder` has to "resize" itself at least once.

Just removing the `- 1`, I could see a noticeable memory usage
difference in a benchmark:

    | Method |     Mean |    Error |   StdDev |   Gen 0 |  Gen 1 | Gen 2 | Allocated |
    |------- |---------:|---------:|---------:|--------:|-------:|------:|----------:|
    |  Hash2 | 85.57 us | 0.364 us | 0.340 us | 14.6484 | 1.5869 |     - |  90.88 KB |
    |  Hash1 | 90.27 us | 0.374 us | 0.313 us | 25.3906 | 5.0049 |     - | 157.18 KB |

I think what is happening here, is the `StringBuilder` is doubling the
capacity in my benchmark when it needs to grow:

https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs,1962

This seems like an "easy fix", so I sent this in.

--- src/Tasks/Hash.cs
+++ src/Tasks/Hash.cs
@@ -90,9 +90,8 @@
                 totalItemSize += item.ItemSpec.Length;
             }

-            var separatorSize = itemsToHash.Length - 1;
-
-            return totalItemSize + separatorSize;
+            // Add one ItemSeparatorCharacter per item
+            return totalItemSize + itemsToHash.Length;
         }
     }
 }


Update memory handling, add work and high ram

--- elbgb.gameboy/Memory/MMU.cs
+++ elbgb.gameboy/Memory/MMU.cs
@@ -7,12 +7,20 @@

 namespace elbgb.gameboy.Memory
 {
-	class MMU : IMemoryMappedComponent
+	class MMU //: IMemoryMappedComponent
 	{
+		public static class Registers
+		{
+			public const ushort BOOTROMLOCK = 0xFF50;
+		}
+
 		private GameBoy _gb;

 		private bool _bootRomLocked;
 		private byte[] _bootRom;
+
+		private byte[] _wram;
+		private byte[] _hram;

 		public MMU(GameBoy gameBoy, byte[] bootRom)
 		{
@@ -20,41 +28,99 @@

 			_bootRom = bootRom;
 			_bootRomLocked = false;
+
+			_wram = new byte[0x2000];
+			_hram = new byte[0x7F];
 		}

 		public byte ReadByte(ushort address)
 		{
-			if (!_bootRomLocked && address < 0x100)
+			switch (address & 0xF000)
 			{
-				return _bootRom[address];
+				case 0x0000:
+					if (!_bootRomLocked && address < 0x100)
+					{
+						return _bootRom[address];
+					}
+
+					throw new NotImplementedException();
+
+				// working ram
+				case 0xC000:
+				case 0xD000:
+					return _wram[address & 0x1FFF];
+
+				case 0xF000:
+					// timer IO registers
+					if (address >= 0xFF04 && address <= 0xFF07)
+					{
+						return _gb.Timer.ReadByte(address);
+					}
+					// restricted area
+					else if (address >= 0xFEA0 && address <= 0xFEFF)
+					{
+						return 0;
+					}
+					// hi ram
+					else if (address >= 0xFF80 && address <= 0xFFFE)
+					{
+						return _hram[address & 0x7F];
+					}
+					else
+						throw new NotImplementedException();
+
+				default:
+					throw new NotImplementedException();
 			}
-			// timer 0xFF04 - 0xFF07
-			else if (address >= 0xFF04 && address <= 0xFF07)
-			{
-				return _gb.Timer.ReadByte(address);
-			}
-
-			throw new NotImplementedException();
 		}

 		public void WriteByte(ushort address, byte value)
 		{
-			if (address == 0xFF50) // Boot ROM lockout address
+			switch (address & 0xF000)
 			{
-				// TODO(david): do we set a value at this location? can we read from it?
-				_bootRomLocked = true;
+				// working ram
+				case 0xC000:
+				case 0xD000:
+					_wram[address & 0x1FFF] = value;
+					return;
+
+				// vram
+				case 0x8000:
+				case 0x9000:
+					_gb.PPU.WriteByte(address, value);
+					return;
+
+				case 0xF000:
+					// timer IO registers
+					if (address >= 0xFF04 && address <= 0xFF07)
+					{
+						_gb.Timer.WriteByte(address, value);
+						return;
+					}
+					// restricted area
+					else if (address >= 0xFEA0 && address <= 0xFEFF)
+					{
+						return;
+					}
+					// hi ram
+					else if (address >= 0xFF80 && address <= 0xFFFE)
+					{
+						_hram[address & 0x7F] = value;
+						return;
+					}
+					else if (address == Registers.BOOTROMLOCK)
+					{
+						// TODO(david): do we set a value at this location? can we read from it?
+						_bootRomLocked = true;
+						return;
+					}
+					else
+						throw new NotImplementedException();
+
+
+				default:
+					throw new NotImplementedException();
 			}
-			else if (address >= 0x8000 && address <= 0x9fff)
-			{
-				_gb.PPU.WriteByte(address, value);
-			}
-			// timer 0xFF04 - 0xFF07
-			else if (address >= 0xFF04 && address <= 0xFF07)
-			{
-				_gb.Timer.WriteByte(address, value);
-			}
-			else
-				throw new NotImplementedException();
 		}
 	}
 }


Add test for Signature equality

Add test for Signature equality

--- tests/SignatureTest.cs
+++ tests/SignatureTest.cs
@@ -21,5 +21,15 @@
 			Assert.IsFalse (sig.IsDict);
 			Assert.IsFalse (sig.IsPrimitive);
 		}
+
+		[Test]
+		public void Equality ()
+		{
+			string sigText = "as";
+			Signature a = new Signature (sigText);
+			Signature b = new Signature (sigText);
+
+			Assert.IsTrue (a == b);
+		}
 	}
 }


Fix maximization resetting restore location and size

--- Core/FormBrowser.cs
+++ Core/FormBrowser.cs
@@ -124,8 +124,12 @@

             if (Location.X != -32000){
                 Config.IsMaximized = WindowState == FormWindowState.Maximized;
-                Config.WindowLocation = Location;
-                Config.WindowSize = Size;
+
+                if (WindowState == FormWindowState.Normal){
+                    Config.WindowLocation = Location;
+                    Config.WindowSize = Size;
+                }
+
                 Config.Save();
             }
         }


Bugfixes. Now compiles.

Bugfixes. Now compiles.

--- AES/AES.cs
+++ AES/AES.cs
@@ -1,5 +1,8 @@

 using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;

 namespace AES
 {
@@ -8,7 +11,7 @@
 		private static byte[] salted_string = Encoding.UTF8.GetBytes("Salted__");
 		private static MD5 md5 = MD5.Create();

-		public static void Decipher(byte[] Data, byte[] key, byte[] iv)
+		public static string Decipher(byte[] Data, byte[] key, byte[] iv)
         {

 			RijndaelManaged rijndael = new RijndaelManaged();
@@ -21,7 +24,7 @@

 			ICryptoTransform rijndaelDecryptor = rijndael.CreateDecryptor();

-			MemoryStream msDecrypt = new MemoryStream(enc_data);
+			MemoryStream msDecrypt = new MemoryStream(Data);
             CryptoStream csDecrypt = new CryptoStream(msDecrypt, rijndaelDecryptor, CryptoStreamMode.Read);
             StreamReader srDecrypt = new StreamReader(csDecrypt);


Fixed archive exploration order

Fixed archive exploration order

--- Archive.cs
+++ Archive.cs
@@ -89,11 +89,6 @@
             return Get<T>(locator.AsEnumerable());
         }

-        public static T Get<T>(String[] locatorPrefix, params String[] locatorSuffix)
-        {
-            return Get<T>(locatorPrefix.Concat(locatorSuffix));
-        }
-
         public static T Get<T>(IEnumerable<String> locator)
         {
             var resType = ResourceTypeFromType(typeof(T));
@@ -101,7 +96,8 @@
                 throw new FileNotFoundException(String.Join("/", locator));
             }

-            foreach (var archive in _mounted) {
+            for (int i = _mounted.Count - 1; i >= 0; --i) {
+                var archive = _mounted[i];
                 var resource = archive.Get(resType, locator);
                 if (resource != null) {
                     return (T) resource;


Don't report power if there aren't any power sources

Solar batteries don't count as they are transient sou

--- BARABAS.cs
+++ BARABAS.cs
@@ -3384,6 +3384,7 @@
 bool s_power() {
 	// determine if we need more uranium
 	bool above_high_watermark = aboveHighWatermark();
+	var max_pwr_output = getMaxReactorPowerOutput() + getMaxBatteryPowerOutput();

 	// if we have enough uranium ingots, business as usual
 	if (!above_high_watermark) {
@@ -3394,7 +3395,7 @@
 			prioritize_uranium = true;
 		}

-		if (!above_low_watermark) {
+		if (!above_low_watermark && max_pwr_output != 0) {
 			addAlert(BLUE_ALERT);
 		} else {
 			removeAlert(BLUE_ALERT);
@@ -3403,47 +3404,36 @@
 		removeAlert(BLUE_ALERT);
 		prioritize_uranium = false;
 	}
-
-	// figure out how much time we have on batteries and reactors
-	Decimal stored_power = getBatteryStoredPower() + getReactorStoredPower();
-
-	var max_pwr_draw = getMaxPowerDraw();
-	var cur_pwr_draw = getCurPowerDraw();
-
-	Decimal time;
-	if ((op_mode & OP_MODE_SHIP) > 0 && connected_to_base) {
-		if (max_pwr_draw == 0) {
-			time = 0;
-		} else {
+	status_report[STATUS_POWER_STATS] = "";
+
+	if (max_pwr_output != 0) {
+		// figure out how much time we have on batteries and reactors
+		Decimal stored_power = getBatteryStoredPower() + getReactorStoredPower();
+		// prevent division by zero
+		var max_pwr_draw = Math.Max(getMaxPowerDraw(), 0.001M);
+		var cur_pwr_draw = Math.Max(getCurPowerDraw(), 0.001M);
+		Decimal time;
+		string time_str;
+		if ((op_mode & OP_MODE_SHIP) > 0 && connected_to_base) {
 			time = Math.Round(stored_power / max_pwr_draw, 0);
-		}
-	} else {
-		if (cur_pwr_draw == 0) {
-			time = 0;
 		} else {
 			time = Math.Round(stored_power / cur_pwr_draw, 0);
 		}
-	}
-
-	var max_pwr_output = getMaxReactorPowerOutput() + getMaxBatteryPowerOutput();
-	if (max_pwr_output == 0) {
-		max_pwr_output = max_pwr_draw;
-	}
-	string time_str;
-	string max_str = String.Format("{0:0.0}%", max_pwr_draw / max_pwr_output * 100);
-	string cur_str = String.Format("{0:0.0}%", cur_pwr_draw / max_pwr_output * 100);
-	if (time > 300) {
-		time = Math.Floor(time / 60M);
-		if (time > 48) {
-			time = Math.Floor(time / 24M);
-			time_str = Convert.ToString(time) + " d";
+		if (time > 300) {
+			time = Math.Floor(time / 60M);
+			if (time > 48) {
+				time = Math.Floor(time / 24M);
+				time_str = Convert.ToString(time) + " d";
+			} else {
+				time_str = Convert.ToString(time) + " h";
+			}
 		} else {
-			time_str = Convert.ToString(time) + " h";
-		}
-	} else {
-		time_str = Convert.ToString(time) + " m";
-	}
-	status_report[STATUS_POWER_STATS] = String.Format("{0}/{1}/{2}", max_str, cur_str, time_str);
+			time_str = Convert.ToString(time) + " m";
+		}
+		string max_str = String.Format("{0:0.0}%", max_pwr_draw / max_pwr_output * 100);
+		string cur_str = String.Format("{0:0.0}%", cur_pwr_draw / max_pwr_output * 100);
+		status_report[STATUS_POWER_STATS] = String.Format("{0}/{1}/{2}", max_str, cur_str, time_str);
+	}

 	if (crisis_mode == CRISIS_MODE_NONE) {
 		bool can_refuel = (op_mode & OP_MODE_SHIP) > 0 && connected_to_base;


Redefining magenta and cyan alerts

Redefining magenta and cyan alerts

Cyan now means "oxygen or hydrogen"
Magenta now means "assemblers or refineries"

--- BARABAS.cs
+++ BARABAS.cs
@@ -9,8 +9,8 @@
  * - Red: storage 98% full
  * - Yellow: storage 75% full
  * - Blue: running out of power
- * - Magenta: refinery clogged
- * - Cyan: assembler clogged
+ * - Cyan: ice shortage (low oxygen or hydrogen)
+ * - Magenta: refinery or assembler clogged
  * - White: running out of materials
  * - Brown: oxygen leak detected
  * - Pink: unfinished/damaged blocks present
@@ -21,13 +21,13 @@
  * To exclude a block from being affected by BARABAS, have its name start with "X".
  *
  * Mandatory requirements:
+ * - Storage containers must be present.
  * - A timer to run this code (recommended timer value is one second)
- * - Storage containers must be present.
  * - Don't go to town with pistons and rotors - while things will likely work,
  *   it is not recommended and may produce weird results.
  *
  * Optional requirements:
- * - Group of text/LCD panels/beacons/antennas or lights named "BARABAS Notify",
+ * - Group of text/LCD panels/beacons/antennas/lights named "BARABAS Notify",
  *   used for notification and status reports.
  * - Text block named "BARABAS Config", used for storing configuration (if not
  *   present, automatic configuration will be used)
@@ -61,6 +61,9 @@
 Decimal oxygen_threshold = 15M;
 Decimal hydrogen_threshold = 0M;
 Decimal prev_pwr_draw = 0M;
+bool refineries_clogged = false;
+bool arc_furnaces_clogged = false;
+bool assemblers_clogged = false;

 bool push_ore_to_base = false;
 bool push_ingots_to_base = false;
@@ -296,8 +299,8 @@
 const int RED_ALERT = 0;
 const int YELLOW_ALERT = 1;
 const int BLUE_ALERT = 2;
-const int MAGENTA_ALERT = 3;
-const int CYAN_ALERT = 4;
+const int CYAN_ALERT = 3;
+const int MAGENTA_ALERT = 4;
 const int WHITE_ALERT = 5;
 const int PINK_ALERT = 6;
 const int BROWN_ALERT = 7;
@@ -318,8 +321,8 @@
 	new Alert(Color.Red, "Very low storage"),
 	new Alert(Color.Yellow, "Low storage"),
 	new Alert(Color.Blue, "Low power"),
-	new Alert(Color.DarkMagenta, "Refinery clogged"),
-	new Alert(Color.Cyan, "Assembler clogged"),
+	new Alert(Color.Cyan, "Ice shortage"),
+	new Alert(Color.DarkMagenta, "Clogged"),
 	new Alert(Color.White, "Material shortage"),
 	new Alert(Color.HotPink, "Damaged blocks"),
 	new Alert(Color.Chocolate, "Oxygen leak"),
@@ -592,7 +595,7 @@
 	}
 	local_refineries = new List < IMyTerminalBlock > (getBlocks());
 	filterBlocks < IMyRefinery > (local_refineries, null, "LargeRefinery");
-	bool alert = false;
+	refineries_clogged = false;
 	for (int i = 0; i < local_refineries.Count; i++) {
 		var refinery = local_refineries[i] as IMyRefinery;
 		var input_inv = refinery.GetInventory(0);
@@ -601,16 +604,11 @@
 		Decimal output_load = (Decimal) output_inv.CurrentVolume / (Decimal) output_inv.MaxVolume;
 		if (input_load > 0.98M || output_load > 0.98M || (!refinery.IsQueueEmpty && !refinery.IsProducing)) {
 			addBlockAlert(refinery, ALERT_CLOGGED);
-			alert = true;
+			refineries_clogged = true;
 		} else {
 			removeBlockAlert(refinery, ALERT_CLOGGED);
 		}
 		displayBlockAlerts(refinery);
-	}
-	if (alert) {
-		addAlert(MAGENTA_ALERT);
-	} else {
-		removeAlert(MAGENTA_ALERT);
 	}
 	return local_refineries;
 }
@@ -619,6 +617,7 @@
 	if (local_arc_furnaces != null && !force_update) {
 		return removeNulls(local_arc_furnaces, 2);
 	}
+	arc_furnaces_clogged = false;
 	local_arc_furnaces = new List < IMyTerminalBlock > (getBlocks());
 	filterBlocks < IMyRefinery > (local_arc_furnaces, null, "Blast Furnace");
 	bool alert = false;
@@ -630,16 +629,11 @@
 		Decimal output_load = (Decimal) output_inv.CurrentVolume / (Decimal) output_inv.MaxVolume;
 		if (input_load > 0.98M || output_load > 0.98M || (!refinery.IsQueueEmpty && !refinery.IsProducing)) {
 			addBlockAlert(refinery, ALERT_CLOGGED);
-			alert = true;
+			arc_furnaces_clogged = true;
 		} else {
 			removeBlockAlert(refinery, ALERT_CLOGGED);
 		}
 		displayBlockAlerts(refinery);
-	}
-	if (alert) {
-		addAlert(MAGENTA_ALERT);
-	} else {
-		removeAlert(MAGENTA_ALERT);
 	}
 	return local_arc_furnaces;
 }
@@ -655,6 +649,7 @@
 	if (local_assemblers != null && !force_update) {
 		return removeNulls(local_assemblers, 2);
 	}
+	assemblers_clogged = false;
 	local_assemblers = new List < IMyTerminalBlock > (getBlocks());
 	filterBlocks < IMyAssembler > (local_assemblers);
 	bool alert = false;
@@ -671,23 +666,18 @@
 			Decimal output_load = (Decimal) output_inv.CurrentVolume / (Decimal) output_inv.MaxVolume;
 			if (input_load > 0.98M || output_load > 0.98M) {
 				addBlockAlert(block, ALERT_CLOGGED);
-				alert = true;
+				assemblers_clogged = true;
 			} else {
 				removeBlockAlert(block, ALERT_CLOGGED);
 			}
 			if (!block.IsQueueEmpty && !block.IsProducing) {
 				addBlockAlert(block, ALERT_MATERIALS_MISSING);
-				alert = true;
+				assemblers_clogged = true;
 			} else {
 				removeBlockAlert(block, ALERT_MATERIALS_MISSING);
 			}
 			displayBlockAlerts(block);
 		}
-	}
-	if (alert) {
-		addAlert(CYAN_ALERT);
-	} else {
-		removeAlert(CYAN_ALERT);
 	}
 	return local_assemblers;
 }
@@ -3938,13 +3928,12 @@
 		if (alert) {
 			status_report[STATUS_OXYHYDRO_LEVEL] = String.Format("{0} / {1} WARNING!",
 					oxy_str, hydro_str);
-			addAlert(WHITE_ALERT);
+			addAlert(CYAN_ALERT);
 		} else {
 			status_report[STATUS_OXYHYDRO_LEVEL] = String.Format("{0} / {1}",
 					oxy_str, hydro_str);
-			removeAlert(WHITE_ALERT);
-		}
-		displayAntennaAlerts();
+			removeAlert(CYAN_ALERT);
+		}
 	}
 	return true;
 }
@@ -3985,6 +3974,12 @@
 		checkOxygenLeaks();
 	}

+	if (refineries_clogged || arc_furnaces_clogged || assemblers_clogged) {
+		addAlert(MAGENTA_ALERT);
+	} else {
+		removeAlert(MAGENTA_ALERT);
+	}
+
 	// display status updates
 	if (has_status_panels) {
 		displayStatusReport();


Also use new method to handle init errors correctly

--- osu.Framework/Audio/AudioManager.cs
+++ osu.Framework/Audio/AudioManager.cs
@@ -250,8 +250,7 @@
                 return true;

             // initialize new device
-            if (!InitBass(deviceIndex) && Bass.LastError != Errors.Already)
-                return false;
+            bool initSuccess = InitBass(deviceIndex);

             if (Bass.LastError == Errors.Already)
             {
@@ -260,11 +259,17 @@
                 // other fuzz.
                 Bass.CurrentDevice = deviceIndex;
                 FreeBass();
-                InitBass(deviceIndex);
+                initSuccess = InitBass(deviceIndex);
             }

             if (BassUtils.CheckFaulted(false))
                 return false;
+
+            if (!initSuccess)
+            {
+                Logger.Log("BASS failed to initialize but did not provide an error code", level: LogLevel.Error);
+                return false;
+            }

             Logger.Log($@"BASS Initialized
                           BASS Version:               {Bass.Version}


Exclude BASS default audio device from the audio device names list

--- osu.Framework/Audio/AudioManager.cs
+++ osu.Framework/Audio/AudioManager.cs
@@ -342,13 +342,13 @@

             audioDevices = updatedAudioDevices;

-            // Bass should always be providing "No sound" device
-            Trace.Assert(audioDevices.Count > 0, "Bass did not provide any audio devices.");
+            // Bass should always be providing "No sound" and "Default" device
+            Trace.Assert(audioDevices.Count >= 2, "Bass did not provide any audio devices.");

             onDevicesChanged();

             var oldDeviceNames = audioDeviceNames;
-            var newDeviceNames = audioDeviceNames = audioDevices.Skip(1).Where(d => d.IsEnabled).Select(d => d.Name).ToImmutableList();
+            var newDeviceNames = audioDeviceNames = audioDevices.Skip(2).Where(d => d.IsEnabled).Select(d => d.Name).ToImmutableList();

             var newDevices = newDeviceNames.Except(oldDeviceNames).ToList();
             var lostDevices = oldDeviceNames.Except(newDeviceNames).ToList();


Avoid IEnumerable laziness

Avoid IEnumerable laziness

--- osu.Framework/Input/ButtonStates.cs
+++ osu.Framework/Input/ButtonStates.cs
@@ -55,9 +55,9 @@
         /// Enumerates the differences between ourselves and a previous <see cref="ButtonStates{TButton}"/>.
         /// </summary>
         /// <param name="lastButtons">The previous <see cref="ButtonStates{TButton}"/>.</param>
-        public (IEnumerable<TButton> Released, IEnumerable<TButton> Pressed) EnumerateDifference(ButtonStates<TButton> lastButtons)
+        public ButtonStateDifference EnumerateDifference(ButtonStates<TButton> lastButtons)
         {
-            return (lastButtons.pressedButtons.Except(pressedButtons), pressedButtons.Except(lastButtons.pressedButtons));
+            return new ButtonStateDifference(lastButtons.Except(this).ToArray(), this.Except(lastButtons).ToArray());
         }

         /// <summary>
@@ -75,17 +75,22 @@
             return $@"{GetType().ReadableName()}({String.Join(" ", pressedButtons)})";
         }

-        public IEnumerator<TButton> GetEnumerator()
-        {
-            return ((IEnumerable<TButton>)pressedButtons).GetEnumerator();
-        }
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return ((IEnumerable<TButton>)pressedButtons).GetEnumerator();
-        }
+        public IEnumerator<TButton> GetEnumerator() => ((IEnumerable<TButton>)pressedButtons).GetEnumerator();
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

         // for collection initializer
         public void Add(TButton button) => SetPressed(button, true);
+
+        public struct ButtonStateDifference
+        {
+            public readonly IEnumerable<TButton> Released;
+            public readonly IEnumerable<TButton> Pressed;
+
+            public ButtonStateDifference(IEnumerable<TButton> released, IEnumerable<TButton> pressed)
+            {
+                Released = released;
+                Pressed = pressed;
+            }
+        }
     }
 }


Fix enumerating HoveredDrawables causing excessive allocations

--- osu.Framework/Input/InputManager.cs
+++ osu.Framework/Input/InputManager.cs
@@ -107,7 +107,7 @@
         /// Top-down in this case means reverse draw order, i.e. the front-most visible
         /// <see cref="Drawable"/> first, and <see cref="Container"/>s after their children.
         /// </summary>
-        public IReadOnlyList<Drawable> HoveredDrawables => hoveredDrawables;
+        public IReadOnlyList<Drawable> HoveredDrawables => hoveredDrawables.AsSlimReadOnly();

         /// <summary>
         /// Contains all <see cref="Drawable"/>s in top-down order which are considered


Fix initial mouse position being potentially in wrong coordinat… (#2940)

Fix initial mouse position being potentially in wrong coordinate space

Co-authored-by: Dean Herbert <1db828bcd41de75377dce59825af73ae7fca5651@ppy.sh>

--- osu.Framework/Input/InputManager.cs
+++ osu.Framework/Input/InputManager.cs
@@ -130,6 +130,15 @@
             }
         }

+        protected override void LoadComplete()
+        {
+            base.LoadComplete();
+
+            // Set mouse position to zero in input manager local space instead of screen space zero.
+            // This ensures initial mouse position is non-negative in nested input managers whose origin is not (0, 0).
+            CurrentState.Mouse.Position = ToScreenSpace(Vector2.Zero);
+        }
+
         /// <summary>
         /// Create a <see cref="MouseButtonEventManager"/> for a specified mouse button.
         /// </summary>


Fix invalidation source not being passed down

--- osu.Framework/Layout/LayoutValue.cs
+++ osu.Framework/Layout/LayoutValue.cs
@@ -15,7 +15,7 @@
         /// <param name="invalidationCondition">Any extra conditions that must be satisfied before this <see cref="LayoutValue"/> is invalidated.</param>
         /// <param name="invalidationSource">The source of the invalidation.</param>
         public LayoutValue(Invalidation invalidationType, InvalidationConditionDelegate invalidationCondition = null, InvalidationSource invalidationSource = InvalidationSource.Default)
-            : base(invalidationType, invalidationCondition)
+            : base(invalidationType, invalidationCondition, invalidationSource)
         {
         }

@@ -34,7 +34,7 @@
         /// <param name="invalidationCondition">Any extra conditions that must be satisfied before this <see cref="LayoutValue{T}"/> is invalidated.</param>
         /// <param name="invalidationSource">The source of the invalidation.</param>
         public LayoutValue(Invalidation invalidationType, InvalidationConditionDelegate invalidationCondition = null, InvalidationSource invalidationSource = InvalidationSource.Default)
-            : base(invalidationType, invalidationCondition)
+            : base(invalidationType, invalidationCondition, invalidationSource)
         {
         }


Remove unnecessary locking; operations are now thread-safe

--- osu.Game/Beatmaps/BeatmapManager.cs
+++ osu.Game/Beatmaps/BeatmapManager.cs
@@ -287,8 +287,7 @@
         /// <param name="beatmapSet">The beatmap set to delete.</param>
         public void Delete(BeatmapSetInfo beatmapSet)
         {
-            lock (beatmaps)
-                if (!beatmaps.Delete(beatmapSet)) return;
+            if (!beatmaps.Delete(beatmapSet)) return;

             if (!beatmapSet.Protected)
                 files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
@@ -298,21 +297,13 @@
         /// Delete a beatmap difficulty.
         /// </summary>
         /// <param name="beatmap">The beatmap difficulty to hide.</param>
-        public void Hide(BeatmapInfo beatmap)
-        {
-            lock (beatmaps)
-                beatmaps.Hide(beatmap);
-        }
+        public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);

         /// <summary>
         /// Restore a beatmap difficulty.
         /// </summary>
         /// <param name="beatmap">The beatmap difficulty to restore.</param>
-        public void Restore(BeatmapInfo beatmap)
-        {
-            lock (beatmaps)
-                beatmaps.Restore(beatmap);
-        }
+        public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);

         /// <summary>
         /// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged.
@@ -321,8 +312,7 @@
         /// <param name="beatmapSet">The beatmap to restore.</param>
         private void undelete(BeatmapStore beatmaps, FileStore files, BeatmapSetInfo beatmapSet)
         {
-            lock (beatmaps)
-                if (!beatmaps.Undelete(beatmapSet)) return;
+            if (!beatmaps.Undelete(beatmapSet)) return;

             if (!beatmapSet.Protected)
                 files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
@@ -357,11 +347,7 @@
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>The first result for the provided query, or null if no results were found.</returns>
-        public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query)
-        {
-            lock (beatmaps)
-                return beatmaps.BeatmapSets.FirstOrDefault(query);
-        }
+        public BeatmapSetInfo QueryBeatmapSet(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.FirstOrDefault(query);

         /// <summary>
         /// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
@@ -375,33 +361,21 @@
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>Results from the provided query.</returns>
-        public List<BeatmapSetInfo> QueryBeatmapSets(Func<BeatmapSetInfo, bool> query)
-        {
-            lock (beatmaps)
-                return beatmaps.BeatmapSets.Where(query).ToList();
-        }
+        public List<BeatmapSetInfo> QueryBeatmapSets(Func<BeatmapSetInfo, bool> query) => beatmaps.BeatmapSets.Where(query).ToList();

         /// <summary>
         /// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>The first result for the provided query, or null if no results were found.</returns>
-        public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query)
-        {
-            lock (beatmaps)
-                return beatmaps.Beatmaps.FirstOrDefault(query);
-        }
+        public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.FirstOrDefault(query);

         /// <summary>
         /// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>Results from the provided query.</returns>
-        public List<BeatmapInfo> QueryBeatmaps(Func<BeatmapInfo, bool> query)
-        {
-            lock (beatmaps)
-                return beatmaps.Beatmaps.Where(query).ToList();
-        }
+        public List<BeatmapInfo> QueryBeatmaps(Func<BeatmapInfo, bool> query) => beatmaps.Beatmaps.Where(query).ToList();

         /// <summary>
         /// Creates an <see cref="ArchiveReader"/> from a valid storage path.
@@ -437,9 +411,7 @@
             var hash = hashable.ComputeSHA2Hash();

             // check if this beatmap has already been imported and exit early if so.
-            BeatmapSetInfo beatmapSet;
-            lock (beatmaps)
-                beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash);
+            var beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == hash);

             if (beatmapSet != null)
             {
@@ -523,8 +495,7 @@
         /// <returns>A list of available <see cref="BeatmapSetInfo"/>.</returns>
         public List<BeatmapSetInfo> GetAllUsableBeatmapSets()
         {
-            lock (beatmaps)
-                return beatmaps.BeatmapSets.Where(s => !s.DeletePending).ToList();
+            return beatmaps.BeatmapSets.Where(s => !s.DeletePending).ToList();
         }

         protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap


Revert "Swap placements of "hold for menu" and key counter"

This reverts commit 5bcb7c167c5ae837be46f56752dd41c1236cec0b.

--- osu.Game/Screens/Play/HUDOverlay.cs
+++ osu.Game/Screens/Play/HUDOverlay.cs
@@ -111,11 +111,8 @@
                     Direction = FillDirection.Vertical,
                     Children = new Drawable[]
                     {
+                        KeyCounter = CreateKeyCounter(),
                         HoldToQuit = CreateHoldForMenuButton(),
-                        KeyCounter = CreateKeyCounter().With(k =>
-                        {
-                            k.Margin = new MarginPadding { Horizontal = 10 };
-                        }),
                     }
                 }
             };
@@ -238,11 +235,13 @@
             {
                 PlayerSettingsOverlay.Show();
                 ModDisplay.FadeIn(200);
+                KeyCounter.Margin = new MarginPadding(10) { Bottom = 30 };
             }
             else
             {
                 PlayerSettingsOverlay.Hide();
                 ModDisplay.Delay(2000).FadeOut(200);
+                KeyCounter.Margin = new MarginPadding(10);
             }

             updateVisibility();


Fix hit error potentially not displaying with null hitwindows

--- osu.Game/Screens/Play/HUDOverlay.cs
+++ osu.Game/Screens/Play/HUDOverlay.cs
@@ -259,7 +259,9 @@
             Margin = new MarginPadding { Top = 20, Right = 10 },
         };

-        protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.Objects.FirstOrDefault()?.HitWindows);
+        protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(
+            scoreProcessor,
+            drawableRuleset.Objects.Concat(drawableRuleset.Objects.SelectMany(h => h.NestedHitObjects)).FirstOrDefault(h => h.HitWindows != null)?.HitWindows);

         protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay();


Apply some of Duane's patches

Apply some of Duane's patches

svn path=/trunk/maccore/; revision=160201

--- src/foundation.cs
+++ src/foundation.cs
@@ -716,6 +716,10 @@

 		[Export ("description")]
 		string Description { get; }
+
+		[Export ("dateWithTimeIntervalSinceNow:")]
+		[Static]
+		NSDate FromTimeIntervalSinceNow (double secs);
 	}

 	[BaseType (typeof (NSObject))]
@@ -1023,6 +1027,18 @@

 		[Export ("nextDaylightSavingTimeTransitionAfterDate:")]
 		NSDate NextDaylightSavingTimeTransitionAfter (NSDate date);
+
+		[Static, Export ("timeZoneWithName:")]
+		NSTimeZone FromName (string tzName);
+
+		[Static, Export ("localTimeZone")]
+		NSTimeZone LocalTimeZone { get; }
+
+		[Export ("description")]
+		string Description { get; }
+
+		[Export ("secondsFromGMT")]
+		int SecondsFromGMT { get; }

 		[Export ("defaultTimeZone"), Static]
 		NSTimeZone DefaultTimeZone { get; set; }


(TgaFormat): RLE compression implementation.

--- GameRes/ImageTGA.cs
+++ GameRes/ImageTGA.cs
@@ -23,6 +23,7 @@
 // IN THE SOFTWARE.
 //

+using System;
 using System.IO;
 using System.Text;
 using System.Windows;
@@ -55,61 +56,12 @@
             var meta = metadata as TgaMetaData;
             if (null == meta)
                 throw new System.ArgumentException ("TgaFormat.Read should be supplied with TgaMetaData", "metadata");
-            int colormap_size = meta.ColormapLength * meta.ColormapDepth / 8;
-            int width  = (int)meta.Width;
-            int height = (int)meta.Height;
-            int bpp    = meta.BPP;
-            long image_offset = meta.ColormapOffset;
-            if (1 == meta.ColormapType)
-                image_offset += colormap_size;
-            switch (meta.ImageType)
-            {
-            case 1:  // Uncompressed, color-mapped images.
-            case 3:  // Uncompressed, black and white images.
-            case 9:  // Runlength encoded color-mapped images.
-            case 10: // Runlength encoded RGB images.
-            case 11: // Compressed, black and white images.
-            case 32: // Compressed color-mapped data, using Huffman, Delta, and
-                    // runlength encoding.
-            case 33: // Compressed color-mapped data, using Huffman, Delta, and
-                    // runlength encoding.  4-pass quadtree-type process.
-                throw new System.NotImplementedException();
-            default:
-                throw new InvalidFormatException();
-            case 2:  // Uncompressed, RGB images.
-                {
-                    PixelFormat pixel_format;
-                    switch (bpp)
-                    {
-                    default: throw new InvalidFormatException();
-                    case 24: pixel_format = PixelFormats.Bgr24; break;
-                    case 32: pixel_format = PixelFormats.Bgra32; break;
-                    case 15: pixel_format = PixelFormats.Bgr555; break;
-                    case 16: pixel_format = PixelFormats.Bgr565; break;
-                    }
-                    stream.Position = image_offset;
-                    int stride = width*((bpp+7)/8);
-                    byte[] data = new byte[stride*height];
-                    if (0 != (meta.Descriptor & 0x20))
-                    {
-                        if (data.Length != stream.Read (data, 0, data.Length))
-                            throw new InvalidFormatException();
-                    }
-                    else
-                    {
-                        for (int row = height-1; row >= 0; --row)
-                        {
-                            if (stride != stream.Read (data, row*stride, stride))
-                                throw new InvalidFormatException();
-                        }
-                    }
-                    var bitmap = BitmapSource.Create (width, height, 96, 96, pixel_format, null,
-                                                      data, stride);
-                    bitmap.Freeze();
-                    return new ImageData (bitmap, meta);
-                }
-                throw new InvalidFormatException();
-            }
+            var reader = new Reader (stream, meta);
+            var pixels = reader.Unpack();
+            var bitmap = BitmapSource.Create ((int)meta.Width, (int)meta.Height, 96, 96,
+                                              reader.Format, reader.Palette, pixels, reader.Stride);
+            bitmap.Freeze();
+            return new ImageData (bitmap, meta);
         }

         public override void Write (Stream stream, ImageData image)
@@ -220,5 +172,134 @@
                 };
             }
         }
+
+        internal class Reader
+        {
+            Stream          m_input;
+            TgaMetaData     m_meta;
+            int             m_width;
+            int             m_height;
+            int             m_stride;
+            byte[]          m_data;
+            long            m_image_offset;
+
+            public PixelFormat    Format { get; private set; }
+            public BitmapPalette Palette { get; private set; }
+            public int            Stride { get { return m_stride; } }
+            public byte[]           Data { get { return m_data; } }
+
+            public Reader (Stream stream, TgaMetaData meta)
+            {
+                m_input = stream;
+                m_meta = meta;
+                switch (meta.BPP)
+                {
+                default: throw new InvalidFormatException();
+                case 8:  throw new NotImplementedException();
+                case 15: Format = PixelFormats.Bgr555; break;
+                case 16: Format = PixelFormats.Bgr555; break;
+                case 32: Format = PixelFormats.Bgra32; break;
+                case 24:
+                    if (8 == (meta.Descriptor & 0xf))
+                        Format = PixelFormats.Bgr32;
+                    else
+                        Format = PixelFormats.Bgr24;
+                    break;
+                }
+                int colormap_size = meta.ColormapLength * meta.ColormapDepth / 8;
+                m_width  = (int)meta.Width;
+                m_height = (int)meta.Height;
+                m_stride = m_width * ((meta.BPP+7) / 8);
+                m_image_offset = meta.ColormapOffset;
+                if (1 == meta.ColormapType)
+                    m_image_offset += colormap_size;
+                m_data = new byte[m_stride*m_height];
+            }
+
+            public byte[] Unpack ()
+            {
+                switch (m_meta.ImageType)
+                {
+                case 1:  // Uncompressed, color-mapped images.
+                case 3:  // Uncompressed, black and white images.
+                case 9:  // Runlength encoded color-mapped images.
+                case 11: // Compressed, black and white images.
+                case 32: // Compressed color-mapped data, using Huffman, Delta, and
+                        // runlength encoding.
+                case 33: // Compressed color-mapped data, using Huffman, Delta, and
+                        // runlength encoding.  4-pass quadtree-type process.
+                    throw new NotImplementedException();
+                default:
+                    throw new InvalidFormatException();
+                case 2:  // Uncompressed, RGB images.
+                    ReadRGB();
+                    break;
+                case 10: // Runlength encoded RGB images.
+                    ReadRLE ((m_meta.BPP+7)/8);
+                    break;
+                }
+                return Data;
+            }
+
+            void ReadRGB ()
+            {
+                m_input.Position = m_image_offset;
+                if (0 != (m_meta.Descriptor & 0x20))
+                {
+                    if (m_data.Length != m_input.Read (m_data, 0, m_data.Length))
+                        throw new InvalidFormatException();
+                }
+                else
+                {
+                    for (int row = m_height-1; row >= 0; --row)
+                    {
+                        if (m_stride != m_input.Read (m_data, row*m_stride, m_stride))
+                            throw new InvalidFormatException();
+                    }
+                }
+            }
+
+            void ReadRLE (int pixel_size)
+            {
+                m_input.Position = m_image_offset;
+                for (int dst = 0; dst < m_data.Length;)
+                {
+                    int packet = m_input.ReadByte();
+                    if (-1 == packet)
+                        break;
+                    int count = (packet & 0x7f) + 1;
+                    if (0 != (packet & 0x80))
+                    {
+                        if (pixel_size != m_input.Read (m_data, dst, pixel_size))
+                            break;
+                        int src = dst;
+                        dst += pixel_size;
+                        for (int i = 1; i < count && dst < m_data.Length; ++i)
+                        {
+                            Buffer.BlockCopy (m_data, src, m_data, dst, pixel_size);
+                            dst += pixel_size;
+                        }
+                    }
+                    else
+                    {
+                        count *= pixel_size;
+                        if (count != m_input.Read (m_data, dst, count))
+                            break;
+                        dst += count;
+                    }
+                }
+                if (0 == (m_meta.Descriptor & 0x20))
+                {
+                    byte[] flipped = new byte[m_stride*m_height];
+                    int dst = 0;
+                    for (int src = m_stride*(m_height-1); src >= 0; src -= m_stride)
+                    {
+                        Buffer.BlockCopy (m_data, src, flipped, dst, m_stride);
+                        dst += m_stride;
+                    }
+                    m_data = flipped;
+                }
+            }
+        }
     }
 }


updated API Key for Bing Maps

updated API Key for Bing Maps

--- GeoCodeFile/run.csx
+++ GeoCodeFile/run.csx
@@ -45,7 +45,7 @@
 }

 private static LatLong GeoCodePostCode(string AddressToParse) {
-    BingMapsGeocoder geocoder = new BingMapsGeocoder("ArHjdhrHGD-Pg81_DVmagV1XF2_7n-5xvrZSLEG-UAKUXOOg-pA4TsZGglN10EJ7");
+    BingMapsGeocoder geocoder = new BingMapsGeocoder(<YOUR API CODE>);
     IEnumerable<BingAddress> addresses = geocoder.Geocode(AddressToParse);
     LatLong result = new LatLong();
     result.Lat = addresses.First().Coordinates.Latitude;


Update CardIds

Update CardIds

--- HearthDb/CardIds.cs
+++ HearthDb/CardIds.cs
@@ -669,7 +669,6 @@
                 public const string ZolaTheGorgon = "LOOT_516";
                 public const string MasterOakheart = "LOOT_521";
                 public const string TheDarkness = "LOOT_526";
-                public const string TheDarkness_DarknessAwaitsEnchantment = "LOOT_526e";
                 public const string VoidRipper = "LOOT_529";
                 public const string SpitefulSummoner = "LOOT_539";
                 public const string Dragonhatcher = "LOOT_540";
@@ -4628,6 +4627,7 @@
                 public const string MurmuringElemental_MurmuringEnchantment = "LOOT_517e2";
                 public const string SeepingOozeling_AlbinoChameleonEnchantment = "LOOT_520e";
                 public const string TheDarkness_TheDarkness = "LOOT_526d";
+                public const string TheDarkness_DarknessAwaitsEnchantment = "LOOT_526e";
                 public const string TheDarkness_DarknessCandleDetectEnchantment = "LOOT_526et";
                 public const string TheDarkness_DarknessCandleToken = "LOOT_526t";
                 public const string VoidRipper_VoidShiftEnchantment = "LOOT_529e";


Added an optional mutex to LogFile to handle cross process logging to the same file.


add use stream method

add use stream method

--- binding/c#/IP2Region/DBSearcher.cs
+++ binding/c#/IP2Region/DBSearcher.cs
@@ -23,7 +23,7 @@
         /// <summary>
         /// db file access handler
         /// </summary>
-        private FileStream _raf = null;
+        private Stream _raf = null;

         /// <summary>
         /// header blocks buffer
@@ -75,6 +75,18 @@

         public DbSearcher(string dbFile) : this(null, dbFile) { }

+        public DbSearcher(DbConfig dbConfig, Stream dbFileStream)
+        {
+            if (_dbConfig == null)
+            {
+                _dbConfig = dbConfig;
+            }
+
+            _raf = dbFileStream;
+        }
+
+        public DbSearcher(Stream dbFileStream) : this(null, dbFileStream) { }
+
         #region Sync Methods
         /// <summary>
         /// Get the region with a int ip address with memory binary search algorithm.


Increase Facebook photo size. Fix bgo#615607

--- extensions/Exporters/FacebookExport/FacebookExport.cs
+++ extensions/Exporters/FacebookExport/FacebookExport.cs
@@ -259,7 +259,7 @@

 	public class FacebookExport : IExporter
 	{
-		private int size = 604;
+		private int size = 720;
 		private int max_photos_per_album = 200;
 		FacebookExportDialog dialog;
 		ThreadProgressDialog progress_dialog;


Updated for logging in. Also better mod name handling.


Remove unused using

Remove unused using


MethodDef

MethodDef


build: add an array test for GConfSchemaExtractor

--- build/GConfSchemaExtractorTests.cs
+++ build/GConfSchemaExtractorTests.cs
@@ -177,6 +177,42 @@
 </gconfschemafile>"
             .Trim ()));
         }
+
+        internal class ArrayType {
+            public static readonly SchemaEntry<string[]> CurrentFiltersSchema = new SchemaEntry<string[]> (
+                "sources.fsq", "current_filters",
+                new string[] { "album", "artist" },
+                null,
+                null
+            );
+        }
+
+        [Test]
+        public void SchemaWithArray ()
+        {
+            StringBuilder result = GConfSchemaExtractorProgram.Extract (new Type [] { typeof (ArrayType) });
+
+            Assert.That (result, Is.Not.Null);
+            Assert.That (result.ToString ().Trim (), Is.EqualTo (@"
+<?xml version=""1.0""?>
+<gconfschemafile>
+  <schemalist>
+    <schema>
+      <key>/schemas/apps/banshee/sources/fsq/current_filters</key>
+      <applyto>/apps/banshee/sources/fsq/current_filters</applyto>
+      <owner>banshee</owner>
+      <type>list</type>
+      <list_type>string</list_type>
+      <default>[album,artist]</default>
+      <locale name=""C"">
+        <short></short>
+        <long></long>
+      </locale>
+    </schema>
+  </schemalist>
+</gconfschemafile>"
+                .Trim ()));
+        }
     }
 }


If we have login saved, login directly

If we have login saved, login directly

--- KraxbotOSS/Form1.cs
+++ KraxbotOSS/Form1.cs
@@ -221,11 +221,21 @@
                 return;
             }
             Log("Ok");
-            Invoke((MethodInvoker)delegate
-            {
-                btnLogin.Enabled = true;
+            if (File.Exists(Path.Combine(configPath, "user")))
+            {
                 lNetwork.Text = "Network: Connected";
-            });
+                string[] user = File.ReadAllLines(Path.Combine(configPath, "user"));
+                FormLogin.Username = user[0];
+                Login(user[0]);
+            }
+            else
+            {
+                Invoke((MethodInvoker)delegate
+                {
+                    btnLogin.Enabled = true;
+                    lNetwork.Text = "Network: Connected";
+                });
+            }
         }
         void OnDisconnected(SteamClient.DisconnectedCallback callback)
         {


use the name that's not Part and remove a spammy trace

--- ksp_plugin_adapter/ksp_plugin_adapter.cs
+++ ksp_plugin_adapter/ksp_plugin_adapter.cs
@@ -423,8 +423,6 @@
       String[] serializations = node.GetValues(principia_key);
       Log.Info("Serialization has " + serializations.Length + " chunks");
       foreach (String serialization in serializations) {
-        Log.Info("serialization is " + serialization.Length +
-                 " characters long");
         Interface.DeserializePlugin(serialization,
                                     serialization.Length,
                                     ref deserializer,
@@ -948,7 +946,7 @@
           foreach (Part part in vessel.parts.Where((part) => part.rb != null)) {
             plugin_.InsertOrKeepLoadedPart(
                 part.flightID,
-                part.partName,
+                part.name,
                 part.physicsMass,
                 vessel.id.ToString(),
                 vessel.mainBody.flightGlobalsIndex,


Ignore ENDLIFE at the end of script

Ignore ENDLIFE at the end of script

--- LifeDISA/Program.cs
+++ LifeDISA/Program.cs
@@ -176,6 +176,12 @@
 					continue;
 				}

+				if((life == LifeEnum.ENDLIFE && pos == allBytes.Length - 2))
+				{
+					pos += 2;
+					continue;
+				}
+
 				string lifeString = life.ToString();
 				if(lifeString.StartsWith("IF")) lifeString = "IF";
 				else if(lifeString == "MULTI_CASE") lifeString = "CASE";
@@ -250,7 +256,7 @@
 						//detect if else
 						int beforeGoto = pos+curr*2 - 4;
 						int next = ReadShort(allBytes[beforeGoto+0], allBytes[beforeGoto+1]);
-						if(next == (int)LifeEnum.GOTO)
+						if(next == (int)LifeEnum.GOTO) //will fail if there is 0x10 (eg: constant) right before end of if
 						{
 							gotosToIgnore.Add(beforeGoto);
 							elseIndent.Add(pos+curr*2);


Must invoke remote, not internal hub

Must invoke remote, not internal hub

--- relmah/src/RElmah.Host/Hubs/Notifiers.cs
+++ relmah/src/RElmah.Host/Hubs/Notifiers.cs
@@ -37,22 +37,22 @@

     public class BackendNotifier : IBackendNotifier
     {
+        private readonly IHubProxy _proxy;
+
         public BackendNotifier(string endpoint, IErrorsInbox errorsInbox)
         {
             var connection = new HubConnection(endpoint);

-            var proxy = connection.CreateHubProxy("relmah-backend");
+            _proxy = connection.CreateHubProxy("relmah-backend");

-            proxy.On<ErrorPayload>(
-                "error",
-                p => errorsInbox.Post(p));
+            _proxy.On<ErrorPayload>("error", p => errorsInbox.Post(p));

             connection.Start();
         }

         public void Error(ErrorPayload payload)
         {
-            BackendHub.Error(payload);
+            _proxy.Invoke("Error", payload);
         }
     }
 }


missing case

missing case

--- mHttp/Http/Handlers/StaticFileHandler.cs
+++ mHttp/Http/Handlers/StaticFileHandler.cs
@@ -92,7 +92,7 @@
                 cachedFile = new CachedFile(fileResponse, gzipFunc != null ? fileResponse.GZip(gzipFunc) : null);
                 cache[fullName] = cachedFile;

-                return req.IsAcceptGZip() ? cachedFile.GZippedResponse : cachedFile.Response;
+                return req.IsAcceptGZip() && cachedFile.GZippedResponse != null ? cachedFile.GZippedResponse : cachedFile.Response;
             }
         }