# @ GenCfgData.py
#
# Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
import os
import re
import sys
import marshal
from functools import reduce
from datetime import date
# Generated file copyright header
__copyright_tmp__ = """/** @file
  Configuration %s File.
  Copyright (c) %4d, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
  This file is automatically generated. Please do NOT modify !!!
**/
"""
__copyright_dsc__ = """## @file
#
#  Copyright (c) %04d, Intel Corporation. All rights reserved.
#  SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[PcdsDynamicVpd.Upd]
  #
  # Global definitions in BSF
  # !BSF BLOCK:{NAME:"FSP UPD Configuration", VER:"0.1"}
  #
"""
def Bytes2Val(Bytes):
    return reduce(lambda x, y: (x << 8) | y, Bytes[::-1])
def Bytes2Str(Bytes):
    return '{ %s }' % (', '.join('0x%02X' % i for i in Bytes))
def Str2Bytes(Value, Blen):
    Result = bytearray(Value[1:-1], 'utf-8')  # Excluding quotes
    if len(Result) < Blen:
        Result.extend(b'\x00' * (Blen - len(Result)))
    return Result
def Val2Bytes(Value, Blen):
    return [(Value >> (i * 8) & 0xff) for i in range(Blen)]
def Array2Val(ValStr):
    ValStr = ValStr.strip()
    if ValStr.startswith('{'):
        ValStr = ValStr[1:]
    if ValStr.endswith('}'):
        ValStr = ValStr[:-1]
    if ValStr.startswith("'"):
        ValStr = ValStr[1:]
    if ValStr.endswith("'"):
        ValStr = ValStr[:-1]
    Value = 0
    for Each in ValStr.split(',')[::-1]:
        Each = Each.strip()
        if Each.startswith('0x'):
            Base = 16
        else:
            Base = 10
        Value = (Value << 8) | int(Each, Base)
    return Value
