Finalize the Ini class.

Finalize the Ini class.

--- src/SilDev/Ini.cs
+++ src/SilDev/Ini.cs
@@ -5,7 +5,7 @@
 // ==============================================
 //
 // Filename: Ini.cs
-// Version:  2017-05-19 05:42
+// Version:  2017-05-21 10:47
 //
 // Copyright (c) 2017, Si13n7 Developments (r)
 // All rights reserved.
@@ -23,22 +23,15 @@
     using System.Linq;
     using System.Text;
     using System.Text.RegularExpressions;
-    using Properties;

     /// <summary>
-    ///     Provides static methods for the handling of initialization files.
+    ///     Provides the functionality to handle the INI format.
     /// </summary>
     public static class Ini
     {
-        private static volatile Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>> _cachedFiles;
-
-        private static Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>> CachedFiles
-        {
-            get { return _cachedFiles; }
-            set { _cachedFiles = value; }
-        }
-
-        private const int MaxCacheSize = 8;
+        private const string NonSectionId = "\0\u0002(NON-SECTION)\u0003\0";
+
+        private static Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>> CachedFiles { get; set; }

         private static void InitializeCache(int code, string section = null, string key = null)
         {
@@ -61,12 +54,102 @@
                 CachedFiles[code][section][key] = new List<string>();
         }

+        /// <summary>
+        ///     Save the cached data to the specified file.
+        /// </summary>
+        /// <param name="cacheFilePath">
+        ///     The full file path of the cache file to create.
+        /// </param>
+        /// <param name="fileOrContent">
+        ///     The full file path or content of an INI file. If this parameter is NULL, all
+        ///     cached data are saved.
+        /// </param>
+        public static void SaveCache(string cacheFilePath, string fileOrContent)
+        {
+            if (CachedFiles?.Any() != true)
+                return;
+            var cache = CachedFiles;
+            var file = fileOrContent ?? GetFile();
+            if (!string.IsNullOrEmpty(file))
+            {
+                var code = GetCode(file);
+                if (CodeExists(code))
+                    foreach (var data in CachedFiles)
+                    {
+                        if (data.Key != code)
+                            continue;
+                        cache = new Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>> { { data.Key, data.Value } };
+                        break;
+                    }
+            }
+            var bytes = cache?.SerializeObject();
+            if (bytes == null)
+                return;
+            var path = PathEx.Combine(cacheFilePath);
+            if (!PathEx.IsValidPath(path))
+                return;
+            try
+            {
+                File.WriteAllBytes(path, bytes.Zip());
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+            }
+        }
+
+        /// <summary>
+        ///     <para>
+        ///         Loads the data of a cache file into memory.
+        ///     </para>
+        ///     <para>
+        ///         Please note that <see cref="MaxCacheSize"/> is ignored in this case.
+        ///     </para>
+        /// </summary>
+        /// <param name="cacheFilePath">
+        ///     The full path of a cache file.
+        /// </param>
+        public static void LoadCache(string cacheFilePath)
+        {
+            var path = PathEx.Combine(cacheFilePath);
+            if (!File.Exists(path))
+                return;
+            try
+            {
+                var cache = File.ReadAllBytes(path).Unzip()?.DeserializeObject<Dictionary<int, Dictionary<string, Dictionary<string, List<string>>>>>();
+                if (cache == null)
+                    return;
+                foreach (var data in cache)
+                {
+                    var code = data.Key;
+                    if (code == 0)
+                        return;
+                    InitializeCache(code);
+                    CachedFiles[code] = data.Value;
+                }
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+            }
+        }
+
+        /// <summary>
+        ///     Gets or sets the maximum number of cached files.
+        /// </summary>
+        public static int MaxCacheSize { get; set; } = 8;
+
+        /// <summary>
+        ///     Specifies a sequence of section names to be sorted first.
+        /// </summary>
+        public static IEnumerable<string> SortBySections { get; set; }
+
         private static Dictionary<string, Dictionary<string, List<string>>> SortHelper(this Dictionary<string, Dictionary<string, List<string>>> source)
         {
             var comparer = new Comparison.AlphanumericComparer();
-            var sorted = source.OrderBy(x => !x.Key.Equals(Resources.Ini_NonSection)).ThenBy(x => x.Key, comparer)
-                               .ToDictionary(x => x.Key, x => x.Value.OrderBy(y => y.Key, comparer)
-                                                               .ToDictionary(y => y.Key, y => y.Value));
+            var sorted = source.OrderBy(x => !x.Key.Equals(NonSectionId))
+                               .ThenBy(x => !SortBySections.ContainsEx(x.Key))
+                               .ThenBy(x => x.Key, comparer).ToDictionary(x => x.Key, x => x.Value.SortHelper());
             return sorted;
         }

@@ -84,7 +167,7 @@
             return sorted;
         }

-        private static volatile string _tmpFileGuid;
+        private static string _tmpFileGuid;

         private static string TmpFileGuid
         {
@@ -97,7 +180,7 @@
             }
         }

-        private static volatile string _filePath;
+        private static string _filePath;

         /// <summary>
         ///     Gets or sets a default INI file.
@@ -128,9 +211,6 @@
             }
         }

-        private static int GetCode(this string str) =>
-            str?.ToLower().GetHashCode() ?? -1;
-
         /// <summary>
         ///     Gets the full path of the default INI file.
         /// </summary>
@@ -146,6 +226,21 @@
         public static bool SetFile(params string[] paths) =>
             File.Exists(FilePath = PathEx.Combine(paths));

+        private static bool CodeExists(int code) =>
+            code != -1 && CachedFiles?.ContainsKey(code) == true && CachedFiles[code]?.Any() == true;
+
+        private static int GetCode(string fileOrContent)
+        {
+            var source = fileOrContent ?? GetFile();
+            if (string.IsNullOrWhiteSpace(source))
+                throw new ArgumentNullException(nameof(source));
+            var path = PathEx.Combine(source);
+            if (!File.Exists(path))
+                path = TmpFileGuid;
+            var code = path?.ToLower().GetHashCode() ?? -1;
+            return code;
+        }
+
         /// <summary>
         ///     Removes the read content of an INI file from cache.
         /// </summary>
@@ -156,13 +251,7 @@
         {
             try
             {
-                var source = fileOrContent ?? GetFile();
-                if (string.IsNullOrWhiteSpace(source))
-                    throw new ArgumentNullException(nameof(source));
-                var path = PathEx.Combine(source);
-                if (!File.Exists(path))
-                    path = TmpFileGuid;
-                var code = path.GetCode();
+                var code = GetCode(fileOrContent);
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));
                 if (CachedFiles?.ContainsKey(code) == true)
@@ -176,9 +265,80 @@
             return false;
         }

-        /// <summary>
-        ///     Removes the full specified section of an INI file or an INI file formatted
-        ///     string value.
+        private static string FindSection(int code, string section)
+        {
+            var sct = section?.Trim();
+            if (string.IsNullOrEmpty(sct))
+                return NonSectionId;
+            if (!CodeExists(code) || CachedFiles[code].ContainsKey(sct))
+                return sct;
+            var newSct = CachedFiles[code].Keys.FirstOrDefault(x => x.EqualsEx(sct));
+            if (!string.IsNullOrEmpty(newSct) && !sct.Equals(newSct))
+                return newSct;
+            return sct;
+        }
+
+        private static bool SectionExists(int code, string section) =>
+            !string.IsNullOrEmpty(section) && CodeExists(code) && CachedFiles[code]?.ContainsKey(section) == true && CachedFiles[code][section]?.Any() == true;
+
+        /// <summary>
+        ///     Retrieves all section names of an INI file or an INI file formatted string value.
+        /// </summary>
+        /// <param name="fileOrContent">
+        ///     The full file path or content of an INI file.
+        /// </param>
+        /// <param name="sorted">
+        ///     true to sort the sections; otherwise, false.
+        /// </param>
+        public static List<string> GetSections(string fileOrContent = null, bool sorted = true)
+        {
+            try
+            {
+                var code = GetCode(fileOrContent);
+                if (code == -1)
+                    throw new ArgumentOutOfRangeException(nameof(code));
+                if (!CodeExists(code))
+                    ReadAll(fileOrContent);
+                if (CodeExists(code))
+                {
+                    var output = CachedFiles[code].Keys.ToList();
+                    if (output.Contains(NonSectionId))
+                        output.Remove(NonSectionId);
+                    if (sorted)
+                        output = output.SortHelper();
+                    return output;
+                }
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+            }
+            return new List<string>();
+        }
+
+        /// <summary>
+        ///     Retrieves all section names of an INI file or an INI file formatted string value.
+        /// </summary>
+        /// <param name="sorted">
+        ///     true to sort the sections; otherwise, false.
+        /// </param>
+        public static List<string> GetSections(bool sorted) =>
+            GetSections(null, sorted);
+
+        private static bool RemoveSection(int code, string section)
+        {
+            if (!CodeExists(code))
+                return true;
+            var sct = FindSection(code, section);
+            if (!CachedFiles[code].ContainsKey(sct))
+                return true;
+            CachedFiles[code].Remove(sct);
+            return true;
+        }
+
+        /// <summary>
+        ///     Removes the specified section including all associated keys of an INI file
+        ///     or an INI file formatted string value.
         /// </summary>
         /// <param name="section">
         ///     The name of the section to remove.
