/** @file
The tool dumps the contents of a firmware volume

Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2022, Konstantin Aladyshev <aladyshev22@gmail.com><BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#ifdef __GNUC__
#include <unistd.h>
#else
#include <direct.h>
#endif

#include <FvLib.h>
#include <Common/UefiBaseTypes.h>
#include <Common/UefiCapsule.h>
#include <Common/PiFirmwareFile.h>
#include <Common/PiFirmwareVolume.h>
#include <Guid/FirmwareFileSystem2.h>
#include <Common/GuidedSectionExtractionEx.h>

#include "Compress.h"
#include "Decompress.h"
#include "VolInfo.h"
#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#include "FirmwareVolumeBufferLib.h"
#include "OsPath.h"
#include "ParseGuidedSectionTools.h"
#include "StringFuncs.h"
#include "ParseInf.h"

#include "../../../ImageTool/ImageToolEmit.h"

//
// Utility global variables
//

EFI_GUID  gEfiCrc32GuidedSectionExtractionProtocolGuid = EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID;
EFI_GUID  gPeiAprioriFileNameGuid = { 0x1b45cc0a, 0x156a, 0x428a, { 0XAF, 0x62,  0x49, 0x86, 0x4d, 0xa0, 0xe6, 0xe6 }};
EFI_GUID  gAprioriGuid = { 0xFC510EE7, 0xFFDC, 0x11D4, { 0xBD, 0x41, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};

#define UTILITY_MAJOR_VERSION      1
#define UTILITY_MINOR_VERSION      0

#define UTILITY_NAME         "VolInfo"

#define EFI_SECTION_ERROR EFIERR (100)

//
// Structure to keep a list of guid-to-basenames
//
typedef struct _GUID_TO_BASENAME {
  struct _GUID_TO_BASENAME  *Next;
  INT8                      Guid[PRINTED_GUID_BUFFER_SIZE];
  INT8                      BaseName[MAX_LINE_LEN];
} GUID_TO_BASENAME;

static GUID_TO_BASENAME *mGuidBaseNameList = NULL;

//
// Store GUIDed Section guid->tool mapping
//
EFI_HANDLE mParsedGuidedSectionTools = NULL;

CHAR8* mUtilityFilename = NULL;

BOOLEAN EnableHash = FALSE;
CHAR8 *OpenSslPath = NULL;

EFI_STATUS
ParseGuidBaseNameFile (
  CHAR8    *FileName
  );

EFI_STATUS
FreeGuidBaseNameList (
  VOID
  );

EFI_STATUS
PrintGuidName (
  IN UINT8    *GuidStr
  );

EFI_STATUS
ParseSection (
  IN UINT8  *SectionBuffer,
  IN UINT32 BufferLength
  );

EFI_STATUS
DumpDepexSection (
  IN UINT8    *Ptr,
  IN UINT32   SectionLength
  );

STATIC
EFI_STATUS
ReadHeader (
  IN FILE       *InputFile,
  OUT UINT32    *FvSize,
  OUT BOOLEAN   *ErasePolarity
  );

STATIC
EFI_STATUS
PrintAprioriFile (
  EFI_FFS_FILE_HEADER         *FileHeader
  );

STATIC
EFI_STATUS
PrintFileInfo (
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage,
  EFI_FFS_FILE_HEADER         *FileHeader,
  BOOLEAN                     ErasePolarity
  );

static
EFI_STATUS
PrintFvInfo (
  IN VOID                         *Fv,
  IN BOOLEAN                      IsChildFv
  );

static
VOID
LoadGuidedSectionToolsTxt (
  IN CHAR8* FirmwareVolumeFilename
  );

EFI_STATUS
CombinePath (
  IN  CHAR8* DefaultPath,
  IN  CHAR8* AppendPath,
  OUT CHAR8* NewPath
);

void
Usage (
  VOID
  );

UINT32
UnicodeStrLen (
  IN CHAR16 *String
  )
  /*++

  Routine Description:

  Returns the length of a null-terminated unicode string.

  Arguments:

    String - The pointer to a null-terminated unicode string.

  Returns:

    N/A

  --*/
{
  UINT32  Length;

  for (Length = 0; *String != L'\0'; String++, Length++) {
    ;
  }
  return Length;
}

VOID
Unicode2AsciiString (
  IN  CHAR16 *Source,
  OUT CHAR8  *Destination
  )
  /*++

  Routine Description:

  Convert a null-terminated unicode string to a null-terminated ascii string.

  Arguments:

    Source      - The pointer to the null-terminated input unicode string.
    Destination - The pointer to the null-terminated output ascii string.

  Returns:

    N/A

  --*/
{
  while (*Source != '\0') {
    *(Destination++) = (CHAR8) *(Source++);
  }
  //
  // End the ascii with a NULL.
  //
  *Destination = '\0';
}

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

Routine Description:

  GC_TODO: Add function description

Arguments:

  argc  - GC_TODO: add argument description
  ]     - GC_TODO: add argument description

Returns:

  GC_TODO: add return values

