/** @file
Creates output file that is a properly formed section per the PI spec.

Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#ifndef __GNUC__
#include <windows.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <Common/UefiBaseTypes.h>
#include <Common/PiFirmwareFile.h>
#include <Protocol/GuidedSectionExtraction.h>
#include <IndustryStandard/PeImage.h>

#include "CommonLib.h"
#include "Compress.h"
#include "Crc32.h"
#include "EfiUtilityMsgs.h"
#include "ParseInf.h"
#include "FvLib.h"
#include "PeCoffLib.h"

//
// GenSec Tool Information
//
#define UTILITY_NAME            "GenSec"
#define UTILITY_MAJOR_VERSION   0
#define UTILITY_MINOR_VERSION   1

STATIC CHAR8      *mSectionTypeName[] = {
  NULL,                                 // 0x00 - reserved
  "EFI_SECTION_COMPRESSION",            // 0x01
  "EFI_SECTION_GUID_DEFINED",           // 0x02
  NULL,                                 // 0x03 - reserved
  NULL,                                 // 0x04 - reserved
  NULL,                                 // 0x05 - reserved
  NULL,                                 // 0x06 - reserved
  NULL,                                 // 0x07 - reserved
  NULL,                                 // 0x08 - reserved
  NULL,                                 // 0x09 - reserved
  NULL,                                 // 0x0A - reserved
  NULL,                                 // 0x0B - reserved
  NULL,                                 // 0x0C - reserved
  NULL,                                 // 0x0D - reserved
  NULL,                                 // 0x0E - reserved
  NULL,                                 // 0x0F - reserved
  "EFI_SECTION_PE32",                   // 0x10
  "EFI_SECTION_PIC",                    // 0x11
  "EFI_SECTION_TE",                     // 0x12
  "EFI_SECTION_DXE_DEPEX",              // 0x13
  "EFI_SECTION_VERSION",                // 0x14
  "EFI_SECTION_USER_INTERFACE",         // 0x15
  "EFI_SECTION_COMPATIBILITY16",        // 0x16
  "EFI_SECTION_FIRMWARE_VOLUME_IMAGE",  // 0x17
  "EFI_SECTION_FREEFORM_SUBTYPE_GUID",  // 0x18
  "EFI_SECTION_RAW",                    // 0x19
  NULL,                                 // 0x1A
  "EFI_SECTION_PEI_DEPEX",              // 0x1B
  "EFI_SECTION_SMM_DEPEX"               // 0x1C
};

STATIC CHAR8      *mCompressionTypeName[]    = { "PI_NONE", "PI_STD" };

#define EFI_GUIDED_SECTION_NONE 0x80
STATIC CHAR8      *mGUIDedSectionAttribue[]  = { "NONE", "PROCESSING_REQUIRED", "AUTH_STATUS_VALID"};

STATIC CHAR8 *mAlignName[] = {
  "1", "2", "4", "8", "16", "32", "64", "128", "256", "512",
  "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K",
  "512K", "1M", "2M", "4M", "8M", "16M"
};

//
// Crc32 GUID section related definitions.
//
typedef struct {
  EFI_GUID_DEFINED_SECTION  GuidSectionHeader;
  UINT32                    CRC32Checksum;
} CRC32_SECTION_HEADER;

typedef struct {
  EFI_GUID_DEFINED_SECTION2 GuidSectionHeader;
  UINT32                    CRC32Checksum;
} CRC32_SECTION_HEADER2;

STATIC EFI_GUID  mZeroGuid                 = {0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
STATIC EFI_GUID  mEfiCrc32SectionGuid      = EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID;

STATIC
VOID
Version (
  VOID
  )
/*++

Routine Description:

  Print out version information for this utility.

Arguments:

  None

Returns:

  None

--*/
{
  fprintf (stdout, "%s Version %d.%d %s \n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION);
}

STATIC
VOID
Usage (
  VOID
  )
/*++

Routine Description:

  Print Help message.

Arguments:

  VOID

Returns:

  None

--*/
{
  //
  // Summary usage
  //
  fprintf (stdout, "\nUsage: %s [options] [input_file]\n\n", UTILITY_NAME);

  //
  // Copyright declaration
  //
  fprintf (stdout, "Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.\n\n");

  //
  // Details Option
  //
  fprintf (stdout, "Options:\n");
  fprintf (stdout, "  -o FileName, --outputfile FileName\n\
                        File is the SectionFile to be created.\n");
  fprintf (stdout, "  -s [SectionType], --sectiontype [SectionType]\n\
                        SectionType defined in PI spec is one type of\n\
                        EFI_SECTION_COMPRESSION, EFI_SECTION_GUID_DEFINED,\n\
                        EFI_SECTION_PE32, EFI_SECTION_PIC, EFI_SECTION_TE,\n\
                        EFI_SECTION_DXE_DEPEX, EFI_SECTION_COMPATIBILITY16,\n\
                        EFI_SECTION_USER_INTERFACE, EFI_SECTION_VERSION,\n\
                        EFI_SECTION_FIRMWARE_VOLUME_IMAGE, EFI_SECTION_RAW,\n\
                        EFI_SECTION_FREEFORM_SUBTYPE_GUID,\n\
                        EFI_SECTION_PEI_DEPEX, EFI_SECTION_SMM_DEPEX.\n\
                        if -s option is not given, \n\
                        EFI_SECTION_ALL is default section type.\n");
  fprintf (stdout, "  -c [Type], --compress [Type]\n\
                        Compress method type can be PI_NONE or PI_STD.\n\
                        if -c option is not given, PI_STD is default type.\n");
  fprintf (stdout, "  -g GuidValue, --vendor GuidValue\n\
                        GuidValue is one specific vendor guid value.\n\
                        Its format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n");
  fprintf (stdout, "  -l GuidHeaderLength, --HeaderLength GuidHeaderLength\n\
                        GuidHeaderLength is the size of header of guided data\n");
  fprintf (stdout, "  -r GuidAttr, --attributes GuidAttr\n\
                        GuidAttr is guid section attributes, which may be\n\
                        PROCESSING_REQUIRED, AUTH_STATUS_VALID and NONE. \n\
                        if -r option is not given, default PROCESSING_REQUIRED\n");
  fprintf (stdout, "  -n String, --name String\n\
                        String is a NULL terminated string used in Ui section.\n");
  fprintf (stdout, "  -j Number, --buildnumber Number\n\
                        Number is an integer value between 0 and 65535\n\
                        used in Ver section.\n");
  fprintf (stdout, "  --sectionalign SectionAlign\n\
                        SectionAlign points to section alignment, which support\n\
                        the alignment scope 0~16M. If SectionAlign is specified\n\
                        as 0, tool get alignment value from SectionFile. It is\n\
                        specified in same order that the section file is input.\n");
  fprintf (stdout, "  --dummy dummyfile\n\
                        compare dummyfile with input_file to decide whether\n\
                        need to set PROCESSING_REQUIRED attribute.\n");
  fprintf (stdout, "  -v, --verbose         Turn on verbose output with informational messages.\n");
  fprintf (stdout, "  -q, --quiet           Disable all messages except key message and fatal error\n");
  fprintf (stdout, "  -d, --debug level     Enable debug messages, at input debug level.\n");
  fprintf (stdout, "  --version             Show program's version number and exit.\n");
  fprintf (stdout, "  -h, --help            Show this help message and exit.\n");
}

VOID
Ascii2UnicodeString (
  CHAR8    *String,
  CHAR16   *UniString
  )
/*++

Routine Description:

  Write ascii string as unicode string format to FILE

Arguments:

  String      - Pointer to string that is written to FILE.
  UniString   - Pointer to unicode string

Returns:

  NULL

--*/
{
  while (*String != '\0') {
    *(UniString++) = (CHAR16) *(String++);
  }
  //
  // End the UniString with a NULL.
  //
  *UniString = '\0';
}

STATUS
GenSectionCommonLeafSection (
  CHAR8   **InputFileName,
  UINT32  InputFileNum,
  UINT8   SectionType,
  UINT8   **OutFileBuffer
  )
/*++

Routine Description:

  Generate a leaf section of type other than EFI_SECTION_VERSION
  and EFI_SECTION_USER_INTERFACE. Input file must be well formed.
  The function won't validate the input file's contents. For
  common leaf sections, the input file may be a binary file.
  The utility will add section header to the file.

Arguments:

  InputFileName  - Name of the input file.

  InputFileNum   - Number of input files. Should be 1 for leaf section.

  SectionType    - A valid section type string

  OutFileBuffer  - Buffer pointer to Output file contents

Returns:

  STATUS_ERROR            - can't continue
  STATUS_SUCCESS          - successful return

--*/
{
  UINT32                    InputFileLength;
  FILE                      *InFile;
  UINT8                     *Buffer;
  UINT32                    TotalLength;
  UINT32                    HeaderLength;
  EFI_COMMON_SECTION_HEADER *CommonSect;
  STATUS                    Status;

  if (InputFileNum > 1) {
    Error (NULL, 0, 2000, "Invalid parameter", "more than one input file specified");
    return STATUS_ERROR;
  } else if (InputFileNum < 1) {
    Error (NULL, 0, 2000, "Invalid parameter", "no input file specified");
    return STATUS_ERROR;
  }
  //
  // Open the input file
  //
  InFile = fopen (LongFilePath (InputFileName[0]), "rb");
  if (InFile == NULL) {
    Error (NULL, 0, 0001, "Error opening file", InputFileName[0]);
    return STATUS_ERROR;
  }

  Status  = STATUS_ERROR;
  Buffer  = NULL;
  //
  // Seek to the end of the input file so we can determine its size
  //
  fseek (InFile, 0, SEEK_END);
  InputFileLength = ftell (InFile);
  fseek (InFile, 0, SEEK_SET);
  DebugMsg (NULL, 0, 9, "Input file", "File name is %s and File size is %u bytes", InputFileName[0], (unsigned) InputFileLength);
  TotalLength     = sizeof (EFI_COMMON_SECTION_HEADER) + InputFileLength;
  //
  // Size must fit in 3 bytes
  //
  //if (TotalLength >= MAX_SECTION_SIZE) {
  //  Error (NULL, 0, 2000, "Invalid parameter", "%s file size (0x%X) exceeds section size limit(%uM).", InputFileName[0], (unsigned) TotalLength, MAX_SECTION_SIZE>>20);
  //  goto Done;
  //}
  HeaderLength = sizeof (EFI_COMMON_SECTION_HEADER);
  if (TotalLength >= MAX_SECTION_SIZE) {
    TotalLength = sizeof (EFI_COMMON_SECTION_HEADER2) + InputFileLength;
    HeaderLength = sizeof (EFI_COMMON_SECTION_HEADER2);
  }
  VerboseMsg ("the size of the created section file is %u bytes", (unsigned) TotalLength);
  //
  // Fill in the fields in the local section header structure
  //
  Buffer = (UINT8 *) malloc ((size_t) TotalLength);
  if (Buffer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
    goto Done;
  }
  CommonSect = (EFI_COMMON_SECTION_HEADER *) Buffer;
  CommonSect->Type     = SectionType;
  if (TotalLength < MAX_SECTION_SIZE) {
    CommonSect->Size[0]  = (UINT8) (TotalLength & 0xff);
    CommonSect->Size[1]  = (UINT8) ((TotalLength & 0xff00) >> 8);
    CommonSect->Size[2]  = (UINT8) ((TotalLength & 0xff0000) >> 16);
  } else {
    memset(CommonSect->Size, 0xff, sizeof(UINT8) * 3);
    ((EFI_COMMON_SECTION_HEADER2 *)CommonSect)->ExtendedSize = TotalLength;
  }

  //
  // read data from the input file.
  //
  if (InputFileLength != 0) {
    if (fread (Buffer + HeaderLength, (size_t) InputFileLength, 1, InFile) != 1) {
      Error (NULL, 0, 0004, "Error reading file", InputFileName[0]);
      goto Done;
    }
  }

  //
  // Set OutFileBuffer
  //
  *OutFileBuffer = Buffer;
  Status = STATUS_SUCCESS;

Done:
  fclose (InFile);

  return Status;
}

STATIC
EFI_STATUS
StringtoAlignment (
  IN  CHAR8  *AlignBuffer,
  OUT UINT32 *AlignNumber
  )
/*++

Routine Description:

  Converts Align String to align value (1~16M).

Arguments:

  AlignBuffer    - Pointer to Align string.
  AlignNumber    - Pointer to Align value.

Returns:

  EFI_SUCCESS             Successfully convert align string to align value.
  EFI_INVALID_PARAMETER   Align string is invalid or align value is not in scope.

--*/
{
  UINT32 Index = 0;
  //
  // Check AlignBuffer
  //
  if (AlignBuffer == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  for (Index = 0; Index < sizeof (mAlignName) / sizeof (CHAR8 *); Index ++) {
    if (stricmp (AlignBuffer, mAlignName [Index]) == 0) {
      *AlignNumber = 1 << Index;
      return EFI_SUCCESS;
    }
  }
  return EFI_INVALID_PARAMETER;
}

EFI_STATUS
GetSectionContents (
  CHAR8   **InputFileName,
  UINT32  *InputFileAlign,
  UINT32  InputFileNum,
  UINT8   *FileBuffer,
  UINT32  *BufferLength
  )
/*++

Routine Description:

  Get the contents of all section files specified in InputFileName
  into FileBuffer.

Arguments:

  InputFileName  - Name of the input file.

  InputFileAlign - Alignment required by the input file data.

  InputFileNum   - Number of input files. Should be at least 1.

  FileBuffer     - Output buffer to contain data

  BufferLength   - On input, this is size of the FileBuffer.
                   On output, this is the actual length of the data.

Returns:

  EFI_SUCCESS on successful return
  EFI_INVALID_PARAMETER if InputFileNum is less than 1 or BufferLength point is NULL.
  EFI_ABORTED if unable to open input file.
  EFI_BUFFER_TOO_SMALL FileBuffer is not enough to contain all file data.
--*/
{
  UINT32                     Size;
  UINT32                     Offset;
  UINT32                     FileSize;
  UINT32                     Index;
  FILE                       *InFile;
  EFI_COMMON_SECTION_HEADER  *SectHeader;
  EFI_COMMON_SECTION_HEADER2 TempSectHeader;
  EFI_TE_IMAGE_HEADER        TeHeader;
  UINT32                     TeOffset;
  EFI_GUID_DEFINED_SECTION   GuidSectHeader;
  EFI_GUID_DEFINED_SECTION2  GuidSectHeader2;
  UINT32                     HeaderSize;

  if (InputFileNum < 1) {
    Error (NULL, 0, 2000, "Invalid parameter", "must specify at least one input file");
    return EFI_INVALID_PARAMETER;
  }

  if (BufferLength == NULL) {
    Error (NULL, 0, 2000, "Invalid parameter", "BufferLength can't be NULL");
    return EFI_INVALID_PARAMETER;
  }

  Size          = 0;
  Offset        = 0;
  TeOffset      = 0;
  //
  // Go through our array of file names and copy their contents
  // to the output buffer.
  //
  for (Index = 0; Index < InputFileNum; Index++) {
    //
    // make sure section ends on a DWORD boundary
    //
    while ((Size & 0x03) != 0) {
      if (FileBuffer != NULL && Size < *BufferLength) {
        FileBuffer[Size] = 0;
      }
      Size++;
    }

    //
    // Open file and read contents
    //
    InFile = fopen (LongFilePath (InputFileName[Index]), "rb");
    if (InFile == NULL) {
      Error (NULL, 0, 0001, "Error opening file", InputFileName[Index]);
      return EFI_ABORTED;
    }

    fseek (InFile, 0, SEEK_END);
    FileSize = ftell (InFile);
    fseek (InFile, 0, SEEK_SET);
    DebugMsg (NULL, 0, 9, "Input files", "the input file name is %s and the size is %u bytes", InputFileName[Index], (unsigned) FileSize);
    //
    // Adjust section buffer when section alignment is required.
    //
    if (InputFileAlign != NULL) {
      //
      // Check this section is Te/Pe section, and Calculate the numbers of Te/Pe section.
      //
      TeOffset = 0;
      //
      // The section might be EFI_COMMON_SECTION_HEADER2
      // But only Type needs to be checked
      //
      if (FileSize >= MAX_SECTION_SIZE) {
        HeaderSize = sizeof (EFI_COMMON_SECTION_HEADER2);
      } else {
        HeaderSize = sizeof (EFI_COMMON_SECTION_HEADER);
      }
      fread (&TempSectHeader, 1, HeaderSize, InFile);
      if (TempSectHeader.Type == EFI_SECTION_TE) {
        fread (&TeHeader, 1, sizeof (TeHeader), InFile);
        if (TeHeader.Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
          TeOffset = TeHeader.StrippedSize - sizeof (TeHeader);
        }
      } else if (TempSectHeader.Type == EFI_SECTION_GUID_DEFINED) {
        fseek (InFile, 0, SEEK_SET);
        if (FileSize >= MAX_SECTION_SIZE) {
          fread (&GuidSectHeader2, 1, sizeof (GuidSectHeader2), InFile);
          if ((GuidSectHeader2.Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) {
            HeaderSize = GuidSectHeader2.DataOffset;
          }
        } else {
          fread (&GuidSectHeader, 1, sizeof (GuidSectHeader), InFile);
          if ((GuidSectHeader.Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) {
            HeaderSize = GuidSectHeader.DataOffset;
          }
        }
      }

      fseek (InFile, 0, SEEK_SET);

      //
      // Revert TeOffset to the converse value relative to Alignment
      // This is to assure the original PeImage Header at Alignment.
      //
      if (TeOffset != 0) {
        TeOffset = InputFileAlign [Index] - (TeOffset % InputFileAlign [Index]);
        TeOffset = TeOffset % InputFileAlign [Index];
      }

      //
      // make sure section data meet its alignment requirement by adding one raw pad section.
      //
      if ((InputFileAlign [Index] != 0) && (((Size + HeaderSize + TeOffset) % InputFileAlign [Index]) != 0)) {
        Offset = (Size + sizeof (EFI_COMMON_SECTION_HEADER) + HeaderSize + TeOffset + InputFileAlign [Index] - 1) & ~(InputFileAlign [Index] - 1);
        Offset = Offset - Size - HeaderSize - TeOffset;

        if (FileBuffer != NULL && ((Size + Offset) < *BufferLength)) {
          //
          // The maximal alignment is 64K, the raw section size must be less than 0xffffff
          //
          memset (FileBuffer + Size, 0, Offset);
          SectHeader          = (EFI_COMMON_SECTION_HEADER *) (FileBuffer + Size);
          SectHeader->Type    = EFI_SECTION_RAW;
          SectHeader->Size[0] = (UINT8) (Offset & 0xff);
          SectHeader->Size[1] = (UINT8) ((Offset & 0xff00) >> 8);
          SectHeader->Size[2] = (UINT8) ((Offset & 0xff0000) >> 16);
        }
        DebugMsg (NULL, 0, 9, "Pad raw section for section data alignment", "Pad Raw section size is %u", (unsigned) Offset);

        Size = Size + Offset;
      }
    }

    //
    // Now read the contents of the file into the buffer
    // Buffer must be enough to contain the file content.
    //
    if ((FileSize > 0) && (FileBuffer != NULL) && ((Size + FileSize) <= *BufferLength)) {
      if (fread (FileBuffer + Size, (size_t) FileSize, 1, InFile) != 1) {
        Error (NULL, 0, 0004, "Error reading file", InputFileName[Index]);
        fclose (InFile);
        return EFI_ABORTED;
      }
    }

    fclose (InFile);
    Size += FileSize;
  }

  //
  // Set the real required buffer size.
  //
  if (Size > *BufferLength) {
    *BufferLength = Size;
    return EFI_BUFFER_TOO_SMALL;
  } else {
    *BufferLength = Size;
    return EFI_SUCCESS;
  }
}

EFI_STATUS
GenSectionCompressionSection (
  CHAR8   **InputFileName,
  UINT32  *InputFileAlign,
  UINT32  InputFileNum,
  UINT8   SectCompSubType,
  UINT8   **OutFileBuffer
  )
/*++

Routine Description:

  Generate an encapsulating section of type EFI_SECTION_COMPRESSION
  Input file must be already sectioned. The function won't validate
  the input files' contents. Caller should hand in files already
  with section header.

Arguments:

  InputFileName  - Name of the input file.

  InputFileAlign - Alignment required by the input file data.

  InputFileNum   - Number of input files. Should be at least 1.

  SectCompSubType - Specify the compression algorithm requested.

  OutFileBuffer   - Buffer pointer to Output file contents

Returns:

  EFI_SUCCESS           on successful return
  EFI_INVALID_PARAMETER if InputFileNum is less than 1
  EFI_ABORTED           if unable to open input file.
  EFI_OUT_OF_RESOURCES  No resource to complete the operation.
--*/
{
  UINT32                  TotalLength;
  UINT32                  InputLength;
  UINT32                  CompressedLength;
  UINT32                  HeaderLength;
  UINT8                   *FileBuffer;
  UINT8                   *OutputBuffer;
  EFI_STATUS              Status;
  EFI_COMPRESSION_SECTION *CompressionSect;
  EFI_COMPRESSION_SECTION2 *CompressionSect2;
  COMPRESS_FUNCTION       CompressFunction;

  InputLength       = 0;
  FileBuffer        = NULL;
  OutputBuffer      = NULL;
  CompressedLength  = 0;
  TotalLength       = 0;
  //
  // read all input file contents into a buffer
  // first get the size of all file contents
  //
  Status = GetSectionContents (
            InputFileName,
            InputFileAlign,
            InputFileNum,
            FileBuffer,
            &InputLength
            );

  if (Status == EFI_BUFFER_TOO_SMALL) {
    FileBuffer = (UINT8 *) malloc (InputLength);
    if (FileBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
      return EFI_OUT_OF_RESOURCES;
    }
    //
    // read all input file contents into a buffer
    //
    Status = GetSectionContents (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              FileBuffer,
              &InputLength
              );
  }

  if (EFI_ERROR (Status)) {
    if (FileBuffer != NULL) {
      free (FileBuffer);
    }
    return Status;
  }

  if (FileBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  CompressFunction = NULL;

  //
  // Now data is in FileBuffer, compress the data
  //
  switch (SectCompSubType) {
  case EFI_NOT_COMPRESSED:
    CompressedLength = InputLength;
    HeaderLength = sizeof (EFI_COMPRESSION_SECTION);
    if (CompressedLength + HeaderLength >= MAX_SECTION_SIZE) {
      HeaderLength = sizeof (EFI_COMPRESSION_SECTION2);
    }
    TotalLength = CompressedLength + HeaderLength;
    //
    // Copy file buffer to the none compressed data.
    //
    OutputBuffer = malloc (TotalLength);
    if (OutputBuffer == NULL) {
      free (FileBuffer);
      return EFI_OUT_OF_RESOURCES;
    }
    memcpy (OutputBuffer + HeaderLength, FileBuffer, CompressedLength);
    free (FileBuffer);
    FileBuffer = OutputBuffer;
    break;

  case EFI_STANDARD_COMPRESSION:
    CompressFunction = (COMPRESS_FUNCTION) EfiCompress;
    break;

  default:
    Error (NULL, 0, 2000, "Invalid parameter", "unknown compression type");
    free (FileBuffer);
    return EFI_ABORTED;
  }

  if (CompressFunction != NULL) {

    Status = CompressFunction (FileBuffer, InputLength, OutputBuffer, &CompressedLength);
    if (Status == EFI_BUFFER_TOO_SMALL) {
      HeaderLength = sizeof (EFI_COMPRESSION_SECTION);
      if (CompressedLength + HeaderLength >= MAX_SECTION_SIZE) {
        HeaderLength = sizeof (EFI_COMPRESSION_SECTION2);
      }
      TotalLength = CompressedLength + HeaderLength;
      OutputBuffer = malloc (TotalLength);
      if (!OutputBuffer) {
        free (FileBuffer);
        return EFI_OUT_OF_RESOURCES;
      }

      Status = CompressFunction (FileBuffer, InputLength, OutputBuffer + HeaderLength, &CompressedLength);
    }

    free (FileBuffer);
    FileBuffer = OutputBuffer;

    if (EFI_ERROR (Status)) {
      if (FileBuffer != NULL) {
        free (FileBuffer);
      }

      return Status;
    }

    if (FileBuffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
  }

  DebugMsg (NULL, 0, 9, "comprss file size",
            "the original section size is %d bytes and the compressed section size is %u bytes", (unsigned) InputLength, (unsigned) CompressedLength);

  //if (TotalLength >= MAX_SECTION_SIZE) {
  //  Error (NULL, 0, 2000, "Invalid parameter", "The size of all files exceeds section size limit(%uM).", MAX_SECTION_SIZE>>20);
  //  if (FileBuffer != NULL) {
  //    free (FileBuffer);
  //  }
  //  if (OutputBuffer != NULL) {
  //    free (OutputBuffer);
  //  }
  //  return STATUS_ERROR;
  //}
  VerboseMsg ("the size of the created section file is %u bytes", (unsigned) TotalLength);

  //
  // Add the section header for the compressed data
  //
  if (TotalLength >= MAX_SECTION_SIZE) {
    CompressionSect2 = (EFI_COMPRESSION_SECTION2 *)FileBuffer;

    memset(CompressionSect2->CommonHeader.Size, 0xff, sizeof(UINT8) * 3);
    CompressionSect2->CommonHeader.Type         = EFI_SECTION_COMPRESSION;
    CompressionSect2->CommonHeader.ExtendedSize = TotalLength;
    CompressionSect2->CompressionType           = SectCompSubType;
    CompressionSect2->UncompressedLength        = InputLength;
  } else {
    CompressionSect = (EFI_COMPRESSION_SECTION *) FileBuffer;

    CompressionSect->CommonHeader.Type     = EFI_SECTION_COMPRESSION;
    CompressionSect->CommonHeader.Size[0]  = (UINT8) (TotalLength & 0xff);
    CompressionSect->CommonHeader.Size[1]  = (UINT8) ((TotalLength & 0xff00) >> 8);
    CompressionSect->CommonHeader.Size[2]  = (UINT8) ((TotalLength & 0xff0000) >> 16);
    CompressionSect->CompressionType       = SectCompSubType;
    CompressionSect->UncompressedLength    = InputLength;
  }

  //
  // Set OutFileBuffer
  //
  *OutFileBuffer = FileBuffer;

  return EFI_SUCCESS;
}

EFI_STATUS
GenSectionGuidDefinedSection (
  CHAR8    **InputFileName,
  UINT32   *InputFileAlign,
  UINT32   InputFileNum,
  EFI_GUID *VendorGuid,
  UINT16   DataAttribute,
  UINT32   DataHeaderSize,
  UINT8    **OutFileBuffer
  )
/*++

Routine Description:

  Generate an encapsulating section of type EFI_SECTION_GUID_DEFINED
  Input file must be already sectioned. The function won't validate
  the input files' contents. Caller should hand in files already
  with section header.

Arguments:

  InputFileName - Name of the input file.

  InputFileAlign - Alignment required by the input file data.

  InputFileNum  - Number of input files. Should be at least 1.

  VendorGuid    - Specify vendor guid value.

  DataAttribute - Specify attribute for the vendor guid data.

  DataHeaderSize- Guided Data Header Size

  OutFileBuffer   - Buffer pointer to Output file contents

Returns:

  EFI_SUCCESS on successful return
  EFI_INVALID_PARAMETER if InputFileNum is less than 1
  EFI_ABORTED if unable to open input file.
  EFI_OUT_OF_RESOURCES  No resource to complete the operation.

--*/
{
  UINT32                TotalLength;
  UINT32                InputLength;
  UINT32                Offset;
  UINT8                 *FileBuffer;
  UINT32                Crc32Checksum;
  EFI_STATUS            Status;
  CRC32_SECTION_HEADER  *Crc32GuidSect;
  CRC32_SECTION_HEADER2  *Crc32GuidSect2;
  EFI_GUID_DEFINED_SECTION  *VendorGuidSect;
  EFI_GUID_DEFINED_SECTION2  *VendorGuidSect2;

  InputLength = 0;
  Offset      = 0;
  FileBuffer  = NULL;
  TotalLength = 0;

  //
  // read all input file contents into a buffer
  // first get the size of all file contents
  //
  Status = GetSectionContents (
            InputFileName,
            InputFileAlign,
            InputFileNum,
            FileBuffer,
            &InputLength
            );

  if (Status == EFI_BUFFER_TOO_SMALL) {
    if (CompareGuid (VendorGuid, &mZeroGuid) == 0) {
      Offset = sizeof (CRC32_SECTION_HEADER);
      if (InputLength + Offset >= MAX_SECTION_SIZE) {
        Offset = sizeof (CRC32_SECTION_HEADER2);
      }
    } else {
      Offset = sizeof (EFI_GUID_DEFINED_SECTION);
      if (InputLength + Offset >= MAX_SECTION_SIZE) {
        Offset = sizeof (EFI_GUID_DEFINED_SECTION2);
      }
    }
    TotalLength = InputLength + Offset;

    FileBuffer = (UINT8 *) malloc (InputLength + Offset);
    if (FileBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
      return EFI_OUT_OF_RESOURCES;
    }
    //
    // read all input file contents into a buffer
    //
    Status = GetSectionContents (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              FileBuffer + Offset,
              &InputLength
              );
  }

  if (EFI_ERROR (Status)) {
    if (FileBuffer != NULL) {
      free (FileBuffer);
    }
    Error (NULL, 0, 0001, "Error opening file for reading", InputFileName[0]);
    return Status;
  }

  if (InputLength == 0) {
    if (FileBuffer != NULL) {
      free (FileBuffer);
    }
    Error (NULL, 0, 2000, "Invalid parameter", "the size of input file %s can't be zero", *InputFileName);
    return EFI_NOT_FOUND;
  }

  //
  // InputLength != 0, but FileBuffer == NULL means out of resources.
  //
  if (FileBuffer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Now data is in FileBuffer + Offset
  //
  if (CompareGuid (VendorGuid, &mZeroGuid) == 0) {
    //
    // Default Guid section is CRC32.
    //
    Crc32Checksum = 0;
    CalculateCrc32 (FileBuffer + Offset, InputLength, &Crc32Checksum);

    if (TotalLength >= MAX_SECTION_SIZE) {
      Crc32GuidSect2 = (CRC32_SECTION_HEADER2 *) FileBuffer;
      Crc32GuidSect2->GuidSectionHeader.CommonHeader.Type     = EFI_SECTION_GUID_DEFINED;
      Crc32GuidSect2->GuidSectionHeader.CommonHeader.Size[0]  = (UINT8) 0xff;
      Crc32GuidSect2->GuidSectionHeader.CommonHeader.Size[1]  = (UINT8) 0xff;
      Crc32GuidSect2->GuidSectionHeader.CommonHeader.Size[2]  = (UINT8) 0xff;
      Crc32GuidSect2->GuidSectionHeader.CommonHeader.ExtendedSize = TotalLength;
      memcpy (&(Crc32GuidSect2->GuidSectionHeader.SectionDefinitionGuid), &mEfiCrc32SectionGuid, sizeof (EFI_GUID));
      Crc32GuidSect2->GuidSectionHeader.Attributes  = EFI_GUIDED_SECTION_AUTH_STATUS_VALID;
      Crc32GuidSect2->GuidSectionHeader.DataOffset  = sizeof (CRC32_SECTION_HEADER2);
      Crc32GuidSect2->CRC32Checksum                 = Crc32Checksum;
      DebugMsg (NULL, 0, 9, "Guided section", "Data offset is %u", Crc32GuidSect2->GuidSectionHeader.DataOffset);
    } else {
      Crc32GuidSect = (CRC32_SECTION_HEADER *) FileBuffer;
      Crc32GuidSect->GuidSectionHeader.CommonHeader.Type     = EFI_SECTION_GUID_DEFINED;
      Crc32GuidSect->GuidSectionHeader.CommonHeader.Size[0]  = (UINT8) (TotalLength & 0xff);
      Crc32GuidSect->GuidSectionHeader.CommonHeader.Size[1]  = (UINT8) ((TotalLength & 0xff00) >> 8);
      Crc32GuidSect->GuidSectionHeader.CommonHeader.Size[2]  = (UINT8) ((TotalLength & 0xff0000) >> 16);
      memcpy (&(Crc32GuidSect->GuidSectionHeader.SectionDefinitionGuid), &mEfiCrc32SectionGuid, sizeof (EFI_GUID));
      Crc32GuidSect->GuidSectionHeader.Attributes  = EFI_GUIDED_SECTION_AUTH_STATUS_VALID;
      Crc32GuidSect->GuidSectionHeader.DataOffset  = sizeof (CRC32_SECTION_HEADER);
      Crc32GuidSect->CRC32Checksum                 = Crc32Checksum;
      DebugMsg (NULL, 0, 9, "Guided section", "Data offset is %u", Crc32GuidSect->GuidSectionHeader.DataOffset);
    }
  } else {
    if (TotalLength >= MAX_SECTION_SIZE) {
      VendorGuidSect2 = (EFI_GUID_DEFINED_SECTION2 *) FileBuffer;
      VendorGuidSect2->CommonHeader.Type     = EFI_SECTION_GUID_DEFINED;
      VendorGuidSect2->CommonHeader.Size[0]  = (UINT8) 0xff;
      VendorGuidSect2->CommonHeader.Size[1]  = (UINT8) 0xff;
      VendorGuidSect2->CommonHeader.Size[2]  = (UINT8) 0xff;
      VendorGuidSect2->CommonHeader.ExtendedSize = InputLength + sizeof (EFI_GUID_DEFINED_SECTION2);
      memcpy (&(VendorGuidSect2->SectionDefinitionGuid), VendorGuid, sizeof (EFI_GUID));
      VendorGuidSect2->Attributes  = DataAttribute;
      VendorGuidSect2->DataOffset  = (UINT16) (sizeof (EFI_GUID_DEFINED_SECTION2) + DataHeaderSize);
      DebugMsg (NULL, 0, 9, "Guided section", "Data offset is %u", VendorGuidSect2->DataOffset);
    } else {
      VendorGuidSect = (EFI_GUID_DEFINED_SECTION *) FileBuffer;
      VendorGuidSect->CommonHeader.Type     = EFI_SECTION_GUID_DEFINED;
      VendorGuidSect->CommonHeader.Size[0]  = (UINT8) (TotalLength & 0xff);
      VendorGuidSect->CommonHeader.Size[1]  = (UINT8) ((TotalLength & 0xff00) >> 8);
      VendorGuidSect->CommonHeader.Size[2]  = (UINT8) ((TotalLength & 0xff0000) >> 16);
      memcpy (&(VendorGuidSect->SectionDefinitionGuid), VendorGuid, sizeof (EFI_GUID));
      VendorGuidSect->Attributes  = DataAttribute;
      VendorGuidSect->DataOffset  = (UINT16) (sizeof (EFI_GUID_DEFINED_SECTION) + DataHeaderSize);
      DebugMsg (NULL, 0, 9, "Guided section", "Data offset is %u", VendorGuidSect->DataOffset);
    }
  }
  VerboseMsg ("the size of the created section file is %u bytes", (unsigned) TotalLength);

  //
  // Set OutFileBuffer
  //
  *OutFileBuffer = FileBuffer;

  return EFI_SUCCESS;
}

EFI_STATUS
GenSectionSubtypeGuidSection (
  CHAR8    **InputFileName,
  UINT32   *InputFileAlign,
  UINT32   InputFileNum,
  EFI_GUID *SubTypeGuid,
  UINT8    **OutFileBuffer
  )
/*++

Routine Description:

  Generate a section of type EFI_SECTION_FREEFORM_SUBTYPE_GUID
  The function won't validate the input file contents.
  The utility will add section header to the file.

Arguments:

  InputFileName - Name of the input file.

  InputFileAlign - Alignment required by the input file data.

  InputFileNum - Number of input files. Should be 1 for this section.

  SubTypeGuid - Specify vendor guid value.

  OutFileBuffer   - Buffer pointer to Output file contents

Returns:

  EFI_SUCCESS on successful return
  EFI_INVALID_PARAMETER if InputFileNum is less than 1
  EFI_ABORTED if unable to open input file.
  EFI_OUT_OF_RESOURCES  No resource to complete the operation.

--*/
{
  UINT32                TotalLength;
  UINT32                InputLength;
  UINT32                Offset;
  UINT8                 *FileBuffer;
  EFI_STATUS            Status;
  EFI_FREEFORM_SUBTYPE_GUID_SECTION  *SubtypeGuidSect;
  EFI_FREEFORM_SUBTYPE_GUID_SECTION2  *SubtypeGuidSect2;


  InputLength = 0;
  Offset      = 0;
  FileBuffer  = NULL;
  TotalLength = 0;

  if (InputFileNum > 1) {
    Error (NULL, 0, 2000, "Invalid parameter", "more than one input file specified");
    return STATUS_ERROR;
  } else if (InputFileNum < 1) {
    Error (NULL, 0, 2000, "Invalid parameter", "no input file specified");
    return STATUS_ERROR;
  }

  //
  // read all input file contents into a buffer
  // first get the size of all file contents
  //
  Status = GetSectionContents (
            InputFileName,
            InputFileAlign,
            InputFileNum,
            FileBuffer,
            &InputLength
            );

  if (Status == EFI_BUFFER_TOO_SMALL) {
    Offset = sizeof (EFI_FREEFORM_SUBTYPE_GUID_SECTION);
    if (InputLength + Offset >= MAX_SECTION_SIZE) {
      Offset = sizeof (EFI_FREEFORM_SUBTYPE_GUID_SECTION2);
    }
    TotalLength = InputLength + Offset;

    FileBuffer = (UINT8 *) malloc (InputLength + Offset);
    if (FileBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
      return EFI_OUT_OF_RESOURCES;
    }
    //
    // read all input file contents into a buffer
    //
    Status = GetSectionContents (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              FileBuffer + Offset,
              &InputLength
              );
  }

  if (EFI_ERROR (Status)) {
    if (FileBuffer != NULL) {
      free (FileBuffer);
    }
    Error (NULL, 0, 0001, "Error opening file for reading", InputFileName[0]);
    return Status;
  }

  if (InputLength == 0) {
    if (FileBuffer != NULL) {
      free (FileBuffer);
    }
    Error (NULL, 0, 2000, "Invalid parameter", "the size of input file %s can't be zero", *InputFileName);
    return EFI_NOT_FOUND;
  }

  //
  // InputLength != 0, but FileBuffer == NULL means out of resources.
  //
  if (FileBuffer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Now data is in FileBuffer + Offset
  //
  if (TotalLength >= MAX_SECTION_SIZE) {
    SubtypeGuidSect2 = (EFI_FREEFORM_SUBTYPE_GUID_SECTION2 *) FileBuffer;
    SubtypeGuidSect2->CommonHeader.Type     = EFI_SECTION_FREEFORM_SUBTYPE_GUID;
    SubtypeGuidSect2->CommonHeader.Size[0]  = (UINT8) 0xff;
    SubtypeGuidSect2->CommonHeader.Size[1]  = (UINT8) 0xff;
    SubtypeGuidSect2->CommonHeader.Size[2]  = (UINT8) 0xff;
    SubtypeGuidSect2->CommonHeader.ExtendedSize = InputLength + sizeof (EFI_FREEFORM_SUBTYPE_GUID_SECTION2);
    memcpy (&(SubtypeGuidSect2->SubTypeGuid), SubTypeGuid, sizeof (EFI_GUID));
  } else {
    SubtypeGuidSect = (EFI_FREEFORM_SUBTYPE_GUID_SECTION *) FileBuffer;
    SubtypeGuidSect->CommonHeader.Type     = EFI_SECTION_FREEFORM_SUBTYPE_GUID;
    SubtypeGuidSect->CommonHeader.Size[0]  = (UINT8) (TotalLength & 0xff);
    SubtypeGuidSect->CommonHeader.Size[1]  = (UINT8) ((TotalLength & 0xff00) >> 8);
    SubtypeGuidSect->CommonHeader.Size[2]  = (UINT8) ((TotalLength & 0xff0000) >> 16);
    memcpy (&(SubtypeGuidSect->SubTypeGuid), SubTypeGuid, sizeof (EFI_GUID));
  }

  VerboseMsg ("the size of the created section file is %u bytes", (unsigned) TotalLength);

  //
  // Set OutFileBuffer
  //
  *OutFileBuffer = FileBuffer;

  return EFI_SUCCESS;
}

EFI_STATUS
FfsRebaseImageRead (
    IN      VOID    *FileHandle,
    IN      UINTN   FileOffset,
    IN OUT  UINT32  *ReadSize,
    OUT     VOID    *Buffer
    )
  /*++

    Routine Description:

    Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file

    Arguments:

   FileHandle - The handle to the PE/COFF file

   FileOffset - The offset, in bytes, into the file to read

   ReadSize   - The number of bytes to read from the file starting at FileOffset

   Buffer     - A pointer to the buffer to read the data into.

   Returns:

   EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset

   --*/
{
  CHAR8   *Destination8;
  CHAR8   *Source8;
  UINT32  Length;

  Destination8  = Buffer;
  Source8       = (CHAR8 *) ((UINTN) FileHandle + FileOffset);
  Length        = *ReadSize;
  while (Length--) {
    *(Destination8++) = *(Source8++);
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
GetAlignmentFromFile(char *InFile, UINT32 *Alignment)
  /*
    InFile is input file for getting alignment
    return the alignment
    */
{
  FILE                           *InFileHandle;
  UINT8                          *PeFileBuffer;
  UINTN                          PeFileSize;
  UINT32                         CurSecHdrSize;
  PE_COFF_LOADER_IMAGE_CONTEXT   ImageContext;
  EFI_COMMON_SECTION_HEADER      *CommonHeader;
  EFI_STATUS                     Status;

  InFileHandle        = NULL;
  PeFileBuffer        = NULL;
  *Alignment          = 0;

  memset (&ImageContext, 0, sizeof (ImageContext));

  InFileHandle = fopen(LongFilePath(InFile), "rb");
  if (InFileHandle == NULL){
    Error (NULL, 0, 0001, "Error opening file", InFile);
    return EFI_ABORTED;
  }
  PeFileSize = _filelength (fileno(InFileHandle));
  PeFileBuffer = (UINT8 *) malloc (PeFileSize);
  if (PeFileBuffer == NULL) {
    fclose (InFileHandle);
    Error(NULL, 0, 4001, "Resource", "memory cannot be allocated for %s", InFile);
    return EFI_OUT_OF_RESOURCES;
  }
  fread (PeFileBuffer, sizeof (UINT8), PeFileSize, InFileHandle);
  fclose (InFileHandle);
  CommonHeader = (EFI_COMMON_SECTION_HEADER *) PeFileBuffer;
  CurSecHdrSize = GetSectionHeaderLength(CommonHeader);
  ImageContext.Handle = (VOID *) ((UINTN)PeFileBuffer + CurSecHdrSize);
  ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)FfsRebaseImageRead;
  Status               = PeCoffLoaderGetImageInfo(&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid PeImage", "The input file is %s and return status is %x", InFile, (int) Status);
    return Status;
   }
  *Alignment = ImageContext.SectionAlignment;
  // Free the allocated memory resource
  if (PeFileBuffer != NULL) {
    free (PeFileBuffer);
    PeFileBuffer = NULL;
  }
  return EFI_SUCCESS;
}

int
main (
  int  argc,
  char *argv[]
  )
/*++

Routine Description:

  Main

Arguments:

  command line parameters

Returns:

  EFI_SUCCESS    Section header successfully generated and section concatenated.
  EFI_ABORTED    Could not generate the section
  EFI_OUT_OF_RESOURCES  No resource to complete the operation.

--*/
{
  UINT32                    Index;
  UINT32                    InputFileNum;
  FILE                      *OutFile;
  CHAR8                     **InputFileName;
  CHAR8                     *OutputFileName;
  CHAR8                     *SectionName;
  CHAR8                     *CompressionName;
  CHAR8                     *StringBuffer;
  EFI_GUID                  VendorGuid = mZeroGuid;
  int                       VersionNumber;
  UINT8                     SectType;
  UINT8                     SectCompSubType;
  UINT16                    SectGuidAttribute;
  UINT64                    SectGuidHeaderLength;
  EFI_VERSION_SECTION       *VersionSect;
  EFI_USER_INTERFACE_SECTION *UiSect;
  UINT32                    InputLength;
  UINT8                     *OutFileBuffer;
  EFI_STATUS                Status;
  UINT64                    LogLevel;
  UINT32                    *InputFileAlign;
  UINT32                    InputFileAlignNum;
  EFI_COMMON_SECTION_HEADER *SectionHeader;
  CHAR8                     *DummyFileName;
  FILE                      *DummyFile;
  UINTN                     DummyFileSize;
  UINT8                     *DummyFileBuffer;
  FILE                      *InFile;
  UINT8                     *InFileBuffer;
  UINTN                     InFileSize;

  InputFileAlign        = NULL;
  InputFileAlignNum     = 0;
  InputFileName         = NULL;
  OutputFileName        = NULL;
  SectionName           = NULL;
  CompressionName       = NULL;
  StringBuffer          = "";
  OutFile               = NULL;
  VersionNumber         = 0;
  InputFileNum          = 0;
  SectType              = EFI_SECTION_ALL;
  SectCompSubType       = 0;
  SectGuidAttribute     = EFI_GUIDED_SECTION_NONE;
  OutFileBuffer         = NULL;
  InputLength           = 0;
  Status                = STATUS_SUCCESS;
  LogLevel              = 0;
  SectGuidHeaderLength  = 0;
  VersionSect           = NULL;
  UiSect                = NULL;
  DummyFileSize         = 0;
  DummyFileName         = NULL;
  DummyFile             = NULL;
  DummyFileBuffer       = NULL;
  InFile                = NULL;
  InFileSize            = 0;
  InFileBuffer          = NULL;

  SetUtilityName (UTILITY_NAME);

  if (argc == 1) {
    Error (NULL, 0, 1001, "Missing options", "No options input");
    Usage ();
    return STATUS_ERROR;
  }

  //
  // Parse command line
  //
  argc --;
  argv ++;

  if ((stricmp (argv[0], "-h") == 0) || (stricmp (argv[0], "--help") == 0)) {
    Version ();
    Usage ();
    return STATUS_SUCCESS;
  }

  if (stricmp (argv[0], "--version") == 0) {
    Version ();
    return STATUS_SUCCESS;
  }

  while (argc > 0) {
    if ((stricmp (argv[0], "-s") == 0) || (stricmp (argv[0], "--SectionType") == 0)) {
      SectionName = argv[1];
      if (SectionName == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "Section Type can't be NULL");
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-o") == 0) || (stricmp (argv[0], "--outputfile") == 0)) {
      OutputFileName = argv[1];
      if (OutputFileName == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "Output file can't be NULL");
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-c") == 0) || (stricmp (argv[0], "--compress") == 0)) {
      CompressionName = argv[1];
      if (CompressionName == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "Compression Type can't be NULL");
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-g") == 0) || (stricmp (argv[0], "--vendor") == 0)) {
      Status = StringToGuid (argv[1], &VendorGuid);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }
    if (stricmp (argv[0], "--dummy") == 0) {
      DummyFileName = argv[1];
      if (DummyFileName == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "Dummy file can't be NULL");
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-r") == 0) || (stricmp (argv[0], "--attributes") == 0)) {
      if (argv[1] == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "Guid section attributes can't be NULL");
        goto Finish;
      }
      if (stricmp (argv[1], mGUIDedSectionAttribue[EFI_GUIDED_SECTION_PROCESSING_REQUIRED]) == 0) {
        SectGuidAttribute |= EFI_GUIDED_SECTION_PROCESSING_REQUIRED;
      } else if (stricmp (argv[1], mGUIDedSectionAttribue[EFI_GUIDED_SECTION_AUTH_STATUS_VALID]) == 0) {
        SectGuidAttribute |= EFI_GUIDED_SECTION_AUTH_STATUS_VALID;
      } else if (stricmp (argv[1], mGUIDedSectionAttribue[0]) == 0) {
        //
        // NONE attribute
        //
        SectGuidAttribute |= EFI_GUIDED_SECTION_NONE;
      } else {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-l") == 0) || (stricmp (argv[0], "--HeaderLength") == 0)) {
      Status = AsciiStringToUint64 (argv[1], FALSE, &SectGuidHeaderLength);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 1003, "Invalid option value for GuidHeaderLength", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-n") == 0) || (stricmp (argv[0], "--name") == 0)) {
      StringBuffer = argv[1];
      if (StringBuffer == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "Name can't be NULL");
        goto Finish;
      }
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-j") == 0) || (stricmp (argv[0], "--buildnumber") == 0)) {
      if (argv[1] == NULL) {
        Error (NULL, 0, 1003, "Invalid option value", "build number can't be NULL");
        goto Finish;
      }
      //
      // Verify string is a integrator number
      //
      for (Index = 0; Index < strlen (argv[1]); Index++) {
        if ((argv[1][Index] != '-') && (isdigit ((int)argv[1][Index]) == 0)) {
          Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
          goto Finish;
        }
      }

      sscanf (argv[1], "%d", &VersionNumber);
      argc -= 2;
      argv += 2;
      continue;
    }

    if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) {
      SetPrintLevel (VERBOSE_LOG_LEVEL);
      VerboseMsg ("Verbose output Mode Set!");
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) {
      SetPrintLevel (KEY_LOG_LEVEL);
      KeyMsg ("Quiet output Mode Set!");
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) {
      Status = AsciiStringToUint64 (argv[1], FALSE, &LogLevel);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        goto Finish;
      }
      if (LogLevel > 9) {
        Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0~9, current input level is %d", (int) LogLevel);
        goto Finish;
      }
      SetPrintLevel (LogLevel);
      DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[1]);
      argc -= 2;
      argv += 2;
      continue;
    }

    //
    // Section File alignment requirement
    //
    if (stricmp (argv[0], "--sectionalign") == 0) {
      if (InputFileAlignNum == 0) {
        InputFileAlign = (UINT32 *) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32));
        if (InputFileAlign == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          goto Finish;
        }
        memset (InputFileAlign, 1, MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32));
      } else if (InputFileAlignNum % MAXIMUM_INPUT_FILE_NUM == 0) {
        InputFileAlign = (UINT32 *) realloc (
          InputFileAlign,
          (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (UINT32)
          );

        if (InputFileAlign == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          goto Finish;
        }
        memset (&(InputFileAlign[InputFileNum]), 1, (MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32)));
      }
      if (stricmp(argv[1], "0") == 0) {
        InputFileAlign[InputFileAlignNum] = 0;
      } else {
        Status = StringtoAlignment (argv[1], &(InputFileAlign[InputFileAlignNum]));
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
          goto Finish;
        }
      }
      argc -= 2;
      argv += 2;
      InputFileAlignNum ++;
      continue;
    }

    //
    // Get Input file name
    //
    if ((InputFileNum == 0) && (InputFileName == NULL)) {
      InputFileName = (CHAR8 **) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *));
      if (InputFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
        goto Finish;
      }
      memset (InputFileName, 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)));
    } else if (InputFileNum % MAXIMUM_INPUT_FILE_NUM == 0) {
      //
      // InputFileName buffer too small, need to realloc
      //
      InputFileName = (CHAR8 **) realloc (
                                  InputFileName,
                                  (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (CHAR8 *)
                                  );

      if (InputFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
        goto Finish;
      }
      memset (&(InputFileName[InputFileNum]), 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)));
    }

    InputFileName[InputFileNum++] = argv[0];
    argc --;
    argv ++;
  }

  if (InputFileAlignNum > 0 && InputFileAlignNum != InputFileNum) {
    Error (NULL, 0, 1003, "Invalid option", "section alignment must be set for each section");
    goto Finish;
  }
  for (Index = 0; Index < InputFileAlignNum; Index++)
  {
    if (InputFileAlign[Index] == 0) {
      Status = GetAlignmentFromFile(InputFileName[Index], &(InputFileAlign[Index]));
      if (EFI_ERROR(Status)) {
        Error (NULL, 0, 1003, "Fail to get Alignment from %s", InputFileName[InputFileNum]);
        goto Finish;
      }
    }
  }

  VerboseMsg ("%s tool start.", UTILITY_NAME);

  if (DummyFileName != NULL) {
      //
      // Open file and read contents
      //
      DummyFile = fopen (LongFilePath (DummyFileName), "rb");
      if (DummyFile == NULL) {
        Error (NULL, 0, 0001, "Error opening file", DummyFileName);
        goto Finish;
      }

      fseek (DummyFile, 0, SEEK_END);
      DummyFileSize = ftell (DummyFile);
      fseek (DummyFile, 0, SEEK_SET);
      DummyFileBuffer = (UINT8 *) malloc (DummyFileSize);
      if (DummyFileBuffer == NULL) {
        fclose(DummyFile);
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
        goto Finish;
      }

      fread(DummyFileBuffer, 1, DummyFileSize, DummyFile);
      fclose(DummyFile);
      DebugMsg (NULL, 0, 9, "Dummy files", "the dummy file name is %s and the size is %u bytes", DummyFileName, (unsigned) DummyFileSize);

      if (InputFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
        goto Finish;
      }
      InFile = fopen(LongFilePath(InputFileName[0]), "rb");
      if (InFile == NULL) {
        Error (NULL, 0, 0001, "Error opening file", InputFileName[0]);
        goto Finish;
      }

      fseek (InFile, 0, SEEK_END);
      InFileSize = ftell (InFile);
      fseek (InFile, 0, SEEK_SET);
      InFileBuffer = (UINT8 *) malloc (InFileSize);
      if (InFileBuffer == NULL) {
        fclose(InFile);
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
        goto Finish;
      }

      fread(InFileBuffer, 1, InFileSize, InFile);
      fclose(InFile);
      DebugMsg (NULL, 0, 9, "Input files", "the input file name is %s and the size is %u bytes", InputFileName[0], (unsigned) InFileSize);
      if (InFileSize > DummyFileSize){
        if (stricmp((CHAR8 *)DummyFileBuffer, (CHAR8 *)(InFileBuffer + (InFileSize - DummyFileSize))) == 0){
          SectGuidHeaderLength = InFileSize - DummyFileSize;
        }
      }
      if (SectGuidHeaderLength == 0) {
        SectGuidAttribute |= EFI_GUIDED_SECTION_PROCESSING_REQUIRED;
      }
      if (DummyFileBuffer != NULL) {
        free (DummyFileBuffer);
        DummyFileBuffer = NULL;
      }
      if (InFileBuffer != NULL) {
        free (InFileBuffer);
      }
    }

  //
  // Parse all command line parameters to get the corresponding section type.
  //
  VerboseMsg ("Section type is %s", SectionName);
  if (SectionName == NULL) {
    //
    // No specified Section type, default is SECTION_ALL.
    //
    SectType = EFI_SECTION_ALL;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_COMPRESSION]) == 0) {
    SectType     = EFI_SECTION_COMPRESSION;
    if (CompressionName == NULL) {
      //
      // Default is PI_STD compression algorithm.
      //
      SectCompSubType = EFI_STANDARD_COMPRESSION;
    } else if (stricmp (CompressionName, mCompressionTypeName[EFI_NOT_COMPRESSED]) == 0) {
      SectCompSubType = EFI_NOT_COMPRESSED;
    } else if (stricmp (CompressionName, mCompressionTypeName[EFI_STANDARD_COMPRESSION]) == 0) {
      SectCompSubType = EFI_STANDARD_COMPRESSION;
    } else {
      Error (NULL, 0, 1003, "Invalid option value", "--compress = %s", CompressionName);
      goto Finish;
    }
    VerboseMsg ("Compress method is %s", mCompressionTypeName [SectCompSubType]);
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_GUID_DEFINED]) == 0) {
    SectType     = EFI_SECTION_GUID_DEFINED;

    if ((SectGuidAttribute & EFI_GUIDED_SECTION_NONE) != 0) {
      //
      // NONE attribute, clear attribute value.
      //
      SectGuidAttribute = SectGuidAttribute & ~EFI_GUIDED_SECTION_NONE;
    }
    VerboseMsg ("Vendor Guid is %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
                (unsigned) VendorGuid.Data1,
                VendorGuid.Data2,
                VendorGuid.Data3,
                VendorGuid.Data4[0],
                VendorGuid.Data4[1],
                VendorGuid.Data4[2],
                VendorGuid.Data4[3],
                VendorGuid.Data4[4],
                VendorGuid.Data4[5],
                VendorGuid.Data4[6],
                VendorGuid.Data4[7]);
    if ((SectGuidAttribute & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) != 0) {
      VerboseMsg ("Guid Attribute is %s", mGUIDedSectionAttribue[EFI_GUIDED_SECTION_PROCESSING_REQUIRED]);
    }
    if ((SectGuidAttribute & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0) {
      VerboseMsg ("Guid Attribute is %s", mGUIDedSectionAttribue[EFI_GUIDED_SECTION_AUTH_STATUS_VALID]);
    }
    if (SectGuidHeaderLength != 0) {
      VerboseMsg ("Guid Data Header size is 0x%llx", (unsigned long long) SectGuidHeaderLength);
    }
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_PE32]) == 0) {
    SectType = EFI_SECTION_PE32;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_PIC]) == 0) {
    SectType = EFI_SECTION_PIC;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_TE]) == 0) {
    SectType = EFI_SECTION_TE;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_DXE_DEPEX]) == 0) {
    SectType = EFI_SECTION_DXE_DEPEX;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_SMM_DEPEX]) == 0) {
    SectType = EFI_SECTION_SMM_DEPEX;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_VERSION]) == 0) {
    SectType = EFI_SECTION_VERSION;
    if (VersionNumber < 0 || VersionNumber > 65535) {
      Error (NULL, 0, 1003, "Invalid option value", "%d is not in 0~65535", VersionNumber);
      goto Finish;
    }
    VerboseMsg ("Version section number is %d", VersionNumber);
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_USER_INTERFACE]) == 0) {
    SectType = EFI_SECTION_USER_INTERFACE;
    if (StringBuffer[0] == '\0') {
      Error (NULL, 0, 1001, "Missing option", "user interface string");
      goto Finish;
    }
    VerboseMsg ("UI section string name is %s", StringBuffer);
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_COMPATIBILITY16]) == 0) {
    SectType = EFI_SECTION_COMPATIBILITY16;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_FIRMWARE_VOLUME_IMAGE]) == 0) {
    SectType = EFI_SECTION_FIRMWARE_VOLUME_IMAGE;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_FREEFORM_SUBTYPE_GUID]) == 0) {
    SectType = EFI_SECTION_FREEFORM_SUBTYPE_GUID;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_RAW]) == 0) {
    SectType = EFI_SECTION_RAW;
  } else if (stricmp (SectionName, mSectionTypeName[EFI_SECTION_PEI_DEPEX]) == 0) {
    SectType = EFI_SECTION_PEI_DEPEX;
  } else {
    Error (NULL, 0, 1003, "Invalid option value", "SectionType = %s", SectionName);
    goto Finish;
  }

  //
  // GuidValue is only required by Guided section and SubtypeGuid section.
  //
  if ((SectType != EFI_SECTION_GUID_DEFINED) && (SectType != EFI_SECTION_FREEFORM_SUBTYPE_GUID) &&
    (SectionName != NULL) &&
    (CompareGuid (&VendorGuid, &mZeroGuid) != 0)) {
    fprintf (stdout, "Warning: the input guid value is not required for this section type %s\n", SectionName);
  }

  //
  // Check whether there is GUID for the SubtypeGuid section
  //
  if ((SectType == EFI_SECTION_FREEFORM_SUBTYPE_GUID) && (CompareGuid (&VendorGuid, &mZeroGuid) == 0)) {
    Error (NULL, 0, 1001, "Missing options", "GUID");
    goto Finish;
  }

  //
  // Check whether there is input file
  //
  if ((SectType != EFI_SECTION_VERSION) && (SectType != EFI_SECTION_USER_INTERFACE)) {
    //
    // The input file are required for other section type.
    //
    if (InputFileNum == 0) {
      Error (NULL, 0, 1001, "Missing options", "Input files");
      goto Finish;
    }
  }
  //
  // Check whether there is output file
  //
  for (Index = 0; Index < InputFileNum; Index ++) {
    VerboseMsg ("the %uth input file name is %s", (unsigned) Index, InputFileName[Index]);
  }
  if (OutputFileName == NULL) {
    Error (NULL, 0, 1001, "Missing options", "Output file");
    goto Finish;
    // OutFile = stdout;
  }
  VerboseMsg ("Output file name is %s", OutputFileName);

  //
  // At this point, we've fully validated the command line, and opened appropriate
  // files, so let's go and do what we've been asked to do...
  //
  //
  // Within this switch, build and write out the section header including any
  // section type specific pieces.  If there's an input file, it's tacked on later
  //
  switch (SectType) {
  case EFI_SECTION_COMPRESSION:
    if (InputFileAlign != NULL) {
      free (InputFileAlign);
      InputFileAlign = NULL;
    }
    Status = GenSectionCompressionSection (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              SectCompSubType,
              &OutFileBuffer
              );
    break;

  case EFI_SECTION_GUID_DEFINED:
    if (InputFileAlign != NULL && (CompareGuid (&VendorGuid, &mZeroGuid) != 0)) {
      //
      // Only process alignment for the default known CRC32 guided section.
      // For the unknown guided section, the alignment is processed when the dummy all section (EFI_SECTION_ALL) is generated.
      //
      free (InputFileAlign);
      InputFileAlign = NULL;
    }
    Status = GenSectionGuidDefinedSection (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              &VendorGuid,
              SectGuidAttribute,
              (UINT32) SectGuidHeaderLength,
              &OutFileBuffer
              );
    break;

  case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
    Status = GenSectionSubtypeGuidSection (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              &VendorGuid,
              &OutFileBuffer
              );
    break;

  case EFI_SECTION_VERSION:
    Index           = sizeof (EFI_COMMON_SECTION_HEADER);
    //
    // 2 bytes for the build number UINT16
    //
    Index += 2;
    //
    // StringBuffer is ascii.. unicode is 2X + 2 bytes for terminating unicode null.
    //
    Index += (strlen (StringBuffer) * 2) + 2;
    OutFileBuffer = (UINT8 *) malloc (Index);
    if (OutFileBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
      goto Finish;
    }
    VersionSect = (EFI_VERSION_SECTION *) OutFileBuffer;
    VersionSect->CommonHeader.Type     = SectType;
    VersionSect->CommonHeader.Size[0]  = (UINT8) (Index & 0xff);
    VersionSect->CommonHeader.Size[1]  = (UINT8) ((Index & 0xff00) >> 8);
    VersionSect->CommonHeader.Size[2]  = (UINT8) ((Index & 0xff0000) >> 16);
    VersionSect->BuildNumber           = (UINT16) VersionNumber;
    Ascii2UnicodeString (StringBuffer, VersionSect->VersionString);
    VerboseMsg ("the size of the created section file is %u bytes", (unsigned) Index);
    break;

  case EFI_SECTION_USER_INTERFACE:
    Index           = sizeof (EFI_COMMON_SECTION_HEADER);
    //
    // StringBuffer is ascii.. unicode is 2X + 2 bytes for terminating unicode null.
    //
    Index += (strlen (StringBuffer) * 2) + 2;
    OutFileBuffer = (UINT8 *) malloc (Index);
    if (OutFileBuffer == NULL) {
      Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
      goto Finish;
    }
    UiSect = (EFI_USER_INTERFACE_SECTION *) OutFileBuffer;
    UiSect->CommonHeader.Type     = SectType;
    UiSect->CommonHeader.Size[0]  = (UINT8) (Index & 0xff);
    UiSect->CommonHeader.Size[1]  = (UINT8) ((Index & 0xff00) >> 8);
    UiSect->CommonHeader.Size[2]  = (UINT8) ((Index & 0xff0000) >> 16);
    Ascii2UnicodeString (StringBuffer, UiSect->FileNameString);
    VerboseMsg ("the size of the created section file is %u bytes", (unsigned) Index);
   break;

  case EFI_SECTION_ALL:
    //
    // read all input file contents into a buffer
    // first get the size of all file contents
    //
    Status = GetSectionContents (
              InputFileName,
              InputFileAlign,
              InputFileNum,
              OutFileBuffer,
              &InputLength
              );

    if (Status == EFI_BUFFER_TOO_SMALL) {
      OutFileBuffer = (UINT8 *) malloc (InputLength);
      if (OutFileBuffer == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated");
        goto Finish;
      }
      //
      // read all input file contents into a buffer
      //
      Status = GetSectionContents (
                InputFileName,
                InputFileAlign,
                InputFileNum,
                OutFileBuffer,
                &InputLength
                );
    }
    VerboseMsg ("the size of the created section file is %u bytes", (unsigned) InputLength);
    break;
  default:
    //
    // All other section types are caught by default (they're all the same)
    //
    Status = GenSectionCommonLeafSection (
              InputFileName,
              InputFileNum,
              SectType,
              &OutFileBuffer
              );
    break;
  }

  if (Status != EFI_SUCCESS || OutFileBuffer == NULL) {
    Error (NULL, 0, 2000, "Status is not successful", "Status value is 0x%X", (int) Status);
    goto Finish;
  }

  //
  // Get output file length
  //
  if (SectType != EFI_SECTION_ALL) {
    SectionHeader = (EFI_COMMON_SECTION_HEADER *)OutFileBuffer;
    InputLength = *(UINT32 *)SectionHeader->Size & 0x00ffffff;
    if (InputLength == 0xffffff) {
      InputLength = ((EFI_COMMON_SECTION_HEADER2 *)SectionHeader)->ExtendedSize;
    }
  }

  //
  // Write the output file
  //
  OutFile = fopen (LongFilePath (OutputFileName), "wb");
  if (OutFile == NULL) {
    Error (NULL, 0, 0001, "Error opening file for writing", OutputFileName);
    goto Finish;
  }

  fwrite (OutFileBuffer, InputLength, 1, OutFile);

Finish:
  if (InputFileName != NULL) {
    free (InputFileName);
  }

  if (InputFileAlign != NULL) {
    free (InputFileAlign);
  }

  if (OutFileBuffer != NULL) {
    free (OutFileBuffer);
  }

  if (OutFile != NULL) {
    fclose (OutFile);
  }

  if (DummyFileBuffer != NULL) {
    free (DummyFileBuffer);
  }

  VerboseMsg ("%s tool done with return code is 0x%x.", UTILITY_NAME, GetUtilityStatus ());

  return GetUtilityStatus ();
}