@@ -190,148 +350,34 @@
         {
             try
             {
-                var source = fileOrContent ?? GetFile();
-                if (string.IsNullOrEmpty(source))
-                    throw new ArgumentNullException(nameof(source));
-                var s = section?.Trim() ?? Resources.Ini_NonSection;
-                if (string.IsNullOrEmpty(section))
-                    throw new ArgumentNullException(nameof(section));
-                var path = PathEx.Combine(source);
-                if (!File.Exists(path))
-                    path = TmpFileGuid;
-                var code = path.GetCode();
+                var code = GetCode(fileOrContent);
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));
-                var d = CachedFiles?.ContainsKey(code) == true ? CachedFiles[code] : ReadAll(fileOrContent);
-                if (CachedFiles?.ContainsKey(code) != true || d.Count == 0)
-                    return true;
-                if (!s.Equals(Resources.Ini_NonSection) && !d.ContainsKey(s))
-                {
-                    var newSection = d.Keys.FirstOrDefault(x => x.EqualsEx(s));
-                    if (!string.IsNullOrEmpty(newSection))
-                        s = newSection;
-                }
-                if (!d.ContainsKey(s))
-                    return true;
-                d.Remove(s);
-                CachedFiles[code] = d;
-                return true;
-            }
-            catch (Exception ex)
-            {
-                Log.Write(ex);
-            }
-            return false;
-        }
-
-        /// <summary>
-        ///     Removes the specified key from the specified section, of an INI file or an INI
-        ///     file formatted string value.
-        /// </summary>
-        /// <param name="section">
-        ///     The name of the section containing the key to remove.
-        /// </param>
-        /// <param name="key">
-        ///     The name of the key to remove.
-        /// </param>
-        /// <param name="fileOrContent">
-        ///     The full file path or content of an INI file.
-        /// </param>
-        public static bool RemoveKey(string section, string key, string fileOrContent = null)
-        {
-            try
-            {
-                var source = fileOrContent ?? GetFile();
-                if (string.IsNullOrEmpty(source))
-                    throw new ArgumentNullException(nameof(source));
-                var s = section?.Trim() ?? Resources.Ini_NonSection;
-                if (string.IsNullOrEmpty(section))
-                    throw new ArgumentNullException(nameof(section));
-                var k = key?.Trim();
-                if (string.IsNullOrEmpty(k))
-                    throw new ArgumentNullException(nameof(key));
-                var path = PathEx.Combine(source);
-                if (!File.Exists(path))
-                    path = TmpFileGuid;
-                var code = path.GetCode();
-                if (code == -1)
-                    throw new ArgumentOutOfRangeException(nameof(code));
-                var d = CachedFiles?.ContainsKey(code) == true ? CachedFiles[code] : ReadAll(fileOrContent);
-                if (CachedFiles?.ContainsKey(code) != true || d.Count == 0)
-                    return true;
-                if (!s.Equals(Resources.Ini_NonSection) && !d.ContainsKey(s))
-                {
-                    var newSection = d.Keys.FirstOrDefault(x => x.EqualsEx(s));
-                    if (!string.IsNullOrEmpty(newSection))
-                        s = newSection;
-                }
-                if (d.ContainsKey(s) && !d[s].ContainsKey(k))
-                {
-                    var newKey = d[s].Keys.FirstOrDefault(x => x.EqualsEx(k));
-                    if (!string.IsNullOrEmpty(newKey))
-                        k = newKey;
-                }
-                if (!d.ContainsKey(s) || !d[s].ContainsKey(k))
-                    return true;
-                d[s].Remove(k);
-                CachedFiles[code] = d;
-                return true;
-            }
-            catch (Exception ex)
-            {
-                Log.Write(ex);
-            }
-            return false;
-        }
-
-        /// <summary>
-        ///     Retrieves all section names of an INI file or an INI file formatted string value.
-        /// </summary>
-        /// <param name="fileOrContent">
-        ///     The full file path or content of an INI file.
-        /// </param>
-        /// <param name="sorted">
-        ///     true to sort the sections; otherwise, false.
-        /// </param>
-        public static List<string> GetSections(string fileOrContent = null, bool sorted = true)
-        {
-            var output = new List<string>();
-            try
-            {
-                var source = fileOrContent ?? GetFile();
-                if (string.IsNullOrEmpty(source))
-                    throw new ArgumentNullException(nameof(source));
-                var path = PathEx.Combine(source);
-                if (!File.Exists(path))
-                    path = TmpFileGuid;
-                var code = path.GetCode();
-                if (code == -1)
-                    throw new ArgumentOutOfRangeException(nameof(code));
-                var d = CachedFiles?.ContainsKey(code) == true ? CachedFiles[code] : ReadAll(fileOrContent);
-                if (d.Count > 0)
-                {
-                    output = d.Keys.ToList();
-                    if (output.Contains(Resources.Ini_NonSection))
-                        output.Remove(Resources.Ini_NonSection);
-                    if (sorted)
-                        output = output.SortHelper();
-                }
-            }
-            catch (Exception ex)
-            {
-                Log.Write(ex);
-            }
-            return output;
-        }
-
-        /// <summary>
-        ///     Retrieves all section names of an INI file or an INI file formatted string value.
-        /// </summary>
-        /// <param name="sorted">
-        ///     true to sort the sections; otherwise, false.
-        /// </param>
-        public static List<string> GetSections(bool sorted) =>
-            GetSections(null, sorted);
+                if (!CodeExists(code))
+                    ReadAll(fileOrContent);
+                return RemoveSection(code, section);
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+                return false;
+            }
+        }
+
+        private static string FindKey(int code, string section, string key)
+        {
+            var sct = FindSection(code, section);
+            var ke = key?.Trim();
+            if (string.IsNullOrEmpty(ke) || !SectionExists(code, sct) || CachedFiles[code][sct].ContainsKey(ke))
+                return ke;
+            var newKey = CachedFiles[code][sct].Keys.FirstOrDefault(x => x.EqualsEx(ke));
+            if (!string.IsNullOrEmpty(newKey) && !ke.Equals(newKey))
+                return newKey;
+            return ke;
+        }
+
+        private static bool KeyExists(int code, string section, string key) =>
+            !string.IsNullOrEmpty(key) && SectionExists(code, section) && CachedFiles[code][section].ContainsKey(key) && CachedFiles[code][section][key]?.Any() == true;

         /// <summary>
         ///     Retrieves all key names of an INI file or an INI file formatted string value.
@@ -348,43 +394,29 @@
         /// </param>
         public static List<string> GetKeys(string section, string fileOrContent = null, bool sorted = true)
         {
-            var output = new List<string>();
-            try
-            {
-                var source = fileOrContent ?? GetFile();
-                if (string.IsNullOrEmpty(source))
-                    throw new ArgumentNullException(nameof(source));
-                var s = section?.Trim() ?? Resources.Ini_NonSection;
-                if (string.IsNullOrEmpty(s))
-                    throw new ArgumentNullException(nameof(section));
-                var path = PathEx.Combine(source);
-                if (!File.Exists(path))
-                    path = TmpFileGuid;
-                var code = path.GetCode();
+            try
+            {
+                var code = GetCode(fileOrContent);
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));
-                var d = CachedFiles?.ContainsKey(code) == true ? CachedFiles[code] : ReadAll(fileOrContent);
-                if (d.Count > 0)
-                {
-                    if (!s.Equals(Resources.Ini_NonSection) && !d.ContainsKey(s))
-                    {
-                        var newSection = d.Keys.FirstOrDefault(x => x.EqualsEx(s));
-                        if (!string.IsNullOrEmpty(newSection))
-                            s = newSection;
-                    }
-                    if (d.ContainsKey(s) && d[s].Count > 0)
-                    {
-                        output = d[s].Keys.ToList();
-                        if (sorted)
-                            output = output.SortHelper();
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                Log.Write(ex);
-            }
-            return output;
+                if (!CodeExists(code))
+                    ReadAll(fileOrContent);
+                var sct = FindSection(code, section);
+                if (SectionExists(code, sct))
+                {
+                    var output = CachedFiles[code][sct].Keys.ToList();
+                    if (output.Contains(NonSectionId))
+                        output.Remove(NonSectionId);
+                    if (sorted)
+                        output = output.SortHelper();
+                    return output;
+                }
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+            }
+            return new List<string>();
         }

         /// <summary>
@@ -399,6 +431,47 @@
         /// </param>
         public static List<string> GetKeys(string section, bool sorted) =>
             GetKeys(section, null, sorted);
+
+        private static bool RemoveKey(int code, string section, string key)
+        {
+            var sct = FindSection(code, section);
+            var ke = FindKey(code, sct, key);
+            if (!KeyExists(code, sct, ke))
+                return true;
+            CachedFiles[code][sct].Remove(ke);
+            return true;
+        }
+
+        /// <summary>
+        ///     Removes the specified key from the specified section, of an INI file or an INI
+        ///     file formatted string value.
+        /// </summary>
+        /// <param name="section">
+        ///     The name of the section containing the key to remove.
+        /// </param>
+        /// <param name="key">
+        ///     The name of the key to remove.
+        /// </param>
+        /// <param name="fileOrContent">
+        ///     The full file path or content of an INI file.
+        /// </param>
+        public static bool RemoveKey(string section, string key, string fileOrContent = null)
+        {
+            try
+            {
+                var code = GetCode(fileOrContent);
+                if (code == -1)
+                    throw new ArgumentOutOfRangeException(nameof(code));
+                if (CachedFiles?.ContainsKey(code) != true)
+                    ReadAll(fileOrContent);
+                return RemoveKey(code, section, key);
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+                return false;
+            }
+        }

         /// <summary>
         ///     Retrieves the full content of an INI file or an INI file formatted string value.
@@ -422,7 +495,7 @@
                     source = File.ReadAllText(path);
                 else
                     path = TmpFileGuid;
-                var code = path.GetCode();
+                var code = path?.ToLower().GetHashCode() ?? -1;
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));

@@ -440,7 +513,7 @@
                 if (string.IsNullOrWhiteSpace(source))
                     throw new ArgumentNullException(nameof(source));
                 if (!source.StartsWith("["))
-                    source = $"[{Resources.Ini_NonSection}]{Environment.NewLine}{source}";
+                    source = $"[{NonSectionId}]{Environment.NewLine}{source}";
                 if (!source.EndsWith(Environment.NewLine))
                     source += Environment.NewLine;

@@ -500,8 +573,6 @@
                         }
                         keys[key].Add(value);
                     }
-                    if (sorted)
-                        keys = keys.SortHelper();
                     sections[section] = keys;
                 }
                 if (sorted)
@@ -512,8 +583,10 @@
                     InitializeCache(code);
                     if (CachedFiles.Count >= MaxCacheSize)
                     {
-                        var defCode = FilePath.GetCode();
-                        CachedFiles.Remove(CachedFiles.Keys.First(x => x != defCode));
+                        var defCode = FilePath?.ToLower().GetHashCode() ?? -1;
+                        var delCode = CachedFiles.Keys.First(x => x != defCode);
+                        if (CodeExists(delCode))
+                            CachedFiles.Remove(delCode);
                     }
                     CachedFiles[code] = output;
                 }
@@ -565,50 +638,63 @@
         /// <param name="reread">
         ///     true to reread the INI file; otherwise, false.
         /// </param>