--*/
{
  FILE                        *InputFile;
  int                         BytesRead;
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage;
  UINT32                      FvSize;
  EFI_STATUS                  Status;
  int                         Offset;
  BOOLEAN                     ErasePolarity;
  UINT64                      LogLevel;
  CHAR8                       *OpenSslEnv;
  CHAR8                       *OpenSslCommand;

  SetUtilityName (UTILITY_NAME);
  //
  // Print utility header
  //
  printf ("%s Version %d.%d Build %s\n",
    UTILITY_NAME,
    UTILITY_MAJOR_VERSION,
    UTILITY_MINOR_VERSION,
    __BUILD_VERSION
    );

  if (argc == 1) {
    Usage ();
    return -1;
  }

  argc--;
  argv++;
  LogLevel = 0;
  Offset = 0;

  //
  // Look for help options
  //
  if ((strcmp(argv[0], "-h") == 0) || (strcmp(argv[0], "--help") == 0) ||
      (strcmp(argv[0], "-?") == 0) || (strcmp(argv[0], "/?") == 0)) {
    Usage();
    return  STATUS_SUCCESS;
  }
  //
  // Version has already be printed, so just return success
  //
  if (strcmp(argv[0], "--version") == 0) {
    return  STATUS_SUCCESS;
  }

  //
  // If they specified -x xref guid/basename cross-reference files, process it.
  // This will print the basename beside each file guid. To use it, specify
  // -x xref_filename to processdsc, then use xref_filename as a parameter
  // here.
  //
  while (argc > 0) {
    if ((strcmp(argv[0], "-x") == 0) || (strcmp(argv[0], "--xref") == 0)) {
      ParseGuidBaseNameFile (argv[1]);
      printf("ParseGuidBaseNameFile: %s\n", argv[1]);
      argc -= 2;
      argv += 2;
      continue;
    }
    if (strcmp(argv[0], "--offset") == 0) {
      //
      // Hex or decimal?
      //
      if ((argv[1][0] == '0') && (tolower ((int)argv[1][1]) == 'x')) {
        if (sscanf (argv[1], "%x", &Offset) != 1) {
          Error (NULL, 0, 1003, "Invalid option value", "Offset = %s", argv[1]);
          return GetUtilityStatus ();
        }
      } else {
        if (sscanf (argv[1], "%d", &Offset) != 1) {
          Error (NULL, 0, 1003, "Invalid option value", "Offset = %s", argv[1]);
          return GetUtilityStatus ();
        }
        //
        // See if they said something like "64K"
        //
        if (tolower ((int)argv[1][strlen (argv[1]) - 1]) == 'k') {
          Offset *= 1024;
        }
      }

      argc -= 2;
      argv += 2;
      continue;
    }
    if ((stricmp (argv[0], "--hash") == 0)) {
      if (EnableHash == TRUE) {
        //
        // --hash already given in the option, ignore this one
        //
        argc --;
        argv ++;
        continue;
      }
      EnableHash = TRUE;
      OpenSslCommand = "openssl";
      OpenSslEnv = getenv("OPENSSL_PATH");
      if (OpenSslEnv == NULL) {
        OpenSslPath = OpenSslCommand;
      } else {
        //
        // We add quotes to the Openssl Path in case it has space characters
        //
        OpenSslPath = malloc(2+strlen(OpenSslEnv)+strlen(OpenSslCommand)+1);
        if (OpenSslPath == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return GetUtilityStatus ();
        }
        CombinePath(OpenSslEnv, OpenSslCommand, OpenSslPath);
      }
      if (OpenSslPath == NULL){
        Error (NULL, 0, 3000, "Open SSL command not available.  Please verify PATH or set OPENSSL_PATH.", NULL);
        return GetUtilityStatus ();
      }
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) {
      SetPrintLevel (VERBOSE_LOG_LEVEL);
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) {
      SetPrintLevel (KEY_LOG_LEVEL);
      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]);
        return -1;
      }
      if (LogLevel > 9) {
        Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, current input level is %d", (int) LogLevel);
        return -1;
      }
      SetPrintLevel (LogLevel);
      DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[1]);
      argc -= 2;
      argv += 2;
      continue;
    }

    mUtilityFilename = argv[0];
    argc --;
    argv ++;
  }

  //
  // Open the file containing the FV
  //
  if (mUtilityFilename == NULL) {
    Error (NULL, 0, 1001, "Missing option", "Input files are not specified");
    return GetUtilityStatus ();
  }
  InputFile = fopen (LongFilePath (mUtilityFilename), "rb");
  if (InputFile == NULL) {
    Error (NULL, 0, 0001, "Error opening the input file", mUtilityFilename);
    return GetUtilityStatus ();
  }
  //
  // Skip over pad bytes if specified. This is used if they prepend 0xff
  // data to the FV image binary.
  //
  if (Offset != 0) {
    fseek (InputFile, Offset, SEEK_SET);
  }
  //
  // Determine size of FV
  //
  Status = ReadHeader (InputFile, &FvSize, &ErasePolarity);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 0003, "error parsing FV image", "%s Header is invalid", mUtilityFilename);
    fclose (InputFile);
    return GetUtilityStatus ();
  }
  //
  // Allocate a buffer for the FV image
  //
  FvImage = malloc (FvSize);
  if (FvImage == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    fclose (InputFile);
    return GetUtilityStatus ();
  }
  //
  // Seek to the start of the image, then read the entire FV to the buffer
  //
  fseek (InputFile, Offset, SEEK_SET);
  BytesRead = fread (FvImage, 1, FvSize, InputFile);
  fclose (InputFile);
  if ((unsigned int) BytesRead != FvSize) {
    Error (NULL, 0, 0004, "error reading FvImage from", mUtilityFilename);
    free (FvImage);
    return GetUtilityStatus ();
  }

  LoadGuidedSectionToolsTxt (mUtilityFilename);

  PrintFvInfo (FvImage, FALSE);

  //
  // Clean up
  //
  free (FvImage);
  FreeGuidBaseNameList ();
  return GetUtilityStatus ();
}


static
EFI_STATUS
PrintFvInfo (
  IN VOID                         *Fv,
  IN BOOLEAN                      IsChildFv
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  Fv            - Firmware Volume to print information about
  IsChildFv     - Flag specifies whether the input FV is a child FV.

Returns:

  EFI_STATUS

--*/
{
  EFI_STATUS                  Status;
  UINTN                       NumberOfFiles;
  BOOLEAN                     ErasePolarity;
  UINTN                       FvSize;
  EFI_FFS_FILE_HEADER         *CurrentFile;
  UINTN                       Key;

  Status = FvBufGetSize (Fv, &FvSize);

  NumberOfFiles = 0;
  ErasePolarity =
    (((EFI_FIRMWARE_VOLUME_HEADER*)Fv)->Attributes & EFI_FVB2_ERASE_POLARITY) ?
      TRUE : FALSE;

  //
  // Get the first file
  //
  Key = 0;
  Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 0003, "error parsing FV image", "cannot find the first file in the FV image");
    return GetUtilityStatus ();
  }
  //
  // Display information about files found
  //
  while (CurrentFile != NULL) {
    //
    // Increment the number of files counter
    //
    NumberOfFiles++;

    //
    // Display info about this file
    //
    Status = PrintFileInfo (Fv, CurrentFile, ErasePolarity);
    if (EFI_ERROR (Status)) {
      Error (NULL, 0, 0003, "error parsing FV image", "failed to parse a file in the FV");
      return GetUtilityStatus ();
    }
    //
    // Get the next file
    //
    Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
    if (Status == EFI_NOT_FOUND) {
      CurrentFile = NULL;
    } else if (EFI_ERROR (Status)) {
      Error (NULL, 0, 0003, "error parsing FV image", "cannot find the next file in the FV image");
      return GetUtilityStatus ();
    }
  }

  if (IsChildFv) {
    printf ("There are a total of %d files in the child FV\n", (int) NumberOfFiles);
  } else {
    printf ("There are a total of %d files in this FV\n", (int) NumberOfFiles);
  }

  return EFI_SUCCESS;
}

UINT32
GetOccupiedSize (
  IN UINT32  ActualSize,
  IN UINT32  Alignment
  )
/*++

Routine Description:

  This function returns the next larger size that meets the alignment
  requirement specified.

Arguments:

  ActualSize      The size.
  Alignment       The desired alignment.

Returns:

  EFI_SUCCESS             Function completed successfully.
  EFI_ABORTED             The function encountered an error.

--*/
{
  UINT32  OccupiedSize;

  OccupiedSize = ActualSize;
  while ((OccupiedSize & (Alignment - 1)) != 0) {
    OccupiedSize++;
  }

  return OccupiedSize;
}

static
CHAR8 *
SectionNameToStr (
  IN EFI_SECTION_TYPE   Type
  )
/*++

Routine Description:

  Converts EFI Section names to Strings

Arguments:

  Type  - The EFI Section type

Returns:

  CHAR8* - Pointer to the String containing the section name.

--*/
{
  CHAR8 *SectionStr;
  CHAR8 *SectionTypeStringTable[] = {
    //
    // 0X00
    //
    "EFI_SECTION_ALL",
    //
    // 0x01
    //
    "EFI_SECTION_COMPRESSION",
    //
    // 0x02
    //
    "EFI_SECTION_GUID_DEFINED",
    //
    // 0x03
    //
    "Unknown section type - Reserved 0x03",
    //
    // 0x04
    //
    "Unknown section type - Reserved 0x04",
    //
    // 0x05
    //
    "Unknown section type - Reserved 0x05",
    //
    // 0x06
    //
    "Unknown section type - Reserved 0x06",
    //
    // 0x07
    //
    "Unknown section type - Reserved 0x07",
    //
    // 0x08
    //
    "Unknown section type - Reserved 0x08",
    //
    // 0x09
    //
    "Unknown section type - Reserved 0x09",
    //
    // 0x0A
    //
    "Unknown section type - Reserved 0x0A",
    //
    // 0x0B
    //
    "Unknown section type - Reserved 0x0B",
    //
    // 0x0C
    //
    "Unknown section type - Reserved 0x0C",
    //
    // 0x0D
    //
    "Unknown section type - Reserved 0x0D",
    //
    // 0x0E
    //
    "Unknown section type - Reserved 0x0E",
    //
    // 0x0F
    //
    "Unknown section type - Reserved 0x0E",
    //
    // 0x10
    //
    "EFI_SECTION_PE32",
    //
    // 0x11
    //
    "EFI_SECTION_PIC",
    //
    // 0x12
    //
    "Unsupported section type - formerly TE",
    //
    // 0x13
    //
    "EFI_SECTION_DXE_DEPEX",
    //
    // 0x14
    //
    "EFI_SECTION_VERSION",
    //
    // 0x15
    //
    "EFI_SECTION_USER_INTERFACE",
    //
    // 0x16
    //
    "EFI_SECTION_COMPATIBILITY16",
    //
    // 0x17
    //
    "EFI_SECTION_FIRMWARE_VOLUME_IMAGE",
    //
    // 0x18
    //
    "EFI_SECTION_FREEFORM_SUBTYPE_GUID",
    //
    // 0x19
    //
    "EFI_SECTION_RAW",
    //
    // 0x1A
    //
    "Unknown section type - 0x1A",
    //
    // 0x1B
    //
    "EFI_SECTION_PEI_DEPEX",
    //
    // 0x1C
    //
    "EFI_SECTION_MM_DEPEX",
    //
    // 0x1C+
    //
    "Unknown section type - Reserved - beyond last defined section"
  };

  if (Type > EFI_SECTION_LAST_SECTION_TYPE) {
    Type = EFI_SECTION_LAST_SECTION_TYPE + 1;
  }

  SectionStr = malloc (100);
  if (SectionStr == NULL) {
    printf ("Error: Out of memory resources.\n");
    return SectionStr;
  }
  strcpy (SectionStr, SectionTypeStringTable[Type]);
  return SectionStr;
}

