context: #region License
  /* 
   *
   * Copyrighted (c) 2017-2021 Christian Hostelet.
   *
   * The contents of this file are subject to the terms of the Common Development
   * and Distribution License (the License), or the GPL v2, or (at your option)
   * any later version. 
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
   * or http://www.gnu.org/licenses/gpl-2.0.html.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; see the file COPYING.  If not, write to
   * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
   *
   * When distributing Covered Code, include this CDDL Header Notice in each file
   * and include the License file at http://www.netbeans.org/cddl.txt.
   * If applicable, add the following below the CDDL Header, with the fields
   * enclosed by brackets [] replaced by your own identifying information:
   * "Portions Copyrighted (c) [year] [name of copyright owner]"
   *
   */
  
  #endregion
  
  namespace Reko.Tools.genPICdb
  {
      using System;
      using System.Collections.Generic;
      using System.Diagnostics;
      using System.Globalization;
      using System.IO;
      using System.IO.Compression;
      using System.Linq;
      using System.Reflection;
      using System.Xml.Linq;
      using System.Diagnostics.CodeAnalysis;
      using System.Text.RegularExpressions;
  
  #if NETCOREAPP || PLATFORM_UNIX
      using System.Runtime.InteropServices;
  #endif
  
      using Reko.Libraries.Microchip;
  
      /// <summary>
      /// A program to generate the PIC definition database from the MPLAB X IDE installation.
      /// <para>
      /// This program is designed to run on Windows (.NET Framework only), Linux (.NET Core only) and OSX (.NET Core only).
      /// </para>
      /// The Microchip MPLAB X IDE is freely available at www.microchip.com as a development tool.
      /// </summary>
      public class Program
      {
  
          private const string edcNamespace = @"http://crownking/edc";
  
          private bool success = false;
  
          private static readonly PICPartInfo picPartsInfo = new PICPartInfo();
  
          private static readonly IMPLABLocations mplabLoc = MPLABLocations.Create();
  
          private static readonly HashSet<string> acceptedPICArchitectures =
              new HashSet<string>() { "16xxxx", "16Exxx", "18xxxx" };
  
          private static readonly Regex picFilterRegex = new Regex(@"PIC1[68][CF][1-9]\w*", RegexOptions.Compiled);
  
  
          // XML elements we are ignoring from the Microchip PIC Device definition.
          // This helps decrease the size of the generated database.
          private static readonly string[] unwantedPICXNodes =
              new string[] {
                  "AliasList",
                  "Breakpoints",
                  "Checksum",
                  "Freeze",
                  "Import",
                  "LCD",
                  "MemoryModeList",
                  "Oscillator",
                  "PinList",
                  "Power",
                  "Programming",
                  "Properties",
                  "StimInfo",
                  "WatchdogTimer",
              };
  
          private static string getExecutingDirectoryName()
          {
              var assembly = Assembly.GetEntryAssembly();
              if (assembly is null)
                  return null;
              return Path.GetDirectoryName(assembly.Location);
          }
  
          private static string workingDir
          {
              get
              {
                  if (workDir is null)
                      workDir = getExecutingDirectoryName();
                  return workDir;
              }
          }
          private static string workDir = null;
  
          private static string picLocalDBFilePath { get; set; }
              = Path.Combine(workingDir, PICConstants.LocalDBFilename);
  
  
          // Prunes the PIC XML document of unwanted/unnecessary information.
          // Adds bit position information for DCRField and SFRField.
          private static XDocument pruneAndPatch(XDocument xdoc)
          {
              XNamespace edc = edcNamespace;
  
              var xroot = xdoc?.Root;
              if (xroot is null)
                  return null;
              if (xroot.Name.LocalName != "PIC")
                  return null;
              if (xroot.Name.Namespace == XNamespace.None)
                  return xdoc;
              var sArch = xroot.Attribute(edc + "arch")?.Value;
              if (!acceptedPICArchitectures.Contains(sArch))
                  return null;
  
              // Remove the useless (for our usage) elements
              foreach (var name in unwantedPICXNodes)
                  xroot.Descendants().Where(p => p.Name.LocalName == name).Remove();
  
              // Remove unwanted sub-elements in various elements that we have no use of.
              xroot.Descendants().Where(p => p.Name.LocalName == "SFRModeList").Where(p => p.IsEmpty).Remove();
  
              // Remove the namespaces and prefixes
              foreach (var e in xroot.DescendantsAndSelf())
              {
                  if (e.Name.Namespace != XNamespace.None)
                      e.Name = XNamespace.None.GetName(e.Name.LocalName);
                  if (e.Attributes().Where(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None).Any())
                      e.ReplaceAttributes(e.Attributes().Select(a => a.IsNamespaceDeclaration ? null : a.Name.Namespace != XNamespace.None ? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) : a));
              }
  
              // Remove the unwanted root's attribute.
              xroot.Attribute("schemaLocation")?.Remove();
  
              // Add the bit fields' bit-location.
              xdoc = addBitPos(xdoc, "DCR");
              xdoc = addBitPos(xdoc, "SFR");
  
              // Must be done only after the addition of the bit locations on the bit fields.
              xdoc.Descendants().Where(p => p.Name.LocalName == "AdjustPoint").Remove();
  
              // Remove optional but useless/confusing attributes in 'JoinedSFRDef' elements.
              foreach (var xjoined in xroot.Descendants().Where(p => p.Name.LocalName == "JoinedSFRDef"))
              {
                  xjoined.Attribute("_modsrc")?.Remove();
                  xjoined.Attribute("_refcount")?.Remove();
              }
  
              // Prune any redundant or meaningless bit field definitions.
              xdoc = removeRedundantFieldDef(xdoc, "DCR");
              xdoc = removeRedundantFieldDef(xdoc, "SFR");
  
              // Finally returns the edulcorated XML document.
              return xdoc;
          }
  
          /// <summary>
          /// Adds a bit position attribute to each bitfield definition (DCRFieldDef, SFRFieldDef).
          /// </summary>
          /// <param name="xdoc">The XML document.</param>
          /// <param name="prefix">The prefix to bitfield definition.</param>
          private static XDocument addBitPos(XDocument xdoc, string prefix)
          {
              var mode = prefix + "Mode";
              var fielddef = prefix + "FieldDef";
  
              foreach (var xfieldmode in xdoc.Root.Descendants().Where(p => p.Name.LocalName == mode))
              {
                  var bitpos = 0;
                  foreach (var xfielddef in xfieldmode.Elements())
                  {
                      var name = xfielddef.Name.LocalName;
                      if (name.Equals(fielddef))
                      {
                          xfielddef.SetAttributeValue("bitpos", bitpos);
                          bitpos += xfielddef.Attribute("nzwidth").Value.ToInt32Ex();
                          continue;
                      }
                      if (name.Equals("AdjustPoint"))
                      {
                          bitpos += xfielddef.Attribute("offset").Value.ToInt32Ex();
                          continue;
                      }
                      throw new NotImplementedException($"Unexpected XElement descendant '{name}' for {mode}.");
                  }
              }
  
              return xdoc;
  
          }
  
          /// <summary>
          /// Removes the redundant register field definitions. Those whose bit width is equal to that of parent register.
          /// </summary>
          /// <param name="xdoc">The XML document.</param>
          /// <param name="prefix">The prefix to register definition.</param>
          private static XDocument removeRedundantFieldDef(XDocument xdoc, string prefix)
          {
              var def = prefix + "Def";
              var fieldmodelist = prefix + "ModeList";
              var fieldmode = prefix + "Mode";
              var fielddef = prefix + "FieldDef";
  
              var xelem2remove = new List<XElement>();
  
              foreach (var xregdef in xdoc.Root.Descendants().Where(p => p.Name.LocalName == def))
              {
                  xregdef.Attribute("_modsrc")?.Remove();
                  xregdef.Attribute("_refcount")?.Remove();
  
                  var defnzwidth = xregdef.Attribute("nzwidth").Value.ToUInt32Ex(); // Size of main register
  
                  foreach (var xfielddef in xregdef.Descendants().Where(p => p.Name.LocalName == fielddef))
                  {
                      var fldnzwidth = xfielddef.Attribute("nzwidth").Value.ToUInt16Ex(); // Size of bit field
                      if (fldnzwidth == defnzwidth)
                      {
                          xelem2remove.Add(xfielddef);
                      }
                  }
                  xelem2remove.ForEach(xelem => xelem.Remove());
                  xelem2remove.Clear();
  
                  foreach (var xregmode in xregdef.Descendants().Where(p => p.Name.LocalName == fieldmode))
                  {
                      if (!xregmode.HasElements)
                          xelem2remove.Add(xregmode);
                  }
                  xelem2remove.ForEach(xelem => xelem.Remove());
                  xelem2remove.Clear();
  
                  foreach (var xfieldmodelist in xregdef.Descendants().Where(p => p.Name.LocalName == fieldmodelist))
                  {
                      if (!xfieldmodelist.HasElements)
                          xelem2remove.Add(xfieldmodelist);
                  }
                  xelem2remove.ForEach(xelem => xelem.Remove());
                  xelem2remove.Clear();
  
              }
  
  
              return xdoc;
          }
  
          // Filters acceptable PIC16 and PIC18 by their names.
          private static bool filterPICName(string s)
          {
              var res = picFilterRegex.Match(s);
              return res.Success && !s.Contains("J") && !s.Contains("Q");
          }
  
          // Writes a PIC XML document to the compressed PIC database. Keeps note of the PIC processor ID for COFF decoding.
          private static void writePICEntry(XDocument xdoc, string subdir, ZipArchive zout)
          {
  
gt:             var xroot = xdoc?.Root;