## @file
# Generate a capsule.
#
# This tool generates a UEFI Capsule around an FMP Capsule. The capsule payload
# be signed using signtool or OpenSSL and if it is signed the signed content
# includes an FMP Payload Header.
#
# This tool is intended to be used to generate UEFI Capsules to update the
# system firmware or device firmware for integrated devices. In order to
# keep the tool as simple as possible, it has the following limitations:
#   * Do not support vendor code bytes in a capsule.
#
# Copyright (c) 2018 - 2022, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#

'''
GenerateCapsule
'''

import sys
import argparse
import uuid
import struct
import subprocess
import os
import tempfile
import shutil
import platform
import json
from edk2basetools.Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass
from edk2basetools.Common.Uefi.Capsule.FmpCapsuleHeader  import FmpCapsuleHeaderClass
from edk2basetools.Common.Uefi.Capsule.FmpAuthHeader     import FmpAuthHeaderClass
from edk2basetools.Common.Uefi.Capsule.CapsuleDependency import CapsuleDependencyClass
from edk2basetools.Common.Edk2.Capsule.FmpPayloadHeader  import FmpPayloadHeaderClass

#
# Globals for help information
#
__prog__        = 'GenerateCapsule'
__version__     = '0.10'
__copyright__   = 'Copyright (c) 2022, Intel Corporation. All rights reserved.'
__description__ = 'Generate a capsule.\n'

def SignPayloadSignTool (Payload, ToolPath, PfxFile, SubjectName, Verbose = False):
    #
    # Create a temporary directory
    #
    TempDirectoryName = tempfile.mkdtemp()

    #
    # Generate temp file name for the payload contents
    #
    TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')

    #
    # Create temporary payload file for signing
    #
    try:
        with open (TempFileName, 'wb') as File:
            File.write (Payload)
    except:
        shutil.rmtree (TempDirectoryName)
        raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')

    #
    # Build signtool command
    #
    if ToolPath is None:
        ToolPath = ''
    Command = ''
    Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'signtool.exe'))
    Command = Command + 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '
    Command = Command + '/p7 {TempDir} '.format (TempDir = TempDirectoryName)
    if PfxFile is not None:
        Command = Command + '/f {PfxFile} '.format (PfxFile = PfxFile)
    if SubjectName is not None:
        Command = Command + '/n {SubjectName} '.format (SubjectName = SubjectName)
    Command = Command + TempFileName
    if Verbose:
        print (Command)

    #
    # Sign the input file using the specified private key
    #
    try:
        Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
        Result = Process.communicate('')
    except:
        shutil.rmtree (TempDirectoryName)
        raise ValueError ('GenerateCapsule: error: can not run signtool.')

    if Process.returncode != 0:
        shutil.rmtree (TempDirectoryName)
        print (Result[1].decode())
        raise ValueError ('GenerateCapsule: error: signtool failed.')

    #
    # Read the signature from the generated output file
    #
    try:
        with open (TempFileName + '.p7', 'rb') as File:
            Signature = File.read ()
    except:
        shutil.rmtree (TempDirectoryName)
        raise ValueError ('GenerateCapsule: error: can not read signature file.')

    shutil.rmtree (TempDirectoryName)
    return Signature

def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile, SubjectName, Verbose = False):
    print ('signtool verify is not supported.')
    raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')

def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):
    #
    # Build openssl command
    #
    if ToolPath is None:
        ToolPath = ''
    Command = ''
    Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))
    Command = Command + 'smime -sign -binary -outform DER -md sha256 '
    Command = Command + '-signer "{Private}" -certfile "{Public}"'.format (Private = SignerPrivateCertFile, Public = OtherPublicCertFile)
    if Verbose:
        print (Command)

    #
    # Sign the input file using the specified private key and capture signature from STDOUT
    #
    try:
        Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
        Result = Process.communicate(input = Payload)
        Signature = Result[0]
    except:
        raise ValueError ('GenerateCapsule: error: can not run openssl.')

    if Process.returncode != 0:
        print (Result[1].decode())
        raise ValueError ('GenerateCapsule: error: openssl failed.')

    return Signature

def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):
    #
    # Create a temporary directory
    #
    TempDirectoryName = tempfile.mkdtemp()

    #
    # Generate temp file name for the payload contents
    #
    TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')

    #
    # Create temporary payload file for verification
    #
    try:
        with open (TempFileName, 'wb') as File:
            File.write (Payload)
    except:
        shutil.rmtree (TempDirectoryName)
        raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')

    #
    # Build openssl command
    #
    if ToolPath is None:
        ToolPath = ''
    Command = ''
    Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))
    Command = Command + 'smime -verify -inform DER '
    Command = Command + '-content {Content} -CAfile "{Public}"'.format (Content = TempFileName, Public = TrustedPublicCertFile)
    if Verbose:
        print (Command)

    #
    # Verify signature
    #
    try:
        Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
        Result = Process.communicate(input = CertData)
    except:
        shutil.rmtree (TempDirectoryName)
        raise ValueError ('GenerateCapsule: error: can not run openssl.')

    if Process.returncode != 0:
        shutil.rmtree (TempDirectoryName)
        print (Result[1].decode())
        raise ValueError ('GenerateCapsule: error: openssl failed.')

    shutil.rmtree (TempDirectoryName)
    return Payload