STATIC
EFI_STATUS
ReadHeader (
  IN FILE       *InputFile,
  OUT UINT32    *FvSize,
  OUT BOOLEAN   *ErasePolarity
  )
/*++

Routine Description:

  This function determines the size of the FV and the erase polarity.  The
  erase polarity is the FALSE value for file state.

Arguments:

  InputFile       The file that contains the FV image.
  FvSize          The size of the FV.
  ErasePolarity   The FV erase polarity.

Returns:

  EFI_SUCCESS             Function completed successfully.
  EFI_INVALID_PARAMETER   A required parameter was NULL or is out of range.
  EFI_ABORTED             The function encountered an error.

--*/
{
  EFI_FIRMWARE_VOLUME_HEADER  VolumeHeader;
  EFI_FV_BLOCK_MAP_ENTRY      BlockMap;
  UINTN                       Signature[2];
  UINTN                       BytesRead;
  UINT32                      Size;
  size_t                      ReadSize;

  BytesRead = 0;
  Size      = 0;
  //
  // Check input parameters
  //
  if (InputFile == NULL || FvSize == NULL || ErasePolarity == NULL) {
    Error (__FILE__, __LINE__, 0, "application error", "invalid parameter to function");
    return EFI_INVALID_PARAMETER;
  }
  //
  // Read the header
  //
  ReadSize = fread (&VolumeHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
  if (ReadSize != 1) {
    return EFI_ABORTED;
  }
  BytesRead     = sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY);
  Signature[0]  = VolumeHeader.Signature;
  Signature[1]  = 0;

  //
  // Print FV header information
  //
  printf ("Signature:        %s (%X)\n", (char *) Signature, (unsigned) VolumeHeader.Signature);
  printf ("Attributes:       %X\n", (unsigned) VolumeHeader.Attributes);

  if (VolumeHeader.Attributes & EFI_FVB2_READ_DISABLED_CAP) {
    printf ("       EFI_FVB2_READ_DISABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_READ_ENABLED_CAP) {
    printf ("       EFI_FVB2_READ_ENABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_READ_STATUS) {
    printf ("       EFI_FVB2_READ_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_DISABLED_CAP) {
    printf ("       EFI_FVB2_WRITE_DISABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_ENABLED_CAP) {
    printf ("       EFI_FVB2_WRITE_ENABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_STATUS) {
    printf ("       EFI_FVB2_WRITE_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_LOCK_CAP) {
    printf ("       EFI_FVB2_LOCK_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_LOCK_STATUS) {
    printf ("       EFI_FVB2_LOCK_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_STICKY_WRITE) {
    printf ("       EFI_FVB2_STICKY_WRITE\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_MEMORY_MAPPED) {
    printf ("       EFI_FVB2_MEMORY_MAPPED\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ERASE_POLARITY) {
    printf ("       EFI_FVB2_ERASE_POLARITY\n");
    *ErasePolarity = TRUE;
  }

#if (PI_SPECIFICATION_VERSION < 0x00010000)
  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT) {
    printf ("       EFI_FVB2_ALIGNMENT\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2) {
    printf ("       EFI_FVB2_ALIGNMENT_2\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4) {
    printf ("       EFI_FVB2_ALIGNMENT_4\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8) {
    printf ("       EFI_FVB2_ALIGNMENT_8\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16) {
    printf ("       EFI_FVB2_ALIGNMENT_16\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32) {
    printf ("       EFI_FVB2_ALIGNMENT_32\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64) {
    printf ("        EFI_FVB2_ALIGNMENT_64\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128) {
    printf ("        EFI_FVB2_ALIGNMENT_128\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_256) {
    printf ("        EFI_FVB2_ALIGNMENT_256\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_512) {
    printf ("        EFI_FVB2_ALIGNMENT_512\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1K) {
    printf ("        EFI_FVB2_ALIGNMENT_1K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2K) {
    printf ("        EFI_FVB2_ALIGNMENT_2K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4K) {
    printf ("        EFI_FVB2_ALIGNMENT_4K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8K) {
    printf ("        EFI_FVB2_ALIGNMENT_8K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16K) {
    printf ("        EFI_FVB2_ALIGNMENT_16K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32K) {
    printf ("        EFI_FVB2_ALIGNMENT_32K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64K) {
    printf ("        EFI_FVB2_ALIGNMENT_64K\n");
  }

#else

  if (VolumeHeader.Attributes & EFI_FVB2_READ_LOCK_CAP) {
    printf ("       EFI_FVB2_READ_LOCK_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_READ_LOCK_STATUS) {
    printf ("       EFI_FVB2_READ_LOCK_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_LOCK_CAP) {
    printf ("       EFI_FVB2_WRITE_LOCK_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_LOCK_STATUS) {
    printf ("       EFI_FVB2_WRITE_LOCK_STATUS\n");
  }

  switch (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT) {
    case EFI_FVB2_ALIGNMENT_1:
    printf ("       EFI_FVB2_ALIGNMENT_1\n");
    break;

    case EFI_FVB2_ALIGNMENT_2:
    printf ("       EFI_FVB2_ALIGNMENT_2\n");
    break;

    case EFI_FVB2_ALIGNMENT_4:
    printf ("       EFI_FVB2_ALIGNMENT_4\n");
    break;

    case EFI_FVB2_ALIGNMENT_8:
    printf ("       EFI_FVB2_ALIGNMENT_8\n");
    break;

    case EFI_FVB2_ALIGNMENT_16:
    printf ("       EFI_FVB2_ALIGNMENT_16\n");
    break;

    case EFI_FVB2_ALIGNMENT_32:
    printf ("       EFI_FVB2_ALIGNMENT_32\n");
    break;

    case EFI_FVB2_ALIGNMENT_64:
    printf ("       EFI_FVB2_ALIGNMENT_64\n");
    break;

    case EFI_FVB2_ALIGNMENT_128:
    printf ("       EFI_FVB2_ALIGNMENT_128\n");
    break;

    case EFI_FVB2_ALIGNMENT_256:
    printf ("       EFI_FVB2_ALIGNMENT_256\n");
    break;

    case EFI_FVB2_ALIGNMENT_512:
    printf ("       EFI_FVB2_ALIGNMENT_512\n");
    break;

    case EFI_FVB2_ALIGNMENT_1K:
    printf ("       EFI_FVB2_ALIGNMENT_1K\n");
    break;

    case EFI_FVB2_ALIGNMENT_2K:
    printf ("       EFI_FVB2_ALIGNMENT_2K\n");
    break;

    case EFI_FVB2_ALIGNMENT_4K:
    printf ("       EFI_FVB2_ALIGNMENT_4K\n");
    break;

    case EFI_FVB2_ALIGNMENT_8K:
    printf ("       EFI_FVB2_ALIGNMENT_8K\n");
    break;

    case EFI_FVB2_ALIGNMENT_16K:
    printf ("       EFI_FVB2_ALIGNMENT_16K\n");
    break;

    case EFI_FVB2_ALIGNMENT_32K:
    printf ("       EFI_FVB2_ALIGNMENT_32K\n");
    break;

    case EFI_FVB2_ALIGNMENT_64K:
    printf ("       EFI_FVB2_ALIGNMENT_64K\n");
    break;

    case EFI_FVB2_ALIGNMENT_128K:
    printf ("       EFI_FVB2_ALIGNMENT_128K\n");
    break;

    case EFI_FVB2_ALIGNMENT_256K:
    printf ("       EFI_FVB2_ALIGNMENT_256K\n");
    break;

    case EFI_FVB2_ALIGNMENT_512K:
    printf ("       EFI_FVB2_ALIGNMENT_512K\n");
    break;

    case EFI_FVB2_ALIGNMENT_1M:
    printf ("       EFI_FVB2_ALIGNMENT_1M\n");
    break;

    case EFI_FVB2_ALIGNMENT_2M:
    printf ("       EFI_FVB2_ALIGNMENT_2M\n");
    break;

    case EFI_FVB2_ALIGNMENT_4M:
    printf ("       EFI_FVB2_ALIGNMENT_4M\n");
    break;

    case EFI_FVB2_ALIGNMENT_8M:
    printf ("       EFI_FVB2_ALIGNMENT_8M\n");
    break;

    case EFI_FVB2_ALIGNMENT_16M:
    printf ("       EFI_FVB2_ALIGNMENT_16M\n");
    break;

    case EFI_FVB2_ALIGNMENT_32M:
    printf ("       EFI_FVB2_ALIGNMENT_32M\n");
    break;

    case EFI_FVB2_ALIGNMENT_64M:
    printf ("       EFI_FVB2_ALIGNMENT_64M\n");
    break;

    case EFI_FVB2_ALIGNMENT_128M:
    printf ("       EFI_FVB2_ALIGNMENT_128M\n");
    break;

    case EFI_FVB2_ALIGNMENT_256M:
    printf ("       EFI_FVB2_ALIGNMENT_256M\n");
    break;

    case EFI_FVB2_ALIGNMENT_512M:
    printf ("       EFI_FVB2_ALIGNMENT_512M\n");
    break;

    case EFI_FVB2_ALIGNMENT_1G:
    printf ("       EFI_FVB2_ALIGNMENT_1G\n");
    break;

    case EFI_FVB2_ALIGNMENT_2G:
    printf ("       EFI_FVB2_ALIGNMENT_2G\n");
    break;
  }

#endif
  printf ("Header Length:         0x%08X\n", VolumeHeader.HeaderLength);
  printf ("File System ID:        ");
  PrintGuid (&VolumeHeader.FileSystemGuid);
  //
  // printf ("\n");
  //
  printf ("Revision:              0x%04X\n", VolumeHeader.Revision);

  do {
    ReadSize = fread (&BlockMap, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
    if (ReadSize != 1) {
      return EFI_ABORTED;
    }
    BytesRead += sizeof (EFI_FV_BLOCK_MAP_ENTRY);

    if (BlockMap.NumBlocks != 0) {
      printf ("Number of Blocks:      0x%08X\n", (unsigned) BlockMap.NumBlocks);
      printf ("Block Length:          0x%08X\n", (unsigned) BlockMap.Length);
      Size += BlockMap.NumBlocks * BlockMap.Length;
    }

  } while (!(BlockMap.NumBlocks == 0 && BlockMap.Length == 0));

  if (BytesRead != VolumeHeader.HeaderLength) {
    printf ("ERROR: Header length not consistent with Block Maps!\n");
    return EFI_ABORTED;
  }

  if (VolumeHeader.FvLength != Size) {
    printf ("ERROR: Volume Size not consistent with Block Maps!\n");
    return EFI_ABORTED;
  }

  printf ("Total Volume Size:     0x%08X\n", (unsigned) Size);

  *FvSize = Size;

  //
  // rewind (InputFile);
  //
  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
PrintAprioriFile (
  EFI_FFS_FILE_HEADER         *FileHeader
  )
/*++

Routine Description:

  Print GUIDs from the APRIORI file

Arguments:

  FileHeader - The file header

Returns:

  EFI_SUCCESS       - The APRIORI file was parsed correctly
  EFI_SECTION_ERROR - Problem with file parsing

--*/
{
  UINT8               GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
  UINT32              HeaderSize;

  HeaderSize = FvBufGetFfsHeaderSize (FileHeader);

  if (FileHeader->Type != EFI_FV_FILETYPE_FREEFORM)
    return EFI_SECTION_ERROR;

  EFI_COMMON_SECTION_HEADER* SectionHeader = (EFI_COMMON_SECTION_HEADER *) ((UINTN) FileHeader + HeaderSize);
  if (SectionHeader->Type != EFI_SECTION_RAW)
    return EFI_SECTION_ERROR;

  UINT32 SectionLength = GetSectionFileLength (SectionHeader);
  EFI_GUID* FileName = (EFI_GUID *) ((UINT8 *) SectionHeader + sizeof (EFI_COMMON_SECTION_HEADER));
  while (((UINT8 *) FileName) < ((UINT8 *) SectionHeader + SectionLength)) {
    PrintGuidToBuffer (FileName, GuidBuffer, sizeof (GuidBuffer), TRUE);
    printf ("%s  ", GuidBuffer);
    PrintGuidName (GuidBuffer);
    printf ("\n");
    FileName++;
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
PrintFileInfo (
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage,
  EFI_FFS_FILE_HEADER         *FileHeader,
  BOOLEAN                     ErasePolarity
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  FvImage       - GC_TODO: add argument description
  FileHeader    - GC_TODO: add argument description
  ErasePolarity - GC_TODO: add argument description

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value
  EFI_ABORTED - GC_TODO: Add description for return value

--*/
{
  UINT32              FileLength;
  UINT8               FileState;
  UINT8               Checksum;
  EFI_FFS_FILE_HEADER2 BlankHeader;
  EFI_STATUS          Status;
  UINT8               GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
  UINT32              HeaderSize;
#if (PI_SPECIFICATION_VERSION < 0x00010000)
  UINT16              *Tail;
#endif
  //
  // Check if we have free space
  //
  HeaderSize = FvBufGetFfsHeaderSize(FileHeader);
  if (ErasePolarity) {
    memset (&BlankHeader, -1, HeaderSize);
  } else {
    memset (&BlankHeader, 0, HeaderSize);
  }

  if (memcmp (&BlankHeader, FileHeader, HeaderSize) == 0) {
    return EFI_SUCCESS;
  }
  //
  // Print file information.
  //
  printf ("============================================================\n");

  printf ("File Name:        ");
  PrintGuidToBuffer (&FileHeader->Name, GuidBuffer, sizeof (GuidBuffer), TRUE);
  printf ("%s  ", GuidBuffer);
  PrintGuidName (GuidBuffer);
  printf ("\n");

  //
  //  PrintGuid (&FileHeader->Name);
  //  printf ("\n");
  //
  FileLength = FvBufGetFfsFileSize (FileHeader);
  printf ("File Offset:      0x%08X\n", (unsigned) ((UINTN) FileHeader - (UINTN) FvImage));
  printf ("File Length:      0x%08X\n", (unsigned) FileLength);
  printf ("File Attributes:  0x%02X\n", FileHeader->Attributes);
  printf ("File State:       0x%02X\n", FileHeader->State);

  //
  // Print file state
  //
  FileState = GetFileState (ErasePolarity, FileHeader);

  switch (FileState) {

  case EFI_FILE_HEADER_CONSTRUCTION:
    printf ("        EFI_FILE_HEADER_CONSTRUCTION\n");
    return EFI_SUCCESS;

  case EFI_FILE_HEADER_INVALID:
    printf ("        EFI_FILE_HEADER_INVALID\n");
    return EFI_SUCCESS;

  case EFI_FILE_HEADER_VALID:
    printf ("        EFI_FILE_HEADER_VALID\n");
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, HeaderSize);
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      printf ("ERROR: Header checksum invalid.\n");
      return EFI_ABORTED;
    }

    return EFI_SUCCESS;

  case EFI_FILE_DELETED:
    printf ("        EFI_FILE_DELETED\n");

  case EFI_FILE_MARKED_FOR_UPDATE:
    printf ("        EFI_FILE_MARKED_FOR_UPDATE\n");

  case EFI_FILE_DATA_VALID:
    printf ("        EFI_FILE_DATA_VALID\n");

    //
    // Calculate header checksum
    //
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, HeaderSize);
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum", GuidBuffer);
      return EFI_ABORTED;
    }

    FileLength = FvBufGetFfsFileSize (FileHeader);

    if (FileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
      //
      // Calculate file checksum
      //
      Checksum  = CalculateSum8 ((UINT8 *)FileHeader + HeaderSize, FileLength - HeaderSize);
      Checksum  = Checksum + FileHeader->IntegrityCheck.Checksum.File;
      if (Checksum != 0) {
        Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid file checksum", GuidBuffer);
        return EFI_ABORTED;
      }
    } else {
      if (FileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
        Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum -- not set to fixed value of 0xAA", GuidBuffer);
        return EFI_ABORTED;
      }
    }
#if (PI_SPECIFICATION_VERSION < 0x00010000)
    //
    // Verify tail if present
    //
    if (FileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
      //
      // Verify tail is complement of integrity check field in the header.
      //
      Tail = (UINT16 *) ((UINTN) FileHeader + GetLength (FileHeader->Size) - sizeof (EFI_FFS_INTEGRITY_CHECK));
      if (FileHeader->IntegrityCheck.TailReference != (UINT16)~(*Tail)) {
        Error (NULL, 0, 0003, "error parsing FFS file", \
        "FFS file with Guid %s failed in the integrity check, tail is not the complement of the header field", GuidBuffer);
        return EFI_ABORTED;
      }
    }
 #endif
    break;

  default:
    Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has the invalid/unrecognized file state bits", GuidBuffer);
    return EFI_ABORTED;
  }

  printf ("File Type:        0x%02X  ", FileHeader->Type);

  switch (FileHeader->Type) {

  case EFI_FV_FILETYPE_RAW:
    printf ("EFI_FV_FILETYPE_RAW\n");
    break;

  case EFI_FV_FILETYPE_FREEFORM:
    printf ("EFI_FV_FILETYPE_FREEFORM\n");
    break;

  case EFI_FV_FILETYPE_SECURITY_CORE:
    printf ("EFI_FV_FILETYPE_SECURITY_CORE\n");
    break;

  case EFI_FV_FILETYPE_PEI_CORE:
    printf ("EFI_FV_FILETYPE_PEI_CORE\n");
    break;

  case EFI_FV_FILETYPE_DXE_CORE:
    printf ("EFI_FV_FILETYPE_DXE_CORE\n");
    break;

  case EFI_FV_FILETYPE_PEIM:
    printf ("EFI_FV_FILETYPE_PEIM\n");
    break;

  case EFI_FV_FILETYPE_DRIVER:
    printf ("EFI_FV_FILETYPE_DRIVER\n");
    break;

  case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
    printf ("EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER\n");
    break;

  case EFI_FV_FILETYPE_APPLICATION:
    printf ("EFI_FV_FILETYPE_APPLICATION\n");
    break;

  case EFI_FV_FILETYPE_SMM:
    printf ("EFI_FV_FILETYPE_MM\n");
    break;

  case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
    printf ("EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE\n");
    break;

  case EFI_FV_FILETYPE_COMBINED_SMM_DXE:
    printf ("EFI_FV_FILETYPE_COMBINED_MM_DXE\n");
    break;

  case EFI_FV_FILETYPE_SMM_CORE:
    printf ("EFI_FV_FILETYPE_MM_CORE\n");
    break;

  case EFI_FV_FILETYPE_MM_STANDALONE:
    printf ("EFI_FV_FILETYPE_MM_STANDALONE\n");
    break;

  case EFI_FV_FILETYPE_MM_CORE_STANDALONE:
    printf ("EFI_FV_FILETYPE_MM_CORE_STANDALONE\n");
    break;

  case EFI_FV_FILETYPE_FFS_PAD:
    printf ("EFI_FV_FILETYPE_FFS_PAD\n");
    break;

  default:
    printf ("\nERROR: Unrecognized file type %X.\n", FileHeader->Type);
    return EFI_ABORTED;
    break;
  }

  switch (FileHeader->Type) {

  case EFI_FV_FILETYPE_ALL:
  case EFI_FV_FILETYPE_RAW:
  case EFI_FV_FILETYPE_FFS_PAD:
    break;

  default:
    //
    // All other files have sections
    //
    Status = ParseSection (
              (UINT8 *) ((UINTN) FileHeader + HeaderSize),
              FvBufGetFfsFileSize (FileHeader) - HeaderSize
              );
    if (EFI_ERROR (Status)) {
      //
      // printf ("ERROR: Parsing the FFS file.\n");
      //
      return EFI_ABORTED;
    }
    break;
  }

  if (!BtCompareGuid (
       &FileHeader->Name,
       &gPeiAprioriFileNameGuid
       ))
  {
    printf("\n");
    printf("PEI APRIORI FILE:\n");
    return PrintAprioriFile (FileHeader);
  }
  if (!BtCompareGuid (
       &FileHeader->Name,
       &gAprioriGuid
       ))
  {
    printf("\n");
    printf("DXE APRIORI FILE:\n");
    return PrintAprioriFile (FileHeader);
  }

  return EFI_SUCCESS;
}

EFI_STATUS
RebaseImage (
  IN     CHAR8   *FileName,
  IN OUT UINT8   *FileBuffer,
  IN     UINT32  FileBufferSize,
  IN     UINT64  NewBaseAddress
  )
/*++

Routine Description:

  Set new base address into UefiImage, and fix up UefiImage based on new address.

Arguments:

  FileName           - Name of file
  FileBuffer         - Pointer to UefiImage.
  NewBaseAddress     - New Base Address for UEFI image.

Returns:

  EFI_INVALID_PARAMETER   - BaseAddress is not valid.
  EFI_SUCCESS             - Update UefiImage is correctly.

--*/
{
  UINT32 RebasedSize;
  VOID   *RebasedBuffer;

  RebasedBuffer = ToolImageEmit (
                    &RebasedSize,
                    FileBuffer,
                    FileBufferSize,
                    -1,
                    -1,
                    true,
                    NewBaseAddress,
                    NULL,
                    TRUE,
                    FALSE,
                    TRUE
                    );
  if (RebasedBuffer == NULL) {
    Error (NULL, 0, 3000, "Invalid", "The input UefiImage %s is not valid", FileName);
    return EFI_UNSUPPORTED;
  }

  if (RebasedSize > FileBufferSize) {
    Error (NULL, 0, 3000, "Invalid", "Rebased %s too large: %u vs %u", FileName, RebasedSize, FileBufferSize);
    return EFI_UNSUPPORTED;
  }

  memmove (FileBuffer, RebasedBuffer, RebasedSize);
  memset (FileBuffer + RebasedSize, 0, FileBufferSize - RebasedSize);

  return EFI_SUCCESS;
}

EFI_STATUS
CombinePath (
  IN  CHAR8* DefaultPath,
  IN  CHAR8* AppendPath,
  OUT CHAR8* NewPath
)
{
  UINT32 DefaultPathLen;
  UINT64 Index;
  CHAR8  QuotesStr[] = "\"";
  strcpy(NewPath, QuotesStr);
  DefaultPathLen = strlen(DefaultPath);
  strcat(NewPath, DefaultPath);
  Index = 0;
  for (; Index < DefaultPathLen + 1; Index ++) {
    if (NewPath[Index] == '\\' || NewPath[Index] == '/') {
      if (NewPath[Index + 1] != '\0') {
        NewPath[Index] = '/';
      }
    }
  }
  if (NewPath[Index -1] != '/') {
    NewPath[Index] = '/';
    NewPath[Index + 1] = '\0';
  }
  strcat(NewPath, AppendPath);
  strcat(NewPath, QuotesStr);
  return EFI_SUCCESS;
}

EFI_STATUS
ParseSection (
  IN UINT8  *SectionBuffer,
  IN UINT32 BufferLength
  )
/*++

Routine Description:

  Parses EFI Sections

Arguments:

  SectionBuffer - Buffer containing the section to parse.
  BufferLength  - Length of SectionBuffer

Returns:

  EFI_SECTION_ERROR - Problem with section parsing.
                      (a) compression errors
                      (b) unrecognized section
  EFI_UNSUPPORTED - Do not know how to parse the section.
  EFI_SUCCESS - Section successfully parsed.
  EFI_OUT_OF_RESOURCES - Memory allocation failed.

--*/
{
  EFI_SECTION_TYPE    Type;
  UINT8               *Ptr;
  UINT32              SectionLength;
  UINT32              SectionHeaderLen;
  CHAR8               *SectionName;
  EFI_STATUS          Status;
  UINT32              ParsedLength;
  UINT8               *CompressedBuffer;
  UINT32              CompressedLength;
  UINT8               *UncompressedBuffer;
  UINT32              UncompressedLength;
  UINT8               *ToolOutputBuffer;
  UINT32              ToolOutputLength;
  UINT8               CompressionType;
  UINT32              DstSize;
  UINT32              ScratchSize;
  UINT8               *ScratchBuffer;
  DECOMPRESS_FUNCTION DecompressFunction;
  GETINFO_FUNCTION    GetInfoFunction;
  // CHAR16              *name;
  CHAR8               *ExtractionTool;
  CHAR8               *ToolInputFile;
  CHAR8               *ToolOutputFile;
  CHAR8               *SystemCommand;
  EFI_GUID            *EfiGuid;
  UINT16              DataOffset;
  UINT16              Attributes;
  UINT32              RealHdrLen;
  CHAR8               *ToolInputFileName;
  CHAR8               *ToolOutputFileName;
  CHAR8               *UIFileName;
  CHAR8               *VersionString;

  ParsedLength = 0;
  ToolInputFileName = NULL;
  ToolOutputFileName = NULL;

  while (ParsedLength < BufferLength) {
    Ptr           = SectionBuffer + ParsedLength;

    SectionLength = GetLength (((EFI_COMMON_SECTION_HEADER *) Ptr)->Size);
    Type          = ((EFI_COMMON_SECTION_HEADER *) Ptr)->Type;

    //
    // This is sort of an odd check, but is necessary because FFS files are
    // padded to a QWORD boundary, meaning there is potentially a whole section
    // header worth of 0xFF bytes.
    //
    if (SectionLength == 0xffffff && Type == 0xff) {
      ParsedLength += 4;
      continue;
    }

    //
    // Get real section file size
    //
    SectionLength = GetSectionFileLength ((EFI_COMMON_SECTION_HEADER *) Ptr);
    SectionHeaderLen = GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);

    SectionName = SectionNameToStr (Type);
    if (SectionName != NULL) {
      printf ("------------------------------------------------------------\n");
      printf ("  Type:  %s\n  Size:  0x%08X\n", SectionName, (unsigned) SectionLength);
      free (SectionName);
    }

    switch (Type) {
    case EFI_SECTION_RAW:
    case EFI_SECTION_PIC:
      // default is no more information
      break;

    case EFI_SECTION_PE32:
      if (EnableHash) {
        ToolInputFileName  = "edk2Temp_InputEfi.tmp";
        ToolOutputFileName = "edk2Temp_OutputHash.tmp";
        RebaseImage(ToolInputFileName, (UINT8*)Ptr + SectionHeaderLen, SectionLength - SectionHeaderLen, 0);
        PutFileImage (
          ToolInputFileName,
          (CHAR8*)Ptr + SectionHeaderLen,
          SectionLength - SectionHeaderLen
          );

        SystemCommand = malloc (
          strlen (OPENSSL_COMMAND_FORMAT_STRING) +
          strlen (OpenSslPath) +
          strlen (ToolInputFileName) +
          strlen (ToolOutputFileName) +
          1
          );
        if (SystemCommand == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        sprintf (
          SystemCommand,
          OPENSSL_COMMAND_FORMAT_STRING,
          OpenSslPath,
          ToolOutputFileName,
          ToolInputFileName
          );

        if (system (SystemCommand) != EFI_SUCCESS) {
          Error (NULL, 0, 3000, "Open SSL command not available.  Please verify PATH or set OPENSSL_PATH.", NULL);
        }
        else {
          FILE *fp;
          CHAR8 *StrLine;
          CHAR8 *NewStr;
          UINT32 nFileLen;
          if((fp = fopen(ToolOutputFileName,"r")) == NULL) {
            Error (NULL, 0, 0004, "Hash the UEFI image failed.", NULL);
          }
          else {
            fseek(fp,0,SEEK_SET);
            fseek(fp,0,SEEK_END);
            nFileLen = ftell(fp);
            fseek(fp,0,SEEK_SET);
            StrLine = malloc(nFileLen);
            if (StrLine == NULL) {
              fclose(fp);
              free (SystemCommand);
              Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
              return EFI_OUT_OF_RESOURCES;
            }
            fgets(StrLine, nFileLen, fp);
            NewStr = strrchr (StrLine, '=');
            printf ("  SHA1: %s\n", NewStr + 1);
            free (StrLine);
            fclose(fp);
          }
        }
        remove(ToolInputFileName);
        remove(ToolOutputFileName);
        free (SystemCommand);
      }
      break;

    case EFI_SECTION_USER_INTERFACE:
      UIFileName = (CHAR8 *) malloc (UnicodeStrLen (((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString) + 1);
      if (UIFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        return EFI_OUT_OF_RESOURCES;
      }
      Unicode2AsciiString (((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString, UIFileName);
      printf ("  String: %s\n", UIFileName);
      free (UIFileName);
      break;

    case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
      printf ("/------------ Firmware Volume section start ---------------\\\n");
      Status = PrintFvInfo (Ptr + SectionHeaderLen, TRUE);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 0003, "printing of FV section contents failed", NULL);
        return EFI_SECTION_ERROR;
      }
      printf ("\\------------ Firmware Volume section end -----------------/\n");
      break;

    case EFI_SECTION_COMPATIBILITY16:
      //
      // Section does not contain any further header information.
      //
      break;

    case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
      printf ("  Guid:  ");
      if (SectionHeaderLen == sizeof (EFI_COMMON_SECTION_HEADER))
        PrintGuid (&((EFI_FREEFORM_SUBTYPE_GUID_SECTION *)Ptr)->SubTypeGuid);
      else
        PrintGuid (&((EFI_FREEFORM_SUBTYPE_GUID_SECTION2 *)Ptr)->SubTypeGuid);
      printf ("\n");
      break;

    case EFI_SECTION_PEI_DEPEX:
    case EFI_SECTION_DXE_DEPEX:
    case EFI_SECTION_SMM_DEPEX:
      DumpDepexSection (Ptr, SectionLength);
      break;

    case EFI_SECTION_VERSION:
      printf ("  Build Number:  0x%04X\n", *(UINT16 *)(Ptr + SectionHeaderLen));
      VersionString = (CHAR8 *) malloc (UnicodeStrLen (((EFI_VERSION_SECTION *) Ptr)->VersionString) + 1);
      if (VersionString == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        return EFI_OUT_OF_RESOURCES;
      }
      Unicode2AsciiString (((EFI_VERSION_SECTION *) Ptr)->VersionString, VersionString);
      printf ("  Version String:  %s\n", VersionString);
      break;

    case EFI_SECTION_COMPRESSION:
      UncompressedBuffer  = NULL;
      if (SectionHeaderLen == sizeof (EFI_COMMON_SECTION_HEADER)) {
        RealHdrLen = sizeof(EFI_COMPRESSION_SECTION);
        UncompressedLength  = ((EFI_COMPRESSION_SECTION *)Ptr)->UncompressedLength;
        CompressionType     = ((EFI_COMPRESSION_SECTION *)Ptr)->CompressionType;
      } else {
        RealHdrLen = sizeof(EFI_COMPRESSION_SECTION2);
        UncompressedLength  = ((EFI_COMPRESSION_SECTION2 *)Ptr)->UncompressedLength;
        CompressionType     = ((EFI_COMPRESSION_SECTION2 *)Ptr)->CompressionType;
      }
      CompressedLength    = SectionLength - RealHdrLen;
      printf ("  Uncompressed Length:  0x%08X\n", (unsigned) UncompressedLength);

      if (CompressionType == EFI_NOT_COMPRESSED) {
        printf ("  Compression Type:  EFI_NOT_COMPRESSED\n");
        if (CompressedLength != UncompressedLength) {
          Error (
            NULL,
            0,
            0,
            "file is not compressed, but the compressed length does not match the uncompressed length",
            NULL
            );
          return EFI_SECTION_ERROR;
        }

        UncompressedBuffer = Ptr + RealHdrLen;
      } else if (CompressionType == EFI_STANDARD_COMPRESSION) {
        GetInfoFunction     = EfiGetInfo;
        DecompressFunction  = EfiDecompress;
        printf ("  Compression Type:  EFI_STANDARD_COMPRESSION\n");

        CompressedBuffer  = Ptr + RealHdrLen;

        Status            = GetInfoFunction (CompressedBuffer, CompressedLength, &DstSize, &ScratchSize);
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "error getting compression info from compression section", NULL);
          return EFI_SECTION_ERROR;
        }

        if (DstSize != UncompressedLength) {
          Error (NULL, 0, 0003, "compression error in the compression section", NULL);
          return EFI_SECTION_ERROR;
        }

        ScratchBuffer       = malloc (ScratchSize);
        if (ScratchBuffer == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        UncompressedBuffer  = malloc (UncompressedLength);
        if (UncompressedBuffer == NULL) {
          free (ScratchBuffer);
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        Status = DecompressFunction (
                  CompressedBuffer,
                  CompressedLength,
                  UncompressedBuffer,
                  UncompressedLength,
                  ScratchBuffer,
                  ScratchSize
                  );
        free (ScratchBuffer);
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "decompress failed", NULL);
          free (UncompressedBuffer);
          return EFI_SECTION_ERROR;
        }
      } else {
        Error (NULL, 0, 0003, "unrecognized compression type", "type 0x%X", CompressionType);
        return EFI_SECTION_ERROR;
      }

      printf ("/------------ Encapsulation section start -----------------\\\n");
      Status = ParseSection (UncompressedBuffer, UncompressedLength);
      printf ("\\------------ Encapsulation section end -------------------/\n");

      if (CompressionType == EFI_STANDARD_COMPRESSION) {
        //
        // We need to deallocate Buffer
        //
        free (UncompressedBuffer);
      }

      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 0003, "failed to parse section", NULL);
        return EFI_SECTION_ERROR;
      }
      break;

    case EFI_SECTION_GUID_DEFINED:
      if (SectionHeaderLen == sizeof(EFI_COMMON_SECTION_HEADER)) {
        EfiGuid = &((EFI_GUID_DEFINED_SECTION *) Ptr)->SectionDefinitionGuid;
        DataOffset = ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset;
        Attributes = ((EFI_GUID_DEFINED_SECTION *) Ptr)->Attributes;
      } else {
        EfiGuid = &((EFI_GUID_DEFINED_SECTION2 *) Ptr)->SectionDefinitionGuid;
        DataOffset = ((EFI_GUID_DEFINED_SECTION2 *) Ptr)->DataOffset;
        Attributes = ((EFI_GUID_DEFINED_SECTION2 *) Ptr)->Attributes;
      }
      printf ("  SectionDefinitionGuid:  ");
      PrintGuid (EfiGuid);
      printf ("\n");
      printf ("  DataOffset:             0x%04X\n", (unsigned) DataOffset);
      printf ("  Attributes:             0x%04X\n", (unsigned) Attributes);

      ExtractionTool =
        LookupGuidedSectionToolPath (
          mParsedGuidedSectionTools,
          EfiGuid
          );

      if (ExtractionTool != NULL) {
       #ifndef __GNUC__
        ToolInputFile = CloneString (tmpnam (NULL));
        ToolOutputFile = CloneString (tmpnam (NULL));
       #else
        char tmp1[] = "/tmp/fileXXXXXX";
        char tmp2[] = "/tmp/fileXXXXXX";
        int fd1;
        int fd2;
        fd1 = mkstemp(tmp1);
        fd2 = mkstemp(tmp2);
        ToolInputFile = CloneString(tmp1);
        ToolOutputFile = CloneString(tmp2);
        close(fd1);
        close(fd2);
       #endif

        if ((ToolInputFile == NULL) || (ToolOutputFile == NULL)) {
          if (ToolInputFile != NULL) {
            free (ToolInputFile);
          }
          if (ToolOutputFile != NULL) {
            free (ToolOutputFile);
          }
          free (ExtractionTool);

          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }

        //
        // Construction 'system' command string
        //
        SystemCommand = malloc (
          strlen (EXTRACT_COMMAND_FORMAT_STRING) +
          strlen (ExtractionTool) +
          strlen (ToolInputFile) +
          strlen (ToolOutputFile) +
          1
          );
        if (SystemCommand == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ExtractionTool);

          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        sprintf (
          SystemCommand,
          EXTRACT_COMMAND_FORMAT_STRING,
          ExtractionTool,
          ToolOutputFile,
          ToolInputFile
          );
        free (ExtractionTool);

        if (!BtCompareGuid (
               EfiGuid,
               &gEfiCrc32GuidedSectionExtractionProtocolGuid
               )
           ) {
          DataOffset -= 4;
        }
        Status =
          PutFileImage (
            ToolInputFile,
            (CHAR8*)Ptr + DataOffset,
            SectionLength - DataOffset
            );

        system (SystemCommand);
        remove (ToolInputFile);
        free (ToolInputFile);

        Status =
          GetFileImage (
            ToolOutputFile,
            (CHAR8 **)&ToolOutputBuffer,
            &ToolOutputLength
            );
        remove (ToolOutputFile);
        free (ToolOutputFile);
        free (SystemCommand);
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0004, "unable to read decoded GUIDED section", NULL);
          return EFI_SECTION_ERROR;
        }

        printf ("/------------ Encapsulation section start -----------------\\\n");
        Status = ParseSection (
                  ToolOutputBuffer,
                  ToolOutputLength
                  );
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL);
          return EFI_SECTION_ERROR;
        }
        printf ("\\------------ Encapsulation section end -------------------/\n");

      //
      // Check for CRC32 sections which we can handle internally if needed.
      //
      } else if (!BtCompareGuid (
                   EfiGuid,
                   &gEfiCrc32GuidedSectionExtractionProtocolGuid
                   )
          ) {
        //
        // CRC32 guided section
        //
        printf ("/------------ Encapsulation section start -----------------\\\n");
        Status = ParseSection (
                  Ptr + DataOffset,
                  SectionLength - DataOffset
                  );
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "parse of CRC32 GUIDED section failed", NULL);
          return EFI_SECTION_ERROR;
        }
        printf ("\\------------ Encapsulation section end -------------------/\n");
      } else {
        //
        // We don't know how to parse it now.
        //
        Error (NULL, 0, 0003, "Error parsing section", \
        "EFI_SECTION_GUID_DEFINED cannot be parsed at this time. Tool to decode this section should have been defined in GuidedSectionTools.txt (built in the FV directory).");
        return EFI_UNSUPPORTED;
      }
      break;

    default:
      //
      // Unknown section, return error
      //
      Error (NULL, 0, 0003, "unrecognized section type found", "section type = 0x%X", Type);
      return EFI_SECTION_ERROR;
    }

    ParsedLength += SectionLength;
    //
    // We make then next section begin on a 4-byte boundary
    //
    ParsedLength = GetOccupiedSize (ParsedLength, 4);
  }

  if (ParsedLength < BufferLength) {
    Error (NULL, 0, 0003, "sections do not completely fill the sectioned buffer being parsed", NULL);
    return EFI_SECTION_ERROR;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
DumpDepexSection (
  IN UINT8    *Ptr,
  IN UINT32   SectionLength
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  Ptr           - GC_TODO: add argument description
  SectionLength - GC_TODO: add argument description

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value

--*/
{
  UINT8 GuidBuffer[PRINTED_GUID_BUFFER_SIZE];

  //
  // Need at least a section header + data
  //
  if (SectionLength <= sizeof (EFI_COMMON_SECTION_HEADER)) {
    return EFI_SUCCESS;
  }

  Ptr += GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
  SectionLength -= GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
  while (SectionLength > 0) {
    printf ("        ");
    switch (*Ptr) {
    case EFI_DEP_BEFORE:
      printf ("BEFORE\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_AFTER:
      printf ("AFTER\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_PUSH:
      printf ("PUSH\n        ");
      PrintGuidToBuffer ((EFI_GUID *) (Ptr + 1), GuidBuffer, sizeof (GuidBuffer), TRUE);
      printf ("%s  ", GuidBuffer);
      PrintGuidName (GuidBuffer);
      printf ("\n");
      //
      // PrintGuid ((EFI_GUID *)(Ptr + 1));
      //
      Ptr += 17;
      SectionLength -= 17;
      break;

    case EFI_DEP_AND:
      printf ("AND\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_OR:
      printf ("OR\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_NOT:
      printf ("NOT\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_TRUE:
      printf ("TRUE\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_FALSE:
      printf ("FALSE\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_END:
      printf ("END DEPEX\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_SOR:
      printf ("SOR\n");
      Ptr++;
      SectionLength--;
      break;

    default:
      printf ("Unrecognized byte in depex: 0x%X\n", *Ptr);
      return EFI_SUCCESS;
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
PrintGuidName (
  IN UINT8    *GuidStr
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  GuidStr - GC_TODO: add argument description

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value
  EFI_INVALID_PARAMETER - GC_TODO: Add description for return value

--*/
{
  GUID_TO_BASENAME  *GPtr;
  //
  // If we have a list of guid-to-basenames, then go through the list to
  // look for a guid string match. If found, print the basename to stdout,
  // otherwise return a failure.
  //
  GPtr = mGuidBaseNameList;
  while (GPtr != NULL) {
    if (_stricmp ((CHAR8*) GuidStr, (CHAR8*) GPtr->Guid) == 0) {
      printf ("%s", GPtr->BaseName);
      return EFI_SUCCESS;
    }

    GPtr = GPtr->Next;
  }

  return EFI_INVALID_PARAMETER;
}

EFI_STATUS
ParseGuidBaseNameFile (
  CHAR8    *FileName
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  FileName  - GC_TODO: add argument description

Returns:

  EFI_DEVICE_ERROR - GC_TODO: Add description for return value
  EFI_OUT_OF_RESOURCES - GC_TODO: Add description for return value
  EFI_SUCCESS - GC_TODO: Add description for return value

--*/
{
  FILE              *Fptr;
  CHAR8             Line[MAX_LINE_LEN];
  CHAR8             FormatString[MAX_LINE_LEN];
  GUID_TO_BASENAME  *GPtr;

  if ((Fptr = fopen (LongFilePath (FileName), "r")) == NULL) {
    printf ("ERROR: Failed to open input cross-reference file '%s'\n", FileName);
    return EFI_DEVICE_ERROR;
  }

  //
  // Generate the format string for fscanf
  //
  sprintf (
    FormatString,
    "%%%us %%%us",
    (unsigned) sizeof (GPtr->Guid) - 1,
    (unsigned) sizeof (GPtr->BaseName) - 1
    );

  while (fgets (Line, sizeof (Line), Fptr) != NULL) {
    //
    // Allocate space for another guid/basename element
    //
    GPtr = malloc (sizeof (GUID_TO_BASENAME));
    if (GPtr == NULL) {
      fclose (Fptr);
      return EFI_OUT_OF_RESOURCES;
    }

    memset ((char *) GPtr, 0, sizeof (GUID_TO_BASENAME));
    if (sscanf (Line, FormatString, GPtr->Guid, GPtr->BaseName) == 2) {
      GPtr->Next        = mGuidBaseNameList;
      mGuidBaseNameList = GPtr;
    } else {
      //
      // Some sort of error. Just continue.
      //
      free (GPtr);
    }
  }

  fclose (Fptr);
  return EFI_SUCCESS;
}

EFI_STATUS
FreeGuidBaseNameList (
  VOID
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  None

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value

--*/
{
  GUID_TO_BASENAME  *Next;

  while (mGuidBaseNameList != NULL) {
    Next = mGuidBaseNameList->Next;
    free (mGuidBaseNameList);
    mGuidBaseNameList = Next;
  }

  return EFI_SUCCESS;
}


static
VOID
LoadGuidedSectionToolsTxt (
  IN CHAR8* FirmwareVolumeFilename
  )
{
  CHAR8* PeerFilename;
  CHAR8* Places[] = {
    NULL,
    //NULL,
    };
  UINTN Index;

  Places[0] = FirmwareVolumeFilename;
  //Places[1] = mUtilityFilename;

  mParsedGuidedSectionTools = NULL;

  for (Index = 0; Index < (sizeof(Places)/sizeof(Places[0])); Index++) {
    PeerFilename = OsPathPeerFilePath (Places[Index], "GuidedSectionTools.txt");
    //printf("Loading %s...\n", PeerFilename);
    if (OsPathExists (PeerFilename)) {
      mParsedGuidedSectionTools = ParseGuidedSectionToolsFile (PeerFilename);
    }
    free (PeerFilename);
    if (mParsedGuidedSectionTools != NULL) {
      return;
    }
  }
}


void
Usage (
  VOID
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  None

Returns:

  GC_TODO: add return values

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

  //
  // Copyright declaration
  //
  fprintf (stdout, "Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.\n\n");
  fprintf (stdout, "  Display Tiano Firmware Volume FFS image information\n\n");

  //
  // Details Option
  //
  fprintf (stdout, "optional arguments:\n");
  fprintf (stdout, "  -h, --help\n\
            Show this help message and exit\n");
  fprintf (stdout, "  --version\n\
           Show program's version number and exit\n");
  fprintf (stdout, "  -d [DEBUG], --debug [DEBUG]\n\
            Output DEBUG statements, where DEBUG_LEVEL is 0 (min) - 9 (max)\n");
  fprintf (stdout, "  -v, --verbose\n\
            Print informational statements\n");
  fprintf (stdout, "  -q, --quiet\n\
            Returns the exit code, error messages will be displayed\n");
  fprintf (stdout, "  -s, --silent\n\
            Returns only the exit code; informational and error\n\
            messages are not displayed\n");
  fprintf (stdout, "  -x XREF_FILENAME, --xref XREF_FILENAME\n\
            Parse the basename to file-guid cross reference file(s)\n");
  fprintf (stdout, "  -f OFFSET, --offset OFFSET\n\
            The offset from the start of the input file to start \n\
            processing an FV\n");
  fprintf (stdout, "  --hash\n\
            Generate HASH value of the entire UEFI image\n");
  fprintf (stdout, "  --sfo\n\
            Reserved for future use\n");
}