-        public static string Read(string section, string key, string fileOrContent = null, bool reread = false)
-        {
-            var output = string.Empty;
-            try
-            {
-                var source = fileOrContent ?? GetFile();
-                if (string.IsNullOrEmpty(source))
-                    throw new ArgumentNullException(nameof(source));
-                var k = key?.Trim();
-                if (string.IsNullOrEmpty(k))
-                    throw new ArgumentNullException(nameof(key));
-                var s = section?.Trim() ?? Resources.Ini_NonSection;
-                var path = PathEx.Combine(source);
-                if (!File.Exists(path))
-                    path = TmpFileGuid;
-                var code = path.GetCode();
+        /// <param name="index">
+        ///     The value index used to handle multiple key value pairs.
+        /// </param>
+        public static string Read(string section, string key, string fileOrContent = null, bool reread = false, int index = 0)
+        {
+            try
+            {
+                var code = GetCode(fileOrContent);
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));
-                var d = !reread && CachedFiles?.ContainsKey(code) == true ? CachedFiles[code] : ReadAll(fileOrContent);
-                if (d.Count > 0)
-                {
-                    if (!s.Equals(Resources.Ini_NonSection) && !d.ContainsKey(s))
-                    {
-                        var newSection = d.Keys.FirstOrDefault(x => x.EqualsEx(s));
-                        if (!string.IsNullOrEmpty(newSection))
-                            s = newSection;
-                    }
-                    if (d.ContainsKey(s))
-                    {
-                        if (!d[s].ContainsKey(k))
-                        {
-                            var newKey = d[s].Keys.FirstOrDefault(x => x.EqualsEx(k));
-                            if (!string.IsNullOrEmpty(newKey))
-                                k = newKey;
-                        }
-                        if (d[s].ContainsKey(k) && d[s][k].Count > 0)
-                            output = d[s][k].FirstOrDefault() ?? string.Empty;
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                Log.Write(ex);
-            }
+                if (reread || !CodeExists(code))
+                    ReadAll(fileOrContent);
+                if (!CodeExists(code))
+                    throw new ArgumentNullException(nameof(fileOrContent));
+                var sct = FindSection(code, section);
+                if (string.IsNullOrEmpty(sct))
+                    throw new ArgumentNullException(nameof(section));
+                var ke = FindKey(code, sct, key);
+                if (string.IsNullOrEmpty(ke))
+                    throw new ArgumentNullException(nameof(key));
+                if (KeyExists(code, sct, ke))
+                {
+                    var i = Math.Abs(index);
+                    if (CachedFiles[code][sct][ke].Count > i)
+                        return CachedFiles[code][sct][ke][i] ?? string.Empty;
+                    return CachedFiles[code][sct][ke]?.FirstOrDefault() ?? string.Empty;
+                }
+            }
+            catch (Exception ex)
+            {
+                Log.Write(ex);
+            }
+            return string.Empty;
+        }
+
+        /// <summary>
+        ///     Retrieves a <see cref="string"/> value from the specified section in an INI file
+        ///     or an INI file formatted string value and release all cached resources used by
+        ///     the specified INI file or the INI file formatted string value.
+        /// </summary>
+        /// <param name="section">
+        ///     The name of the section containing the key name. The value must be NULL for a
+        ///     non-section key.
+        /// </param>
+        /// <param name="key">
+        ///     The name of the key whose associated value is to be retrieved.
+        /// </param>
+        /// <param name="fileOrContent">
+        ///     The full file path or content of an INI file.
+        /// </param>
+        /// <param name="index">
+        ///     The value index used to handle multiple key value pairs.
+        /// </param>
+        public static string ReadOnly(string section, string key, string fileOrContent, int index = 0)
+        {
+            var output = Read(section, key, fileOrContent, false, index);
+            Detach(fileOrContent);
             return output;
         }

@@ -631,13 +717,15 @@
         /// <param name="fileOrContent">
         ///     The full file path or content of an INI file.
         /// </param>
-        public static TValue Read<TValue>(string section, string key, TValue defValue = default(TValue), string fileOrContent = null)
-        {
-            dynamic output = defValue;
-            try
-            {
-                dynamic newValue;
-                var strValue = Read(section, key, fileOrContent);
+        /// <param name="reread">
+        ///     true to reread the INI file; otherwise, false.
+        /// </param>
+        public static TValue Read<TValue>(string section, string key, TValue defValue = default(TValue), string fileOrContent = null, bool reread = false)
+        {
+            try
+            {
+                object newValue;
+                var strValue = Read(section, key, fileOrContent, reread);
                 var type = typeof(TValue);
                 if (string.IsNullOrEmpty(strValue))
                 {
@@ -646,38 +734,32 @@
                         var message = $"The value is not defined. (Section: '{section}'; Key: '{key}'; File: '{(fileOrContent ?? GetFile())?.EncodeToBase85()}';)";
                         throw new WarningException(message);
                     }
-                    newValue = defValue;
-                }
-                else if (type == typeof(string))
-                    newValue = string.IsNullOrEmpty(strValue) && !string.IsNullOrEmpty(defValue as string) ? (dynamic)defValue : strValue;
-                else if (type == typeof(string[]) || type == typeof(List<string>) || type == typeof(IEnumerable<string>))
-                {
-                    var sequence = strValue.FromHexStringToByteArray()?.TextFromZip()?.Split('\0')
-                                           .Reverse().Skip(1).Reverse().Select(x => x?.FromHexString());
-                    if (type == typeof(string[]))
-                        newValue = sequence?.ToArray();
-                    else if (type == typeof(List<string>))
-                        newValue = sequence?.ToList();
-                    else
-                        newValue = sequence;
-                }
-                else if (type == typeof(byte[]))
-                    newValue = strValue.FromHexStringToByteArray();
-                else if (type == typeof(Bitmap) || type == typeof(Image))
-                    newValue = strValue.FromHexStringToImage();
-                else if (type == typeof(Icon))
-                    newValue = strValue.FromHexStringToIcon();
-                else if (type == typeof(Rectangle))
-                    newValue = strValue.ToRectangle();
-                else if (type == typeof(Point))
-                    newValue = strValue.ToPoint();
-                else if (type == typeof(Size))
-                    newValue = strValue.ToSize();
-                else if (type == typeof(Version))
-                    newValue = Version.Parse(strValue);
-                else if (!strValue.TryParse<TValue>(out newValue))
-                    newValue = defValue;
-                output = newValue;
+                    newValue = (object)defValue ?? string.Empty;
+                }
+                else if (strValue.StartsWith("\u0001Object\u0002") && strValue.EndsWith("\u0003"))
+                {
+                    var bytes = strValue.Substring(8).TrimEnd('\u0003').DecodeBytesFromBase85(null, null);
+                    var unzipped = bytes?.Unzip();
+                    if (unzipped != null)
+                        bytes = unzipped;
+                    newValue = bytes?.DeserializeObject<object>() ?? defValue;
+                }
+                else
+                {
+                    if (type == typeof(string))
+                        newValue = strValue;
+                    else if (type == typeof(Rectangle))
+                        newValue = strValue.ToRectangle();
+                    else if (type == typeof(Point))
+                        newValue = strValue.ToPoint();
+                    else if (type == typeof(Size))
+                        newValue = strValue.ToSize();
+                    else if (type == typeof(Version))
+                        newValue = Version.Parse(strValue);
+                    else if (!strValue.TryParse<TValue>(out newValue))
+                        newValue = defValue;
+                }
+                return (TValue)newValue;
             }
             catch (FormatException)
             {
@@ -687,7 +769,7 @@
             {
                 Log.Write(ex);
             }
-            return output;
+            return defValue;
         }

         /// <summary>
@@ -710,31 +792,12 @@
         /// <param name="fileOrContent">
         ///     The full file path or content of an INI file.
         /// </param>
-        public static TValue ReadOnly<TValue>(string section, string key, TValue defValue = default(TValue), string fileOrContent = null)
+        /// <param name="index">
+        ///     The value index used to handle multiple key value pairs.
+        /// </param>
+        public static TValue ReadOnly<TValue>(string section, string key, TValue defValue, string fileOrContent, int index = 0)
         {
             var output = Read(section, key, defValue, fileOrContent);
-            Detach(fileOrContent);
-            return output;
-        }
-
-        /// <summary>
-        ///     Retrieves a <see cref="string"/> value from the specified section in an INI file
-        ///     or an INI file formatted string value and release all cached resources used by
-        ///     the specified INI file or the INI file formatted string value.
-        /// </summary>
-        /// <param name="section">
-        ///     The name of the section containing the key name. The value must be NULL for a
-        ///     non-section key.
-        /// </param>
-        /// <param name="key">
-        ///     The name of the key whose associated value is to be retrieved.
-        /// </param>
-        /// <param name="fileOrContent">
-        ///     The full file path or content of an INI file.
-        /// </param>
-        public static string ReadOnly(string section, string key, string fileOrContent = null)
-        {
-            var output = Read(section, key, fileOrContent);
             Detach(fileOrContent);
             return output;
         }
@@ -789,7 +852,12 @@
         ///     </para>
         /// </param>
         /// <param name="file">
-        ///     The full file path of an INI file.
+        ///     <para>
+        ///         The full file path of an INI file.
+        ///     </para>
+        ///     <para>
+        ///         If this parameter is NULL, the default INI file is used.
+        ///     </para>
         /// </param>
         /// <param name="sorted">
         ///     true to sort the sections and keys; otherwise, false.
@@ -802,39 +870,35 @@
         {
             try
             {
-                var source = content ?? new Dictionary<string, Dictionary<string, List<string>>>();
                 var path = PathEx.Combine(file ?? GetFile());
-                var code = path.GetCode();
+                if (string.IsNullOrEmpty(path))
+                    throw new ArgumentNullException(nameof(path));
+
+                var code = path.ToLower().GetHashCode();
                 if (code == -1)
                     throw new ArgumentOutOfRangeException(nameof(code));
-                if (source.Count == 0 && CachedFiles?.ContainsKey(code) == true)
+
+                var source = content ?? new Dictionary<string, Dictionary<string, List<string>>>();
+                if (source.Count == 0 && CodeExists(code))
                     source = CachedFiles[code];
                 if (source.Count == 0 && File.Exists(path))
                     source = ReadAll(path);
                 if (source.Count == 0 || source.Values.Count == 0)
                     throw new ArgumentNullException(nameof(source));
-                if (!File.Exists(path))
-                    File.Create(path).Close();
                 if (sorted)
-                {
-                    var comparer = new Comparison.AlphanumericComparer();
-                    var sort = source.OrderBy(d => !d.Key.Equals(Resources.Ini_NonSection))
-                                     .ThenBy(d => d.Key, comparer)
-                                     .ToDictionary(d => d.Key, d => d.Value
-                                                                     .OrderBy(p => p.Key, comparer)
-                                                                     .ToDictionary(p => p.Key, p => p.Value));
-                    source = sort;
-                }
-
-                var hash = new Crypto.Md5().EncryptFile(path);
-                var temp = path + ".new";
-                File.CreateText(temp).Close();
+                    source = source.SortHelper();
+
+                if (!File.Exists(path) && !PathEx.IsValidPath(path))
+                    throw new ArgumentException(nameof(path));
+
+                var hash = File.Exists(path) ? new Crypto.Md5().EncryptFile(path) : null;
+                var temp = Path.GetRandomFileName();
                 using (var sw = new StreamWriter(temp, true))
                     foreach (var dict in source)
                     {
                         if (string.IsNullOrWhiteSpace(dict.Key) || dict.Value.Count == 0)
                             continue;
-                        if (!dict.Key.Equals(Resources.Ini_NonSection))
+                        if (!dict.Key.Equals(NonSectionId))
                         {
                             sw.Write('[');
                             sw.Write(dict.Key.Trim());
@@ -845,12 +909,11 @@
                         {
                             if (string.IsNullOrWhiteSpace(pair.Key) || pair.Value.Count == 0)
                                 continue;
-                            var key = pair.Key.Trim();
                             foreach (var value in pair.Value)
                             {
                                 if (string.IsNullOrWhiteSpace(value))
                                     continue;
-                                sw.Write(key);
+                                sw.Write(pair.Key.Trim());
                                 sw.Write('=');
                                 sw.Write(value.Trim());
                                 sw.WriteLine();
@@ -858,47 +921,13 @@
                         }
                         sw.WriteLine();
                     }
-                if (hash.Equals(new Crypto.Md5().EncryptFile(temp)))
+                if (hash?.Equals(new Crypto.Md5().EncryptFile(temp)) == true)
                 {
                     File.Delete(temp);
                     return true;
                 }
                 File.Delete(path);
                 File.Move(temp, path);
-                Data.SetAttributes(path, FileAttributes.Normal);
-
-                /*
-                var sb = new StringBuilder();
-                foreach (var dict in source)
-                {
-                    if (string.IsNullOrWhiteSpace(dict.Key) || dict.Value.Count == 0)
-                        continue;
-                    if (!dict.Key.Equals(NonSectionGuid))
-                    {
-                        sb.Append('[');
-                        sb.Append(dict.Key.Trim());
-                        sb.Append(']');
-                        sb.AppendLine();
-                    }
-                    foreach (var pair in dict.Value)
-                    {
-                        if (string.IsNullOrWhiteSpace(pair.Key) || pair.Value.Count == 0)
-                            continue;
-                        var key = pair.Key.Trim();
-                        foreach (var value in pair.Value)
-                        {
-                            if (string.IsNullOrWhiteSpace(value))
-                                continue;
-                            sb.Append(key);
-                            sb.Append('=');
-                            sb.Append(value.Trim());
-                            sb.AppendLine();
-                        }
-                    }
-                    sb.AppendLine();
-                }
-                File.WriteAllText(path, sb.ToString());
-                */

                 if (detach)
                     Detach(path);
@@ -915,7 +944,12 @@
         ///     Writes all the cached data from the specified INI file to the disk.
         /// </summary>
         /// <param name="file">
-        ///     The full file path of an INI file.
+        ///     <para>
+        ///         The full file path of an INI file.
+        ///     </para>
+        ///     <para>
+        ///         If this parameter is NULL, the default INI file is used.
+        ///     </para>
         /// </param>
         /// <param name="sorted">
         ///     true to sort the sections and keys; otherwise, false.
@@ -973,133 +1007,84 @@
         /// <param name="index">
         ///     The value index used to handle multiple key value pairs.
         /// </param>
-        public static bool Write<TValue>(string section, string key, TValue value, string fileOrContent = null, bool forceOverwrite = true, bool skipExistValue = false, uint index = 0)
-        {
-            try
-            {
-                var s = section?.Trim() ?? Resources.Ini_NonSection;
-                if (string.IsNullOrEmpty(s))
+        public static bool Write<TValue>(string section, string key, TValue value, string fileOrContent = null, bool forceOverwrite = true, bool skipExistValue = false, int index = 0)
+        {
+            try
+            {
+                var code = GetCode(fileOrContent);
+                if (code == -1)
+                    throw new ArgumentOutOfRangeException(nameof(code));
+
+                if (!CodeExists(code))
+                    ReadAll(fileOrContent);
+
+                var sct = FindSection(code, section);
+                if (string.IsNullOrEmpty(sct))
                     throw new ArgumentNullException(nameof(section));
-
-                int code;
-                var path = PathEx.Combine(fileOrContent ?? GetFile());
-                if (File.Exists(path))
-                {
-                    code = path.GetCode();
-                    if (code == -1)
-                        throw new ArgumentOutOfRangeException(nameof(code));
-                    if (CachedFiles?.ContainsKey(code) != true)
-                        ReadAll(fileOrContent);
-                }
-                else
-                {
-                    path = TmpFileGuid;
-                    code = path.GetCode();
-                    if (code == -1)
-                        throw new ArgumentOutOfRangeException(nameof(code));
-                }
-
-                var newValue = string.Empty;
-                if (value != null)
-                {
-                    // To allow the saving of a sequence of strings,
-                    // it's required to bring it in a single string
+                if (sct.Any(TextEx.IsLineSeparator))
+                    throw new ArgumentOutOfRangeException(nameof(section));
+
+                var ke = FindSection(code, key);
+                if (ke.Any(TextEx.IsLineSeparator))
+                    throw new ArgumentOutOfRangeException(nameof(key));
+
+                var val = string.Empty;
+                if (!string.IsNullOrEmpty(sct) && !string.IsNullOrEmpty(ke) && Comparison.IsNotEmpty(value))
+                {
+                    var str = value.ToString();
                     var type = typeof(TValue);
-                    if (type == typeof(string[]) || type == typeof(List<string>) || type == typeof(IEnumerable<string>))
+                    if (type.IsSerializable && (type.ToString() == str || $"({type.Name})" == str || str.Any(TextEx.IsLineSeparator)))
                     {
-                        var sa = type == typeof(string[]) ? value as string[] : (value as IEnumerable<string>)?.ToArray();
-                        if (sa != null)
-                        {
-                            // Convert each part into hexadecimal to make sure there are no null
-                            // terminated character in it; concatenates the result using the null
-                            // terminated character as separator, in the next step
-                            var str = sa.Select(x => x.ToHexString()).Join('\0');
-                            if (!str.Contains('\0'))
-                                str += '\0';
-                            // To shorten it, zip the result and save it in hexadecimal
-                            newValue = str.TextToZip().ToHexString();
-                        }
-                    }
-                    // No special conversion for anything else
-                    else if (type == typeof(Bitmap) || type == typeof(Image) || type == typeof(Icon) || type == typeof(byte[]))
-                    {
-                        byte[] ba;
-                        if (type == typeof(Bitmap) || type == typeof(Image))
-                            ba = (value as Bitmap)?.ToByteArray();
-                        else if (type == typeof(Icon))
-                            ba = (value as Icon)?.ToByteArray();
-                        else
-                            ba = value as byte[];
-                        if (ba != null)
-                            newValue = ba.ToHexString();
+                        var bytes = value.SerializeObject();
+                        var zipped = bytes?.Zip();
+                        if (zipped?.Length < bytes?.Length)
+                            bytes = zipped;
+                        val = "\u0001Object\u0002" + bytes.EncodeToBase85(null, null) + "\u0003";
                     }
                     else
-                        newValue = value.ToString();
+                        val = str;
                 }

                 if (!forceOverwrite || skipExistValue)
                 {
-                    var c = Read(section, key, fileOrContent);
-                    if (!forceOverwrite && c == newValue || skipExistValue && !string.IsNullOrWhiteSpace(c))
+                    var c = Read(section, key, fileOrContent, false, index);
+                    if (!forceOverwrite && c == val || skipExistValue && !string.IsNullOrWhiteSpace(c))
                         return false;
                 }

-                var i = (int)index;
-                var k = key?.Trim();
-                if (CachedFiles?.ContainsKey(code) == true && CachedFiles[code].Count > 0)
-                {
-                    // Find the correct section
-                    if (!s.Equals(Resources.Ini_NonSection) && !CachedFiles[code].ContainsKey(s))
+                var i = Math.Abs(index);
+                if (string.IsNullOrEmpty(ke) || string.IsNullOrEmpty(val))
+                {
+                    if (!SectionExists(code, sct))
+                        return true;
+                    if (string.IsNullOrEmpty(ke))
                     {
-                        var newSection = CachedFiles[code].Keys.FirstOrDefault(x => x.EqualsEx(s));
-                        if (!string.IsNullOrEmpty(newSection))
-                            s = newSection;
-                    }
-
-                    // Remove key value pairs
-                    if (string.IsNullOrEmpty(k) || string.IsNullOrEmpty(newValue))
-                    {
-                        if (!CachedFiles[code].ContainsKey(s))
-                            return true;
-                        if (string.IsNullOrEmpty(k))
-                            CachedFiles[code][s].Clear();
-                        else if (string.IsNullOrEmpty(newValue))
-                        {
-                            if (!CachedFiles[code][s].ContainsKey(k))
-                                return true;
-                            if (CachedFiles[code][s][k].Count > 1 && CachedFiles[code][s][k].Count > i)
-                                CachedFiles[code][s][k] = CachedFiles[code][s][k].Skip(i).ToList();
-                            else
-                                CachedFiles[code][s].Remove(k);
-                        }
+                        RemoveSection(code, sct);
                         return true;
                     }
-
-                    // Find the correct key
-                    if (CachedFiles[code].ContainsKey(s))
-                        if (!CachedFiles[code][s].ContainsKey(k))
-                        {
-                            var newKey = CachedFiles[code][s].Keys.FirstOrDefault(x => x.EqualsEx(k));
-                            if (!string.IsNullOrEmpty(newKey))
-                                k = newKey;
-                        }
-                }
-
-                if (string.IsNullOrEmpty(k))
-                    throw new ArgumentNullException(nameof(key));
-                if (string.IsNullOrEmpty(newValue))
-                    throw new ArgumentNullException(nameof(value));
-
-                // Finally write the value in cache
-                InitializeCache(code, s, k);
-                if (CachedFiles[code][s][k].Count > i)
-                {
-                    CachedFiles[code][s][k][i] = newValue;
+                    if (!KeyExists(code, sct, ke))
+                        return true;
+                    if (CachedFiles?[code][sct][ke].Count > i)
+                    {
+                        CachedFiles[code][sct][ke].RemoveAt(i);
+                        return true;
+                    }
+                    RemoveKey(code, sct, ke);
                     return true;
                 }
-                if (CachedFiles[code][s][k].Count != i)
-                    throw new ArgumentOutOfRangeException(nameof(index));
-                CachedFiles[code][s][k].Add(newValue);
+
+                InitializeCache(code, sct, ke);
+                if (KeyExists(code, sct, ke))
+                {
+                    if (CachedFiles?[code][sct][ke].Count > i)
+                    {
+                        CachedFiles[code][sct][ke][i] = val;
+                        return true;
+                    }
+                    if (CachedFiles?[code][sct][ke].Count != i)
+                        throw new ArgumentOutOfRangeException(nameof(index));
+                }
+                CachedFiles?[code][sct][ke].Add(val);
                 return true;
             }
             catch (Exception ex)
@@ -1152,7 +1137,7 @@
         /// <param name="index">
         ///     The value index used to handle multiple key value pairs.
         /// </param>
-        public static bool Write<TValue>(string section, string key, TValue value, bool forceOverwrite, bool skipExistValue = false, uint index = 0) =>
+        public static bool Write<TValue>(string section, string key, TValue value, bool forceOverwrite, bool skipExistValue = false, int index = 0) =>
             Write(section, key, value, null, forceOverwrite, skipExistValue);

         /// <summary>


Clean up Log class.

Clean up Log class.

--- src/SilDev/Log.cs
+++ src/SilDev/Log.cs
@@ -5,7 +5,7 @@
 // ==============================================
 //
 // Filename: Log.cs
-// Version:  2017-05-13 04:35
+// Version:  2017-05-22 18:45
 //
 // Copyright (c) 2017, Si13n7 Developments (r)
 // All rights reserved.
@@ -32,15 +32,15 @@
     /// </summary>
     public static class Log
     {
-        private static volatile bool _conIsOpen, _firstCall, _firstEntry;
-        private static volatile IntPtr _stdHandle = IntPtr.Zero;
-        private static volatile SafeFileHandle _sfh;
-        private static volatile FileStream _fs;
-        private static volatile StreamWriter _sw;
+        private static bool _conIsOpen, _firstCall, _firstEntry;
+        private static IntPtr _stdHandle = IntPtr.Zero;
+        private static SafeFileHandle _sfh;
+        private static FileStream _fs;
+        private static StreamWriter _sw;
         private static readonly AssemblyName AssemblyEntryName = Assembly.GetEntryAssembly().GetName();
         private static readonly string AssemblyName = AssemblyEntryName.Name;
         private static readonly Version AssemblyVersion = AssemblyEntryName.Version;
-        private static readonly string ConsoleTitle = $"Debug Console ('{AssemblyName}')";
+        private static readonly object Locker = new object();

         /// <summary>
         ///     true to enable the catching of unhandled <see cref="Exception"/>'s; otherwise, false.
@@ -116,7 +116,7 @@
                 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
                 Application.ThreadException += (s, e) => Write(e.Exception, true, true);
                 AppDomain.CurrentDomain.UnhandledException += (s, e) => WriteUnhandled(
-                    new ApplicationException("Error in the application. Sender object: '" + s + "'; Exception object: '" + e.ExceptionObject + "'"));
+                    new ApplicationException("Error in the application. Sender object: '" + s + "'; Exception object: '" + e.ExceptionObject + "';"));
                 AppDomain.CurrentDomain.ProcessExit += (s, e) => Close();
             }
             if (DebugMode <= 0)
@@ -207,60 +207,61 @@
         /// </param>
         public static void Write(string logMessage, bool exitProcess = false)
         {
-            if (!_firstCall || DebugMode < 1 || string.IsNullOrEmpty(logMessage))
-                return;
-            var dat = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff zzz") + Environment.NewLine;
-            var log = dat;
-            if (!_firstEntry)
-            {
-                _firstEntry = true;
-                if (File.Exists(FilePath))
-                    log = new string('-', 120) + Environment.NewLine + Environment.NewLine + dat;
-                log += "***Logging has been started***" + Environment.NewLine;
-                log += "   '" + Environment.OSVersion + "'; '" + AssemblyName + "'; '" + AssemblyVersion + "'; '" + FilePath + "'" + Environment.NewLine + Environment.NewLine;
+            lock (Locker)
+            {
+                if (!_firstCall || DebugMode < 1 || string.IsNullOrEmpty(logMessage))
+                    return;
+                var dat = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff zzz");
+                var log = string.Empty;
+                if (_firstEntry)
+                    log = string.Concat(dat, Environment.NewLine);
+                else
+                {
+                    _firstEntry = true;
+                    if (File.Exists(FilePath))
+                        log = string.Concat(Environment.NewLine, new string('-', 120), Environment.NewLine, Environment.NewLine);
+                    log = string.Concat(log, dat, Environment.NewLine, "System: '", Environment.OSVersion, "'; Runtime: '", EnvironmentEx.Version, "'; Assembly: '", AssemblyName, "'; Version: '", AssemblyVersion, "';", Environment.NewLine, Environment.NewLine, dat, Environment.NewLine);
+                }
+                log = string.Concat(log, logMessage, Environment.NewLine, Environment.NewLine);
                 File.AppendAllText(FilePath, log);
-                log = dat;
-            }
-            log += logMessage + Environment.NewLine + Environment.NewLine;
-            File.AppendAllText(FilePath, log);
-            if (DebugMode <= 1)
-            {
+                if (DebugMode < 2)
+                {
+                    if (!exitProcess)
+                        return;
+                    Environment.ExitCode = 1;
+                    Environment.Exit(Environment.ExitCode);
+                }
+                if (!_conIsOpen)
+                {
+                    _conIsOpen = true;
+                    WinApi.SafeNativeMethods.AllocConsole();
+                    var hWnd = WinApi.SafeNativeMethods.GetConsoleWindow();
+                    if (hWnd != IntPtr.Zero)
+                    {
+                        var hMenu = WinApi.UnsafeNativeMethods.GetSystemMenu(hWnd, false);
+                        if (hMenu != IntPtr.Zero)
+                            WinApi.UnsafeNativeMethods.DeleteMenu(hMenu, 0xf060, 0x0);
+                    }
+                    _stdHandle = WinApi.UnsafeNativeMethods.GetStdHandle(-11);
+                    _sfh = new SafeFileHandle(_stdHandle, true);
+                    _fs = new FileStream(_sfh, FileAccess.Write);
+                    var title = $"Debug Console ('{AssemblyName}')";
+                    if (Console.Title != title)
+                    {
+                        Console.Title = title;
+                        Console.BufferHeight = short.MaxValue - 1;
+                        Console.BufferWidth = Console.WindowWidth;
+                        Console.SetWindowSize(Math.Min(100, Console.LargestWindowWidth), Math.Min(40, Console.LargestWindowHeight));
+                    }
+                }
+                Console.Write(log);
+                _sw = new StreamWriter(_fs, Encoding.ASCII) { AutoFlush = true };
+                Console.SetOut(_sw);
                 if (!exitProcess)
                     return;
                 Environment.ExitCode = 1;
                 Environment.Exit(Environment.ExitCode);
             }
-            if (!_conIsOpen)
-            {
-                _conIsOpen = true;
-                WinApi.SafeNativeMethods.AllocConsole();
-                var hWnd = WinApi.SafeNativeMethods.GetConsoleWindow();
-                if (hWnd != IntPtr.Zero)
-                {
-                    var hMenu = WinApi.UnsafeNativeMethods.GetSystemMenu(hWnd, false);
-                    if (hMenu != IntPtr.Zero)
-                        WinApi.UnsafeNativeMethods.DeleteMenu(hMenu, 0xf060, 0x0);
-                }
-                _stdHandle = WinApi.UnsafeNativeMethods.GetStdHandle(-11);
-                _sfh = new SafeFileHandle(_stdHandle, true);
-                _fs = new FileStream(_sfh, FileAccess.Write);
-                if (Console.Title != ConsoleTitle)
-                {
-                    Console.Title = ConsoleTitle;
-                    Console.BufferHeight = short.MaxValue - 1;
-                    Console.BufferWidth = Console.WindowWidth;
-                    Console.SetWindowSize(Math.Min(100, Console.LargestWindowWidth), Math.Min(40, Console.LargestWindowHeight));
-                }
-            }
-            Console.ForegroundColor = ConsoleColor.Gray;
-            Console.Write(log);
-            Console.ResetColor();
-            _sw = new StreamWriter(_fs, Encoding.ASCII) { AutoFlush = true };
-            Console.SetOut(_sw);
-            if (!exitProcess)
-                return;
-            Environment.ExitCode = 1;
-            Environment.Exit(Environment.ExitCode);
         }

         /// <summary>
@@ -302,11 +303,11 @@
                 return;
             try
             {
-                foreach (var file in Directory.GetFiles(FileDir, $"{AssemblyName}*.log", SearchOption.TopDirectoryOnly))
+                foreach (var file in Directory.EnumerateFiles(FileDir, AssemblyName + "*.log", SearchOption.TopDirectoryOnly))
                 {
                     if (FilePath.EqualsEx(file))
                         continue;
-                    if ((DateTime.Now - new FileInfo(file).LastWriteTime).TotalDays >= 7d)
+                    if ((DateTime.Now - File.GetLastWriteTime(file)).TotalDays >= 7d)
                         File.Delete(file);
                 }
             }


Update Store.SqlServer connection string constructor

--- Store.Storage.SqlServer/src/Client.cs
+++ Store.Storage.SqlServer/src/Client.cs
@@ -9,16 +9,17 @@
   public class Client {
     public Client(DBContext _dbContext) {
       var fac = new Factory<SqlConnection>();
-      dbConnection = fac.Get(_dbContext);
+      Func<DBContext, string> func = (DBContext _db) => _db.userId.Length == 0 || _db.password.Length == 0 ? string.Format("Data Source={0};Initial Catalog={1};Integrated Security=SSPI;", _db.server, _db.database) : string.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3};", _db.server, _db.database, _db.userId, _db.password);
+      dbConnection = fac.Get(_dbContext, func);
     }

     protected string tryCatch(Action act) { try { act(); } catch (Exception e) { return e.Message; } return OperatonResult.Succeeded.ToString("F"); }

-    internal string runSql(string sql) {
+    public string runSql(string sql) {
       return tryCatch(() => { var runner = new CommandRunner(dbConnection); runner.Transact(new DbCommand[] { runner.BuildCommand(sql, null) }); });
     }

-    internal IEnumerable<dynamic> runSqlDynamic(string sql) {
+    public IEnumerable<dynamic> runSqlDynamic(string sql) {
       var runner = new CommandRunner(dbConnection); return runner.ExecuteDynamic(sql, null);
     }


Set god mode max rainfall to 7000. That should be enough (see #33).

--- src/TabGodMode.cs
+++ src/TabGodMode.cs
@@ -261,7 +261,7 @@

             // min is obviously 0; max is defined in RimWorld.Planet.WorldGenStep_Terrain.RainfallFinishFallAltitude
             const float minRainfall = 0f;
-            const float maxRainfall = 6000f;
+            const float maxRainfall = 7000f;

             var rainFall = _gameData.GodModeData.Rainfall;
             _chosenRainfallString = rainFall.ToString("F1", CultureInfo.InvariantCulture);


Fix spelling mistake.

Fix spelling mistake.

--- src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs
+++ src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs
@@ -142,11 +142,11 @@
             bool fuzzyMatch)
         {
             return fuzzyMatch
-                ? FuzzyyMatchPatternChunk(candidate, patternChunk, punctuationStripped)
+                ? FuzzyMatchPatternChunk(candidate, patternChunk, punctuationStripped)
                 : NonFuzzyMatchPatternChunk(candidate, patternChunk, punctuationStripped);
         }

-        private PatternMatch? FuzzyyMatchPatternChunk(
+        private PatternMatch? FuzzyMatchPatternChunk(
             string candidate,
             TextChunk patternChunk,
             bool punctuationStripped)


Don't wait when detaching the debugger after running the test

--- XUnitRemote/Local/Debugging/RemoteDebugger.cs
+++ XUnitRemote/Local/Debugging/RemoteDebugger.cs
@@ -53,7 +53,7 @@
                         {
                             if (Retry(process.HasDebuggerAttached))
                             {
-                                Retry(() => process.Detach());
+                                Retry(() => process.Detach(WaitForBreakOrEnd: false));
                             }
                         });
                     }


[Mac] Fixes in the image builder backend

The origin of the drawing context must be the top left corner

--- Xwt.Mac/Xwt.Mac/ImageBuilderBackendHandler.cs
+++ Xwt.Mac/Xwt.Mac/ImageBuilderBackendHandler.cs
@@ -58,9 +58,14 @@
 			default:
 				throw new NotImplementedException ("ImageFormat: " + format.ToString ());
 			}
+
+			var bmp = new CGBitmapContext (IntPtr.Zero, width, height, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags);
+			bmp.TranslateCTM (0, height);
+			bmp.ScaleCTM (1, -1);
 			return new CGContextBackend {
-				Context = new CGBitmapContext (IntPtr.Zero, width, height, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags),
-				Size = new SizeF (width, height)
+				Context = bmp,
+				Size = new SizeF (width, height),
+				InverseViewTransform = bmp.GetCTM ().Invert ()
 			};
 		}


some assembly information

some assembly information

--- YubicoDotNetClient/Properties/AssemblyInfo.cs
+++ YubicoDotNetClient/Properties/AssemblyInfo.cs
@@ -6,9 +6,9 @@
 // set of attributes. Change these attribute values to modify the information
 // associated with an assembly.
 [assembly: AssemblyTitle("YubicoDotNetClient")]
-[assembly: AssemblyDescription("")]
+[assembly: AssemblyDescription("Validation client for the Yubico validation protocol version 2.0")]
 [assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
+[assembly: AssemblyCompany("Yubico AB")]
 [assembly: AssemblyProduct("YubicoDotNetClient")]
 [assembly: AssemblyCopyright("Copyright ©  2012")]
 [assembly: AssemblyTrademark("")]


Added HelpCenter Article Search Methods

Added HelpCenter Article Search Methods

--- ZendeskApi_v2/Requests/HelpCenter/Articles.cs
+++ ZendeskApi_v2/Requests/HelpCenter/Articles.cs
@@ -17,10 +17,10 @@
 		GroupArticleResponse GetArticlesBySectionId(long sectionId);
 		GroupArticleResponse GetArticlesByUserId(long userId);
 		GroupArticleResponse GetArticlesSinceDateTime(DateTime startTime);
+        ArticleSearchResults SearchArticlesFor(string query, string category = "", string section = "", string labels = "", string locale = "", DateTime? createdBefore = null, DateTime? createdAfter = null, DateTime? createdAt = null, DateTime? updatedBefore = null, DateTime? updatedAfter = null, DateTime? updatedAt = null);
 		IndividualArticleResponse CreateArticle(long sectionId, Article article);
 		IndividualArticleResponse UpdateArticle(Article article);
 		bool DeleteArticle(long id);
-
 #endif
 #if ASYNC
 		Task<GroupArticleResponse> GetArticlesAsync();
@@ -28,6 +28,7 @@
 		Task<GroupArticleResponse> GetArticlesBySectionIdAsync(long sectionId);
 		Task<GroupArticleResponse> GetArticlesByUserIdAsync(long userId);
 		Task<GroupArticleResponse> GetArticlesSinceDateTimeAsync(DateTime startTime);
+        Task<ArticleSearchResults> SearchArticlesForAsync(string query, string category = "", string section = "", string labels = "", string locale = "", DateTime? createdBefore = null, DateTime? createdAfter = null, DateTime? createdAt = null, DateTime? updatedBefore = null, DateTime? updatedAfter = null, DateTime? updatedAt = null);
 		Task<IndividualArticleResponse> CreateArticleAsync(long sectionId, Article article);
 		Task<IndividualArticleResponse> UpdateArticleAsync(Article article);
 		Task<bool> DeleteArticleAsync(long id);
@@ -45,7 +46,7 @@
 #if SYNC

 		public GroupArticleResponse GetArticles()
-		{
+		{
 			return GenericGet<GroupArticleResponse>("help_center/articles.json");
 		}
 		public GroupArticleResponse GetArticlesByCategoryId(long categoryId)
@@ -64,6 +65,21 @@
 		{
 			return GenericGet<GroupArticleResponse>(string.Format("help_center/incremental/articles.json?start_time={0}", startTime.GetEpoch()));
 		}
+        public ArticleSearchResults SearchArticlesFor(string query, string category = "", string section = "", string labels = "", string locale = "", DateTime? createdBefore = null, DateTime? createdAfter = null, DateTime? createdAt = null, DateTime? updatedBefore = null, DateTime? updatedAfter = null, DateTime? updatedAt = null)
+		{
+			return GenericGet<ArticleSearchResults>(string.Format("help_center/articles/search.json?query={0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}",
+                                                                            query,
+                                                                            String.IsNullOrEmpty(category) ? "" : "&category=" + category,
+                                                                            String.IsNullOrEmpty(section) ? "" : "&section=" + section,
+                                                                            String.IsNullOrEmpty(labels) ? "" : "&label_names=" + labels,
+                                                                            String.IsNullOrEmpty(locale) ? "" : "&locale=" + locale,
+                                                                            !createdBefore.HasValue ? "" : "&created_before=" + createdBefore.Value.ToString("yyyy-MM-dd"),
+                                                                            !createdAfter.HasValue ? "" : "&created_after=" + createdAfter.Value.ToString("yyyy-MM-dd"),
+                                                                            !createdAt.HasValue ? "" : "&created_at=" + createdAt.Value.ToString("yyyy-MM-dd"),
+                                                                            !updatedBefore.HasValue ? "" : "&updated_before=" + updatedBefore.Value.ToString("yyyy-MM-dd"),
+                                                                            !updatedAfter.HasValue ? "" : "&updated_after=" + updatedAfter.Value.ToString("yyyy-MM-dd"),
+                                                                            !updatedAt.HasValue ? "" : "&updated_at=" + updatedAt.Value.ToString("yyyy-MM-dd")));
+		}
 		public IndividualArticleResponse CreateArticle(long sectionId, Article article)
 		{
 			var body = new { article };
@@ -80,7 +96,6 @@
 		{
 			return GenericDelete(string.Format("help_center/articles/{0}.json", id));
 		}
-
 #endif
 #if ASYNC

@@ -108,6 +123,22 @@
 		{
 			return await GenericGetAsync<GroupArticleResponse>(string.Format("help_center/incremental/articles.json?start_time={0}", startTime.GetEpoch()));
 		}
+
+        public async Task<ArticleSearchResults> SearchArticlesForAsync(string query, string category = "", string section = "", string labels = "", string locale = "", DateTime? createdBefore = null, DateTime? createdAfter = null, DateTime? createdAt = null, DateTime? updatedBefore = null, DateTime? updatedAfter = null, DateTime? updatedAt = null)
+		{
+			return await GenericGetAsync<ArticleSearchResults>(string.Format("help_center/articles/search.json?query={0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}",
+                                                                            query,
+                                                                            String.IsNullOrEmpty(category) ? "" : "&category=" + category,
+                                                                            String.IsNullOrEmpty(section) ? "" : "&section=" + section,
+                                                                            String.IsNullOrEmpty(labels) ? "" : "&label_names=" + labels,
+                                                                            String.IsNullOrEmpty(locale) ? "" : "&locale=" + locale,
+                                                                            !createdBefore.HasValue ? "" : "&created_before=" + createdBefore.Value.ToString("yyyy-MM-dd"),
+                                                                            !createdAfter.HasValue ? "" : "&created_after=" + createdAfter.Value.ToString("yyyy-MM-dd"),
+                                                                            !createdAt.HasValue ? "" : "&created_at=" + createdAt.Value.ToString("yyyy-MM-dd"),
+                                                                            !updatedBefore.HasValue ? "" : "&updated_before=" + updatedBefore.Value.ToString("yyyy-MM-dd"),
+                                                                            !updatedAfter.HasValue ? "" : "&updated_after=" + updatedAfter.Value.ToString("yyyy-MM-dd"),
+                                                                            !updatedAt.HasValue ? "" : "&updated_at=" + updatedAt.Value.ToString("yyyy-MM-dd")));
+		}

 		public async Task<IndividualArticleResponse> CreateArticleAsync(long sectionId, Article article)
 		{
@@ -125,7 +156,6 @@
 		{
 			return await GenericDeleteAsync(string.Format("help_center/articles/{0}.json", id));
 		}
-
 #endif
 	}
 }


Remove obsolete LoC

Remove obsolete LoC

--- GooseTest/Program.cs
+++ GooseTest/Program.cs
@@ -46,7 +46,7 @@
             LuaFunction func = vm.DoString("return function(whatsUp) Console.WriteLine(whatsUp) end")[0] as LuaFunction;
             func.Call(whatsUp);

-            vm.DoString("HelloWorld()");
+

             Console.ReadLine();


In the event of an exception, I dont really care why since it will almost alway be trying to encrypt too much data...

--- ToolKit/Cryptography/RSAEncryption.cs
+++ ToolKit/Cryptography/RSAEncryption.cs
@@ -169,28 +169,25 @@
             }
             catch (CryptographicException ex)
             {
-                _log.Error(ex.Message, ex);
-
-                if ((ex.Message.ToLower().IndexOf("bad length", StringComparison.Ordinal) > -1)
-                    || (ex.Message.ToLower()
-                        .IndexOf("key not valid for use in specified state.", StringComparison.Ordinal) > -1))
-                {
-                    var sb = new StringBuilder();
-
-                    sb.Append("Your data is too large; RSA implementation in .Net is designed to encrypt ");
-                    sb.Append("relatively small amounts of data. The exact byte limit depends ");
-                    sb.Append("on the key size. To encrypt more data, use symmetric encryption ");
-                    sb.Append("and then encrypt that symmetric key with ");
-                    sb.Append("asymmetric encryption.");
-
-                    _log.Warn(sb.ToString());
-
-                    throw new CryptographicException(sb.ToString(), ex);
-                }
-                else
-                {
-                    throw;
-                }
+                _log.Error(m => m(ex.Message), ex);
+
+                var sb = new StringBuilder();
+
+                sb.Append("Your data is too large; RSA implementation in .Net is designed to encrypt ");
+                sb.Append("relatively small amounts of data. The exact byte limit depends ");
+                sb.Append("on the key size. To encrypt more data, use symmetric encryption ");
+                sb.Append("and then encrypt that symmetric key with ");
+                sb.Append("asymmetric encryption.");
+
+                _log.Warn(sb.ToString());
+
+                throw new CryptographicException(sb.ToString(), ex);
+            }
+            catch (Exception ex)
+            {
+                _log.Error(m => m(ex.ToString()), ex);
+
+                throw;
             }
             finally
             {


forgotten to actually rise the changed event

--- Transcription/TranscriptionElement.cs
+++ Transcription/TranscriptionElement.cs
@@ -496,6 +496,9 @@
         {
             if (!_Updating)
             {
+                if (ContentChanged != null)
+                    ContentChanged(this, new TranscriptionElementChangedEventArgs(actions));
+
                 if (Parent != null)
                     Parent.OnContentChanged(actions);
             }


fix slow bootstrap

fix slow bootstrap

--- Tweek.Drivers.Blob/BlobRulesDriver.cs
+++ Tweek.Drivers.Blob/BlobRulesDriver.cs
@@ -3,6 +3,7 @@
 using System.Diagnostics;
 using System.IO;
 using System.Net;
+using System.Reactive.Concurrency;
 using System.Reactive.Linq;
 using System.Reactive.Subjects;
 using System.Threading.Tasks;
@@ -25,6 +26,7 @@

             _subscription = Observable.Interval(TimeSpan.FromSeconds(30))
                 .StartWith(0)
+                .SubscribeOn(TaskPoolScheduler.Default)
                 .SelectMany(async _ =>
                 {
                     using (var client = webClientFactory.Create())


Add more tests; implement Tuple tests

Add more tests; implement Tuple tests

--- ValueUtilsTest/FieldwiseHasherTest.cs
+++ ValueUtilsTest/FieldwiseHasherTest.cs
@@ -24,7 +24,7 @@
         static readonly Func<SampleStruct, int> hash = FieldwiseHasher<SampleStruct>.Instance;

         [Fact]
-        public void IdenticalStructsWork() {
+        public void IdenticalValuesHaveIdenticalHashes() {
             PAssert.That(() =>
                 hash(new SampleStruct(1, 2, "3", 4))
                 == hash(new SampleStruct(1, 2, "3", 4)));
@@ -32,7 +32,14 @@

         [Fact]
-        public void ChangingMembersHaveDifferentHashCodes() {
+        public void IsNotJustAWrapperAroundGetHashCode() {
+            PAssert.That(() =>
+                hash(new SampleStruct(1, 2, "3", 4))
+                != new SampleStruct(1, 2, "3", 4).GetHashCode());
+        }
+
+        [Fact]
+        public void OneDifferentValueMemberChangesHash() {
             PAssert.That(() =>
                 hash(new SampleStruct(1, 2, "3", 4))
                 != hash(new SampleStruct(11, 2, "3", 4)));
@@ -48,21 +55,28 @@
         }

         [Fact]
-        public void HashFuncWorksForExternalClasses() {
-
+        public void IdenticalObjectsHaveIdenticalHashes() {
+            //it's important that this is a class not struct instance so we've checked that
+            //also, that means we're accessing another assemblies private fields
             PAssert.That(() =>
-                hash(new SampleStruct(1, 2, "3", 4))
-                !=hash( new SampleStruct(11, 2, "3", 4)));
-            PAssert.That(() =>
-                hash(new SampleStruct(1, 2, "3", 4))
-                != hash(new SampleStruct(1, 12, "3", 4)));
-            PAssert.That(() =>
-                hash(new SampleStruct(1, 2, "3", 4))
-                != hash(new SampleStruct(1, 2, "13", 4)));
-            PAssert.That(() =>
-                hash(new SampleStruct(1, 2, "3", 4))
-                != hash(new SampleStruct(1, 2, "3", 14)));
+                FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 4))
+                == FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 4)));
         }

+        [Fact]
+        public void OneDifferentObjectMemberChangesHash() {
+            PAssert.That(() =>
+                FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 4))
+                != FieldwiseHasher.Get(Tuple.Create(11, 2, "3", 4)));
+            PAssert.That(() =>
+                FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 4))
+                != FieldwiseHasher.Get(Tuple.Create(1, 12, "3", 4)));
+            PAssert.That(() =>
+                FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 4))
+                != FieldwiseHasher.Get(Tuple.Create(1, 2, "13", 4)));
+            PAssert.That(() =>
+                FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 4))
+                != FieldwiseHasher.Get(Tuple.Create(1, 2, "3", 14)));
+        }
     }
 }


Fix new entry point file not being marked on output type changed

--- VisualRust.Project/RustProjectNode.cs
+++ VisualRust.Project/RustProjectNode.cs
@@ -87,7 +87,11 @@
             if(String.Equals(e.PropertyName, "OutputType", StringComparison.OrdinalIgnoreCase)
                 && !String.Equals(e.OldValue, e.NewValue, StringComparison.OrdinalIgnoreCase))
             {
+                TrackedFileNode crateNode =  this.GetCrateFileNode(e.OldValue);
+                if(crateNode != null)
+                    crateNode.IsEntryPoint = false;
                 this.ReloadCore();
+                this.GetCrateFileNode(e.NewValue).IsEntryPoint = true;
             }
         }

@@ -138,11 +142,22 @@
             EventTriggeringFlag = ProjectNode.EventTriggering.TriggerAll;
         }

+        public string GetCrateFileNodePath(string outputType)
+        {
+            BuildOutputType output = BuildOutputTypeExtension.Parse(outputType);
+            return Path.Combine(Path.GetDirectoryName(this.FileName), "src", output.ToCrateFile());
+        }
+
+        public TrackedFileNode GetCrateFileNode(string outputType)
+        {
+            return FindNodeByFullPath(GetCrateFileNodePath(outputType)) as TrackedFileNode;
+        }
+
         protected void ReloadCore()
         {
-            BuildOutputType outputType = BuildOutputTypeExtension.Parse(GetProjectProperty(ProjectFileConstants.OutputType, true));
-            string entryPoint = Path.Combine(Path.GetDirectoryName(this.FileName), "src", outputType.ToCrateFile());
-            containsEntryPoint = FindNodeByFullPath(entryPoint) != null;
+            string outputType = GetProjectProperty(ProjectFileConstants.OutputType, true);
+            string entryPoint = GetCrateFileNodePath(outputType);
+            containsEntryPoint = GetCrateFileNode(outputType) != null;
             ModuleTracker = new ModuleTracker(entryPoint);
             base.Reload();
             // This project for some reason doesn't include entrypoint node, add it


Set owner on dialog objects

Set owner on dialog objects

--- WPFSKillTree/Views/MainWindow.xaml.cs
+++ WPFSKillTree/Views/MainWindow.xaml.cs
@@ -410,19 +410,19 @@

         private void Menu_OpenHelp(object sender, RoutedEventArgs e)
         {
-            var helpWindow = new HelpWindow();
+            var helpWindow = new HelpWindow() { Owner = this };
             helpWindow.ShowDialog();
         }

         private void Menu_OpenHotkeys(object sender, RoutedEventArgs e)
         {
-            var aboutWindow = new HotkeysWindow();
+            var aboutWindow = new HotkeysWindow() { Owner = this };
             aboutWindow.ShowDialog();
         }

         private void Menu_OpenAbout(object sender, RoutedEventArgs e)
         {
-            var aboutWindow = new AboutWindow();
+            var aboutWindow = new AboutWindow() { Owner = this };
             aboutWindow.ShowDialog();
         }

@@ -971,6 +971,7 @@
         {
             var selectedBuild = (PoEBuild)lvSavedBuilds.SelectedItem;
             var formBuildName = new FormChooseBuildName(selectedBuild.Name, selectedBuild.Note, selectedBuild.CharacterName, selectedBuild.ItemData);
+            formBuildName.Owner = this;
             var show_dialog = formBuildName.ShowDialog();
             if (show_dialog != null && (bool)show_dialog)
             {
@@ -1086,6 +1087,7 @@
         private void SaveNewBuild()
         {
             var formBuildName = new FormChooseBuildName(_persistentData.CurrentBuild.CharacterName, _persistentData.CurrentBuild.ItemData);
+            formBuildName.Owner = this;
             var show_dialog = formBuildName.ShowDialog();
             if (show_dialog != null && (bool)show_dialog)
             {


Update WalletWasabi/Wallets/WalletManager.cs

Co-Authored-By: yahiheb <1ef907ec03476373b9c05d7f089a7d2fed4d8ea8@users.noreply.github.com>

--- WalletWasabi/Wallets/WalletManager.cs
+++ WalletWasabi/Wallets/WalletManager.cs
@@ -115,8 +115,7 @@
 		{
 			var wallets = GetWallets(refreshWalletList);

-			return wallets
-				.Select(x => x.KeyManager);
+			return wallets.Select(x => x.KeyManager);
 		}

 		public IEnumerable<Wallet> GetWallets(bool refreshWalletList = true)


forgot to map the rating name field on the language view model

--- Web/MapperProfiles/LanguageProfile.cs
+++ Web/MapperProfiles/LanguageProfile.cs
@@ -1,4 +1,6 @@
+using System.Linq;
 using AutoMapper;
+using Core.Enums;
 using Core.Models;
 using Web.Models.LanguageModels;

@@ -10,8 +12,14 @@
         {
             CreateMap<LanguageViewModel, LanguageDomainModel>();
             CreateMap<AddLanguageViewModel, LanguageDomainModel>();
-            CreateMap<LanguageDomainModel, LanguageViewModel>();
-            CreateMap<UpdateLanguageViewModel, LanguageViewModel>();
+
+            CreateMap<LanguageDomainModel, LanguageViewModel>()
+                .ForMember(dest => dest.RatingName,
+                           opt => opt.MapFrom(src => RatingEnum.GetAll().SingleOrDefault(x => x.Key == src.Rating).Display));
+
+            CreateMap<UpdateLanguageViewModel, LanguageViewModel>()
+                .ForMember(dest => dest.RatingName,
+                           opt => opt.MapFrom(src => RatingEnum.GetAll().SingleOrDefault(x => x.Key == src.Rating).Display));

         }
     }


Fix receiving of binary data over 1016 bytes

--- WebSocket4Net/WebSocketCommandInfo.cs
+++ WebSocket4Net/WebSocketCommandInfo.cs
@@ -121,6 +121,8 @@
                     }

                     frame.InnerData.CopyTo(resultBuffer, offset, copied, length);
+
+                    copied += length;
                 }

                 Data = resultBuffer;


Update Week Model

Update Week Model

--- ACStalkMarket/ACStalkMarket/Models/Week.cs
+++ ACStalkMarket/ACStalkMarket/Models/Week.cs
@@ -47,7 +47,7 @@
         public bool WeekActive { get; set; }

         [Required]
-        [Range(0, int.MaxValue)]
+        [Range(int.MinValue, int.MaxValue)]
         [Display(Name = "Ganancias")]
         public int Profit { get; set; }


interpolation: relax test precision

interpolation: relax test precision

--- projects/InterpolationTests/UnitTest1.cs
+++ projects/InterpolationTests/UnitTest1.cs
@@ -49,7 +49,7 @@
             Assert.IsNotNull(interpolatedYs);
         }

-        private string GetCodeToInstantiateArray(double[] values, string name, int columns = 5)
+        private string GetCodeToInstantiateArray(double[] values, string name, int columns = 10)
         {
             StringBuilder sb = new();
             sb.AppendLine($"{name} = new double[]");
@@ -58,7 +58,7 @@
             {
                 if (i % columns == 0 && i > 0)
                     sb.Append("\n");
-                sb.Append($"{values[i]}, ");
+                sb.Append($"{values[i]:0.00000}, ");
             }
             sb.AppendLine("\n};");
             return sb.ToString();
@@ -68,57 +68,39 @@
         public void Test_KnownValues_Match()
         {
             (double[] xs, double[] ys) = RandomWalks(20);
-            (double[] interpolatedXs, double[] interpolatedYs) = Interpolation.Cubic.InterpolateXY(xs, ys, 5);
+            (double[] interpolatedXs, double[] interpolatedYs) = Interpolation.Cubic.InterpolateXY(xs, ys, 100);
             Assert.IsNotNull(interpolatedXs);
             Assert.IsNotNull(interpolatedYs);

-            Console.WriteLine(GetCodeToInstantiateArray(interpolatedXs, "xs"));
-            Console.WriteLine(GetCodeToInstantiateArray(interpolatedYs, "ys"));
+            //Console.WriteLine(GetCodeToInstantiateArray(interpolatedXs, "xs"));
+            //Console.WriteLine(GetCodeToInstantiateArray(interpolatedYs, "ys"));

-            double[] expectedXs = {
-                22.624326996795986,32.019630850401846,29.343715252454555,23.3007244045564,19.23010618329573,
-                16.787322302362085,15.491919532672515,14.863444645144094,14.421444410693885,13.685465600238945,
-                12.17505498469635,9.409759334983153,4.910265216198772,-1.434405548179225,-8.84023420484501,
-                -16.389881649676518,-23.166008778551667,-28.2512764873484,-30.72834567194463,-29.67987722821829,
-                -24.450794309231465,-15.743692305698374,-4.69917885246317,7.541817983498482,19.77343136397489,
-                30.614377765026624,38.654047865499805,42.48183234424059,40.68712188009511,32.59876285101555,
-                20.653006654295233,8.102223438279335,-1.80121319409447,-5.8049296398885035,-1.542040880700494,
-                9.021920230138413,22.59848910287441,35.89887361638896,45.634281649563526,48.710596479947256,
-                44.835258808453716,35.81677953206771,23.514601591690816,9.788167928224674,-3.5030785174291106,
-                -14.499694804368929,-21.34223799169315,-22.318153857409143,-19.09173715705384,-14.307239515801566,
-                -7.949673796438698,0.10595141596835139,8.514837406410711,14.003316025273142,14.618012888242848,
-                11.576735967186709,6.515810606638248,1.0715621511309972,-3.1196840548015268,-4.421602666625791,
-                -1.2302076142862974,6.612408590971121,17.26842142215534,28.75414741590025,39.085903108839716,
-                46.28000503760763,48.394942905840736,44.86636883108056,36.780425498238465,25.32065670461398,
-                11.670606247506608,-2.9861820757841944,-17.46616446795889,-30.585797131718014,-41.16153626976204,
-                -48.01243676205682,-50.47027550695317,-48.992567285438646,-44.186363358925746,-36.65871498882698,
-                -27.01667343655487,-15.867289963521916,-3.8176158311406443,8.525297699176456,20.554399366016842,
-                31.662637907968016,41.22499143558036,48.28050300707636,51.57036445601545,49.82544833872013,
-                42.352365379232864,31.212980225761946,19.29241072263329,9.132063694497665,1.1656973254780523,
-                -4.971095059719222,-9.644228257858618,-13.219617065704604,-16.063176280021644,-18.540820697574222
+            double[] expectedXs = new double[]
+            {
+                22.62433, 32.01963, 29.34372, 23.30072, 19.23011, 16.78732, 15.49192, 14.86344, 14.42144, 13.68547,
+                12.17506, 9.40976, 4.91027, -1.43440, -8.84023, -16.38988, -23.16601, -28.25128, -30.72835, -29.67988,
+                -24.45079, -15.74369, -4.69918, 7.54182, 19.77343, 30.61438, 38.65405, 42.48183, 40.68712, 32.59876,
+                20.65301, 8.10222, -1.80121, -5.80493, -1.54204, 9.02192, 22.59849, 35.89887, 45.63428, 48.71060,
+                44.83526, 35.81678, 23.51460, 9.78817, -3.50308, -14.49969, -21.34224, -22.31815, -19.09174, -14.30724,
+                -7.94967, 0.10595, 8.51484, 14.00332, 14.61801, 11.57674, 6.51581, 1.07156, -3.11968, -4.42160,
+                -1.23021, 6.61241, 17.26842, 28.75415, 39.08590, 46.28000, 48.39494, 44.86637, 36.78043, 25.32066,
+                11.67061, -2.98618, -17.46616, -30.58580, -41.16154, -48.01244, -50.47028, -48.99257, -44.18636, -36.65871,
+                -27.01667, -15.86729, -3.81761, 8.52530, 20.55440, 31.66264, 41.22499, 48.28050, 51.57036, 49.82545,
+                42.35236, 31.21298, 19.29241, 9.13206, 1.16570, -4.97109, -9.64423, -13.21962, -16.06318, -18.54082,
             };

-            double[] expectedYs = {
-                31.690790868220287,35.3396393662554,45.56326402539062,52.24932430599314,50.57549118581606,
-                42.241349529237255,29.1024355923628,13.014285631298621,-4.167564097849176,-20.58757733897459,
-                -34.3902178359716,-43.71994933273423,-46.72449292402444,-42.604213411856236,-33.1172347721597,
-                -20.402689383246173,-6.599709623426989,6.152572128986532,15.715023495683056,19.94851209835124,
-                17.50325552747191,11.115757177389076,4.84083804920694,2.731312273387354,7.2025505465044715,
-                16.246758562590227,27.116686476585,37.06508444342917,43.34470261806313,43.94288264879679,
-                39.93393041509217,33.20290204531299,25.634857099690926,19.114855138457624,15.111651787824103,
-                13.057158116857538,11.761321573713525,10.033935620829455,6.684793720642718,0.6145126524871783,
-                -7.969260026879537,-17.878649338305973,-27.902018609090796,-36.82773116653265,-43.44415033793019,
-                -46.53963945058208,-44.90256183178695,-37.42574593121469,-25.47925440895904,-13.245178778278984,
-                -5.361344328389992,-6.333796068058976,-14.660680059796185,-20.943530917210378,-18.83525970721601,
-                -9.830459696987282,3.541368400709855,18.75072387310943,33.26810600744551,44.56401409095213,
-                50.148521248883256,49.3012789431795,43.74413243255064,35.37741576418509,26.101462985271255,
-                17.81660814299754,12.386783441853837,10.487220945364385,11.36852890341605,14.197243625754925,
-                18.139901422127085,22.36303860227862,26.03319147595561,28.316896352904152,28.380689542870318,
-                25.393615284901884,19.019535158656605,10.00873692666765,-0.7441771059242743,-12.344604633978442,
-                -23.897943352354126,-34.50959095591061,-43.28494513950717,-49.32940359800308,-51.74836402625762,
-                -49.647224119130065,-42.25561890415289,-31.125621710204086,-19.868457143966268,-12.166690639853831,
-                -10.66684478746432,-13.057353111204014,-15.5452021320817,-14.80352127467525,-10.364244728369988,
-                -2.841933767220736,7.1468047258037295,18.9853638677346,32.0571367756031,45.745516566440564
+            double[] expectedYs = new double[]
+            {
+                31.69079, 35.33964, 45.56326, 52.24932, 50.57549, 42.24135, 29.10244, 13.01429, -4.16756, -20.58758,
+                -34.39022, -43.71995, -46.72449, -42.60421, -33.11723, -20.40269, -6.59971, 6.15257, 15.71502, 19.94851,
+                17.50325, 11.11576, 4.84084, 2.73131, 7.20255, 16.24676, 27.11669, 37.06509, 43.34470, 43.94288,
+                39.93393, 33.20290, 25.63486, 19.11486, 15.11165, 13.05716, 11.76132, 10.03394, 6.68479, 0.61451,
+                -7.96926, -17.87865, -27.90202, -36.82773, -43.44415, -46.53964, -44.90256, -37.42575, -25.47926, -13.24518,
+                -5.36134, -6.33380, -14.66068, -20.94353, -18.83526, -9.83046, 3.54137, 18.75072, 33.26810, 44.56401,
+                50.14852, 49.30128, 43.74413, 35.37742, 26.10146, 17.81661, 12.38678, 10.48722, 11.36853, 14.19724,
+                18.13990, 22.36304, 26.03319, 28.31690, 28.38069, 25.39362, 19.01954, 10.00874, -0.74418, -12.34460,
+                -23.89794, -34.50959, -43.28495, -49.32940, -51.74836, -49.64722, -42.25562, -31.12562, -19.86846, -12.16669,
+                -10.66684, -13.05735, -15.54520, -14.80352, -10.36424, -2.84193, 7.14680, 18.98536, 32.05714, 45.74552,
             };

             Assert.AreEqual(expectedXs.Length, interpolatedXs.Length);
@@ -126,8 +108,8 @@

             for (int i = 0; i < expectedXs.Length; i++)
             {
-                Assert.AreEqual(expectedXs[i], interpolatedXs[i]);
-                Assert.AreEqual(expectedYs[i], interpolatedYs[i]);
+                Assert.AreEqual(expectedXs[i], interpolatedXs[i], delta: 1e-5);
+                Assert.AreEqual(expectedYs[i], interpolatedYs[i], delta: 1e-5);
             }
         }
     }


IrcCalc: Make digits/number conform to the rest

--- IrcCalc/Tokenizer.cs
+++ IrcCalc/Tokenizer.cs
@@ -28,6 +28,8 @@
         // Also keep track if the number has a decimal point, to make sure it only has one.
         List<char> tmpNum;
         bool decimalPoint;
+        // CalcToken needs to know the original starting index of tokens, keep track of it for numbers.
+        int numberStart;

         // For keeping track of the number of opening and closing parentheses.
         int parenBalance;
@@ -41,6 +43,7 @@
             // Set initial values of control and housekeeping variables.
             TokenTypes allowedTokens = TokenTypes.ExprBegin;
             decimalPoint = false;
+            numberStart = -1;
             parenBalance = 0;

             for (int i = 0; i < expr.Length; i++)
@@ -54,7 +57,7 @@
                 {
                     if (allowedTokens.HasFlag(TokenTypes.Number))
                     {
-                        tmpNum.Add(c);
+                        AddDigitToNum(c, i);
                         // Set the allowed tokens for the next character.
                         allowedTokens = TokenTypes.Number | TokenTypes.ExprEnd;
                     }
@@ -157,6 +160,14 @@
         }

+        void AddDigitToNum(char c, int index)
+        {
+            if (tmpNum.Count == 0)
+                numberStart = index;
+
+            tmpNum.Add(c);
+        }
+
         void AddDotToNum()
         {
             if (tmpNum.Count == 0)
@@ -199,10 +210,11 @@
                 return;

             var num = new string( tmpNum.ToArray() );
-            exprList.Add( CalcToken.Number(num, -1) );
+            exprList.Add( CalcToken.Number(num, numberStart) );

             tmpNum.Clear();
             decimalPoint = false;
+            numberStart = -1;
         }
     }
 }


Add some basic error handling.

Add some basic error handling.

--- ItemCollage/Form1.cs
+++ ItemCollage/Form1.cs
@@ -38,6 +38,8 @@
             var skip = 0;
             const int MAX_SKIP = 3;

+            if (!bmp.IsBlackAt(p.X, p.Y)) return new Rectangle();
+
             if (twoDim)
             {
                 extentUp = p.Y -
@@ -113,6 +115,7 @@
             // then, its left and right border
             var left = frames.OrderBy(f => f.Left).FirstOrDefault();
             var right = frames.OrderBy(f => f.Right).LastOrDefault();
+            if (left.Width == 0 && right.Width == 0) return null;

             // and from there find the outer frame
             var leftTarget = FindOuter(bmp, left.Left, left.Top, -1);
@@ -128,6 +131,8 @@
             {
                 itemFrame = leftFrame;
             }
+
+            if (itemFrame.Width == 0) return null;

             Bitmap item = new Bitmap(itemFrame.Width, itemFrame.Height,
                 PixelFormat.Format24bppRgb);
@@ -157,6 +162,12 @@

             var item = ExtractItem(screen, cursorPos);
             sw.Stop();
+
+            if (item == null)
+            {
+                label1.Text = "No item found";
+                return;
+            }

             items.Add(item);
             pictureBox1.Image = item;