if __name__ == '__main__':
    def convert_arg_line_to_args(arg_line):
        for arg in arg_line.split():
            if not arg.strip():
                continue
            yield arg

    def ValidateUnsignedInteger (Argument):
        try:
            Value = int (Argument, 0)
        except:
            Message = '{Argument} is not a valid integer value.'.format (Argument = Argument)
            raise argparse.ArgumentTypeError (Message)
        if Value < 0:
            Message = '{Argument} is a negative value.'.format (Argument = Argument)
            raise argparse.ArgumentTypeError (Message)
        return Value

    def ValidateRegistryFormatGuid (Argument):
        try:
            Value = uuid.UUID (Argument)
        except:
            Message = '{Argument} is not a valid registry format GUID value.'.format (Argument = Argument)
            raise argparse.ArgumentTypeError (Message)
        return Value

    def ConvertJsonValue (Config, FieldName, Convert, Required = True, Default = None, Open = False):
        if FieldName not in Config:
            if Required:
                print ('GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.'.format(Key = FieldName))
                sys.exit (1)
            return Default
        try:
            Value = Convert (Config[FieldName])
        except:
            print ('GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.'.format (Key = FieldName))
            sys.exit (1)
        if Open:
            try:
                Value = open (Value, "rb")
            except:
                print ('GenerateCapsule: error: can not open file {File}'.format (File = FieldName))
                sys.exit (1)
        return Value

    def DecodeJsonFileParse (Json):
        if 'Payloads' not in Json:
            print ('GenerateCapsule: error "Payloads" section not found in JSON file {File}'.format (File = args.JsonFile.name))
            sys.exit (1)
        for Config in Json['Payloads']:
            #
            # Parse fields from JSON
            #
            PayloadFile                  = ConvertJsonValue (Config, 'Payload', os.path.expandvars, Required = False)
            Guid                         = ConvertJsonValue (Config, 'Guid', ValidateRegistryFormatGuid, Required = False)
            FwVersion                    = ConvertJsonValue (Config, 'FwVersion', ValidateUnsignedInteger, Required = False)
            LowestSupportedVersion       = ConvertJsonValue (Config, 'LowestSupportedVersion', ValidateUnsignedInteger, Required = False)
            HardwareInstance             = ConvertJsonValue (Config, 'HardwareInstance', ValidateUnsignedInteger, Required = False, Default = 0)
            MonotonicCount               = ConvertJsonValue (Config, 'MonotonicCount', ValidateUnsignedInteger, Required = False, Default = 0)
            SignToolPfxFile              = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)
            SignToolSubjectName          = ConvertJsonValue (Config, 'SignToolSubjectName', os.path.expandvars, Required = False, Default = None, Open = True)
            OpenSslSignerPrivateCertFile = ConvertJsonValue (Config, 'OpenSslSignerPrivateCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
            OpenSslOtherPublicCertFile   = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
            OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
            SigningToolPath              = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)
            UpdateImageIndex             = ConvertJsonValue (Config, 'UpdateImageIndex', ValidateUnsignedInteger, Required = False, Default = 1)

            PayloadDescriptorList.append (PayloadDescriptor (
                                            PayloadFile,
                                            Guid,
                                            FwVersion,
                                            LowestSupportedVersion,
                                            MonotonicCount,
                                            HardwareInstance,
                                            UpdateImageIndex,
                                            SignToolPfxFile,
                                            SignToolSubjectName,
                                            OpenSslSignerPrivateCertFile,
                                            OpenSslOtherPublicCertFile,
                                            OpenSslTrustedPublicCertFile,
                                            SigningToolPath
                                            ))

    def EncodeJsonFileParse (Json):
        if 'EmbeddedDrivers' not in Json:
            print ('GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File}'.format (File = args.JsonFile.name))
        else:
            for Config in Json['EmbeddedDrivers']:
                EmbeddedDriverFile      = ConvertJsonValue(Config, 'Driver', os.path.expandvars, Open = True)
                #
                #Read EmbeddedDriver file
                #
                try:
                    if args.Verbose:
                        print ('Read EmbeddedDriver file {File}'.format (File = EmbeddedDriverFile.name))
                    Driver = EmbeddedDriverFile.read()
                except:
                    print ('GenerateCapsule: error: can not read EmbeddedDriver file {File}'.format (File = EmbeddedDriverFile.name))
                    sys.exit (1)
                EmbeddedDriverDescriptorList.append (Driver)

        if 'Payloads' not in Json:
            print ('GenerateCapsule: error: "Payloads" section not found in JSON file {File}'.format (File = args.JsonFile.name))
            sys.exit (1)
        for Config in Json['Payloads']:
            #
            # Parse fields from JSON
            #
            PayloadFile                  = ConvertJsonValue (Config, 'Payload', os.path.expandvars, Open = True)
            Guid                         = ConvertJsonValue (Config, 'Guid', ValidateRegistryFormatGuid)
            FwVersion                    = ConvertJsonValue (Config, 'FwVersion', ValidateUnsignedInteger)
            LowestSupportedVersion       = ConvertJsonValue (Config, 'LowestSupportedVersion', ValidateUnsignedInteger)
            HardwareInstance             = ConvertJsonValue (Config, 'HardwareInstance', ValidateUnsignedInteger, Required = False, Default = 0)
            UpdateImageIndex             = ConvertJsonValue (Config, 'UpdateImageIndex', ValidateUnsignedInteger, Required = False, Default = 1)
            MonotonicCount               = ConvertJsonValue (Config, 'MonotonicCount', ValidateUnsignedInteger, Required = False, Default = 0)
            SignToolPfxFile              = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)
            SignToolSubjectName          = ConvertJsonValue (Config, 'SignToolSubjectName', os.path.expandvars, Required = False, Default = None, Open = True)
            OpenSslSignerPrivateCertFile = ConvertJsonValue (Config, 'OpenSslSignerPrivateCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
            OpenSslOtherPublicCertFile   = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
            OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
            SigningToolPath              = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)
            DepexExp                     = ConvertJsonValue (Config, 'Dependencies', str, Required = False, Default = None)

            #
            # Read binary input file
            #
            try:
                if args.Verbose:
                    print ('Read binary input file {File}'.format (File = PayloadFile.name))
                Payload = PayloadFile.read()
                PayloadFile.close ()
            except:
                print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = PayloadFile.name))
                sys.exit (1)
            PayloadDescriptorList.append (PayloadDescriptor (
                                            Payload,
                                            Guid,
                                            FwVersion,
                                            LowestSupportedVersion,
                                            MonotonicCount,
                                            HardwareInstance,
                                            UpdateImageIndex,
                                            SignToolPfxFile,
                                            SignToolSubjectName,
                                            OpenSslSignerPrivateCertFile,
                                            OpenSslOtherPublicCertFile,
                                            OpenSslTrustedPublicCertFile,
                                            SigningToolPath,
                                            DepexExp
                                            ))

    def GenerateOutputJson (PayloadJsonDescriptorList):
        PayloadJson = {
                          "Payloads" : [
                              {
                                  "Guid": str(PayloadDescriptor.Guid).upper(),
                                  "FwVersion": str(PayloadDescriptor.FwVersion),
                                  "LowestSupportedVersion": str(PayloadDescriptor.LowestSupportedVersion),
                                  "MonotonicCount": str(PayloadDescriptor.MonotonicCount),
                                  "Payload": PayloadDescriptor.Payload,
                                  "HardwareInstance": str(PayloadDescriptor.HardwareInstance),
                                  "UpdateImageIndex": str(PayloadDescriptor.UpdateImageIndex),
                                  "SignToolPfxFile": str(PayloadDescriptor.SignToolPfxFile),
                                  "SignToolSubjectName": str(PayloadDescriptor.SignToolSubjectName),
                                  "OpenSslSignerPrivateCertFile": str(PayloadDescriptor.OpenSslSignerPrivateCertFile),
                                  "OpenSslOtherPublicCertFile": str(PayloadDescriptor.OpenSslOtherPublicCertFile),
                                  "OpenSslTrustedPublicCertFile": str(PayloadDescriptor.OpenSslTrustedPublicCertFile),
                                  "SigningToolPath": str(PayloadDescriptor.SigningToolPath),
                                  "Dependencies" : str(PayloadDescriptor.DepexExp)
                              }for PayloadDescriptor in PayloadJsonDescriptorList
                          ]
                      }
        OutputJsonFile = args.OutputFile.name + '.json'
        if 'Payloads' in PayloadJson:
            PayloadSection = PayloadJson ['Payloads']
        Index = 0
        for PayloadField in PayloadSection:
            if PayloadJsonDescriptorList[Index].SignToolPfxFile is None:
                del PayloadField ['SignToolPfxFile']
            if PayloadJsonDescriptorList[Index].SignToolSubjectName is None:
                del PayloadField ['SignToolSubjectName']
            if PayloadJsonDescriptorList[Index].OpenSslSignerPrivateCertFile is None:
                del PayloadField ['OpenSslSignerPrivateCertFile']
            if PayloadJsonDescriptorList[Index].OpenSslOtherPublicCertFile is None:
                del PayloadField ['OpenSslOtherPublicCertFile']
            if PayloadJsonDescriptorList[Index].OpenSslTrustedPublicCertFile is None:
                del PayloadField ['OpenSslTrustedPublicCertFile']
            if PayloadJsonDescriptorList[Index].SigningToolPath is None:
                del PayloadField ['SigningToolPath']
            Index = Index + 1
        Result = json.dumps (PayloadJson, indent=4, sort_keys=True, separators=(',', ': '))
        with open (OutputJsonFile, 'w') as OutputFile:
            OutputFile.write (Result)

    def CheckArgumentConflict (args):
        if args.Encode:
            if args.InputFile:
                print ('GenerateCapsule: error: Argument InputFile conflicts with Argument -j')
                sys.exit (1)
            if args.EmbeddedDriver:
                print ('GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j')
                sys.exit (1)
        if args.Guid:
            print ('GenerateCapsule: error: Argument --guid conflicts with Argument -j')
            sys.exit (1)
        if args.FwVersion:
            print ('GenerateCapsule: error: Argument --fw-version conflicts with Argument -j')
            sys.exit (1)
        if args.LowestSupportedVersion:
            print ('GenerateCapsule: error: Argument --lsv conflicts with Argument -j')
            sys.exit (1)
        if args.MonotonicCount:
            print ('GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j')
            sys.exit (1)
        if args.HardwareInstance:
            print ('GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j')
            sys.exit (1)
        if args.SignToolPfxFile:
            print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')
            sys.exit (1)
        if args.SignToolSubjectName:
            print ('GenerateCapsule: error: Argument --SubjectName conflicts with Argument -j')
            sys.exit (1)
        if args.OpenSslSignerPrivateCertFile:
            print ('GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j')
            sys.exit (1)
        if args.OpenSslOtherPublicCertFile:
            print ('GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j')
            sys.exit (1)
        if args.OpenSslTrustedPublicCertFile:
            print ('GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j')
            sys.exit (1)
        if args.SigningToolPath:
            print ('GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j')
            sys.exit (1)

    class PayloadDescriptor (object):
        def __init__(self,
                     Payload,
                     Guid,
                     FwVersion,
                     LowestSupportedVersion,
                     MonotonicCount               = 0,
                     HardwareInstance             = 0,
                     UpdateImageIndex             = 1,
                     SignToolPfxFile              = None,
                     SignToolSubjectName          = None,
                     OpenSslSignerPrivateCertFile = None,
                     OpenSslOtherPublicCertFile   = None,
                     OpenSslTrustedPublicCertFile = None,
                     SigningToolPath              = None,
                     DepexExp                     = None
                     ):
            self.Payload                      = Payload
            self.Guid                         = Guid
            self.FwVersion                    = FwVersion
            self.LowestSupportedVersion       = LowestSupportedVersion
            self.MonotonicCount               = MonotonicCount
            self.HardwareInstance             = HardwareInstance
            self.UpdateImageIndex             = UpdateImageIndex
            self.SignToolPfxFile              = SignToolPfxFile
            self.SignToolSubjectName          = SignToolSubjectName
            self.OpenSslSignerPrivateCertFile = OpenSslSignerPrivateCertFile
            self.OpenSslOtherPublicCertFile   = OpenSslOtherPublicCertFile
            self.OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile
            self.SigningToolPath              = SigningToolPath
            self.DepexExp                     = DepexExp

            self.UseSignTool = (self.SignToolPfxFile is not None or
                                self.SignToolSubjectName is not None)
            self.UseOpenSsl  = (self.OpenSslSignerPrivateCertFile is not None and
                                self.OpenSslOtherPublicCertFile is not None and
                                self.OpenSslTrustedPublicCertFile is not None)
            self.AnyOpenSsl  = (self.OpenSslSignerPrivateCertFile is not None or
                                self.OpenSslOtherPublicCertFile is not None or
                                self.OpenSslTrustedPublicCertFile is not None)
            self.UseDependency = self.DepexExp is not None

        def Validate(self, args):
            if self.UseSignTool and self.AnyOpenSsl:
                raise argparse.ArgumentTypeError ('Providing both signtool and OpenSSL options is not supported')
            if not self.UseSignTool and not self.UseOpenSsl and self.AnyOpenSsl:
                if args.JsonFile:
                    raise argparse.ArgumentTypeError ('the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile')
                else:
                    raise argparse.ArgumentTypeError ('the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert')
            if self.UseSignTool and platform.system() != 'Windows':
                raise argparse.ArgumentTypeError ('Use of signtool is not supported on this operating system.')
            if args.Encode:
                if self.FwVersion is None or self.LowestSupportedVersion is None:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('the following JSON fields are required: FwVersion, LowestSupportedVersion')
                    else:
                        raise argparse.ArgumentTypeError ('the following options are required: --fw-version, --lsv')
                if self.FwVersion > 0xFFFFFFFF:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('JSON field FwVersion must be an integer in range 0x0..0xffffffff')
                    else:
                        raise argparse.ArgumentTypeError ('--fw-version must be an integer in range 0x0..0xffffffff')
                if self.LowestSupportedVersion > 0xFFFFFFFF:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff')
                    else:
                        raise argparse.ArgumentTypeError ('--lsv must be an integer in range 0x0..0xffffffff')

            if args.Encode:
                if self.Guid is None:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('the following JSON field is required: Guid')
                    else:
                        raise argparse.ArgumentTypeError ('the following option is required: --guid')
                if self.HardwareInstance > 0xFFFFFFFFFFFFFFFF:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff')
                    else:
                        raise argparse.ArgumentTypeError ('--hardware-instance must be an integer in range 0x0..0xffffffffffffffff')
                if self.MonotonicCount > 0xFFFFFFFFFFFFFFFF:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff')
                    else:
                        raise argparse.ArgumentTypeError ('--monotonic-count must be an integer in range 0x0..0xffffffffffffffff')
                if self.UpdateImageIndex >0xFF:
                    if args.JsonFile:
                        raise argparse.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')
                    else:
                        raise argparse.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')

            if self.UseSignTool:
                if self.SignToolPfxFile is not None:
                    self.SignToolPfxFile.close()
                    self.SignToolPfxFile = self.SignToolPfxFile.name
            if self.UseOpenSsl:
                self.OpenSslSignerPrivateCertFile.close()
                self.OpenSslOtherPublicCertFile.close()
                self.OpenSslTrustedPublicCertFile.close()
                self.OpenSslSignerPrivateCertFile = self.OpenSslSignerPrivateCertFile.name
                self.OpenSslOtherPublicCertFile   = self.OpenSslOtherPublicCertFile.name
                self.OpenSslTrustedPublicCertFile = self.OpenSslTrustedPublicCertFile.name

            #
            # Perform additional argument verification
            #
            if args.Encode:
                if 'PersistAcrossReset' not in args.CapsuleFlag:
                    if 'InitiateReset' in args.CapsuleFlag:
                        raise argparse.ArgumentTypeError ('--capflag InitiateReset also requires --capflag PersistAcrossReset')
                if args.CapsuleOemFlag > 0xFFFF:
                    raise argparse.ArgumentTypeError ('--capoemflag must be an integer between 0x0000 and 0xffff')

            return True


    def Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffer):
        if args.JsonFile:
            CheckArgumentConflict(args)
            try:
                Json = json.loads (args.JsonFile.read ())
            except:
                print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile = args.JsonFile))
                sys.exit (1)
            EncodeJsonFileParse(Json)
        else:
            for Driver in args.EmbeddedDriver:
                EmbeddedDriverDescriptorList.append (Driver.read())
            PayloadDescriptorList.append (PayloadDescriptor (
                                            Buffer,
                                            args.Guid,
                                            args.FwVersion,
                                            args.LowestSupportedVersion,
                                            args.MonotonicCount,
                                            args.HardwareInstance,
                                            args.UpdateImageIndex,
                                            args.SignToolPfxFile,
                                            args.SignToolSubjectName,
                                            args.OpenSslSignerPrivateCertFile,
                                            args.OpenSslOtherPublicCertFile,
                                            args.OpenSslTrustedPublicCertFile,
                                            args.SigningToolPath,
                                            None
                                            ))
        for SinglePayloadDescriptor in PayloadDescriptorList:
            try:
                SinglePayloadDescriptor.Validate (args)
            except Exception as Msg:
                print ('GenerateCapsule: error:' + str(Msg))
                sys.exit (1)
        for SinglePayloadDescriptor in PayloadDescriptorList:
            ImageCapsuleSupport = 0x0000000000000000
            Result = SinglePayloadDescriptor.Payload
            try:
                FmpPayloadHeader.FwVersion              = SinglePayloadDescriptor.FwVersion
                FmpPayloadHeader.LowestSupportedVersion = SinglePayloadDescriptor.LowestSupportedVersion
                FmpPayloadHeader.Payload                = SinglePayloadDescriptor.Payload
                Result = FmpPayloadHeader.Encode ()
                if args.Verbose:
                    FmpPayloadHeader.DumpInfo ()
            except:
                print ('GenerateCapsule: error: can not encode FMP Payload Header')
                sys.exit (1)
            if SinglePayloadDescriptor.UseDependency:
                CapsuleDependency.Payload = Result
                CapsuleDependency.DepexExp = SinglePayloadDescriptor.DepexExp
                ImageCapsuleSupport        |= FmpCapsuleHeader.CAPSULE_SUPPORT_DEPENDENCY
                Result = CapsuleDependency.Encode ()
                if args.Verbose:
                    CapsuleDependency.DumpInfo ()
            if SinglePayloadDescriptor.UseOpenSsl or SinglePayloadDescriptor.UseSignTool:
                #
                # Sign image with 64-bit MonotonicCount appended to end of image
                #
                try:
                    if SinglePayloadDescriptor.UseSignTool:
                        CertData = SignPayloadSignTool (
                            Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),
                            SinglePayloadDescriptor.SigningToolPath,
                            SinglePayloadDescriptor.SignToolPfxFile,
                            SinglePayloadDescriptor.SignToolSubjectName,
                            Verbose = args.Verbose
                        )
                    else:
                        CertData = SignPayloadOpenSsl (
                            Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),
                            SinglePayloadDescriptor.SigningToolPath,
                            SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,
                            SinglePayloadDescriptor.OpenSslOtherPublicCertFile,
                            SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,
                            Verbose = args.Verbose
                        )
                except Exception as Msg:
                    print ('GenerateCapsule: error: can not sign payload \n' + str(Msg))
                    sys.exit (1)

                try:
                    FmpAuthHeader.MonotonicCount = SinglePayloadDescriptor.MonotonicCount
                    FmpAuthHeader.CertData       = CertData
                    FmpAuthHeader.Payload        = Result
                    ImageCapsuleSupport          |= FmpCapsuleHeader.CAPSULE_SUPPORT_AUTHENTICATION
                    Result = FmpAuthHeader.Encode ()
                    if args.Verbose:
                        FmpAuthHeader.DumpInfo ()
                except:
                    print ('GenerateCapsule: error: can not encode FMP Auth Header')
                    sys.exit (1)
            FmpCapsuleHeader.AddPayload (SinglePayloadDescriptor.Guid, Result, HardwareInstance = SinglePayloadDescriptor.HardwareInstance, UpdateImageIndex = SinglePayloadDescriptor.UpdateImageIndex, CapsuleSupport = ImageCapsuleSupport)
        try:
            for EmbeddedDriver in EmbeddedDriverDescriptorList:
                FmpCapsuleHeader.AddEmbeddedDriver(EmbeddedDriver)

            Result = FmpCapsuleHeader.Encode ()
            if args.Verbose:
                FmpCapsuleHeader.DumpInfo ()
        except:
            print ('GenerateCapsule: error: can not encode FMP Capsule Header')
            sys.exit (1)

        try:
            UefiCapsuleHeader.OemFlags            = args.CapsuleOemFlag
            UefiCapsuleHeader.PersistAcrossReset  = 'PersistAcrossReset'  in args.CapsuleFlag
            UefiCapsuleHeader.PopulateSystemTable = False
            UefiCapsuleHeader.InitiateReset       = 'InitiateReset'       in args.CapsuleFlag
            UefiCapsuleHeader.Payload             = Result
            Result = UefiCapsuleHeader.Encode ()
            if args.Verbose:
                UefiCapsuleHeader.DumpInfo ()
        except:
            print ('GenerateCapsule: error: can not encode UEFI Capsule Header')
            sys.exit (1)
        try:
            if args.Verbose:
                print ('Write binary output file {File}'.format (File = args.OutputFile.name))
            args.OutputFile.write (Result)
            args.OutputFile.close ()
        except:
            print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = args.OutputFile.name))
            sys.exit (1)

    def Decode (PayloadDescriptorList, PayloadJsonDescriptorList, Buffer):
        if args.JsonFile:
            CheckArgumentConflict(args)
        #
        # Parse payload descriptors from JSON
        #
            try:
                Json = json.loads (args.JsonFile.read())
            except:
                print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile = args.JsonFile))
                sys.exit (1)
            DecodeJsonFileParse (Json)
        else:
            PayloadDescriptorList.append (PayloadDescriptor (
                                            Buffer,
                                            args.Guid,
                                            args.FwVersion,
                                            args.LowestSupportedVersion,
                                            args.MonotonicCount,
                                            args.HardwareInstance,
                                            args.UpdateImageIndex,
                                            args.SignToolPfxFile,
                                            args.SignSubjectName,
                                            args.OpenSslSignerPrivateCertFile,
                                            args.OpenSslOtherPublicCertFile,
                                            args.OpenSslTrustedPublicCertFile,
                                            args.SigningToolPath,
                                            None
                                            ))
        #
        # Perform additional verification on payload descriptors
        #
        for SinglePayloadDescriptor in PayloadDescriptorList:
            try:
                SinglePayloadDescriptor.Validate (args)
            except Exception as Msg:
                print ('GenerateCapsule: error:' + str(Msg))
                sys.exit (1)
        try:
            Result = UefiCapsuleHeader.Decode (Buffer)
            if len (Result) > 0:
                Result = FmpCapsuleHeader.Decode (Result)
                if args.JsonFile:
                    if FmpCapsuleHeader.PayloadItemCount != len (PayloadDescriptorList):
                        CapsulePayloadNum = FmpCapsuleHeader.PayloadItemCount
                        JsonPayloadNum = len (PayloadDescriptorList)
                        print ('GenerateCapsule: Decode error: {JsonPayloadNumber} payloads in JSON file {File} and {CapsulePayloadNumber} payloads in Capsule {CapsuleName}'.format (JsonPayloadNumber = JsonPayloadNum, File = args.JsonFile.name, CapsulePayloadNumber = CapsulePayloadNum, CapsuleName = args.InputFile.name))
                        sys.exit (1)
                    for Index in range (0, FmpCapsuleHeader.PayloadItemCount):
                        if Index < len (PayloadDescriptorList):
                            GUID = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageTypeId
                            HardwareInstance = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateHardwareInstance
                            UpdateImageIndex = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageIndex
                            if PayloadDescriptorList[Index].Guid != GUID or PayloadDescriptorList[Index].HardwareInstance != HardwareInstance:
                                print ('GenerateCapsule: Decode error: Guid or HardwareInstance pair in input JSON file {File} does not match the payload {PayloadIndex} in Capsule {InputCapsule}'.format (File = args.JsonFile.name, PayloadIndex = Index + 1, InputCapsule = args.InputFile.name))
                                sys.exit (1)
                            PayloadDescriptorList[Index].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload
                            DecodeJsonOutput = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = Index + 1)
                            PayloadJsonDescriptorList.append (PayloadDescriptor (
                                                                DecodeJsonOutput,
                                                                GUID,
                                                                None,
                                                                None,
                                                                None,
                                                                HardwareInstance,
                                                                UpdateImageIndex,
                                                                PayloadDescriptorList[Index].SignToolPfxFile,
                                                                PayloadDescriptorList[Index].SignToolSubjectName,
                                                                PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
                                                                PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
                                                                PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
                                                                PayloadDescriptorList[Index].SigningToolPath,
                                                                None
                                                                ))
                else:
                    PayloadDescriptorList[0].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload
                    for Index in range (0, FmpCapsuleHeader.PayloadItemCount):
                        if Index > 0:
                            PayloadDecodeFile = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload
                            PayloadDescriptorList.append (PayloadDescriptor (PayloadDecodeFile,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None,
                                                            None
                                                            ))
                        GUID = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageTypeId
                        HardwareInstance = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateHardwareInstance
                        UpdateImageIndex = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageIndex
                        DecodeJsonOutput = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = Index + 1)
                        PayloadJsonDescriptorList.append (PayloadDescriptor (
                                                            DecodeJsonOutput,
                                                            GUID,
                                                            None,
                                                            None,
                                                            None,
                                                            HardwareInstance,
                                                            UpdateImageIndex,
                                                            PayloadDescriptorList[Index].SignToolPfxFile,
                                                            PayloadDescriptorList[Index].SignToolSubjectName,
                                                            PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
                                                            PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
                                                            PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
                                                            PayloadDescriptorList[Index].SigningToolPath,
                                                            None
                                                            ))
                JsonIndex = 0
                for SinglePayloadDescriptor in PayloadDescriptorList:
                    if args.Verbose:
                        print ('========')
                        UefiCapsuleHeader.DumpInfo ()
                        print ('--------')
                        FmpCapsuleHeader.DumpInfo ()
                    if FmpAuthHeader.IsSigned(SinglePayloadDescriptor.Payload):
                        if not SinglePayloadDescriptor.UseOpenSsl and not SinglePayloadDescriptor.UseSignTool:
                            print ('GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index}'.format (Index = JsonIndex + 1))
                        SinglePayloadDescriptor.Payload = FmpAuthHeader.Decode (SinglePayloadDescriptor.Payload)
                        PayloadJsonDescriptorList[JsonIndex].MonotonicCount = FmpAuthHeader.MonotonicCount
                        if args.Verbose:
                            print ('--------')
                            FmpAuthHeader.DumpInfo ()

                        #
                        # Verify Image with 64-bit MonotonicCount appended to end of image
                        #
                        try:
                          if SinglePayloadDescriptor.UseSignTool:
                              CertData = VerifyPayloadSignTool (
                                           FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),
                                           FmpAuthHeader.CertData,
                                           SinglePayloadDescriptor.SigningToolPath,
                                           SinglePayloadDescriptor.SignToolPfxFile,
                                           SinglePayloadDescriptor.SignToolSubjectName,
                                           Verbose = args.Verbose
                                           )
                          else:
                              CertData = VerifyPayloadOpenSsl (
                                           FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),
                                           FmpAuthHeader.CertData,
                                           SinglePayloadDescriptor.SigningToolPath,
                                           SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,
                                           SinglePayloadDescriptor.OpenSslOtherPublicCertFile,
                                           SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,
                                           Verbose = args.Verbose
                                           )
                        except Exception as Msg:
                            print ('GenerateCapsule: warning: payload verification failed Index = {Index} \n'.format (Index = JsonIndex + 1) + str(Msg))
                    else:
                        if args.Verbose:
                            print ('--------')
                            print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')

                    PayloadSignature = struct.unpack ('<I', SinglePayloadDescriptor.Payload[0:4])
                    if PayloadSignature != FmpPayloadHeader.Signature:
                        SinglePayloadDescriptor.UseDependency = True
                        try:
                            SinglePayloadDescriptor.Payload = CapsuleDependency.Decode (SinglePayloadDescriptor.Payload)
                            PayloadJsonDescriptorList[JsonIndex].DepexExp = CapsuleDependency.DepexExp
                            if args.Verbose:
                                print ('--------')
                                CapsuleDependency.DumpInfo ()
                        except Exception as Msg:
                            print ('GenerateCapsule: error: invalid dependency expression')
                    else:
                        if args.Verbose:
                            print ('--------')
                            print ('No EFI_FIRMWARE_IMAGE_DEP')

                    try:
                        SinglePayloadDescriptor.Payload = FmpPayloadHeader.Decode (SinglePayloadDescriptor.Payload)
                        PayloadJsonDescriptorList[JsonIndex].FwVersion = FmpPayloadHeader.FwVersion
                        PayloadJsonDescriptorList[JsonIndex].LowestSupportedVersion = FmpPayloadHeader.LowestSupportedVersion
                        JsonIndex = JsonIndex + 1
                        if args.Verbose:
                            print ('--------')
                            FmpPayloadHeader.DumpInfo ()
                            print ('========')
                    except:
                        if args.Verbose:
                            print ('--------')
                            print ('No FMP_PAYLOAD_HEADER')
                            print ('========')
                        sys.exit (1)
                #
                # Write embedded driver file(s)
                #
                for Index in range (0, FmpCapsuleHeader.EmbeddedDriverCount):
                    EmbeddedDriverBuffer = FmpCapsuleHeader.GetEmbeddedDriver (Index)
                    EmbeddedDriverPath = args.OutputFile.name + '.EmbeddedDriver.{Index:d}.efi'.format (Index = Index + 1)
                    try:
                        if args.Verbose:
                            print ('Write embedded driver file {File}'.format (File = EmbeddedDriverPath))
                        with open (EmbeddedDriverPath, 'wb') as EmbeddedDriverFile:
                            EmbeddedDriverFile.write (EmbeddedDriverBuffer)
                    except:
                        print ('GenerateCapsule: error: can not write embedded driver file {File}'.format (File = EmbeddedDriverPath))
                        sys.exit (1)

        except:
            print ('GenerateCapsule: error: can not decode capsule')
            sys.exit (1)
        GenerateOutputJson(PayloadJsonDescriptorList)
        PayloadIndex = 0
        for SinglePayloadDescriptor in PayloadDescriptorList:
            if args.OutputFile is None:
                print ('GenerateCapsule: Decode error: OutputFile is needed for decode output')
                sys.exit (1)
            try:
                if args.Verbose:
                    print ('Write binary output file {File}'.format (File = args.OutputFile.name))
                PayloadDecodePath = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = PayloadIndex + 1)
                with open (PayloadDecodePath, 'wb') as PayloadDecodeFile:
                    PayloadDecodeFile.write (SinglePayloadDescriptor.Payload)
                PayloadIndex = PayloadIndex + 1
            except:
                print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = SinglePayloadDescriptor.OutputFile.name))
                sys.exit (1)

    def DumpInfo (Buffer, args):
        if args.OutputFile is not None:
            raise argparse.ArgumentTypeError ('the following option is not supported for dumpinfo operations: --output')
        try:
            Result = UefiCapsuleHeader.Decode (Buffer)
            print ('========')
            UefiCapsuleHeader.DumpInfo ()
            if len (Result) > 0:
                FmpCapsuleHeader.Decode (Result)
                print ('--------')
                FmpCapsuleHeader.DumpInfo ()
                for Index in range (0, FmpCapsuleHeader.PayloadItemCount):
                    Result = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload
                    try:
                        Result = FmpAuthHeader.Decode (Result)
                        print ('--------')
                        FmpAuthHeader.DumpInfo ()
                    except:
                        print ('--------')
                        print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')

                    PayloadSignature = struct.unpack ('<I', Result[0:4])
                    if PayloadSignature != FmpPayloadHeader.Signature:
                        try:
                            Result = CapsuleDependency.Decode (Result)
                            print ('--------')
                            CapsuleDependency.DumpInfo ()
                        except:
                            print ('GenerateCapsule: error: invalid dependency expression')
                    else:
                        print ('--------')
                        print ('No EFI_FIRMWARE_IMAGE_DEP')
                    try:
                        Result = FmpPayloadHeader.Decode (Result)
                        print ('--------')
                        FmpPayloadHeader.DumpInfo ()
                    except:
                        print ('--------')
                        print ('No FMP_PAYLOAD_HEADER')
                    print ('========')
        except:
            print ('GenerateCapsule: error: can not decode capsule')
            sys.exit (1)
    #
    # Create command line argument parser object
    #
    parser = argparse.ArgumentParser (
                        prog = __prog__,
                        description = __description__ + __copyright__,
                        conflict_handler = 'resolve',
                        fromfile_prefix_chars = '@'
                        )
    parser.convert_arg_line_to_args = convert_arg_line_to_args

    #
    # Add input and output file arguments
    #
    parser.add_argument("InputFile",  type = argparse.FileType('rb'), nargs='?',
                        help = "Input binary payload filename.")
    parser.add_argument("-o", "--output", dest = 'OutputFile', type = argparse.FileType('wb'),
                        help = "Output filename.")
    #
    # Add group for -e and -d flags that are mutually exclusive and required
    #
    group = parser.add_mutually_exclusive_group (required = True)
    group.add_argument ("-e", "--encode", dest = 'Encode', action = "store_true",
                        help = "Encode file")
    group.add_argument ("-d", "--decode", dest = 'Decode', action = "store_true",
                        help = "Decode file")
    group.add_argument ("--dump-info", dest = 'DumpInfo', action = "store_true",
                        help = "Display FMP Payload Header information")
    #
    # Add optional arguments for this command
    #
    parser.add_argument ("-j", "--json-file", dest = 'JsonFile', type=argparse.FileType('r'),
                         help = "JSON configuration file for multiple payloads and embedded drivers.")
    parser.add_argument ("--capflag", dest = 'CapsuleFlag', action='append', default = [],
                         choices=['PersistAcrossReset', 'InitiateReset'],
                         help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set")
    parser.add_argument ("--capoemflag", dest = 'CapsuleOemFlag', type = ValidateUnsignedInteger, default = 0x0000,
                         help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")

    parser.add_argument ("--guid", dest = 'Guid', type = ValidateRegistryFormatGuid,
                         help = "The FMP/ESRT GUID in registry format.  Required for single payload encode operations.")
    parser.add_argument ("--hardware-instance", dest = 'HardwareInstance', type = ValidateUnsignedInteger, default = 0x0000000000000000,
                         help = "The 64-bit hardware instance.  The default is 0x0000000000000000")


    parser.add_argument ("--monotonic-count", dest = 'MonotonicCount', type = ValidateUnsignedInteger, default = 0x0000000000000000,
                         help = "64-bit monotonic count value in header.  Default is 0x0000000000000000.")

    parser.add_argument ("--fw-version", dest = 'FwVersion', type = ValidateUnsignedInteger,
                         help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678).  Required for encode operations.")
    parser.add_argument ("--lsv", dest = 'LowestSupportedVersion', type = ValidateUnsignedInteger,
                         help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678).  Required for encode operations.")

    parser.add_argument ("--pfx-file", dest='SignToolPfxFile', type=argparse.FileType('rb'),
                         help="signtool PFX certificate filename.")
    parser.add_argument ("--subject-name", dest='SignToolSubjectName',
                         help="signtool certificate subject name.")

    parser.add_argument ("--signer-private-cert", dest='OpenSslSignerPrivateCertFile', type=argparse.FileType('rb'),
                         help="OpenSSL signer private certificate filename.")
    parser.add_argument ("--other-public-cert", dest='OpenSslOtherPublicCertFile', type=argparse.FileType('rb'),
                         help="OpenSSL other public certificate filename.")
    parser.add_argument ("--trusted-public-cert", dest='OpenSslTrustedPublicCertFile', type=argparse.FileType('rb'),
                         help="OpenSSL trusted public certificate filename.")

    parser.add_argument ("--signing-tool-path", dest = 'SigningToolPath',
                         help = "Path to signtool or OpenSSL tool.  Optional if path to tools are already in PATH.")

    parser.add_argument ("--embedded-driver", dest = 'EmbeddedDriver', type = argparse.FileType('rb'), action='append', default = [],
                         help = "Path to embedded UEFI driver to add to capsule.")

    #
    # Add optional arguments common to all operations
    #
    parser.add_argument ('--version', action='version', version='%(prog)s ' + __version__)
    parser.add_argument ("-v", "--verbose", dest = 'Verbose', action = "store_true",
                         help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages.")
    parser.add_argument ("-q", "--quiet", dest = 'Quiet', action = "store_true",
                         help = "Disable all messages except fatal errors.")
    parser.add_argument ("--debug", dest = 'Debug', type = int, metavar = '[0-9]', choices = range (0, 10), default = 0,
                         help = "Set debug level")
    parser.add_argument ("--update-image-index", dest = 'UpdateImageIndex', type = ValidateUnsignedInteger, default = 0x01, help = "unique number identifying the firmware image within the device ")

    #
    # Parse command line arguments
    #
    args = parser.parse_args()

    #
    # Read binary input file
    #
    Buffer = ''
    if args.InputFile:
        if os.path.getsize (args.InputFile.name) == 0:
            print ('GenerateCapsule: error: InputFile {File} is empty'.format (File = args.InputFile.name))
            sys.exit (1)
        try:
            if args.Verbose:
                print ('Read binary input file {File}'.format (File = args.InputFile.name))
            Buffer = args.InputFile.read ()
            args.InputFile.close ()
        except:
            print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = args.InputFile.name))
            sys.exit (1)

    #
    # Create objects
    #
    UefiCapsuleHeader = UefiCapsuleHeaderClass ()
    FmpCapsuleHeader  = FmpCapsuleHeaderClass ()
    FmpAuthHeader     = FmpAuthHeaderClass ()
    FmpPayloadHeader  = FmpPayloadHeaderClass ()
    CapsuleDependency = CapsuleDependencyClass ()

    EmbeddedDriverDescriptorList = []
    PayloadDescriptorList = []
    PayloadJsonDescriptorList = []

    #
    #Encode Operation
    #
    if args.Encode:
        Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffer)

    #
    #Decode Operation
    #
    if args.Decode:
        Decode (PayloadDescriptorList, PayloadJsonDescriptorList, Buffer)

    #
    #Dump Info Operation
    #
    if args.DumpInfo:
        DumpInfo (Buffer, args)

    if args.Verbose:
        print('Success')