def GetCopyrightHeader(FileType, AllowModify=False):
    FileDescription = {
        'bsf': 'Boot Setting',
        'dsc': 'Definition',
        'dlt': 'Delta',
        'inc': 'C Binary Blob',
        'h': 'C Struct Header'
    }
    if FileType in ['bsf', 'dsc', 'dlt']:
        CommentChar = '#'
    else:
        CommentChar = ''
    Lines = __copyright_tmp__.split('\n')
    if AllowModify:
        Lines = [Line for Line in Lines if 'Please do NOT modify' not in Line]
    CopyrightHdr = '\n'.join('%s%s' % (
        CommentChar, Line) for Line in Lines)[:-1] + '\n'
    return CopyrightHdr % (FileDescription[FileType], date.today().year)
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 CFspBsf2Dsc:
    def __init__(self, bsf_file):
        self.cfg_list = CFspBsf2Dsc.parse_bsf(bsf_file)
    def get_dsc_lines(self):
        return CFspBsf2Dsc.generate_dsc(self.cfg_list)
    def save_dsc(self, dsc_file):
        return CFspBsf2Dsc.generate_dsc(self.cfg_list, dsc_file)
    @staticmethod
    def parse_bsf(bsf_file):
        fd = open(bsf_file, 'r')
        bsf_txt = fd.read()
        fd.close()
        find_list = []
        regex = re.compile(r'\s+Find\s+"(.*?)"(.*?)^\s+(\$(.*?)|Skip)\s+',
                           re.S | re.MULTILINE)
        for match in regex.finditer(bsf_txt):
            find = match.group(1)
            name = match.group(3)
            line = bsf_txt[:match.end()].count("\n")
            find_list.append((name, find, line))
        idx = 0
        count = 0
        prefix = ''
        chk_dict = {}
        cfg_list = []
        cfg_temp = {'find': '', 'cname': '', 'length': 0, 'value': '0',
                    'type': 'Reserved', 'isbit': False,
                    'embed': '', 'page': '', 'option': '', 'instance': 0}
        regex = re.compile(
            r'^\s+(\$(.*?)|Skip)\s+(\d+)\s+(bits|bytes)(\s+\$_DEFAULT_\s'
            r'+=\s+(.+?))?$', re.S |
            re.MULTILINE)
        for match in regex.finditer(bsf_txt):
            dlen = int(match.group(3))
            if match.group(1) == 'Skip':
                key = 'gPlatformFspPkgTokenSpaceGuid_BsfSkip%d' % idx
                val = ', '.join(['%02X' % ord(i) for i in '\x00' * dlen])
                idx += 1
                option = '$SKIP'
            else:
                key = match.group(2)
                val = match.group(6)
                option = ''
            is_bit = True if match.group(4) == 'bits' else False
            cfg_item = dict(cfg_temp)
            line = bsf_txt[:match.end()].count("\n")
            finds = [i for i in find_list if line >= i[2]]
            if len(finds) > 0:
                prefix = finds[0][1]
                cfg_item['embed'] = '%s:TAG_%03X:START' % \
                    (prefix, ord(prefix[-1]))
                cfg_item['find'] = prefix
                cfg_item['cname'] = 'Signature'
                cfg_item['length'] = len(finds[0][1])
                str2byte = Str2Bytes("'" + finds[0][1] + "'",
                                     len(finds[0][1]))
                cfg_item['value'] = '0x%X' % Bytes2Val(str2byte)
                cfg_list.append(dict(cfg_item))
                cfg_item = dict(cfg_temp)
                find_list.pop(0)
                count = 0
            cfg_item['cname'] = key
            cfg_item['length'] = dlen
            cfg_item['value'] = val
            cfg_item['option'] = option
            cfg_item['isbit'] = is_bit
            if key not in chk_dict.keys():
                chk_dict[key] = 0
            else:
                chk_dict[key] += 1
            cfg_item['instance'] = chk_dict[key]
            cfg_list.append(cfg_item)
            count += 1
        if prefix:
            cfg_item = dict(cfg_temp)
            cfg_item['cname'] = 'Dummy'
            cfg_item['embed'] = '%s:%03X:END' % (prefix, ord(prefix[-1]))
            cfg_list.append(cfg_item)
        option_dict = {}
        selreg = re.compile(
            r'\s+Selection\s*(.+?)\s*,\s*"(.*?)"$', re.S |
            re.MULTILINE)
        regex = re.compile(
            r'^List\s&(.+?)$(.+?)^EndList$', re.S | re.MULTILINE)
        for match in regex.finditer(bsf_txt):
            key = match.group(1)
            option_dict[key] = []
            for select in selreg.finditer(match.group(2)):
                option_dict[key].append(
                    (int(select.group(1), 0), select.group(2)))
        chk_dict = {}
        pagereg = re.compile(
            r'^Page\s"(.*?)"$(.+?)^EndPage$', re.S | re.MULTILINE)
        for match in pagereg.finditer(bsf_txt):
            page = match.group(1)
            for line in match.group(2).splitlines():
                match = re.match(
                    r'\s+(Combo|EditNum)\s\$(.+?),\s"(.*?)",\s(.+?),$', line)
                if match:
                    cname = match.group(2)
                    if cname not in chk_dict.keys():
                        chk_dict[cname] = 0
                    else:
                        chk_dict[cname] += 1
                    instance = chk_dict[cname]
                    cfg_idxs = [i for i, j in enumerate(cfg_list)
                                if j['cname'] == cname and
                                j['instance'] == instance]
                    if len(cfg_idxs) != 1:
                        raise Exception(
                            "Multiple CFG item '%s' found !" % cname)
                    cfg_item = cfg_list[cfg_idxs[0]]
                    cfg_item['page'] = page
                    cfg_item['type'] = match.group(1)
                    cfg_item['prompt'] = match.group(3)
                    cfg_item['range'] = None
                    if cfg_item['type'] == 'Combo':
                        cfg_item['option'] = option_dict[match.group(4)[1:]]
                    elif cfg_item['type'] == 'EditNum':
                        cfg_item['option'] = match.group(4)
                match = re.match(r'\s+ Help\s"(.*?)"$', line)
                if match:
                    cfg_item['help'] = match.group(1)
                match = re.match(r'\s+"Valid\srange:\s(.*)"$', line)
                if match:
                    parts = match.group(1).split()
                    cfg_item['option'] = (
                        (int(parts[0], 0), int(parts[2], 0),
                         cfg_item['option']))
        return cfg_list
    @staticmethod
    def generate_dsc(option_list, dsc_file=None):
        dsc_lines = []
        header = '%s' % (__copyright_dsc__ % date.today().year)
        dsc_lines.extend(header.splitlines())
        pages = []
        for cfg_item in option_list:
            if cfg_item['page'] and (cfg_item['page'] not in pages):
                pages.append(cfg_item['page'])
        page_id = 0
        for page in pages:
            dsc_lines.append('  # !BSF PAGES:{PG%02X::"%s"}' % (page_id, page))
            page_id += 1
        dsc_lines.append('')
        last_page = ''
        is_bit = False
        dlen = 0
        dval = 0
        bit_fields = []
        for idx, option in enumerate(option_list):
            if not is_bit and option['isbit']:
                is_bit = True
                dlen = 0
                dval = 0
                idxs = idx
            if is_bit and not option['isbit']:
                is_bit = False
                if dlen % 8 != 0:
                    raise Exception("Bit fields are not aligned at "
                                    "byte boundary !")
                bit_fields.append((idxs, idx, dlen, dval))
            if is_bit:
                blen = option['length']
                bval = int(option['value'], 0)
                dval = dval + ((bval & ((1 << blen) - 1)) << dlen)
                print(dlen, blen, bval, hex(dval))
                dlen += blen
        struct_idx = 0
        for idx, option in enumerate(option_list):
            dsc_lines.append('')
            default = option['value']
            pos = option['cname'].find('_')
            name = option['cname'][pos + 1:]
            for start_idx, end_idx, bits_len, bits_val in bit_fields:
                if idx == start_idx:
                    val_str = Bytes2Str(Val2Bytes(bits_val, bits_len // 8))
                    dsc_lines.append('  # !HDR STRUCT:{BIT_FIELD_DATA_%d}'
                                     % struct_idx)
                    dsc_lines.append('  # !BSF NAME:{BIT_FIELD_STRUCT}')
                    dsc_lines.append('  gCfgData.BitFiledStruct%d            '
                                     ' | * | 0x%04X | %s' %
                                     (struct_idx, bits_len // 8, val_str))
                    dsc_lines.append('')
                    struct_idx += 1
            if option['find']:
                dsc_lines.append('  # !BSF FIND:{%s}' % option['find'])
                dsc_lines.append('')
            if option['instance'] > 0:
                name = name + '_%s' % option['instance']
            if option['embed']:
                dsc_lines.append('  # !HDR EMBED:{%s}' % option['embed'])
            if option['type'] == 'Reserved':
                dsc_lines.append('  # !BSF NAME:{Reserved} TYPE:{Reserved}')
                if option['option'] == '$SKIP':
                    dsc_lines.append('  # !BSF OPTION:{$SKIP}')
            else:
                prompt = option['prompt']
                if last_page != option['page']:
                    last_page = option['page']
                    dsc_lines.append('  # !BSF PAGE:{PG%02X}' %
                                     (pages.index(option['page'])))
                if option['type'] == 'Combo':
                    dsc_lines.append('  # !BSF NAME:{%s} TYPE:{%s}' %
                                     (prompt, option['type']))
                    ops = []
                    for val, text in option['option']:
                        ops.append('0x%x:%s' % (val, text))
                    dsc_lines.append('  # !BSF OPTION:{%s}' % (', '.join(ops)))
                elif option['type'] == 'EditNum':
                    cfg_len = option['length']
                    if ',' in default and cfg_len > 8:
                        dsc_lines.append('  # !BSF NAME:{%s} TYPE:{Table}' %
                                         (prompt))
                        if cfg_len > 16:
                            cfg_len = 16
                        ops = []
                        for i in range(cfg_len):
                            ops.append('%X:1:HEX' % i)
                        dsc_lines.append('  # !BSF OPTION:{%s}' %
                                         (', '.join(ops)))
                    else:
                        dsc_lines.append(
                            '  # !BSF NAME:{%s} TYPE:{%s, %s, (0x%X, 0x%X)}' %
                            (prompt, option['type'], option['option'][2],
                             option['option'][0], option['option'][1]))
                dsc_lines.append('  # !BSF HELP:{%s}' % option['help'])
            if ',' in default:
                default = '{%s}' % default
            if option['isbit']:
                dsc_lines.append('  # !BSF FIELD:{%s:%db}'
                                 % (name, option['length']))
            else:
                dsc_lines.append('  gCfgData.%-30s | * | 0x%04X | %s' %
                                 (name, option['length'], default))
        if dsc_file:
            fd = open(dsc_file, 'w')
            fd.write('\n'.join(dsc_lines))
            fd.close()
        return dsc_lines
class CGenCfgData:
    def __init__(self, Mode=''):
        self.Debug = False
        self.Error = ''
        self.ReleaseMode = True
        self.Mode = Mode
        self._GlobalDataDef = """
GlobalDataDef
    SKUID = 0, "DEFAULT"
EndGlobalData
"""
        self._BuidinOptionTxt = """
List &EN_DIS
    Selection 0x1 , "Enabled"
    Selection 0x0 , "Disabled"
EndList
"""
        self._StructType = ['UINT8', 'UINT16', 'UINT32', 'UINT64']
        self._BsfKeyList = ['FIND', 'NAME', 'HELP', 'TYPE', 'PAGE', 'PAGES',
                            'BLOCK', 'OPTION', 'CONDITION', 'ORDER', 'MARKER',
                            'SUBT']
        self._HdrKeyList = ['HEADER', 'STRUCT', 'EMBED', 'COMMENT']
        self._BuidinOption = {'$EN_DIS': 'EN_DIS'}
        self._MacroDict = {}
        self._VarDict = {}
        self._PcdsDict = {}
        self._CfgBlkDict = {}
        self._CfgPageDict = {}
        self._CfgOptsDict = {}
        self._BsfTempDict = {}
        self._CfgItemList = []
        self._DscLines = []
        self._DscFile = ''
        self._CfgPageTree = {}
        self._MapVer = 0
        self._MinCfgTagId = 0x100
    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, Preserve=False):
        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)
                    if not Preserve:
                        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 ValueToByteArray(self, ValueStr, Length):
        Match = re.match("\\{\\s*FILE:(.+)\\}", ValueStr)
        if Match:
            FileList = Match.group(1).split(',')
            Result = bytearray()
            for File in FileList:
                File = File.strip()
                BinPath = os.path.join(os.path.dirname(self._DscFile), File)
                Result.extend(bytearray(open(BinPath, 'rb').read()))
        else:
            try:
                Result = bytearray(self.ValueToList(ValueStr, Length))
            except ValueError:
                raise Exception("Bytes in '%s' must be in range 0~255 !" %
                                ValueStr)
        if len(Result) < Length:
            Result.extend(b'\x00' * (Length - len(Result)))
        elif len(Result) > Length:
            raise Exception("Value '%s' is too big to fit into %d bytes !" %
                            (ValueStr, Length))
        return Result[:Length]
    def ValueToList(self, ValueStr, Length):
        if ValueStr[0] == '{':
            Result = []
            BinList = ValueStr[1:-1].split(',')
            InBitField = False
            LastInBitField = False
            Value = 0
            BitLen = 0
            for Element in BinList:
                InBitField = False
                Each = Element.strip()
                if len(Each) == 0:
                    pass
                else:
                    if Each[0] in ['"', "'"]:
                        Result.extend(list(bytearray(Each[1:-1], 'utf-8')))
                    elif ':' in Each:
                        Match = re.match("(.+):(\\d+)b", Each)
                        if Match is None:
                            raise Exception("Invald value list format '%s' !"
                                            % Each)
                        InBitField = True
                        CurrentBitLen = int(Match.group(2))
                        CurrentValue = ((self.EvaluateExpress(Match.group(1))
                                         & (1 << CurrentBitLen) - 1)) << BitLen
                    else:
                        Result.append(self.EvaluateExpress(Each.strip()))
                if InBitField:
                    Value += CurrentValue
                    BitLen += CurrentBitLen
                if LastInBitField and ((not InBitField) or (Element ==
                                                            BinList[-1])):
                    if BitLen % 8 != 0:
                        raise Exception("Invald bit field length!")
                    Result.extend(Val2Bytes(Value, BitLen // 8))
                    Value = 0
                    BitLen = 0
                LastInBitField = InBitField
        elif ValueStr.startswith("'") and ValueStr.endswith("'"):
            Result = Str2Bytes(ValueStr, Length)
        elif ValueStr.startswith('"') and ValueStr.endswith('"'):
            Result = Str2Bytes(ValueStr, Length)
        else:
            Result = Val2Bytes(self.EvaluateExpress(ValueStr), Length)
        return Result
    def FormatDeltaValue(self, ConfigDict):
        ValStr = ConfigDict['value']
        if ValStr[0] == "'":
            # Remove padding \x00 in the value string
            ValStr = "'%s'" % ValStr[1:-1].rstrip('\x00')
        Struct = ConfigDict['struct']
        if Struct in self._StructType:
            # Format the array using its struct type
            Unit = int(Struct[4:]) // 8
            Value = Array2Val(ConfigDict['value'])
            Loop = ConfigDict['length'] // Unit
            Values = []
            for Each in range(Loop):
                Values.append(Value & ((1 << (Unit * 8)) - 1))
                Value = Value >> (Unit * 8)
            ValStr = '{ ' + ', '.join([('0x%%0%dX' % (Unit * 2)) %
                                       x for x in Values]) + ' }'
        return ValStr
    def FormatListValue(self, ConfigDict):
        Struct = ConfigDict['struct']
        if Struct not in self._StructType:
            return
        DataList = self.ValueToList(ConfigDict['value'], ConfigDict['length'])
        Unit = int(Struct[4:]) // 8
        if int(ConfigDict['length']) != Unit * len(DataList):
            # Fallback to byte array
            Unit = 1
            if int(ConfigDict['length']) != len(DataList):
                raise Exception("Array size is not proper for '%s' !" %
                                ConfigDict['cname'])
        ByteArray = []
        for Value in DataList:
            for Loop in range(Unit):
                ByteArray.append("0x%02X" % (Value & 0xFF))
                Value = Value >> 8
        NewValue = '{' + ','.join(ByteArray) + '}'
        ConfigDict['value'] = NewValue
        return ""
    def GetOrderNumber(self, Offset, Order, BitOff=0):
        if isinstance(Order, int):
            if Order == -1:
                Order = Offset << 16
        else:
            (Major, Minor) = Order.split('.')
            Order = (int(Major, 16) << 16) + ((int(Minor, 16) & 0xFF) << 8)
        return Order + (BitOff & 0xFF)
    def SubtituteLine(self, Line, Args):
        Args = Args.strip()
        Vars = Args.split(':')
        Line = self.ExpandMacros(Line, True)
        for Idx in range(len(Vars)-1, 0, -1):
            Line = Line.replace('$(%d)' % Idx, Vars[Idx].strip())
        return Line
    def CfgDuplicationCheck(self, CfgDict, Name):
        if not self.Debug:
            return
        if Name == 'Dummy':
            return
        if Name not in CfgDict:
            CfgDict[Name] = 1
        else:
            print("WARNING: Duplicated item found '%s' !" %
                  CfgDict['cname'])
    def AddBsfChildPage(self, Child, Parent='root'):
        def AddBsfChildPageRecursive(PageTree, Parent, Child):
            Key = next(iter(PageTree))
            if Parent == Key:
                PageTree[Key].append({Child: []})
                return True
            else:
                Result = False
                for Each in PageTree[Key]:
                    if AddBsfChildPageRecursive(Each, Parent, Child):
                        Result = True
                        break
                return Result
        return AddBsfChildPageRecursive(self._CfgPageTree, Parent, Child)
    def ParseDscFile(self, DscFile):
        self._DscLines = []
        self._CfgItemList = []
        self._CfgPageDict = {}
        self._CfgBlkDict = {}
        self._BsfTempDict = {}
        self._CfgPageTree = {'root': []}
        CfgDict = {}
        SectionNameList = ["Defines".lower(), "PcdsFeatureFlag".lower(),
                           "PcdsDynamicVpd.Tmp".lower(),
                           "PcdsDynamicVpd.Upd".lower()]
        IsDefSect = False
        IsPcdSect = False
        IsUpdSect = False
        IsTmpSect = False
        TemplateName = ''
        IfStack = []
        ElifStack = []
        Error = 0
        ConfigDict = {}
        if type(DscFile) is list:
            # it is DSC lines already
            DscLines = DscFile
            self._DscFile = '.'
        else:
            DscFd = open(DscFile, "r")
            DscLines = DscFd.readlines()
            DscFd.close()
            self._DscFile = DscFile
        BsfRegExp = re.compile("(%s):{(.+?)}(?:$|\\s+)" % '|'.
                               join(self._BsfKeyList))
        HdrRegExp = re.compile("(%s):{(.+?)}" % '|'.join(self._HdrKeyList))
        CfgRegExp = re.compile("^([_a-zA-Z0-9]+)\\s*\\|\\s*\
(0x[0-9A-F]+|\\*)\\s*\\|\\s*(\\d+|0x[0-9a-fA-F]+)\\s*\\|\\s*(.+)")
        TksRegExp = re.compile("^(g[_a-zA-Z0-9]+\\.)(.+)")
        SkipLines = 0
        while len(DscLines):
            DscLine = DscLines.pop(0).strip()
            if SkipLines == 0:
                self._DscLines.append(DscLine)
            else:
                SkipLines = SkipLines - 1
            if len(DscLine) == 0:
                continue
            Handle = False
            Match = re.match("^\\[(.+)\\]", DscLine)
            if Match is not None:
                IsDefSect = False
                IsPcdSect = False
                IsUpdSect = False
                IsTmpSect = False
                SectionName = Match.group(1).lower()
                if SectionName == SectionNameList[0]:
                    IsDefSect = True
                if SectionName == SectionNameList[1]:
                    IsPcdSect = True
                elif SectionName == SectionNameList[2]:
                    IsTmpSect = True
                elif SectionName == SectionNameList[3]:
                    ConfigDict = {
                        'header': 'ON',
                        'page': '',
                        'name': '',
                        'find': '',
                        'struct': '',
                        'embed': '',
                        'marker': '',
                        'option': '',
                        'comment': '',
                        'condition': '',
                        'order': -1,
                        'subreg': []
                    }
                    IsUpdSect = True
                    Offset = 0
            else:
                if IsDefSect or IsPcdSect or IsUpdSect or IsTmpSect:
                    Match = False if DscLine[0] != '!' else True
                    if Match:
                        Match = re.match("^!(else|endif|ifdef|ifndef|if|elseif\
|include)\\s*(.+)?$", DscLine.split("#")[0])
                    Keyword = Match.group(1) if Match else ''
                    Remaining = Match.group(2) if Match else ''
                    Remaining = '' if Remaining is None else Remaining.strip()
                    if Keyword in ['if', 'elseif', 'ifdef', 'ifndef', 'include'
                                   ] and not Remaining:
                        raise Exception("ERROR: Expression is expected after \
'!if' or !elseif' for line '%s'" % DscLine)
                    if Keyword == 'else':
                        if IfStack:
                            IfStack[-1] = not IfStack[-1]
                        else:
                            raise Exception("ERROR: No paired '!if' found for \
'!else' for line '%s'" % DscLine)
                    elif Keyword == 'endif':
                        if IfStack:
                            IfStack.pop()
                            Level = ElifStack.pop()
                            if Level > 0:
                                del IfStack[-Level:]
                        else:
                            raise Exception("ERROR: No paired '!if' found for \
'!endif' for line '%s'" % DscLine)
                    elif Keyword == 'ifdef' or Keyword == 'ifndef':
                        Result = self.EvaulateIfdef(Remaining)
                        if Keyword == 'ifndef':
                            Result = not Result
                        IfStack.append(Result)
                        ElifStack.append(0)
                    elif Keyword == 'if' or Keyword == 'elseif':
                        Result = self.EvaluateExpress(Remaining)
                        if Keyword == "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:
                                raise Exception("ERROR: No paired '!if' found for \
'!elif' for line '%s'" % DscLine)
                    else:
                        if IfStack:
                            Handle = reduce(lambda x, y: x and y, IfStack)
                        else:
                            Handle = True
                        if Handle:
                            if Keyword == 'include':
                                Remaining = self.ExpandMacros(Remaining)
                                # Relative to DSC filepath
                                IncludeFilePath = os.path.join(
                                    os.path.dirname(self._DscFile), Remaining)
                                if not os.path.exists(IncludeFilePath):
                                    # Relative to repository to find \
                                    # dsc in common platform
                                    IncludeFilePath = os.path.join(
                                        os.path.dirname(self._DscFile), "..",
                                        Remaining)
                                try:
                                    IncludeDsc = open(IncludeFilePath, "r")
                                except Exception:
                                    raise Exception("ERROR: Cannot open \
file '%s'." % IncludeFilePath)
                                NewDscLines = IncludeDsc.readlines()
                                IncludeDsc.close()
                                DscLines = NewDscLines + DscLines
                                del self._DscLines[-1]
                            else:
                                if DscLine.startswith('!'):
                                    raise Exception("ERROR: Unrecoginized \
directive for line '%s'" % DscLine)
            if not Handle:
                del self._DscLines[-1]
                continue
            if IsDefSect:
                Match = re.match("^\\s*(?:DEFINE\\s+)*(\\w+)\\s*=\\s*(.+)",
                                 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:
                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)))
            elif IsTmpSect:
                # !BSF DEFT:{GPIO_TMPL:START}
                Match = re.match("^\\s*#\\s+(!BSF)\\s+DEFT:{(.+?):\
(START|END)}", DscLine)
                if Match:
                    if Match.group(3) == 'START' and not TemplateName:
                        TemplateName = Match.group(2).strip()
                        self._BsfTempDict[TemplateName] = []
                    if Match.group(3) == 'END' and (
                        TemplateName == Match.group(2).strip()
                            ) and TemplateName:
                        TemplateName = ''
                else:
                    if TemplateName:
                        Match = re.match("^!include\\s*(.+)?$", DscLine)
                        if Match:
                            continue
                        self._BsfTempDict[TemplateName].append(DscLine)
            else:
                Match = re.match("^\\s*#\\s+(!BSF|!HDR)\\s+(.+)", DscLine)
                if Match:
                    Remaining = Match.group(2)
                    if Match.group(1) == '!BSF':
                        Result = BsfRegExp.findall(Remaining)
                        if Result:
                            for Each in Result:
                                Key = Each[0]
                                Remaining = Each[1]
                                if Key == 'BLOCK':
                                    Match = re.match(
                                        "NAME:\"(.+)\"\\s*,\\s*\
VER:\"(.+)\"\\s*", Remaining)
                                    if Match:
                                        self._CfgBlkDict['name'] = \
                                                                 Match.group(1)
                                        self._CfgBlkDict['ver'] = Match.group(2
                                                                              )
                                elif Key == 'SUBT':
                                    # GPIO_TMPL:1:2:3
                                    Remaining = Remaining.strip()
                                    Match = re.match("(\\w+)\\s*:", Remaining)
                                    if Match:
                                        TemplateName = Match.group(1)
                                        for Line in self._BsfTempDict[
                                                TemplateName][::-1]:
                                            NewLine = self.SubtituteLine(
                                                Line, Remaining)
                                            DscLines.insert(0, NewLine)
                                            SkipLines += 1
                                elif Key == 'PAGES':
                                    # !BSF PAGES:{HSW:"Haswell System Agent", \
                                    # LPT:"Lynx Point PCH"}
                                    PageList = Remaining.split(',')
                                    for Page in PageList:
                                        Page = Page.strip()
                                        Match = re.match('(\\w+):\
(\\w*:)?\\"(.+)\\"', Page)
                                        if Match:
                                            PageName = Match.group(1)
                                            ParentName = Match.group(2)
                                            if not ParentName or \
                                               ParentName == ':':
                                                ParentName = 'root'
                                            else:
                                                ParentName = ParentName[:-1]
                                            if not self.AddBsfChildPage(
                                                   PageName, ParentName):
                                                raise Exception("Cannot find \
parent page '%s'!" % ParentName)
                                            self._CfgPageDict[
                                                PageName] = Match.group(3)
                                        else:
                                            raise Exception("Invalid page \
definitions '%s'!" % Page)
                                elif Key in ['NAME', 'HELP', 'OPTION'
                                             ] and Remaining.startswith('+'):
                                    # Allow certain options to be extended \
                                    # to multiple lines
                                    ConfigDict[Key.lower()] += Remaining[1:]
                                else:
                                    if Key == 'NAME':
                                        Remaining = Remaining.strip()
                                    elif Key == 'CONDITION':
                                        Remaining = self.ExpandMacros(
                                            Remaining.strip())
                                    ConfigDict[Key.lower()] = Remaining
                    else:
                        Match = HdrRegExp.match(Remaining)
                        if Match:
                            Key = Match.group(1)
                            Remaining = Match.group(2)
                            if Key == 'EMBED':
                                Parts = Remaining.split(':')
                                Names = Parts[0].split(',')
                                DummyDict = ConfigDict.copy()
                                if len(Names) > 1:
                                    Remaining = Names[0] + ':' + ':'.join(
                                        Parts[1:])
                                    DummyDict['struct'] = Names[1]
                                else:
                                    DummyDict['struct'] = Names[0]
                                DummyDict['cname'] = 'Dummy'
                                DummyDict['name'] = ''
                                DummyDict['embed'] = Remaining
                                DummyDict['offset'] = Offset
                                DummyDict['length'] = 0
                                DummyDict['value'] = '0'
                                DummyDict['type'] = 'Reserved'
                                DummyDict['help'] = ''
                                DummyDict['subreg'] = []
                                self._CfgItemList.append(DummyDict)
                            else:
                                ConfigDict[Key.lower()] = Remaining
                # Check CFG line
                #   gCfgData.VariableName   |    * | 0x01 | 0x1
                Clear = False
                Match = TksRegExp.match(DscLine)
                if Match:
                    DscLine = 'gCfgData.%s' % Match.group(2)
                if DscLine.startswith('gCfgData.'):
                    Match = CfgRegExp.match(DscLine[9:])
                else:
                    Match = None
                if Match:
                    ConfigDict['space'] = 'gCfgData'
                    ConfigDict['cname'] = Match.group(1)
                    if Match.group(2) != '*':
                        Offset = int(Match.group(2), 16)
                    ConfigDict['offset'] = Offset
                    ConfigDict['order'] = self.GetOrderNumber(
                        ConfigDict['offset'], ConfigDict['order'])
                    Value = Match.group(4).strip()
                    if Match.group(3).startswith("0x"):
                        Length = int(Match.group(3), 16)
                    else:
                        Length = int(Match.group(3))
                    Offset += Length
                    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 re.match("\\{\\s*FILE:(.+)\\}", Value):
                        # Expand embedded binary file
                        ValArray = self.ValueToByteArray(ConfigDict['value'],
                                                         ConfigDict['length'])
                        NewValue = Bytes2Str(ValArray)
                        self._DscLines[-1] = re.sub(r'(.*)(\{\s*FILE:.+\})',
                                                    r'\1 %s' % NewValue,
                                                    self._DscLines[-1])
                        ConfigDict['value'] = NewValue
                    if ConfigDict['name'] == '':
                        # Clear BSF specific items
                        ConfigDict['bsfname'] = ''
                        ConfigDict['help'] = ''
                        ConfigDict['type'] = ''
                        ConfigDict['option'] = ''
                    self.CfgDuplicationCheck(CfgDict, ConfigDict['cname'])
                    self._CfgItemList.append(ConfigDict.copy())
                    Clear = True
                else:
                    # It could be a virtual item as below
                    # !BSF FIELD:{SerialDebugPortAddress0:1}
                    # or
                    # @Bsf FIELD:{SerialDebugPortAddress0:1b}
                    Match = re.match(r"^\s*#\s+(!BSF)\s+FIELD:{(.+)}", DscLine)
                    if Match:
                        BitFieldTxt = Match.group(2)
                        Match = re.match("(.+):(\\d+)b([BWDQ])?", BitFieldTxt)
                        if not Match:
                            raise Exception("Incorrect bit field \
format '%s' !" % BitFieldTxt)
                        UnitBitLen = 1
                        SubCfgDict = ConfigDict.copy()
                        SubCfgDict['cname'] = Match.group(1)
                        SubCfgDict['bitlength'] = int(
                            Match.group(2)) * 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']
                            if Match.group(3) == 'B':
                                SubCfgDict['bitunit'] = 1
                            elif Match.group(3) == 'W':
                                SubCfgDict['bitunit'] = 2
                            elif Match.group(3) == 'Q':
                                SubCfgDict['bitunit'] = 8
                            else:
                                SubCfgDict['bitunit'] = 4
                            SubCfgDict['bitoffset'] = SubOffset
                            SubCfgDict['order'] = self.GetOrderNumber(
                                SubCfgDict['offset'], SubCfgDict['order'],
                                SubOffset)
                            SubCfgDict['value'] = ''
                            SubCfgDict['cname'] = '%s_%s' % (LastItem['cname'],
                                                             Match.group(1))
                            self.CfgDuplicationCheck(CfgDict,
                                                     SubCfgDict['cname'])
                            LastItem['subreg'].append(SubCfgDict.copy())
                        Clear = True
                if Clear:
                    ConfigDict['name'] = ''
                    ConfigDict['find'] = ''
                    ConfigDict['struct'] = ''
                    ConfigDict['embed'] = ''
                    ConfigDict['marker'] = ''
                    ConfigDict['comment'] = ''
                    ConfigDict['order'] = -1
                    ConfigDict['subreg'] = []
                    ConfigDict['option'] = ''
                    ConfigDict['condition'] = ''
        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:
            raise Exception("Invalid bits offset [%d,%d] %d for %s" %
                            (start, end, bitslen, subitem['name']))
        return '0x%X' % (int(bitsvalue[start:end][::-1], 2))
    def UpdateBsfBitFields(self, SubItem, NewValue, ValueArray):
        Start = SubItem['bitoffset']
        End = Start + SubItem['bitlength']
        Blen = len(ValueArray)
        BitsValue = ''.join('{0:08b}'.format(i) for i in ValueArray[::-1])
        BitsValue = BitsValue[::-1]
        BitsLen = len(BitsValue)
        if Start > BitsLen or End > BitsLen:
            raise Exception("Invalid bits offset [%d,%d] %d for %s" %
                            (Start, End, BitsLen, SubItem['name']))
        BitsValue = BitsValue[:Start] + '{0:0{1}b}'.format(
            NewValue, SubItem['bitlength'])[::-1] + BitsValue[End:]
        ValueArray[:] = bytearray.fromhex(
            '{0:0{1}x}'.format(int(BitsValue[::-1], 2), Blen * 2))[::-1]
    def CreateVarDict(self):
        Error = 0
        self._VarDict = {}
        if len(self._CfgItemList) > 0:
            Item = self._CfgItemList[-1]
            self._VarDict['_LENGTH_'] = '%d' % (Item['offset'] +
                                                Item['length'])
        for Item in self._CfgItemList:
            Embed = Item['embed']
            Match = re.match("^(\\w+):(\\w+):(START|END)", Embed)
            if Match:
                StructName = Match.group(1)
                VarName = '_%s_%s_' % (Match.group(3), StructName)
                if Match.group(3) == 'END':
                    self._VarDict[VarName] = Item['offset'] + Item['length']
                    self._VarDict['_LENGTH_%s_' % StructName] = \
                        self._VarDict['_END_%s_' % StructName] - \
                        self._VarDict['_START_%s_' % StructName]
                    if Match.group(2).startswith('TAG_'):
                        if (self.Mode != 'FSP') and (self._VarDict
                                                     ['_LENGTH_%s_' %
                                                      StructName] % 4):
                            raise Exception("Size of structure '%s' is %d, \
not DWORD aligned !" % (StructName, self._VarDict['_LENGTH_%s_' % StructName]))
                        self._VarDict['_TAG_%s_' % StructName] = int(
                            Match.group(2)[4:], 16) & 0xFFF
                else:
                    self._VarDict[VarName] = Item['offset']
            if Item['marker']:
                self._VarDict['_OFFSET_%s_' % Item['marker'].strip()] = \
                                            Item['offset']
        return Error
    def UpdateBsfBitUnit(self, Item):
        BitTotal = 0
        BitOffset = 0
        StartIdx = 0
        Unit = None
        UnitDec = {1: 'BYTE', 2: 'WORD', 4: 'DWORD', 8: 'QWORD'}
        for Idx, SubItem in enumerate(Item['subreg']):
            if Unit is None:
                Unit = SubItem['bitunit']
            BitLength = SubItem['bitlength']
            BitTotal += BitLength
            BitOffset += BitLength
            if BitOffset > 64 or BitOffset > Unit * 8:
                break
            if BitOffset == Unit * 8:
                for SubIdx in range(StartIdx, Idx + 1):
                    Item['subreg'][SubIdx]['bitunit'] = Unit
                BitOffset = 0
                StartIdx = Idx + 1
                Unit = None
        if BitOffset > 0:
            raise Exception("Bit fields cannot fit into %s for \
'%s.%s' !" % (UnitDec[Unit], Item['cname'], SubItem['cname']))
        ExpectedTotal = Item['length'] * 8
        if Item['length'] * 8 != BitTotal:
            raise Exception("Bit fields total length (%d) does not match \
length (%d) of '%s' !" % (BitTotal, ExpectedTotal, Item['cname']))
    def UpdateDefaultValue(self):
        Error = 0
        for Idx, Item in enumerate(self._CfgItemList):
            if len(Item['subreg']) == 0:
                Value = Item['value']
                if (len(Value) > 0) and (Value[0] == '{' or Value[0] == "'" or
                                         Value[0] == '"'):
                    # {XXX} or 'XXX' strings
                    self.FormatListValue(self._CfgItemList[Idx])
                else:
                    Match = re.match("(0x[0-9a-fA-F]+|[0-9]+)", Value)
                    if not Match:
                        NumValue = self.EvaluateExpress(Value)
                        Item['value'] = '0x%X' % NumValue
            else:
                ValArray = self.ValueToByteArray(Item['value'], Item['length'])
                for SubItem in Item['subreg']:
                    SubItem['value'] = self.GetBsfBitFields(SubItem, ValArray)
                self.UpdateBsfBitUnit(Item)
        return Error
    @staticmethod
    def ExpandIncludeFiles(FilePath, CurDir=''):
        if CurDir == '':
            CurDir = os.path.dirname(FilePath)
            FilePath = os.path.basename(FilePath)
        InputFilePath = os.path.join(CurDir, FilePath)
        File = open(InputFilePath, "r")
        Lines = File.readlines()
        File.close()
        NewLines = []
        for LineNum, Line in enumerate(Lines):
            Match = re.match("^!include\\s*(.+)?$", Line)
            if Match:
                IncPath = Match.group(1)
                TmpPath = os.path.join(CurDir, IncPath)
                OrgPath = TmpPath
                if not os.path.exists(TmpPath):
                    CurDir = os.path.join(os.path.dirname(
                        os.path.realpath(__file__)), "..", "..")
                TmpPath = os.path.join(CurDir, IncPath)
                if not os.path.exists(TmpPath):
                    raise Exception("ERROR: Cannot open include file '%s'." %
                                    OrgPath)
                else:
                    NewLines.append(('# Included from file: %s\n' %
                                     IncPath, TmpPath, 0))
                    NewLines.append(('# %s\n' % ('=' * 80), TmpPath, 0))
                    NewLines.extend(CGenCfgData.ExpandIncludeFiles
                                    (IncPath, CurDir))
            else:
                NewLines.append((Line, InputFilePath, LineNum))
        return NewLines
    def OverrideDefaultValue(self, DltFile):
        Error = 0
        DltLines = CGenCfgData.ExpandIncludeFiles(DltFile)
        PlatformId = None
        for Line, FilePath, LineNum in DltLines:
            Line = Line.strip()
            if not Line or Line.startswith('#'):
                continue
            Match = re.match("\\s*(\\w+)\\.(\\w+)(\\.\\w+)?\\s*\\|\\s*(.+)",
                             Line)
            if not Match:
                raise Exception("Unrecognized line '%s' (File:'%s' Line:%d) !"
                                % (Line, FilePath, LineNum + 1))
            Found = False
            InScope = False
            for Idx, Item in enumerate(self._CfgItemList):
                if not InScope:
                    if not (Item['embed'].endswith(':START') and
                            Item['embed'].startswith(Match.group(1))):
                        continue
                InScope = True
                if Item['cname'] == Match.group(2):
                    Found = True
                    break
                if Item['embed'].endswith(':END') and \
                   Item['embed'].startswith(Match.group(1)):
                    break
            Name = '%s.%s' % (Match.group(1), Match.group(2))
            if not Found:
                ErrItem = Match.group(2) if InScope else Match.group(1)
                raise Exception("Invalid configuration '%s' in '%s' \
(File:'%s' Line:%d) !" % (ErrItem, Name, FilePath, LineNum + 1))
            ValueStr = Match.group(4).strip()
            if Match.group(3) is not None:
                # This is a subregion item
                BitField = Match.group(3)[1:]
                Found = False
                if len(Item['subreg']) > 0:
                    for SubItem in Item['subreg']:
                        if SubItem['cname'] == '%s_%s' % \
                           (Item['cname'], BitField):
                            Found = True
                            break
                if not Found:
                    raise Exception("Invalid configuration bit field \
'%s' in '%s.%s' (File:'%s' Line:%d) !" % (BitField, Name, BitField,
                                          FilePath, LineNum + 1))
                try:
                    Value = int(ValueStr, 16) if ValueStr.startswith('0x') \
                            else int(ValueStr, 10)
                except Exception:
                    raise Exception("Invalid value '%s' for bit field '%s.%s' \
(File:'%s' Line:%d) !" % (ValueStr, Name, BitField, FilePath, LineNum + 1))
                if Value >= 2 ** SubItem['bitlength']:
                    raise Exception("Invalid configuration bit field value \
'%s' for '%s.%s' (File:'%s' Line:%d) !" % (Value, Name, BitField,
                                           FilePath, LineNum + 1))
                ValArray = self.ValueToByteArray(Item['value'], Item['length'])
                self.UpdateBsfBitFields(SubItem, Value, ValArray)
                if Item['value'].startswith('{'):
                    Item['value'] = '{' + ', '.join('0x%02X' % i
                                                    for i in ValArray) + '}'
                else:
                    BitsValue = ''.join('{0:08b}'.format(i)
                                        for i in ValArray[::-1])
                    Item['value'] = '0x%X' % (int(BitsValue, 2))
            else:
                if Item['value'].startswith('{') and  \
                   not ValueStr.startswith('{'):
                    raise Exception("Data array required for '%s' \
(File:'%s' Line:%d) !" % (Name, FilePath, LineNum + 1))
                Item['value'] = ValueStr
            if Name == 'PLATFORMID_CFG_DATA.PlatformId':
                PlatformId = ValueStr
            if (PlatformId is None) and (self.Mode != 'FSP'):
                raise Exception("PLATFORMID_CFG_DATA.PlatformId is missing \
in file '%s' !" % (DltFile))
        return Error
    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 is 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, BitsLength=None):
        PosName = 28
        NameLine = ''
        HelpLine = ''
        OptionLine = ''
        if Length == 0 and Name == 'Dummy':
            return '\n'
        IsArray = False
        if Length in [1, 2, 4, 8]:
            Type = "UINT%d" % (Length * 8)
        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 BitsLength is None:
            BitsLength = ''
        else:
            BitsLength = ' : %d' % BitsLength
        return "\n/** %s%s%s**/\n  %s%s%s%s;\n" % \
               (NameLine, HelpLine, OptionLine, Type, ' ' * Space1, Name,
                BitsLength)
    def SplitTextBody(self, TextBody):
        Marker1 = '{ /* _COMMON_STRUCT_START_ */'
        Marker2 = '; /* _COMMON_STRUCT_END_ */'
        ComBody = []
        TxtBody = []
        IsCommon = False
        for Line in TextBody:
            if Line.strip().endswith(Marker1):
                Line = Line.replace(Marker1[1:], '')
                IsCommon = True
            if Line.strip().endswith(Marker2):
                Line = Line.replace(Marker2[1:], '')
                if IsCommon:
                    ComBody.append(Line)
                    IsCommon = False
                    continue
            if IsCommon:
                ComBody.append(Line)
            else:
                TxtBody.append(Line)
        return ComBody, TxtBody
    def GetStructArrayInfo(self, Input):
        ArrayStr = Input.split('[')
        Name = ArrayStr[0]
        if len(ArrayStr) > 1:
            NumStr = ''.join(c for c in ArrayStr[-1] if c.isdigit())
            NumStr = '1000' if len(NumStr) == 0 else NumStr
            ArrayNum = int(NumStr)
        else:
            ArrayNum = 0
        return Name, ArrayNum
    def PostProcessBody(self, TextBody, IncludeEmbedOnly=True):
        NewTextBody = []
        OldTextBody = []
        IncTextBody = []
        StructBody = []
        IncludeLine = False
        EmbedFound = False
        StructName = ''
        ArrayVarName = ''
        VariableName = ''
        Count = 0
        Level = 0
        IsCommonStruct = False
        for Line in TextBody:
            if Line.startswith('#define '):
                IncTextBody.append(Line)
                continue
            if not Line.startswith('/* EMBED_STRUCT:'):
                Match = False
            else:
                Match = re.match("^/\\*\\sEMBED_STRUCT:([\\w\\[\\]\\*]+):\
([\\w\\[\\]\\*]+):(\\w+):(START|END)([\\s\\d]+)\\*/([\\s\\S]*)", Line)
            if Match:
                ArrayMarker = Match.group(5)
                if Match.group(4) == 'END':
                    Level -= 1
                    if Level == 0:
                        Line = Match.group(6)
                else:   # 'START'
                    Level += 1
                    if Level == 1:
                        Line = Match.group(6)
                    else:
                        EmbedFound = True
                    TagStr = Match.group(3)
                    if TagStr.startswith('TAG_'):
                        try:
                            TagVal = int(TagStr[4:], 16)
                        except Exception:
                            TagVal = -1
                        if (TagVal >= 0) and (TagVal < self._MinCfgTagId):
                            IsCommonStruct = True
                    if Level == 1:
                        if IsCommonStruct:
                            Suffix = ' /* _COMMON_STRUCT_START_ */'
                        else:
                            Suffix = ''
                        StructBody = ['typedef struct {%s' % Suffix]
                        StructName = Match.group(1)
                        StructType = Match.group(2)
                        VariableName = Match.group(3)
                        MatchOffset = re.search('/\\*\\*\\sOffset\\s0x\
([a-fA-F0-9]+)', Line)
                        if MatchOffset:
                            Offset = int(MatchOffset.group(1), 16)
                        else:
                            Offset = None
                        IncludeLine = True
                        ModifiedStructType = StructType.rstrip()
                        if ModifiedStructType.endswith(']'):
                            Idx = ModifiedStructType.index('[')
                            if ArrayMarker != ' ':
                                # Auto array size
                                OldTextBody.append('')
                                ArrayVarName = VariableName
                                if int(ArrayMarker) == 1000:
                                    Count = 1
                                else:
                                    Count = int(ArrayMarker) + 1000
                            else:
                                if Count < 1000:
                                    Count += 1
                            VariableTemp = ArrayVarName + '[%d]' % (
                                Count if Count < 1000 else Count - 1000)
                            OldTextBody[-1] = self.CreateField(
                                None, VariableTemp, 0, Offset,
                                ModifiedStructType[:Idx], '',
                                'Structure Array', '')
                        else:
                            ArrayVarName = ''
                            OldTextBody.append(self.CreateField(
                                None, VariableName, 0, Offset,
                                ModifiedStructType, '', '', ''))
            if IncludeLine:
                StructBody.append(Line)
            else:
                OldTextBody.append(Line)
            if Match and Match.group(4) == 'END':
                if Level == 0:
                    if (StructType != Match.group(2)) or \
                       (VariableName != Match.group(3)):
                        print("Unmatched struct name '%s' and '%s' !" %
                              (StructName, Match.group(2)))
                    else:
                        if IsCommonStruct:
                            Suffix = ' /* _COMMON_STRUCT_END_ */'
                        else:
                            Suffix = ''
                        Line = '} %s;%s\n\n\n' % (StructName, Suffix)
                        StructBody.append(Line)
                        if (Line not in NewTextBody) and \
                           (Line not in OldTextBody):
                            NewTextBody.extend(StructBody)
                    IncludeLine = False
                IsCommonStruct = False
        if not IncludeEmbedOnly:
            NewTextBody.extend(OldTextBody)
        if EmbedFound:
            NewTextBody = self.PostProcessBody(NewTextBody, False)
        NewTextBody = IncTextBody + NewTextBody
        return NewTextBody
    def WriteHeaderFile(self, TxtBody, FileName, Type='h'):
        FileNameDef = os.path.basename(FileName).replace('.', '_')
        FileNameDef = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', FileNameDef)
        FileNameDef = re.sub('([a-z0-9])([A-Z])', r'\1_\2',
                             FileNameDef).upper()
        Lines = []
        Lines.append("%s\n" % GetCopyrightHeader(Type))
        Lines.append("#ifndef __%s__\n" % FileNameDef)
        Lines.append("#define __%s__\n\n" % FileNameDef)
        if Type == 'h':
            Lines.append("#pragma pack(1)\n\n")
        Lines.extend(TxtBody)
        if Type == 'h':
            Lines.append("#pragma pack()\n\n")
        Lines.append("#endif\n")
        # Don't rewrite if the contents are the same
        Create = True
        if os.path.exists(FileName):
            HdrFile = open(FileName, "r")
            OrgTxt = HdrFile.read()
            HdrFile.close()
            NewTxt = ''.join(Lines)
            if OrgTxt == NewTxt:
                Create = False
        if Create:
            HdrFile = open(FileName, "w")
            HdrFile.write(''.join(Lines))
            HdrFile.close()
    def CreateHeaderFile(self, HdrFileName, ComHdrFileName=''):
        LastStruct = ''
        SpaceIdx = 0
        Offset = 0
        FieldIdx = 0
        LastFieldIdx = 0
        ResvOffset = 0
        ResvIdx = 0
        TxtBody = []
        LineBuffer = []
        CfgTags = []
        LastVisible = True
        TxtBody.append("typedef struct {\n")
        for Item in self._CfgItemList:
            # Search for CFGDATA tags
            Embed = Item["embed"].upper()
            if Embed.endswith(':START'):
                Match = re.match(r'(\w+)_CFG_DATA:TAG_([0-9A-F]+):START',
                                 Embed)
                if Match:
                    TagName = Match.group(1)
                    TagId = int(Match.group(2), 16)
                    CfgTags.append((TagId, TagName))
            # Only process visible items
            NextVisible = LastVisible
            if LastVisible and (Item['header'] == 'OFF'):
                NextVisible = False
                ResvOffset = Item['offset']
            elif (not LastVisible) and Item['header'] == 'ON':
                NextVisible = True
                Name = "ReservedUpdSpace%d" % ResvIdx
                ResvIdx = ResvIdx + 1
                TxtBody.append(self.CreateField(
                    Item, Name, Item["offset"] - ResvOffset,
                    ResvOffset, '', '', '', ''))
                FieldIdx += 1
            if Offset < Item["offset"]:
                if LastVisible:
                    Name = "UnusedUpdSpace%d" % SpaceIdx
                    LineBuffer.append(self.CreateField
                                      (Item, Name, Item["offset"] -
                                       Offset, Offset, '', '', '', ''))
                    FieldIdx += 1
                SpaceIdx = SpaceIdx + 1
                Offset = Item["offset"]
            LastVisible = NextVisible
            Offset = Offset + Item["length"]
            if LastVisible:
                for Each in LineBuffer:
                    TxtBody.append(Each)
                LineBuffer = []
                Embed = Item["embed"].upper()
                if Embed.endswith(':START') or Embed.endswith(':END'):
                    # EMBED_STRUCT: StructName : \
                    # ItemName : VariableName : START|END
                    Name, ArrayNum = self.GetStructArrayInfo(Item["struct"])
                    Remaining = Item["embed"]
                    if (LastFieldIdx + 1 == FieldIdx) and (LastStruct == Name):
                        ArrayMarker = ' '
                    else:
                        ArrayMarker = '%d' % ArrayNum
                    LastFieldIdx = FieldIdx
                    LastStruct = Name
                    Marker = '/* EMBED_STRUCT:%s:%s%s*/ ' % (Name, Remaining,
                                                             ArrayMarker)
                    # if Embed.endswith(':START') and Comment != '':
                    # Marker = '/* COMMENT:%s */ \n' % Item["comment"] + Marker
                else:
                    if Embed == '':
                        Marker = ''
                    else:
                        self.Error = "Invalid embedded structure \
format '%s'!\n" % Item["embed"]
                        return 4
                # Generate bit fields for structure
                if len(Item['subreg']) > 0 and Item["struct"]:
                    StructType = Item["struct"]
                    StructName, ArrayNum = self.GetStructArrayInfo(StructType)
                    if (LastFieldIdx + 1 == FieldIdx) and \
                       (LastStruct == Item["struct"]):
                        ArrayMarker = ' '
                    else:
                        ArrayMarker = '%d' % ArrayNum
                    TxtBody.append('/* EMBED_STRUCT:%s:%s:%s:START%s*/\n' %
                                   (StructName, StructType, Item["cname"],
                                    ArrayMarker))
                    for SubItem in Item['subreg']:
                        Name = SubItem["cname"]
                        if Name.startswith(Item["cname"]):
                            Name = Name[len(Item["cname"]) + 1:]
                        Line = self.CreateField(
                            SubItem, Name, SubItem["bitunit"],
                            SubItem["offset"], SubItem['struct'],
                            SubItem['name'], SubItem['help'],
                            SubItem['option'], SubItem['bitlength'])
                        TxtBody.append(Line)
                    TxtBody.append('/* EMBED_STRUCT:%s:%s:%s:END%s*/\n' %
                                   (StructName, StructType, Item["cname"],
                                    ArrayMarker))
                    LastFieldIdx = FieldIdx
                    LastStruct = Item["struct"]
                    FieldIdx += 1
                else:
                    FieldIdx += 1
                    Line = Marker + self.CreateField(
                        Item, Item["cname"], Item["length"], Item["offset"],
                        Item['struct'], Item['name'], Item['help'],
                        Item['option'])
                    TxtBody.append(Line)
        TxtBody.append("}\n\n")
        # Handle the embedded data structure
        TxtBody = self.PostProcessBody(TxtBody)
        ComBody, TxtBody = self.SplitTextBody(TxtBody)
        # Prepare TAG defines
        PltTagDefTxt = ['\n']
        ComTagDefTxt = ['\n']
        for TagId, TagName in sorted(CfgTags):
            TagLine = '#define  %-30s  0x%03X\n' % ('CDATA_%s_TAG' %
                                                    TagName, TagId)
            if TagId < self._MinCfgTagId:
                # TAG ID < 0x100, it is a generic TAG
                ComTagDefTxt.append(TagLine)
            else:
                PltTagDefTxt.append(TagLine)
        PltTagDefTxt.append('\n\n')
        ComTagDefTxt.append('\n\n')
        # Write file back
        self.WriteHeaderFile(PltTagDefTxt + TxtBody, HdrFileName)
        if ComHdrFileName:
            self.WriteHeaderFile(ComTagDefTxt + ComBody, ComHdrFileName)
        return 0
    def UpdateConfigItemValue(self, Item, ValueStr):
        IsArray = True if Item['value'].startswith('{') else False
        IsString = True if Item['value'].startswith("'") else False
        Bytes = self.ValueToByteArray(ValueStr, Item['length'])
        if IsString:
            NewValue = "'%s'" % Bytes.decode("utf-8")
        elif IsArray:
            NewValue = Bytes2Str(Bytes)
        else:
            Fmt = '0x%X' if Item['value'].startswith('0x') else '%d'
            NewValue = Fmt % Bytes2Val(Bytes)
        Item['value'] = NewValue
    def LoadDefaultFromBinaryArray(self, BinDat, IgnoreFind=False):
        FindOff = 0
        StartOff = 0
        for Item in self._CfgItemList:
            if Item['length'] == 0:
                continue
            if not IgnoreFind and Item['find']:
                FindBin = Item['find'].encode()
                Offset = BinDat.find(FindBin)
                if Offset >= 0:
                    TestOff = BinDat[Offset+len(FindBin):].find(FindBin)
                    if TestOff >= 0:
                        raise Exception('Multiple match found for "%s" !' %
                                        Item['find'])
                    FindOff = Offset + len(FindBin)
                    StartOff = Item['offset']
                else:
                    raise Exception('Could not find "%s" !' % Item['find'])
            if Item['offset'] + Item['length'] > len(BinDat):
                raise Exception('Mismatching format between DSC \
and BIN files !')
            Offset = FindOff + (Item['offset'] - StartOff)
            ValStr = Bytes2Str(BinDat[Offset: Offset + Item['length']])
            self.UpdateConfigItemValue(Item, ValStr)
        self.UpdateDefaultValue()
    def PatchBinaryArray(self, BinDat):
        FileOff = 0
        Offset = 0
        FindOff = 0
        PatchList = []
        CfgBin = bytearray()
        for Item in self._CfgItemList:
            if Item['length'] == 0:
                continue
            if Item['find']:
                if len(CfgBin) > 0:
                    PatchList.append((FileOff, CfgBin))
                FindBin = Item['find'].encode()
                FileOff = BinDat.find(FindBin)
                if FileOff < 0:
                    raise Exception('Could not find "%s" !' % Item['find'])
                else:
                    TestOff = BinDat[FileOff+len(FindBin):].find(FindBin)
                    if TestOff >= 0:
                        raise Exception('Multiple match found for "%s" !' %
                                        Item['find'])
                FileOff += len(FindBin)
                Offset = Item['offset']
                FindOff = Offset
                CfgBin = bytearray()
            if Item['offset'] > Offset:
                Gap = Item['offset'] - Offset
                CfgBin.extend(b'\x00' * Gap)
            if Item['type'] == 'Reserved' and Item['option'] == '$SKIP':
                # keep old data
                NewOff = FileOff + (Offset - FindOff)
                FileData = bytearray(BinDat[NewOff: NewOff + Item['length']])
                CfgBin.extend(FileData)
            else:
                CfgBin.extend(self.ValueToByteArray(Item['value'],
                                                    Item['length']))
            Offset = Item['offset'] + Item['length']
        if len(CfgBin) > 0:
            PatchList.append((FileOff, CfgBin))
        for FileOff, CfgBin in PatchList:
            Length = len(CfgBin)
            if FileOff + Length < len(BinDat):
                BinDat[FileOff:FileOff+Length] = CfgBin[:]
        return BinDat
    def GenerateBinaryArray(self):
        Offset = 0
        BinDat = bytearray()
        for Item in self._CfgItemList:
            if Item['offset'] > Offset:
                Gap = Item['offset'] - Offset
                BinDat.extend(b'\x00' * Gap)
            BinDat.extend(self.ValueToByteArray(Item['value'], Item['length']))
            Offset = Item['offset'] + Item['length']
        return BinDat
    def GenerateBinary(self, BinFileName):
        BinFile = open(BinFileName, "wb")
        BinFile.write(self.GenerateBinaryArray())
        BinFile.close()
        return 0
    def GenerateDataIncFile(self, DatIncFileName, BinFile=None):
        # Put a prefix GUID before CFGDATA so that it can be located later on
        Prefix = b'\xa7\xbd\x7f\x73\x20\x1e\x46\xd6\xbe\x8f\
x64\x12\x05\x8d\x0a\xa8'
        if BinFile:
            Fin = open(BinFile, 'rb')
            BinDat = Prefix + bytearray(Fin.read())
            Fin.close()
        else:
            BinDat = Prefix + self.GenerateBinaryArray()
        FileName = os.path.basename(DatIncFileName).upper()
        FileName = FileName.replace('.', '_')
        TxtLines = []
        TxtLines.append("UINT8  mConfigDataBlob[%d] = {\n" % len(BinDat))
        Count = 0
        Line = ['  ']
        for Each in BinDat:
            Line.append('0x%02X, ' % Each)
            Count = Count + 1
            if (Count & 0x0F) == 0:
                Line.append('\n')
                TxtLines.append(''.join(Line))
                Line = ['  ']
        if len(Line) > 1:
            TxtLines.append(''.join(Line) + '\n')
        TxtLines.append("};\n\n")
        self.WriteHeaderFile(TxtLines, DatIncFileName, 'inc')
        return 0
    def CheckCfgData(self):
        # Check if CfgData contains any duplicated name
        def AddItem(Item, ChkList):
            Name = Item['cname']
            if Name in ChkList:
                return Item
            if Name not in ['Dummy', 'Reserved', 'CfgHeader', 'CondValue']:
                ChkList.append(Name)
            return None
        Duplicate = None
        ChkList = []
        for Item in self._CfgItemList:
            Duplicate = AddItem(Item, ChkList)
            if not Duplicate:
                for SubItem in Item['subreg']:
                    Duplicate = AddItem(SubItem, ChkList)
                    if Duplicate:
                        break
            if Duplicate:
                break
        if Duplicate:
            self.Error = "Duplicated CFGDATA '%s' found !\n" % \
                         Duplicate['cname']
            return -1
        return 0
    def PrintData(self):
        for Item in self._CfgItemList:
            if not Item['length']:
                continue
            print("%-10s @Offset:0x%04X  Len:%3d  Val:%s" %
                  (Item['cname'], Item['offset'], Item['length'],
                   Item['value']))
            for SubItem in Item['subreg']:
                print("  %-20s  BitOff:0x%04X  BitLen:%-3d  Val:%s" %
                      (SubItem['cname'], SubItem['bitoffset'],
                       SubItem['bitlength'], SubItem['value']))
    def FormatArrayValue(self, Input, Length):
        Dat = self.ValueToByteArray(Input, Length)
        return ','.join('0x%02X' % Each for Each in Dat)
    def GetItemOptionList(self, Item):
        TmpList = []
        if Item['type'] == "Combo":
            if not Item['option'] in self._BuidinOption:
                OptList = Item['option'].split(',')
                for Option in OptList:
                    Option = Option.strip()
                    try:
                        (OpVal, OpStr) = Option.split(':')
                    except Exception:
                        raise Exception("Invalide option format '%s' !" %
                                        Option)
                    TmpList.append((OpVal, OpStr))
        return TmpList
    def WriteBsfStruct(self, BsfFd, Item):
        if Item['type'] == "None":
            Space = "gPlatformFspPkgTokenSpaceGuid"
        else:
            Space = Item['space']
        Line = "    $%s_%s" % (Space, Item['cname'])
        Match = re.match("\\s*(\\{.+\\})\\s*", Item['value'])
        if Match:
            DefaultValue = self.FormatArrayValue(Match.group(1).strip(),
                                                 Item['length'])
        else:
            DefaultValue = Item['value'].strip()
        if 'bitlength' in Item:
            if Item['bitlength']:
                BsfFd.write("    %s%s%4d bits     $_DEFAULT_ = %s\n" %
                            (Line, ' ' * (64 - len(Line)), Item['bitlength'],
                             DefaultValue))
        else:
            if Item['length']:
                BsfFd.write("    %s%s%4d bytes    $_DEFAULT_ = %s\n" %
                            (Line, ' ' * (64 - len(Line)), Item['length'],
                             DefaultValue))
        return self.GetItemOptionList(Item)
    def GetBsfOption(self, OptionName):
        if OptionName in self._CfgOptsDict:
            return self._CfgOptsDict[OptionName]
        else:
            return OptionName
    def WriteBsfOption(self, BsfFd, Item):
        PcdName = Item['space'] + '_' + Item['cname']
        WriteHelp = 0
        BsfLines = []
        if Item['type'] == "Combo":
            if Item['option'] in self._BuidinOption:
                Options = self._BuidinOption[Item['option']]
            else:
                Options = self.GetBsfOption(PcdName)
            BsfLines.append('    %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:
                BsfLines.append('    EditNum $%s, "%s", %s,\n' % (
                    PcdName, Item['name'], Match.group(1)))
                WriteHelp = 2
        elif Item['type'].startswith("EditText"):
            BsfLines.append('    %s $%s, "%s",\n' % (Item['type'], PcdName,
                                                     Item['name']))
            WriteHelp = 1
        elif Item['type'] == "Table":
            Columns = Item['option'].split(',')
            if len(Columns) != 0:
                BsfLines.append('    %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 Exception:
                        raise Exception("Column size '%s' is invalid !" %
                                        Fmt[1])
                    BsfLines.append('\n        Column "%s", %d bytes, %s' %
                                    (Fmt[0].strip(), Dtype, Fmt[2].strip()))
                BsfLines.append(',\n')
                WriteHelp = 1
        if WriteHelp > 0:
            HelpLines = Item['help'].split('\\n\\r')
            FirstLine = True
            for HelpLine in HelpLines:
                if FirstLine:
                    FirstLine = False
                    BsfLines.append('        Help "%s"\n' % (HelpLine))
                else:
                    BsfLines.append('             "%s"\n' % (HelpLine))
            if WriteHelp == 2:
                BsfLines.append('             "Valid range: %s ~ %s"\n' %
                                (Match.group(2), Match.group(3)))
            if len(Item['condition']) > 4:
                CondList = Item['condition'].split(',')
                Idx = 0
                for Cond in CondList:
                    Cond = Cond.strip()
                    if Cond.startswith('#'):
                        BsfLines.insert(Idx, Cond + '\n')
                        Idx += 1
                    elif Cond.startswith('@#'):
                        BsfLines.append(Cond[1:] + '\n')
        for Line in BsfLines:
            BsfFd.write(Line)
    def WriteBsfPages(self, PageTree, BsfFd):
        BsfFd.write('\n')
        Key = next(iter(PageTree))
        for Page in PageTree[Key]:
            PageName = next(iter(Page))
            BsfFd.write('Page "%s"\n' % self._CfgPageDict[PageName])
            if len(PageTree[Key]):
                self.WriteBsfPages(Page, BsfFd)
            BsfItems = []
            for Item in self._CfgItemList:
                if Item['name'] != '':
                    if Item['page'] != PageName:
                        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")
    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" % GetCopyrightHeader('bsf'))
        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)
        NameList = []
        OptionList = []
        for Each in sorted(OptionDict):
            if OptionDict[Each] not in OptionList:
                NameList.append(Each)
                OptionList.append(OptionDict[Each])
                BsfFd.write("List &%s\n" % Each)
                for Item in OptionDict[Each]:
                    BsfFd.write('    Selection %s , "%s"\n' %
                                (self.EvaluateExpress(Item[0]), Item[1]))
                BsfFd.write("EndList\n\n")
            else:
                # Item has idential options as other item
                # Try to reuse the previous options instead
                Idx = OptionList.index(OptionDict[Each])
                self._CfgOptsDict[Each] = NameList[Idx]
        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")
        self.WriteBsfPages(self._CfgPageTree, BsfFd)
        BsfFd.close()
        return Error
    def WriteDeltaLine(self, OutLines, Name, ValStr, IsArray):
        if IsArray:
            Output = '%s | { %s }' % (Name, ValStr)
        else:
            Output = '%s | 0x%X' % (Name, Array2Val(ValStr))
        OutLines.append(Output)
    def WriteDeltaFile(self, OutFile, PlatformId, OutLines):
        DltFd = open(OutFile, "w")
        DltFd.write("%s\n" % GetCopyrightHeader('dlt', True))
        if PlatformId is not None:
            DltFd.write('#\n')
            DltFd.write('# Delta configuration values \
for platform ID 0x%04X\n' % PlatformId)
            DltFd.write('#\n\n')
        for Line in OutLines:
            DltFd.write('%s\n' % Line)
        DltFd.close()
    def GenerateDeltaFile(self, OutFile, AbsfFile):
        # Parse ABSF Build in dict
        if not os.path.exists(AbsfFile):
            Lines = []
        else:
            with open(AbsfFile) as Fin:
                Lines = Fin.readlines()
        AbsfBuiltValDict = {}
        Process = False
        for Line in Lines:
            Line = Line.strip()
            if Line.startswith('StructDef'):
                Process = True
            if Line.startswith('EndStruct'):
                break
            if not Process:
                continue
            Match = re.match('\\s*\\$gCfgData_(\\w+)\\s+\
(\\d+)\\s+(bits|bytes)\\s+\\$_AS_BUILT_\\s+=\\s+(.+)\\$', Line)
            if Match:
                if Match.group(1) not in AbsfBuiltValDict:
                    AbsfBuiltValDict[Match.group(1)] = Match.group(4).strip()
                else:
                    raise Exception("Duplicated configuration \
name '%s' found !", Match.group(1))
        # Match config item in DSC
        PlatformId = None
        OutLines = []
        TagName = ''
        Level = 0
        for Item in self._CfgItemList:
            Name = None
            if Level == 0 and Item['embed'].endswith(':START'):
                TagName = Item['embed'].split(':')[0]
                Level += 1
            if Item['cname'] in AbsfBuiltValDict:
                ValStr = AbsfBuiltValDict[Item['cname']]
                Name = '%s.%s' % (TagName, Item['cname'])
                if not Item['subreg'] and Item['value'].startswith('{'):
                    Value = Array2Val(Item['value'])
                    IsArray = True
                else:
                    Value = int(Item['value'], 16)
                    IsArray = False
                AbsfVal = Array2Val(ValStr)
                if AbsfVal != Value:
                    if 'PLATFORMID_CFG_DATA.PlatformId' == Name:
                        PlatformId = AbsfVal
                    self.WriteDeltaLine(OutLines, Name, ValStr, IsArray)
                else:
                    if 'PLATFORMID_CFG_DATA.PlatformId' == Name:
                        raise Exception("'PlatformId' has the \
same value as DSC default !")
            if Item['subreg']:
                for SubItem in Item['subreg']:
                    if SubItem['cname'] in AbsfBuiltValDict:
                        ValStr = AbsfBuiltValDict[SubItem['cname']]
                        if Array2Val(ValStr) == int(SubItem['value'], 16):
                            continue
                        Name = '%s.%s.%s' % (TagName, Item['cname'],
                                             SubItem['cname'])
                        self.WriteDeltaLine(OutLines, Name, ValStr, False)
            if Item['embed'].endswith(':END'):
                Level -= 1
        if PlatformId is None and Lines:
            raise Exception("'PlatformId' configuration \
is missing in ABSF file!")
        else:
            PlatformId = 0
        self.WriteDeltaFile(OutFile, PlatformId, Lines)
        return 0
    def GenerateDscFile(self, OutFile):
        DscFd = open(OutFile, "w")
        for Line in self._DscLines:
            DscFd.write(Line + '\n')
        DscFd.close()
        return 0
def Usage():
    print('\n'.join([
          "GenCfgData Version 0.01",
          "Usage:",
          "    GenCfgData  GENINC  BinFile             \
IncOutFile   [-D Macros]",
          "    GenCfgData  GENPKL  DscFile             \
PklOutFile   [-D Macros]",
          "    GenCfgData  GENINC  DscFile[;DltFile]   \
IncOutFile   [-D Macros]",
          "    GenCfgData  GENBIN  DscFile[;DltFile]   \
BinOutFile   [-D Macros]",
          "    GenCfgData  GENBSF  DscFile[;DltFile]   \
BsfOutFile   [-D Macros]",
          "    GenCfgData  GENDLT  DscFile[;AbsfFile]  \
DltOutFile   [-D Macros]",
          "    GenCfgData  GENDSC  DscFile             \
DscOutFile   [-D Macros]",
          "    GenCfgData  GENHDR  DscFile[;DltFile]   \
HdrOutFile[;ComHdrOutFile]   [-D Macros]"
          ]))
def Main():
    #
    # Parse the options and args
    #
    argc = len(sys.argv)
    if argc < 4:
        Usage()
        return 1
    GenCfgData = CGenCfgData()
    Command = sys.argv[1].upper()
    OutFile = sys.argv[3]
    if argc > 5 and GenCfgData.ParseMacros(sys.argv[4:]) != 0:
        raise Exception("ERROR: Macro parsing failed !")
    FileList = sys.argv[2].split(';')
    if len(FileList) == 2:
        DscFile = FileList[0]
        DltFile = FileList[1]
    elif len(FileList) == 1:
        DscFile = FileList[0]
        DltFile = ''
    else:
        raise Exception("ERROR: Invalid parameter '%s' !" % sys.argv[2])
    if Command == "GENDLT" and DscFile.endswith('.dlt'):
        # It needs to expand an existing DLT file
        DltFile = DscFile
        Lines = CGenCfgData.ExpandIncludeFiles(DltFile)
        OutTxt = ''.join([x[0] for x in Lines])
        OutFile = open(OutFile, "w")
        OutFile.write(OutTxt)
        OutFile.close()
        return 0
    if not os.path.exists(DscFile):
        raise Exception("ERROR: Cannot open file '%s' !" % DscFile)
    CfgBinFile = ''
    if DltFile:
        if not os.path.exists(DltFile):
            raise Exception("ERROR: Cannot open file '%s' !" % DltFile)
        if Command == "GENDLT":
            CfgBinFile = DltFile
            DltFile = ''
    BinFile = ''
    if (DscFile.lower().endswith('.bin')) and (Command == "GENINC"):
        # It is binary file
        BinFile = DscFile
        DscFile = ''
    if BinFile:
        if GenCfgData.GenerateDataIncFile(OutFile, BinFile) != 0:
            raise Exception(GenCfgData.Error)
        return 0
    if DscFile.lower().endswith('.pkl'):
        with open(DscFile, "rb") as PklFile:
            GenCfgData.__dict__ = marshal.load(PklFile)
    else:
        if GenCfgData.ParseDscFile(DscFile) != 0:
            raise Exception(GenCfgData.Error)
        # if GenCfgData.CheckCfgData() != 0:
        #    raise Exception(GenCfgData.Error)
        if GenCfgData.CreateVarDict() != 0:
            raise Exception(GenCfgData.Error)
        if Command == 'GENPKL':
            with open(OutFile, "wb") as PklFile:
                marshal.dump(GenCfgData.__dict__, PklFile)
            return 0
    if DltFile and Command in ['GENHDR', 'GENBIN', 'GENINC', 'GENBSF']:
        if GenCfgData.OverrideDefaultValue(DltFile) != 0:
            raise Exception(GenCfgData.Error)
    if GenCfgData.UpdateDefaultValue() != 0:
        raise Exception(GenCfgData.Error)
    # GenCfgData.PrintData ()
    if sys.argv[1] == "GENBIN":
        if GenCfgData.GenerateBinary(OutFile) != 0:
            raise Exception(GenCfgData.Error)
    elif sys.argv[1] == "GENHDR":
        OutFiles = OutFile.split(';')
        BrdOutFile = OutFiles[0].strip()
        if len(OutFiles) > 1:
            ComOutFile = OutFiles[1].strip()
        else:
            ComOutFile = ''
        if GenCfgData.CreateHeaderFile(BrdOutFile, ComOutFile) != 0:
            raise Exception(GenCfgData.Error)
    elif sys.argv[1] == "GENBSF":
        if GenCfgData.GenerateBsfFile(OutFile) != 0:
            raise Exception(GenCfgData.Error)
    elif sys.argv[1] == "GENINC":
        if GenCfgData.GenerateDataIncFile(OutFile) != 0:
            raise Exception(GenCfgData.Error)
    elif sys.argv[1] == "GENDLT":
        if GenCfgData.GenerateDeltaFile(OutFile, CfgBinFile) != 0:
            raise Exception(GenCfgData.Error)
    elif sys.argv[1] == "GENDSC":
        if GenCfgData.GenerateDscFile(OutFile) != 0:
            raise Exception(GenCfgData.Error)
    else:
        raise Exception("Unsuported command '%s' !" % Command)
    return 0
if __name__ == '__main__':
    sys.exit(Main())