text: ## @ GenCfgOpt.py
  #
  # Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
  # SPDX-License-Identifier: BSD-2-Clause-Patent
  #
  ##
  
  import os
  import re
  import sys
  import struct
  from   datetime import date
  
  # Generated file copyright header
  
  __copyright_txt__ = """## @file
  #
  #  THIS IS AUTO-GENERATED FILE BY BUILD TOOLS AND PLEASE DO NOT MAKE MODIFICATION.
  #
  #  This file lists all VPD informations for a platform collected by build.exe.
  #
  # Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
  # This program and the accompanying materials
  # are licensed and made available under the terms and conditions of the BSD License
  # which accompanies this distribution.  The full text of the license may be found at
  # http://opensource.org/licenses/bsd-license.php
  #
  # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
  #
  """
  
  __copyright_bsf__ = """/** @file
  
    Boot Setting File for Platform Configuration.
  
    Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
    This program and the accompanying materials
    are licensed and made available under the terms and conditions of the BSD License
    which accompanies this distribution.  The full text of the license may be found at
    http://opensource.org/licenses/bsd-license.php
  
    THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
    WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
  
    This file is automatically generated. Please do NOT modify !!!
  
  **/
  
  """
  
  __copyright_h__ = """/** @file
  
  Copyright (c) %4d, Intel Corporation. All rights reserved.<BR>
  
  Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:
  
  * Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice, this
    list of conditions and the following disclaimer in the documentation and/or
    other materials provided with the distribution.
  * Neither the name of Intel Corporation nor the names of its contributors may
    be used to endorse or promote products derived from this software without
    specific prior written permission.
  
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
    THE POSSIBILITY OF SUCH DAMAGE.
  
    This file is automatically generated. Please do NOT modify !!!
  
  **/
  """
  
  BuildOptionPcd = []
  
  class CLogicalExpression:
      def __init__(self):
          self.index    = 0
          self.string   = ''
  
      def errExit(self, err = ''):
          print "ERROR: Express parsing for:"
          print "       %s" % self.string
          print "       %s^" % (' ' * self.index)
          if err:
              print "INFO : %s" % err
          raise SystemExit
  
      def getNonNumber (self, n1, n2):
          if not n1.isdigit():
              return n1
          if not n2.isdigit():
              return n2
          return None
  
      def getCurr(self, lens = 1):
          try:
              if lens == -1:
                  return self.string[self.index :]
              else:
                  if self.index + lens > len(self.string):
                      lens = len(self.string) - self.index
                  return self.string[self.index : self.index + lens]
          except Exception:
              return ''
  
      def isLast(self):
          return self.index == len(self.string)
  
      def moveNext(self, len = 1):
          self.index += len
  
      def skipSpace(self):
          while not self.isLast():
              if self.getCurr() in ' \t':
                  self.moveNext()
              else:
                  return
  
      def normNumber (self, val):
          return True if val else False
  
      def getNumber(self, var):
          var = var.strip()
          if   re.match('^0x[a-fA-F0-9]+$', var):
              value = int(var, 16)
          elif re.match('^[+-]?\d+$', var):
              value = int(var, 10)
          else:
              value = None
          return value
  
      def parseValue(self):
          self.skipSpace()
          var = ''
          while not self.isLast():
              char = self.getCurr()
              if re.match('^[\w.]', char):
                  var += char
                  self.moveNext()
              else:
                  break
          val = self.getNumber(var)
          if val is None:
              value = var
          else:
              value = "%d" % val
          return value
  
      def parseSingleOp(self):
          self.skipSpace()
          if re.match('^NOT\W', self.getCurr(-1)):
              self.moveNext(3)
              op  = self.parseBrace()
              val = self.getNumber (op)
              if val is None:
                  self.errExit ("'%s' is not a number" % op)
              return "%d" % (not self.normNumber(int(op)))
          else:
              return self.parseValue()
  
      def parseBrace(self):
          self.skipSpace()
          char = self.getCurr()
          if char == '(':
              self.moveNext()
              value = self.parseExpr()
              self.skipSpace()
              if self.getCurr() != ')':
                  self.errExit ("Expecting closing brace or operator")
              self.moveNext()
              return value
          else:
              value = self.parseSingleOp()
              return value
  
      def parseCompare(self):
          value = self.parseBrace()
          while True:
              self.skipSpace()
              char = self.getCurr()
              if char in ['<', '>']:
                  self.moveNext()
                  next = self.getCurr()
                  if next == '=':
                      op = char + next
                      self.moveNext()
                  else:
                      op = char
                  result = self.parseBrace()
                  test = self.getNonNumber(result, value)
                  if test is None:
                      value = "%d" % self.normNumber(eval (value + op + result))
                  else:
                      self.errExit ("'%s' is not a valid number for comparision" % test)
              elif char in ['=', '!']:
                  op = self.getCurr(2)
                  if op in ['==', '!=']:
                      self.moveNext(2)
                      result = self.parseBrace()
                      test = self.getNonNumber(result, value)
                      if test is None:
                          value = "%d" % self.normNumber((eval (value + op + result)))
                      else:
                          value = "%d" % self.normNumber(eval ("'" + value + "'" + op + "'" + result + "'"))
                  else:
                      break
              else:
                  break
          return value
  
      def parseAnd(self):
          value = self.parseCompare()
          while True:
              self.skipSpace()
              if re.match('^AND\W', self.getCurr(-1)):
                  self.moveNext(3)
                  result = self.parseCompare()
                  test = self.getNonNumber(result, value)
                  if test is None:
                      value = "%d" % self.normNumber(int(value) & int(result))
                  else:
                      self.errExit ("'%s' is not a valid op number for AND" % test)
              else:
                  break
          return value
  
      def parseOrXor(self):
          value  = self.parseAnd()
          op     = None
          while True:
              self.skipSpace()
              op = None
              if re.match('^XOR\W', self.getCurr(-1)):
                  self.moveNext(3)
                  op = '^'
              elif re.match('^OR\W', self.getCurr(-1)):
                  self.moveNext(2)
                  op = '|'
              else:
                  break
              if op:
                  result = self.parseAnd()
                  test = self.getNonNumber(result, value)
                  if test is None:
                      value = "%d" % self.normNumber(eval (value + op + result))
                  else:
                      self.errExit ("'%s' is not a valid op number for XOR/OR" % test)
          return value
  
      def parseExpr(self):
          return self.parseOrXor()
  
      def getResult(self):
          value = self.parseExpr()
          self.skipSpace()
          if not self.isLast():
              self.errExit ("Unexpected character found '%s'" % self.getCurr())
          test = self.getNumber(value)
          if test is None:
              self.errExit ("Result '%s' is not a number" % value)
          return int(value)
  
      def evaluateExpress (self, Expr):
          self.index     = 0
          self.string    = Expr
          if self.getResult():
              Result = True
          else:
              Result = False
          return Result
  
  class CGenCfgOpt:
      def __init__(self):
          self.Debug          = False
          self.Error          = ''
  
          self._GlobalDataDef = """
  GlobalDataDef
      SKUID = 0, "DEFAULT"
  EndGlobalData
  
  """
          self._BuidinOptionTxt = """
  List &EN_DIS
      Selection 0x1 , "Enabled"
      Selection 0x0 , "Disabled"
  EndList
  
  """
  
          self._BsfKeyList    = ['FIND','NAME','HELP','TYPE','PAGE','OPTION','ORDER']
          self._HdrKeyList    = ['HEADER','STRUCT', 'EMBED', 'COMMENT']
          self._BuidinOption  = {'$EN_DIS' : 'EN_DIS'}
  
          self._MacroDict   = {}
          self._PcdsDict    = {}
          self._CfgBlkDict  = {}
          self._CfgPageDict = {}
          self._CfgItemList = []
          self._DscFile     = ''
          self._FvDir       = ''
          self._MapVer      = 0
  
      def ParseMacros (self, MacroDefStr):
          # ['-DABC=1', '-D', 'CFG_DEBUG=1', '-D', 'CFG_OUTDIR=Build']
          self._MacroDict = {}
          IsExpression = False
          for Macro in MacroDefStr:
              if Macro.startswith('-D'):
                  IsExpression = True
                  if len(Macro) > 2:
                      Macro = Macro[2:]
                  else :
                      continue
              if IsExpression:
                  IsExpression = False
                  Match = re.match("(\w+)=(.+)", Macro)
                  if Match:
                      self._MacroDict[Match.group(1)] = Match.group(2)
                  else:
                      Match = re.match("(\w+)", Macro)
                      if Match:
                          self._MacroDict[Match.group(1)] = ''
          if len(self._MacroDict) == 0:
              Error = 1
          else:
              Error = 0
              if self.Debug:
                  print "INFO : Macro dictionary:"
                  for Each in self._MacroDict:
                      print "       $(%s) = [ %s ]" % (Each , self._MacroDict[Each])
          return Error
  
      def EvaulateIfdef   (self, Macro):
          Result = Macro in self._MacroDict
          if self.Debug:
              print "INFO : Eval Ifdef [%s] : %s" % (Macro, Result)
          return  Result
  
      def ExpandMacros (self, Input):
          Line = Input
          Match = re.findall("\$\(\w+\)", Input)
          if Match:
              for Each in Match:
                Variable = Each[2:-1]
                if Variable in self._MacroDict:
                    Line = Line.replace(Each, self._MacroDict[Variable])
                else:
                    if self.Debug:
                        print "WARN : %s is not defined" % Each
                    Line = Line.replace(Each, Each[2:-1])
          return Line
  
      def ExpandPcds (self, Input):
          Line = Input
          Match = re.findall("(\w+\.\w+)", Input)
          if Match:
              for PcdName in Match:
                if PcdName in self._PcdsDict:
                    Line = Line.replace(PcdName, self._PcdsDict[PcdName])
                else:
                    if self.Debug:
                        print "WARN : %s is not defined" % PcdName
          return Line
  
      def EvaluateExpress (self, Expr):
          ExpExpr = self.ExpandPcds(Expr)
          ExpExpr = self.ExpandMacros(ExpExpr)
          LogExpr = CLogicalExpression()
          Result  = LogExpr.evaluateExpress (ExpExpr)
          if self.Debug:
              print "INFO : Eval Express [%s] : %s" % (Expr, Result)
          return Result
  
      def FormatListValue(self, ConfigDict):
          Struct = ConfigDict['struct']
          if Struct not in ['UINT8','UINT16','UINT32','UINT64']:
              return
  
          dataarray = []
          binlist = ConfigDict['value'][1:-1].split(',')
          for each in binlist:
              each = each.strip()
              if each.startswith('0x'):
                  value = int(each, 16)
              else:
                  value = int(each)
              dataarray.append(value)
  
          unit = int(Struct[4:]) / 8
          if int(ConfigDict['length']) != unit * len(dataarray):
              raise Exception("Array size is not proper for '%s' !" % ConfigDict['cname'])
  
          bytearray = []
          for each in dataarray:
              value = each
              for loop in xrange(unit):
                  bytearray.append("0x%02X" % (value & 0xFF))
                  value = value >> 8
          newvalue  = '{'  + ','.join(bytearray) + '}'
          ConfigDict['value'] = newvalue
          return ""
  
      def ParseDscFile (self, DscFile, FvDir):
          Hardcode = False
          AutoAlign = False
          self._CfgItemList = []
          self._CfgPageDict = {}
          self._CfgBlkDict  = {}
          self._DscFile     = DscFile
          self._FvDir       = FvDir
  
          IsDefSect       = False
          IsPcdSect       = False
          IsUpdSect       = False
          IsVpdSect       = False
  
          IfStack         = []
          ElifStack       = []
          Error           = 0
          ConfigDict      = {}
  
          DscFd        = open(DscFile, "r")
          DscLines     = DscFd.readlines()
          DscFd.close()
  
          MaxAlign = 32   #Default align to 32, but if there are 64 bit unit, align to 64
          SizeAlign = 0   #record the struct max align
          while len(DscLines):
              DscLine  = DscLines.pop(0).strip()
              Handle   = False
              Match    = re.match("^\[(.+)\]", DscLine)
              if Match is not None:
                  IsDefSect = False
                  IsPcdSect = False
                  IsVpdSect = False
                  IsUpdSect = False
                  if  Match.group(1).lower() == "Defines".lower():
                      IsDefSect = True
                  if  (Match.group(1).lower() == "PcdsFeatureFlag".lower() or Match.group(1).lower() == "PcdsFixedAtBuild".lower()):
                      IsPcdSect = True
                  elif Match.group(1).lower() == "PcdsDynamicVpd.Upd".lower():
                      ConfigDict = {}
                      ConfigDict['header']  = 'ON'
                      ConfigDict['region']  = 'UPD'
                      ConfigDict['order']   = -1
                      ConfigDict['page']    = ''
                      ConfigDict['name']    = ''
                      ConfigDict['find']    = ''
                      ConfigDict['struct']  = ''
                      ConfigDict['embed']   = ''
                      ConfigDict['comment'] = ''
                      ConfigDict['subreg']  = []
                      IsUpdSect = True
                      Offset    = 0
              else:
                  if IsDefSect or IsPcdSect or IsUpdSect or IsVpdSect:
                      if re.match("^!else($|\s+#.+)", DscLine):
                          if IfStack:
                              IfStack[-1] = not IfStack[-1]
                          else:
                              print("ERROR: No paired '!if' found for '!else' for line '%s'" % DscLine)
                              raise SystemExit
                      elif re.match("^!endif($|\s+#.+)", DscLine):
                          if IfStack:
                              IfStack.pop()
                              Level = ElifStack.pop()
                              if Level > 0:
                                  del IfStack[-Level:]
                          else:
                              print("ERROR: No paired '!if' found for '!endif' for line '%s'" % DscLine)
                              raise SystemExit
                      else:
                          Result = False
                          Match = re.match("!(ifdef|ifndef)\s+(.+)", DscLine)
                          if Match:
                              Result = self.EvaulateIfdef (Match.group(2))
                              if Match.group(1) == 'ifndef':
                                  Result = not Result
                              IfStack.append(Result)
                              ElifStack.append(0)
                          else:
                              Match  = re.match("!(if|elseif)\s+(.+)", DscLine.split("#")[0])
                              if Match:
                                  Result = self.EvaluateExpress(Match.group(2))
                                  if Match.group(1) == "if":
                                      ElifStack.append(0)
                                      IfStack.append(Result)
                                  else:   #elseif
                                      if IfStack:
                                          IfStack[-1] = not IfStack[-1]
                                          IfStack.append(Result)
                                          ElifStack[-1] = ElifStack[-1] + 1
                                      else:
                                          print("ERROR: No paired '!if' found for '!elif' for line '%s'" % DscLine)
                                          raise SystemExit
                              else:
                                  if IfStack:
                                      Handle = reduce(lambda x,y: x and y, IfStack)
                                  else:
                                      Handle = True
                                  if Handle:
                                      Match = re.match("!include\s+(.+)", DscLine)
                                      if Match:
                                          IncludeFilePath = Match.group(1)
                                          IncludeFilePath = self.ExpandMacros(IncludeFilePath)
                                          PackagesPath = os.getenv("PACKAGES_PATH")
                                          if PackagesPath:
                                            for PackagePath in PackagesPath.split(os.pathsep):
                                                IncludeFilePathAbs = os.path.join(os.path.normpath(PackagePath), os.path.normpath(IncludeFilePath))
                                                if os.path.exists(IncludeFilePathAbs):
                                                    IncludeDsc  = open(IncludeFilePathAbs, "r")
                                                    break
                                          else:
                                            IncludeDsc  = open(IncludeFilePath, "r")
                                          if IncludeDsc == None:
                                              print("ERROR: Cannot open file '%s'" % IncludeFilePath)
                                              raise SystemExit
                                          NewDscLines = IncludeDsc.readlines()
                                          IncludeDsc.close()
                                          DscLines = NewDscLines + DscLines
                                          Offset = 0
                                      else:
                                          if DscLine.startswith('!'):
                                              print("ERROR: Unrecoginized directive for line '%s'" % DscLine)
                                              raise SystemExit
              if not Handle:
                  continue
  
              if IsDefSect:
                  #DEFINE UPD_TOOL_GUID       = 8C3D856A-9BE6-468E-850A-24F7A8D38E09
                  #DEFINE FSP_T_UPD_TOOL_GUID = 34686CA3-34F9-4901-B82A-BA630F0714C6
                  #DEFINE FSP_M_UPD_TOOL_GUID = 39A250DB-E465-4DD1-A2AC-E2BD3C0E2385
                  #DEFINE FSP_S_UPD_TOOL_GUID = CAE3605B-5B34-4C85-B3D7-27D54273C40F
                  Match = re.match("^\s*(?:DEFINE\s+)*(\w+)\s*=\s*([-.\w]+)", DscLine)
                  if Match:
                      self._MacroDict[Match.group(1)] = Match.group(2)
                      if self.Debug:
                          print "INFO : DEFINE %s = [ %s ]" % (Match.group(1), Match.group(2))
              elif IsPcdSect:
                  #gSiPkgTokenSpaceGuid.PcdTxtEnable|FALSE
                  #gSiPkgTokenSpaceGuid.PcdOverclockEnable|TRUE
                  Match = re.match("^\s*([\w\.]+)\s*\|\s*(\w+)", DscLine)
                  if Match:
                      self._PcdsDict[Match.group(1)] = Match.group(2)
                      if self.Debug:
                          print "INFO : PCD %s = [ %s ]" % (Match.group(1), Match.group(2))
                      i = 0
                      while i < len(BuildOptionPcd):
                          Match = re.match("\s*([\w\.]+)\s*\=\s*(\w+)", BuildOptionPcd[i])
                          if Match:
                              self._PcdsDict[Match.group(1)] = Match.group(2)
                          i += 1
              else:
                  Match = re.match("^\s*#\s+(!BSF|@Bsf|!HDR)\s+(.+)", DscLine)
                  if Match:
                      Remaining = Match.group(2)
                      if Match.group(1) == '!BSF' or Match.group(1) == '@Bsf':
                          Match = re.match("(?:^|.+\s+)PAGES:{(.+?)}", Remaining)
                          if Match:
                              # !BSF PAGES:{HSW:"Haswell System Agent", LPT:"Lynx Point PCH"}
                              PageList = Match.group(1).split(',')
                              for Page in PageList:
                                  Page  = Page.strip()
                                  Match = re.match("(\w+):\"(.+)\"", Page)
                                  self._CfgPageDict[Match.group(1)] = Match.group(2)
  
                          Match = re.match("(?:^|.+\s+)BLOCK:{NAME:\"(.+)\"\s*,\s*VER:\"(.+)\"\s*}", Remaining)
                          if Match:
                              self._CfgBlkDict['name'] = Match.group(1)
                              self._CfgBlkDict['ver']  = Match.group(2)
  
                          for Key in self._BsfKeyList:
                              Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining)
                              if Match:
                                  if Key in ['NAME', 'HELP', 'OPTION'] and Match.group(1).startswith('+'):
                                      ConfigDict[Key.lower()] += Match.group(1)[1:]
                                  else:
                                      ConfigDict[Key.lower()]  = Match.group(1)
                      else:
                          for Key in self._HdrKeyList:
                              Match = re.match("(?:^|.+\s+)%s:{(.+?)}" % Key, Remaining)
                              if Match:
                                  ConfigDict[Key.lower()]  = Match.group(1)
  
                  Match = re.match("^\s*#\s+@Prompt\s+(.+)", DscLine)
                  if Match:
                      ConfigDict['name'] = Match.group(1)
  
                  Match = re.match("^\s*#\s*@ValidList\s*(.+)\s*\|\s*(.+)\s*\|\s*(.+)\s*", DscLine)
                  if Match:
                      if Match.group(2).strip() in self._BuidinOption:
                          ConfigDict['option'] = Match.group(2).strip()
                      else:
                          OptionValueList = Match.group(2).split(',')
                          OptionStringList = Match.group(3).split(',')
                          Index = 0
                          for Option in OptionValueList:
                               Option = Option.strip()
                               ConfigDict['option'] = ConfigDict['option'] + str(Option) + ':' + OptionStringList[Index].strip()
                               Index += 1
                               if Index in range(len(OptionValueList)):
                                   ConfigDict['option'] += ', '
                      ConfigDict['type'] = "Combo"
  
                  Match = re.match("^\s*#\s*@ValidRange\s*(.+)\s*\|\s*(.+)\s*-\s*(.+)\s*", DscLine)
                  if Match:
                      if "0x" in Match.group(2) or "0x" in Match.group(3):
                         ConfigDict['type'] = "EditNum, HEX, (%s,%s)" % (Match.group(2), Match.group(3))
                      else:
                         ConfigDict['type'] = "EditNum, DEC, (%s,%s)" % (Match.group(2), Match.group(3))
  
                  Match = re.match("^\s*##\s+(.+)", DscLine)
                  if Match:
                      ConfigDict['help'] = Match.group(1)
  
                  # Check VPD/UPD
                  if IsUpdSect:
                      Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+|\*)\s*\|\s*(\d+|0x[0-9a-fA-F]+)\s*\|\s*(.+)",DscLine)
                  else:
                      Match = re.match("^([_a-zA-Z0-9]+).([_a-zA-Z0-9]+)\s*\|\s*(0x[0-9A-F]+)(?:\s*\|\s*(.+))?",  DscLine)
                  if Match:
                      ConfigDict['space']  = Match.group(1)
                      ConfigDict['cname']  = Match.group(2)
                      if Match.group(3) != '*':
                          Hardcode = True
                          Offset =  int (Match.group(3), 16)
                      else:
                          AutoAlign = True
  
                      if Hardcode and AutoAlign:
                          print("Hardcode and auto-align mixed mode is not supported by GenCfgOpt")
                          raise SystemExit
                      ConfigDict['offset'] = Offset
                      if ConfigDict['order'] == -1:
                          ConfigDict['order'] = ConfigDict['offset'] << 8
                      else:
                          (Major, Minor) = ConfigDict['order'].split('.')
                          ConfigDict['order'] = (int (Major, 16) << 8 ) +  int (Minor, 16)
                      if IsUpdSect:
                          Value = Match.group(5).strip()
                          if Match.group(4).startswith("0x"):
                              Length  = int (Match.group(4), 16)
                          else :
                              Length  = int (Match.group(4))
                          Offset += Length
                      else:
                          Value = Match.group(4)
                          if Value is None:
                              Value = ''
                          Value = Value.strip()
                          if '|' in Value:
                              Match = re.match("^.+\s*\|\s*(.+)", Value)
                              if Match:
                                  Value = Match.group(1)
                          Length = -1
  
                      ConfigDict['length'] = Length
                      Match = re.match("\$\((\w+)\)", Value)
                      if Match:
                          if Match.group(1) in self._MacroDict:
                              Value = self._MacroDict[Match.group(1)]
  
                      ConfigDict['value']  = Value
                      if (len(Value) > 0)  and (Value[0] == '{'):
                          Value = self.FormatListValue(ConfigDict)
  
                      if ConfigDict['name']  == '':
                          # Clear BSF specific items
                          ConfigDict['bsfname']   = ''
                          ConfigDict['help']   = ''
                          ConfigDict['type']   = ''
                          ConfigDict['option'] = ''
                      if IsUpdSect and AutoAlign:
                          ItemLength = int(ConfigDict['length'])
                          ItemOffset = int(ConfigDict['offset'])
                          ItemStruct = ConfigDict['struct']
                          Unit = 1
                          if ItemLength in [1, 2, 4, 8] and not ConfigDict['value'].startswith('{'):
                              Unit = ItemLength
                              # If there are 64 bit unit, align to 64
                              if Unit == 8:
                                  MaxAlign = 64
                                  SizeAlign = 8
                          if ItemStruct != '':
                              UnitDict = {'UINT8':1, 'UINT16':2, 'UINT32':4, 'UINT64':8}
                              if ItemStruct in ['UINT8', 'UINT16', 'UINT32', 'UINT64']:
                                  Unit = UnitDict[ItemStruct]
                                  # If there are 64 bit unit, align to 64
                                  if Unit == 8:
                                      MaxAlign = 64
                                  SizeAlign = max(SizeAlign, Unit)
                          if (ConfigDict['embed'].find(':START') != -1):
                              Base = ItemOffset
                          SubOffset = ItemOffset - Base
                          SubRemainder = SubOffset % Unit
                          if SubRemainder:
                              Diff = Unit - SubRemainder
                              Offset = Offset + Diff
                              ItemOffset = ItemOffset + Diff
  
                          if (ConfigDict['embed'].find(':END') != -1):
                              Remainder = Offset % (MaxAlign/8)   # MaxAlign is either 32 or 64
                              if Remainder:
                                  Diff = (MaxAlign/8) - Remainder
                                  Offset = Offset + Diff
                                  ItemOffset = ItemOffset + Diff
                              MaxAlign = 32                       # Reset to default 32 align when struct end
                          if (ConfigDict['cname'] == 'UpdTerminator'):
                              # ItemLength is the size of UpdTerminator
                              # Itemlength might be 16, 32, or 64
                              # Struct align to 64 if UpdTerminator
                              # or struct size is 64 bit, else align to 32
                              Remainder = Offset % max(ItemLength/8, 4, SizeAlign)
                              Offset = Offset + ItemLength
                              if Remainder:
                                  Diff = max(ItemLength/8, 4, SizeAlign) - Remainder
                                  ItemOffset = ItemOffset + Diff
                          ConfigDict['offset'] = ItemOffset
  
                      self._CfgItemList.append(ConfigDict.copy())
                      ConfigDict['name']   = ''
                      ConfigDict['find']   = ''
                      ConfigDict['struct'] = ''
                      ConfigDict['embed']  = ''
                      ConfigDict['comment'] = ''
                      ConfigDict['order']  = -1
                      ConfigDict['subreg'] = []
                      ConfigDict['option'] = ''
                  else:
                      # It could be a virtual item as below
                      # !BSF FIELD:{SerialDebugPortAddress0:1}
                      # or
                      # @Bsf FIELD:{SerialDebugPortAddress0:1b}
                      Match = re.match("^\s*#\s+(!BSF|@Bsf)\s+FIELD:{(.+):(\d+)([Bb])?}", DscLine)
                      if Match:
                          SubCfgDict = ConfigDict.copy()
                          if (Match.group(4) == None) or (Match.group(4) == 'B'):
                            UnitBitLen = 8
                          elif Match.group(4) == 'b':
                            UnitBitLen = 1
                          else:
                            print("ERROR: Invalide BSF FIELD length for line '%s'" % DscLine)
                            raise SystemExit
                          SubCfgDict['cname']  = Match.group(2)
                          SubCfgDict['bitlength'] = int (Match.group(3)) * UnitBitLen
                          if SubCfgDict['bitlength'] > 0:
                              LastItem =  self._CfgItemList[-1]
                              if len(LastItem['subreg']) == 0:
                                  SubOffset  = 0
                              else:
                                  SubOffset  = LastItem['subreg'][-1]['bitoffset'] + LastItem['subreg'][-1]['bitlength']
                              SubCfgDict['bitoffset'] = SubOffset
                              LastItem['subreg'].append (SubCfgDict.copy())
                          ConfigDict['name']   = ''
          return Error
  
      def GetBsfBitFields (self, subitem, bytes):
          start = subitem['bitoffset']
          end   = start + subitem['bitlength']
          bitsvalue = ''.join('{0:08b}'.format(i) for i in bytes[::-1])
          bitsvalue = bitsvalue[::-1]
          bitslen   = len(bitsvalue)
          if start > bitslen or end > bitslen:
              print "Invalid bits offset [%d,%d] for %s" % (start, end, subitem['name'])
              raise SystemExit
          return hex(int(bitsvalue[start:end][::-1], 2))
  
      def UpdateSubRegionDefaultValue (self):
          Error = 0
          for Item in self._CfgItemList:
              if len(Item['subreg']) == 0:
                  continue
              bytearray = []
              if Item['value'][0] == '{':
                  binlist = Item['value'][1:-1].split(',')
                  for each in binlist:
                      each = each.strip()
                      if each.startswith('0x'):
                          value = int(each, 16)
                      else:
                          value = int(each)
                      bytearray.append(value)
              else:
                  if Item['value'].startswith('0x'):
                      value = int(Item['value'], 16)
                  else:
                      value = int(Item['value'])
                  idx = 0
                  while  idx < Item['length']:
                      bytearray.append(value & 0xFF)
                      value = value >> 8
                      idx = idx + 1
              for SubItem in Item['subreg']:
                  valuestr = self.GetBsfBitFields(SubItem, bytearray)
                  SubItem['value'] = valuestr
          return Error
  
      def CreateSplitUpdTxt (self, UpdTxtFile):
          GuidList = ['FSP_T_UPD_TOOL_GUID','FSP_M_UPD_TOOL_GUID','FSP_S_UPD_TOOL_GUID']
          SignatureList = ['0x545F', '0x4D5F','0x535F']        #  _T, _M, and _S signature for FSPT, FSPM, FSPS
          for Index in range(len(GuidList)):
              UpdTxtFile = ''
              FvDir = self._FvDir
              if GuidList[Index] not in self._MacroDict:
                  self.Error = "%s definition is missing in DSC file" % (GuidList[Index])
                  return 1
  
              if UpdTxtFile == '':
                  UpdTxtFile = os.path.join(FvDir, self._MacroDict[GuidList[Index]] + '.txt')
  
              ReCreate = False
              if not os.path.exists(UpdTxtFile):
                  ReCreate = True
              else:
                  DscTime = os.path.getmtime(self._DscFile)
                  TxtTime = os.path.getmtime(UpdTxtFile)
                  if DscTime > TxtTime:
                      ReCreate = True
  
              if not  ReCreate:
                  # DSC has not been modified yet
                  # So don't have to re-generate other files
                  self.Error = 'No DSC file change, skip to create UPD TXT file'
                  return 256
  
              TxtFd = open(UpdTxtFile, "w")
              TxtFd.write("%s\n"   % (__copyright_txt__ % date.today().year))
  
              NextOffset = 0
              SpaceIdx   = 0
              StartAddr  = 0
              EndAddr    = 0
              Default = 'DEFAULT|'
              InRange = False
              for Item in self._CfgItemList:
                  if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]:
                      StartAddr = Item['offset']
                      NextOffset = StartAddr
                      InRange = True
                  if Item['cname'] == 'UpdTerminator' and InRange == True:
                      EndAddr = Item['offset']
                      InRange = False
              InRange = False
              for Item in self._CfgItemList:
                  if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == SignatureList[Index]:
                      InRange = True
                  if InRange != True:
                      continue
                  if Item['cname'] == 'UpdTerminator':
                      InRange = False
                  if Item['region'] != 'UPD':
                      continue
                  Offset = Item['offset']
                  if StartAddr > Offset or EndAddr < Offset:
                      continue
                  if NextOffset < Offset:
                      # insert one line
                      TxtFd.write("%s.UnusedUpdSpace%d|%s0x%04X|0x%04X|{0}\n" % (Item['space'], SpaceIdx, Default, NextOffset - StartAddr, Offset - NextOffset))
                      SpaceIdx = SpaceIdx + 1
                  NextOffset = Offset + Item['length']
                  TxtFd.write("%s.%s|%s0x%04X|%s|%s\n" % (Item['space'],Item['cname'],Default,Item['offset'] - StartAddr,Item['length'],Item['value']))
              TxtFd.close()
          return 0
  
      def ProcessMultilines (self, String, MaxCharLength):
              Multilines = ''
              StringLength = len(String)
              CurrentStringStart = 0
              StringOffset = 0
              BreakLineDict = []
              if len(String) <= MaxCharLength:
                  while (StringOffset < StringLength):
                      if StringOffset >= 1:
                          if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n':
                              BreakLineDict.append (StringOffset + 1)
                      StringOffset += 1
                  if BreakLineDict != []:
                      for Each in BreakLineDict:
                          Multilines += "  %s\n" % String[CurrentStringStart:Each].lstrip()
                          CurrentStringStart = Each
                      if StringLength - CurrentStringStart > 0:
                          Multilines += "  %s\n" % String[CurrentStringStart:].lstrip()
                  else:
                      Multilines = "  %s\n" % String
              else:
                  NewLineStart = 0
                  NewLineCount = 0
                  FoundSpaceChar = False
                  while (StringOffset < StringLength):
                      if StringOffset >= 1:
                          if NewLineCount >= MaxCharLength - 1:
                              if String[StringOffset] == ' ' and StringLength - StringOffset > 10:
                                  BreakLineDict.append (NewLineStart + NewLineCount)
                                  NewLineStart = NewLineStart + NewLineCount
                                  NewLineCount = 0
                                  FoundSpaceChar = True
                              elif StringOffset == StringLength - 1 and FoundSpaceChar == False:
                                  BreakLineDict.append (0)
                          if String[StringOffset - 1] == '\\' and String[StringOffset] == 'n':
                              BreakLineDict.append (StringOffset + 1)
                              NewLineStart = StringOffset + 1
                              NewLineCount = 0
                      StringOffset += 1
                      NewLineCount += 1
                  if BreakLineDict != []:
                      BreakLineDict.sort ()
                      for Each in BreakLineDict:
                          if Each > 0:
                              Multilines += "  %s\n" % String[CurrentStringStart:Each].lstrip()
                          CurrentStringStart = Each
                      if StringLength - CurrentStringStart > 0:
                          Multilines += "  %s\n" % String[CurrentStringStart:].lstrip()
              return Multilines
  
      def CreateField (self, Item, Name, Length, Offset, Struct, BsfName, Help, Option):
          PosName    = 28
          PosComment = 30
          NameLine=''
          HelpLine=''
          OptionLine=''
  
          IsArray = False
          if Length in [1,2,4,8]:
              Type = "UINT%d" % (Length * 8)
              if Name.startswith("UnusedUpdSpace") and Length != 1:
                  IsArray = True
                  Type = "UINT8"
          else:
              IsArray = True
              Type = "UINT8"
  
          if Item and Item['value'].startswith('{'):
              Type = "UINT8"
              IsArray = True
  
          if Struct != '':
              Type = Struct
              if Struct in ['UINT8','UINT16','UINT32','UINT64']:
                  IsArray = True
                  Unit = int(Type[4:]) / 8
                  Length = Length / Unit
              else:
                  IsArray = False
  
          if IsArray:
              Name = Name + '[%d]' % Length
  
          if len(Type) < PosName:
              Space1 = PosName - len(Type)
          else:
              Space1 = 1
  
          if BsfName != '':
              NameLine=" - %s\n" % BsfName
          else:
              NameLine="\n"
  
          if Help != '':
              HelpLine = self.ProcessMultilines (Help, 80)
  
          if Option != '':
              OptionLine = self.ProcessMultilines (Option, 80)
  
          if Offset is None:
              OffsetStr = '????'
          else:
              OffsetStr = '0x%04X' % Offset
  
          return "\n/** Offset %s%s%s%s**/\n  %s%s%s;\n" % (OffsetStr, NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name,)
  
      def PostProcessBody (self, TextBody):
          NewTextBody = []
          OldTextBody = []
          IncludeLine = False
          StructName  = ''
          VariableName = ''
          IsUpdHdrDefined = False
          IsUpdHeader = False
          for Line in TextBody:
             SplitToLines = Line.splitlines()
             MatchComment = re.match("^/\*\sCOMMENT:(\w+):([\w|\W|\s]+)\s\*/\s([\s\S]*)", SplitToLines[0])
             if MatchComment:
                if MatchComment.group(1) == 'FSP_UPD_HEADER':
                    IsUpdHeader = True
                else:
                    IsUpdHeader = False
                if IsUpdHdrDefined != True or IsUpdHeader != True:
                  CommentLine = " " + MatchComment.group(2) + "\n"
                  NewTextBody.append("/**" + CommentLine + "**/\n")
                Line = Line[(len(SplitToLines[0]) + 1):]
  
             Match = re.match("^/\*\sEMBED_STRUCT:(\w+):(\w+):(START|END)\s\*/\s([\s\S]*)", Line)
             if Match:
                 Line = Match.group(4)
                 if Match.group(1) == 'FSP_UPD_HEADER':
                     IsUpdHeader = True
                 else:
                     IsUpdHeader = False
  
             if Match and Match.group(3) == 'START':
                 if IsUpdHdrDefined != True or IsUpdHeader != True:
                     NewTextBody.append ('typedef struct {\n')
                 StructName   = Match.group(1)
                 VariableName = Match.group(2)
                 MatchOffset = re.search('/\*\*\sOffset\s0x([a-fA-F0-9]+)', Line)
                 if MatchOffset:
                     Offset = int(MatchOffset.group(1), 16)
                 else:
                     Offset = None
                 Line
                 IncludeLine = True
                 OldTextBody.append (self.CreateField (None, VariableName, 0, Offset, StructName, '', '', ''))
             if IncludeLine:
                 if IsUpdHdrDefined != True or IsUpdHeader != True:
                     NewTextBody.append (Line)
             else:
                 OldTextBody.append (Line)
  
             if Match and Match.group(3) == 'END':
                 if (StructName != Match.group(1)) or (VariableName != Match.group(2)):
                     print "Unmatched struct name '%s' and '%s' !"  % (StructName, Match.group(1))
                 else:
                     if IsUpdHdrDefined != True or IsUpdHeader != True:
                        NewTextBody.append ('} %s;\n\n' %  StructName)
                        IsUpdHdrDefined = True
                 IncludeLine = False
          NewTextBody.extend(OldTextBody)
          return NewTextBody
  
      def WriteLinesWithoutTailingSpace (self, HeaderFd, Line):
          TxtBody2 = Line.splitlines(True)
          for Line2 in TxtBody2:
              Line2 = Line2.rstrip()
              Line2 += '\n'
              HeaderFd.write (Line2)
          return 0
      def CreateHeaderFile (self, InputHeaderFile):
          FvDir = self._FvDir
  
          HeaderFileName = 'FspUpd.h'
          HeaderFile = os.path.join(FvDir, HeaderFileName)
  
          # Check if header needs to be recreated
          ReCreate = False
  
          TxtBody = []
          for Item in self._CfgItemList:
             if str(Item['cname']) == 'Signature' and Item['length'] == 8:
                 Value = int(Item['value'], 16)
                 Chars = []
                 while Value != 0x0:
                     Chars.append(chr(Value & 0xFF))
                     Value = Value >> 8
                 SignatureStr = ''.join(Chars)
                 # Signature will be _T / _M / _S for FSPT / FSPM / FSPS accordingly
                 if '_T' in SignatureStr[6:6+2]:
                     TxtBody.append("#define FSPT_UPD_SIGNATURE               %s        /* '%s' */\n\n" % (Item['value'], SignatureStr))
                 elif '_M' in SignatureStr[6:6+2]:
                     TxtBody.append("#define FSPM_UPD_SIGNATURE               %s        /* '%s' */\n\n" % (Item['value'], SignatureStr))
                 elif '_S' in SignatureStr[6:6+2]:
                     TxtBody.append("#define FSPS_UPD_SIGNATURE               %s        /* '%s' */\n\n" % (Item['value'], SignatureStr))
          TxtBody.append("\n")
  
          for Region in ['UPD']:
              UpdOffsetTable = []
              UpdSignature = ['0x545F', '0x4D5F', '0x535F']   #['_T', '_M', '_S'] signature for FSPT, FSPM, FSPS
              UpdStructure = ['FSPT_UPD', 'FSPM_UPD', 'FSPS_UPD']
              for Item in self._CfgItemList:
                  if Item["cname"] == 'Signature' and Item["value"][0:6] in UpdSignature:
                      UpdOffsetTable.append (Item["offset"])
  
              for UpdIdx in range(len(UpdOffsetTable)):
                  CommentLine = ""
                  for Item in self._CfgItemList:
                      if Item["comment"] != '' and Item["offset"] >= UpdOffsetTable[UpdIdx]:
                          MatchComment = re.match("^(U|V)PD_DATA_REGION:([\w|\W|\s]+)", Item["comment"])
                          if MatchComment and MatchComment.group(1) == Region[0]:
                              CommentLine = " " + MatchComment.group(2) + "\n"
                              TxtBody.append("/**" + CommentLine + "**/\n")
                      elif Item["offset"] >= UpdOffsetTable[UpdIdx] and Item["comment"] == '':
                          Match = re.match("^FSP([\w|\W|\s])_UPD", UpdStructure[UpdIdx])
                          if Match:
                              TxtBody.append("/** Fsp " + Match.group(1) + " UPD Configuration\n**/\n")
                  TxtBody.append("typedef struct {\n")
                  NextOffset  = 0
                  SpaceIdx    = 0
                  Offset      = 0
  
                  LastVisible = True
                  ResvOffset  = 0
                  ResvIdx     = 0
                  LineBuffer  = []
                  InRange = False
                  for Item in self._CfgItemList:
                      if Item['cname'] == 'Signature' and str(Item['value'])[0:6] == UpdSignature[UpdIdx] or Region[0] == 'V':
                          InRange = True
                      if InRange != True:
                          continue
                      if Item['cname'] == 'UpdTerminator':
                          InRange = False
  
                      if Item['region'] != Region:
                          continue
  
                      if Item["offset"] < UpdOffsetTable[UpdIdx]:
                          continue
  
                      NextVisible = LastVisible
  
                      if LastVisible and (Item['header'] == 'OFF'):
                          NextVisible = False
                          ResvOffset  = Item['offset']
                      elif (not LastVisible) and Item['header'] == 'ON':
                          NextVisible = True
                          Name = "Reserved" + Region[0] + "pdSpace%d" % ResvIdx
                          ResvIdx = ResvIdx + 1
                          TxtBody.append(self.CreateField (Item, Name, Item["offset"] - ResvOffset, ResvOffset, '', '', '', ''))
  
                      if  Offset < Item["offset"]:
                          if LastVisible:
                              Name = "Unused" + Region[0] + "pdSpace%d" % SpaceIdx
                              LineBuffer.append(self.CreateField (Item, Name, Item["offset"] - Offset, Offset, '', '', '', ''))
                          SpaceIdx = SpaceIdx + 1
                          Offset   = Item["offset"]
  
                      LastVisible = NextVisible
  
                      Offset = Offset + Item["length"]
                      if LastVisible:
                          for Each in LineBuffer:
                              TxtBody.append (Each)
                          LineBuffer = []
                          Comment = Item["comment"]
                          Embed = Item["embed"].upper()
                          if Embed.endswith(':START') or Embed.endswith(':END'):
                              if not Comment == '' and Embed.endswith(':START'):
                                 Marker = '/* COMMENT:%s */ \n' % Item["comment"]
                                 Marker = Marker + '/* EMBED_STRUCT:%s */ ' % Item["embed"]
                              else:
                                 Marker = '/* EMBED_STRUCT:%s */ ' % Item["embed"]
                          else:
                              if Embed == '':
                                  Marker = ''
                              else:
                                  self.Error = "Invalid embedded structure format '%s'!\n" % Item["embed"]
                                  return 4
                          Line = Marker + self.CreateField (Item, Item["cname"], Item["length"], Item["offset"], Item['struct'], Item['name'], Item['help'], Item['option'])
                          TxtBody.append(Line)
                      if Item['cname'] == 'UpdTerminator':
                          break
                  TxtBody.append("} " + UpdStructure[UpdIdx] + ";\n\n")
  
          # Handle the embedded data structure
          TxtBody = self.PostProcessBody (TxtBody)
  
          HeaderTFileName = 'FsptUpd.h'
          HeaderMFileName = 'FspmUpd.h'
          HeaderSFileName = 'FspsUpd.h'
  
          UpdRegionCheck = ['FSPT', 'FSPM', 'FSPS']     # FSPX_UPD_REGION
          UpdConfigCheck = ['FSP_T', 'FSP_M', 'FSP_S']  # FSP_X_CONFIG, FSP_X_TEST_CONFIG, FSP_X_RESTRICTED_CONFIG
          UpdSignatureCheck = ['FSPT_UPD_SIGNATURE', 'FSPM_UPD_SIGNATURE', 'FSPS_UPD_SIGNATURE']
          ExcludedSpecificUpd = 'FSPM_ARCH_UPD'
  
          if InputHeaderFile != '':
              if not os.path.exists(InputHeaderFile):
                   self.Error = "Input header file '%s' does not exist" % InputHeaderFile
                   return 6
  
              InFd         = open(InputHeaderFile, "r")
              IncLines     = InFd.readlines()
              InFd.close()
  
          for item in range(len(UpdRegionCheck)):
              if UpdRegionCheck[item] == 'FSPT':
                  HeaderFd = open(os.path.join(FvDir, HeaderTFileName), "w")
                  FileBase = os.path.basename(os.path.join(FvDir, HeaderTFileName))
              elif UpdRegionCheck[item] == 'FSPM':
                  HeaderFd = open(os.path.join(FvDir, HeaderMFileName), "w")
                  FileBase = os.path.basename(os.path.join(FvDir, HeaderMFileName))
              elif UpdRegionCheck[item] == 'FSPS':
                  HeaderFd = open(os.path.join(FvDir, HeaderSFileName), "w")
                  FileBase = os.path.basename(os.path.join(FvDir, HeaderSFileName))
              FileName = FileBase.replace(".", "_").upper()
              HeaderFd.write("%s\n"   % (__copyright_h__ % date.today().year))
              HeaderFd.write("#ifndef __%s__\n"   % FileName)
              HeaderFd.write("#define __%s__\n\n" % FileName)
              HeaderFd.write("#include <%s>\n\n" % HeaderFileName)
              HeaderFd.write("#pragma pack(1)\n\n")
  
              Export = False
              for Line in IncLines:
                  Match = re.search ("!EXPORT\s+([A-Z]+)\s+EXTERNAL_BOOTLOADER_STRUCT_(BEGIN|END)\s+", Line)
                  if Match:
                      if Match.group(2) == "BEGIN" and Match.group(1) == UpdRegionCheck[item]:
                          Export = True
                          continue
                      else:
                          Export = False
                          continue
                  if Export:
                      HeaderFd.write(Line)
              HeaderFd.write("\n")
  
              Index = 0
              StartIndex = 0
              EndIndex = 0
              StructStart = []
              StructStartWithComment = []
              StructEnd = []
              for Line in TxtBody:
                  Index += 1
                  Match = re.match("(typedef struct {)", Line)
                  if Match:
                      StartIndex = Index - 1
                  Match = re.match("}\s([_A-Z0-9]+);", Line)
                  if Match and (UpdRegionCheck[item] in Match.group(1) or UpdConfigCheck[item] in Match.group(1)) and (ExcludedSpecificUpd not in Match.group(1)):
                      EndIndex = Index
                      StructStart.append(StartIndex)
                      StructEnd.append(EndIndex)
              Index = 0
              for Line in TxtBody:
                  Index += 1
                  for Item in range(len(StructStart)):
                      if Index == StructStart[Item]:
                          Match = re.match("^(/\*\*\s*)", Line)
                          if Match:
                              StructStartWithComment.append(StructStart[Item])
                          else:
                              StructStartWithComment.append(StructStart[Item] + 1)
              Index = 0
              for Line in TxtBody:
                  Index += 1
                  for Item in range(len(StructStart)):
                      if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]:
                          self.WriteLinesWithoutTailingSpace(HeaderFd, Line)
              HeaderFd.write("#pragma pack()\n\n")
              HeaderFd.write("#endif\n")
              HeaderFd.close()
  
          HeaderFd = open(HeaderFile, "w")
          FileBase = os.path.basename(HeaderFile)
          FileName = FileBase.replace(".", "_").upper()
          HeaderFd.write("%s\n"   % (__copyright_h__ % date.today().year))
          HeaderFd.write("#ifndef __%s__\n"   % FileName)
          HeaderFd.write("#define __%s__\n\n" % FileName)
          HeaderFd.write("#include <FspEas.h>\n\n")
          HeaderFd.write("#pragma pack(1)\n\n")
  
          for item in range(len(UpdRegionCheck)):
              Index = 0
              StartIndex = 0
              EndIndex = 0
              StructStart = []
              StructStartWithComment = []
              StructEnd = []
              for Line in TxtBody:
                  Index += 1
                  Match = re.match("(typedef struct {)", Line)
                  if Match:
                      StartIndex = Index - 1
                  Match = re.match("#define\s([_A-Z0-9]+)\s*", Line)
                  if Match and (UpdSignatureCheck[item] in Match.group(1) or UpdSignatureCheck[item] in Match.group(1)):
                      StructStart.append(Index - 1)
                      StructEnd.append(Index)
              Index = 0
              for Line in TxtBody:
                  Index += 1
                  for Item in range(len(StructStart)):
                      if Index == StructStart[Item]:
                          Match = re.match("^(/\*\*\s*)", Line)
                          if Match:
                              StructStartWithComment.append(StructStart[Item])
                          else:
                              StructStartWithComment.append(StructStart[Item] + 1)
              Index = 0
              for Line in TxtBody:
                  Index += 1
                  for Item in range(len(StructStart)):
                      if Index >= StructStartWithComment[Item] and Index <= StructEnd[Item]:
                          self.WriteLinesWithoutTailingSpace(HeaderFd, Line)
          HeaderFd.write("#pragma pack()\n\n")
          HeaderFd.write("#endif\n")
          HeaderFd.close()
  
          return 0
  
      def WriteBsfStruct  (self, BsfFd, Item):
          LogExpr = CLogicalExpression()
          if Item['type'] == "None":
              Space = "gPlatformFspPkgTokenSpaceGuid"
          else:
              Space = Item['space']
          Line = "    $%s_%s" % (Space, Item['cname'])
          Match = re.match("\s*\{([x0-9a-fA-F,\s]+)\}\s*", Item['value'])
          if Match:
              DefaultValue = Match.group(1).strip()
          else:
              DefaultValue = Item['value'].strip()
          if 'bitlength' in Item:
              BsfFd.write("    %s%s%4d bits     $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['bitlength'], DefaultValue))
          else:
              BsfFd.write("    %s%s%4d bytes    $_DEFAULT_ = %s\n" % (Line, ' ' * (64 - len(Line)), Item['length'], DefaultValue))
          TmpList = []
          if  Item['type'] == "Combo":
              if not Item['option'] in self._BuidinOption:
                  OptList = Item['option'].split(',')
                  for Option in OptList:
                      Option = Option.strip()
                      (OpVal, OpStr) = Option.split(':')
                      test = LogExpr.getNumber (OpVal)
                      if test is None:
                          raise Exception("Selection Index '%s' is not a number" % OpVal)
                      TmpList.append((OpVal, OpStr))
          return  TmpList
  
      def WriteBsfOption  (self, BsfFd, Item):
          PcdName   = Item['space'] + '_' + Item['cname']
          WriteHelp = 0
          if Item['type'] == "Combo":
              if Item['option'] in self._BuidinOption:
                  Options = self._BuidinOption[Item['option']]
              else:
                  Options = PcdName
              BsfFd.write('    %s $%s, "%s", &%s,\n' % (Item['type'], PcdName, Item['name'], Options))
              WriteHelp = 1
          elif Item['type'].startswith("EditNum"):
              Match = re.match("EditNum\s*,\s*(HEX|DEC)\s*,\s*\((\d+|0x[0-9A-Fa-f]+)\s*,\s*(\d+|0x[0-9A-Fa-f]+)\)", Item['type'])
              if Match:
                  BsfFd.write('    EditNum $%s, "%s", %s,\n' % (PcdName, Item['name'], Match.group(1)))
                  WriteHelp = 2
          elif Item['type'].startswith("EditText"):
              BsfFd.write('    %s $%s, "%s",\n' % (Item['type'], PcdName, Item['name']))
              WriteHelp = 1
          elif Item['type'] == "Table":
              Columns = Item['option'].split(',')
              if len(Columns) != 0:
                  BsfFd.write('    %s $%s "%s",' % (Item['type'], PcdName, Item['name']))
                  for Col in Columns:
                      Fmt = Col.split(':')
                      if len(Fmt) != 3:
                          raise Exception("Column format '%s' is invalid !" % Fmt)
                      try:
                          Dtype = int(Fmt[1].strip())
                      except:
                          raise Exception("Column size '%s' is invalid !" % Fmt[1])
                      BsfFd.write('\n        Column "%s", %d bytes, %s' % (Fmt[0].strip(), Dtype, Fmt[2].strip()))
                  BsfFd.write(',\n')
                  WriteHelp = 1
  
          if WriteHelp  > 0:
              HelpLines = Item['help'].split('\\n\\r')
              FirstLine = True
              for HelpLine in HelpLines:
                  if FirstLine:
                      FirstLine = False
                      BsfFd.write('        Help "%s"\n' % (HelpLine))
                  else:
                      BsfFd.write('             "%s"\n' % (HelpLine))
              if WriteHelp == 2:
                      BsfFd.write('             "Valid range: %s ~ %s"\n' % (Match.group(2), Match.group(3)))
  
      def GenerateBsfFile (self, BsfFile):
  
          if BsfFile == '':
              self.Error = "BSF output file '%s' is invalid" % BsfFile
              return 1
  
          Error = 0
          OptionDict = {}
          BsfFd      = open(BsfFile, "w")
          BsfFd.write("%s\n" % (__copyright_bsf__ % date.today().year))
          BsfFd.write("%s\n" % self._GlobalDataDef)
          BsfFd.write("StructDef\n")
          NextOffset = -1
          for Item in self._CfgItemList:
              if Item['find'] != '':
                  BsfFd.write('\n    Find "%s"\n' % Item['find'])
                  NextOffset = Item['offset'] + Item['length']
              if Item['name'] != '':
                  if NextOffset != Item['offset']:
                      BsfFd.write("        Skip %d bytes\n" % (Item['offset'] - NextOffset))
                  if len(Item['subreg']) > 0:
                      NextOffset =  Item['offset']
                      BitsOffset =  NextOffset * 8
                      for SubItem in Item['subreg']:
                          BitsOffset += SubItem['bitlength']
                          if SubItem['name'] == '':
                              if 'bitlength' in SubItem:
                                  BsfFd.write("        Skip %d bits\n" % (SubItem['bitlength']))
                              else:
                                  BsfFd.write("        Skip %d bytes\n" % (SubItem['length']))
                          else:
                              Options = self.WriteBsfStruct(BsfFd, SubItem)
                              if len(Options) > 0:
                                  OptionDict[SubItem['space']+'_'+SubItem['cname']] = Options
  
                      NextBitsOffset = (Item['offset'] + Item['length']) * 8
                      if NextBitsOffset > BitsOffset:
                          BitsGap     = NextBitsOffset - BitsOffset
                          BitsRemain  = BitsGap % 8
                          if BitsRemain:
                              BsfFd.write("        Skip %d bits\n" % BitsRemain)
                              BitsGap -= BitsRemain
                          BytesRemain = BitsGap / 8
                          if BytesRemain:
                              BsfFd.write("        Skip %d bytes\n" % BytesRemain)
                      NextOffset = Item['offset'] + Item['length']
                  else:
                      NextOffset = Item['offset'] + Item['length']
                      Options = self.WriteBsfStruct(BsfFd, Item)
                      if len(Options) > 0:
                          OptionDict[Item['space']+'_'+Item['cname']] = Options
          BsfFd.write("\nEndStruct\n\n")
  
          BsfFd.write("%s" % self._BuidinOptionTxt)
  
          for Each in OptionDict:
              BsfFd.write("List &%s\n" % Each)
              for Item in OptionDict[Each]:
                  BsfFd.write('    Selection %s , "%s"\n' % (Item[0], Item[1]))
              BsfFd.write("EndList\n\n")
  
          BsfFd.write("BeginInfoBlock\n")
          BsfFd.write('    PPVer       "%s"\n' % (self._CfgBlkDict['ver']))
          BsfFd.write('    Description "%s"\n' % (self._CfgBlkDict['name']))
          BsfFd.write("EndInfoBlock\n\n")
  
          for Each in self._CfgPageDict:
              BsfFd.write('Page "%s"\n' % self._CfgPageDict[Each])
              BsfItems = []
              for Item in self._CfgItemList:
                  if Item['name'] != '':
                      if Item['page'] != Each:
                          continue
                      if len(Item['subreg']) > 0:
                          for SubItem in Item['subreg']:
                              if SubItem['name'] != '':
                                  BsfItems.append(SubItem)
                      else:
                          BsfItems.append(Item)
  
              BsfItems.sort(key=lambda x: x['order'])
  
              for Item in BsfItems:
                  self.WriteBsfOption (BsfFd, Item)
              BsfFd.write("EndPage\n\n")
  
          BsfFd.close()
          return  Error
  
  
  def Usage():
      print "GenCfgOpt Version 0.53"
      print "Usage:"
      print "    GenCfgOpt  UPDTXT  PlatformDscFile BuildFvDir                 [-D Macros]"
      print "    GenCfgOpt  HEADER  PlatformDscFile BuildFvDir  InputHFile     [-D Macros]"
      print "    GenCfgOpt  GENBSF  PlatformDscFile BuildFvDir  BsfOutFile     [-D Macros]"
  
  def Main():
      #
      # Parse the options and args
      #
      i = 1
  
      GenCfgOpt = CGenCfgOpt()
      while i < len(sys.argv):
          if sys.argv[i].strip().lower() == "--pcd":
              BuildOptionPcd.append(sys.argv[i+1])
              i += 1
          i += 1
      argc = len(sys.argv)
      if argc < 4:
          Usage()
          return 1
      else:
          DscFile = sys.argv[2]
          if not os.path.exists(DscFile):
              print "ERROR: Cannot open DSC file '%s' !" % DscFile
              return 2
  
          OutFile = ''
          if argc > 4:
              if sys.argv[4][0] == '-':
                  Start = 4
              else:
                  OutFile = sys.argv[4]
                  Start = 5
              if argc > Start:
                  if GenCfgOpt.ParseMacros(sys.argv[Start:]) != 0:
                      print "ERROR: Macro parsing failed !"
                      return 3
  
          FvDir = sys.argv[3]
          if not os.path.exists(FvDir):
              os.makedirs(FvDir)
  
          if GenCfgOpt.ParseDscFile(DscFile, FvDir) != 0:
              print "ERROR: %s !" % GenCfgOpt.Error
              return 5
  
          if GenCfgOpt.UpdateSubRegionDefaultValue() != 0:
              print "ERROR: %s !" % GenCfgOpt.Error
              return 7
  
          if sys.argv[1] == "UPDTXT":
              Ret = GenCfgOpt.CreateSplitUpdTxt(OutFile)
              if Ret != 0:
                  # No change is detected
                  if Ret == 256:
                      print "INFO: %s !" % (GenCfgOpt.Error)
                  else :
                      print "ERROR: %s !" % (GenCfgOpt.Error)
              return Ret
          elif sys.argv[1] == "HEADER":
              if GenCfgOpt.CreateHeaderFile(OutFile) != 0:
                  print "ERROR: %s !" % GenCfgOpt.Error
                  return 8
          elif sys.argv[1] == "GENBSF":
              if GenCfgOpt.GenerateBsfFile(OutFile) != 0:
                  print "ERROR: %s !" % GenCfgOpt.Error
                  return 9
          else:
              if argc < 5:
                  Usage()
                  return 1
              print "ERROR: Unknown command '%s' !" % sys.argv[1]
              Usage()
              return 1
          return 0
      return 0
  
  
  if __name__ == '__main__':
      sys.exit(Main())
  