/** @file  FmmtLib.c

 Library to parse and generate FV image.

 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>

 This program and the accompanying materials are licensed and made available under
 the terms and conditions of the BSD License which accompanies this distribution.
 The full text of the license may be found at
 http://opensource.org/licenses/bsd-license.php

 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "FirmwareModuleManagement.h"

#define EFI_TEST_FFS_ATTRIBUTES_BIT(FvbAttributes, TestAttributes, Bit) \
    ( \
      (BOOLEAN) ( \
          (FvbAttributes & EFI_FVB2_ERASE_POLARITY) ? (((~TestAttributes) & Bit) == Bit) : ((TestAttributes & Bit) == Bit) \
        ) \
    )

CHAR8      mFirmwareFileSystem2Guid[16] = {0x78, 0xE5, 0x8C, 0x8C, 0x3D, 0x8A, 0x1C, 0x4F, 0x99, 0x35, 0x89, 0x61, 0x85, 0xC3, 0x2D, 0xD3};

CHAR8      mFirmwareFileSystem3Guid[16] = {0x7A, 0xC0, 0x73, 0x54, 0xCB, 0x3D, 0xCA, 0x4D, 0xBD, 0x6F, 0x1E, 0x96, 0x89, 0xE7, 0x34, 0x9A };

EFI_GUID   mEfiCrc32GuidedSectionExtractionProtocolGuid = EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID;
extern     CHAR8*      mGuidToolDefinition;

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 *mFfsFileType[] = {
  NULL,                                   // 0x00
  "EFI_FV_FILETYPE_RAW",                  // 0x01
  "EFI_FV_FILETYPE_FREEFORM",             // 0x02
  "EFI_FV_FILETYPE_SECURITY_CORE",        // 0x03
  "EFI_FV_FILETYPE_PEI_CORE",             // 0x04
  "EFI_FV_FILETYPE_DXE_CORE",             // 0x05
  "EFI_FV_FILETYPE_PEIM",                 // 0x06
  "EFI_FV_FILETYPE_DRIVER",               // 0x07
  "EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER", // 0x08
  "EFI_FV_FILETYPE_APPLICATION",          // 0x09
  "EFI_FV_FILETYPE_SMM",                  // 0x0A
  "EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE",// 0x0B
  "EFI_FV_FILETYPE_COMBINED_SMM_DXE",     // 0x0C
  "EFI_FV_FILETYPE_SMM_CORE"              // 0x0D
 };

STATIC CHAR8 *mGuidSectionAttr[] = {
  "NONE",                                 // 0x00
  "PROCESSING_REQUIRED",                  // 0x01
  "AUTH_STATUS_VALID"                     // 0x02
};

STATIC EFI_GUID mFvUiGuid = {
  0xA67DF1FA, 0x8DE8, 0x4E98, {
    0xAF, 0x09, 0x4B, 0xDF, 0x2E, 0xFF, 0xBC, 0x7C
  }
};


/**
  Generate the unique template filename.
**/
CHAR8 *
GenTempFile (
 VOID
 )
{
  CHAR8   *TemString;
  TemString = NULL;
#ifndef __GNUC__
  TemString = CloneString (tmpnam (NULL));
#else
  CHAR8 tmp[] = "/tmp/fileXXXXXX";
  UINTN Fdtmp;
  Fdtmp = mkstemp(tmp);
  TemString = CloneString(tmp);
  close(Fdtmp);
#endif
  return TemString;
}

STATIC
EFI_STATUS
LibExtractFvUiName(CONST EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader, CHAR8 **FvUiName)
{
  UINT8 *ExtEnd;
  UINT32 ExtDataSize;
  EFI_FIRMWARE_VOLUME_EXT_ENTRY *ExtEntry;
  EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE *GuidEntry;


  ExtEnd = (UINT8 *)FvExtHeader + FvExtHeader->ExtHeaderSize;
  ExtEntry = (EFI_FIRMWARE_VOLUME_EXT_ENTRY *)(FvExtHeader + 1);
  while ((UINT8 *)ExtEntry < ExtEnd) {
    //
    // GUID type EXT
    //
    if (ExtEntry->ExtEntryType == 0x0002) {
      GuidEntry = (EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE *)ExtEntry;
      if (memcmp(&GuidEntry->FormatType, &mFvUiGuid, sizeof(EFI_GUID)) == 0) {
        ExtDataSize = ExtEntry->ExtEntrySize - (sizeof(EFI_GUID)+sizeof(*ExtEntry));
        *FvUiName = malloc(ExtDataSize + 1);
        if (*FvUiName != NULL) {
          memcpy(*FvUiName, (UINT8 *)GuidEntry + sizeof(EFI_GUID)+sizeof(*ExtEntry), ExtDataSize);
          (*FvUiName)[ExtDataSize] = '\0';
          return EFI_SUCCESS;
        }
      }
    }

    ExtEntry = (EFI_FIRMWARE_VOLUME_EXT_ENTRY *)((UINT8 *)ExtEntry + ExtEntry->ExtEntrySize);
  }
  return EFI_NOT_FOUND;
}

FV_INFORMATION *
LibInitializeFvStruct (
  FV_INFORMATION *Fv
)
{
  UINT32     Index;

  if (Fv == NULL) {
    return NULL;
  }

  for (Index = 0; Index < MAX_NUMBER_OF_FILES_IN_FV; Index ++) {
    memset (Fv->FfsAttuibutes[Index].FfsName, '\0', _MAX_PATH);
    memset (Fv->FfsAttuibutes[Index].UiName, '\0', _MAX_PATH);
    memset (&Fv->FfsAttuibutes[Index].GuidName, '\0', sizeof(EFI_GUID));
    Fv->FfsAttuibutes[Index].UiNameSize           = 0;
    Fv->FfsAttuibutes[Index].IsLeaf               = TRUE;
    Fv->FfsAttuibutes[Index].Level                = 0xFF;
    Fv->FfsAttuibutes[Index].TotalSectionNum      = 0;
    Fv->FfsAttuibutes[Index].Depex                = NULL;
    Fv->FfsAttuibutes[Index].DepexLen             = 0;
    Fv->FfsAttuibutes[Index].IsHandle             = FALSE;
    Fv->FfsAttuibutes[Index].IsFvStart            = FALSE;
    Fv->FfsAttuibutes[Index].IsFvEnd              = FALSE;
  }

  Fv->EncapData = NULL;
  Fv->FvNext = NULL;
  Fv->ChildFvFFS = NULL;
  Fv->FvLevel   = 0;
  Fv->MulFvLevel = 1;
  strcpy(Fv->AlignmentStr,"8");
  return Fv;
}


EFI_STATUS
LibFindFvInFd (
  IN     FILE             *InputFile,
  IN OUT FIRMWARE_DEVICE  **FdData
)
{
  FIRMWARE_DEVICE             *LocalFdData;
  UINT16                      Index;
  CHAR8                       Ffs2Guid[16];
  CHAR8                       SignatureCheck[5] = "";
  CHAR8                       Signature[5] = "_FVH";
  FV_INFORMATION              *CurrentFv;
  FV_INFORMATION              *NewFoundFv;
  BOOLEAN                     FirstMatch;
  UINT32                      FdSize;
  UINT16                      FvCount;
  UINT8                       *FdBuffer;
  UINT8                       *FdBufferEnd;
  UINT8                       *FdBufferOri;
  EFI_FIRMWARE_VOLUME_HEADER  *FvHeader;

  CurrentFv      = NULL;
  NewFoundFv     = NULL;
  FdBuffer       = NULL;
  FdBufferOri    = NULL;
  FirstMatch     = TRUE;
  Index          = 0;
  FdSize         = 0;
  FvCount        = 0;
  LocalFdData    = NULL;

  if (InputFile == NULL) {
    Error ("FMMT", 0, 0001, "Error opening the input file", "");
    return EFI_ABORTED;
  }

  //
  // Find each FVs in the FD
  //

  fseek(InputFile,0,SEEK_SET);
  fseek(InputFile,0,SEEK_END);

  FdSize = ftell(InputFile);

  fseek(InputFile,0,SEEK_SET);
  //
  // Create an FD structure to store useful information.
  //
  LocalFdData     = (FIRMWARE_DEVICE *) malloc (sizeof (FIRMWARE_DEVICE));
  if (LocalFdData == NULL) {
    Error ("FMMT", 0, 0002, "Error searching FVs in the input fd", "Allocate memory error");
    return EFI_OUT_OF_RESOURCES;
  }
  LocalFdData->Fv = (FV_INFORMATION *)  malloc (sizeof (FV_INFORMATION));
  if (LocalFdData->Fv == NULL) {
    Error ("FMMT", 0, 0002, "Error searching FVs in the input fd", "Allocate memory error");
    free (LocalFdData);
    return EFI_OUT_OF_RESOURCES;
  }

  LibInitializeFvStruct (LocalFdData->Fv);

  //
  // Readout the FD file data to buffer.
  //
  FdBuffer = malloc (FdSize);

  if (FdBuffer == NULL) {
    Error ("FMMT", 0, 0002, "Error searching FVs in the input fd", "Allocate memory error");
    free (LocalFdData->Fv);
    free (LocalFdData);
    return EFI_OUT_OF_RESOURCES;
  }

  if (fread (FdBuffer, 1, FdSize, InputFile) != FdSize) {
    Error ("FMMT", 0, 0002, "Error searching FVs in the input fd", "Read FD file error!");
    free (LocalFdData->Fv);
    free (LocalFdData);
    free (FdBuffer);
    return EFI_ABORTED;
  }

  FdBufferOri = FdBuffer;
  FdBufferEnd = FdBuffer + FdSize;

  while (FdBuffer <= FdBufferEnd - sizeof (EFI_FIRMWARE_VOLUME_HEADER)) {
    FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) FdBuffer;
    //
    // Copy 4 bytes of fd data to check the _FVH signature
    //
    memcpy (SignatureCheck, &FvHeader->Signature, 4);

    if (strncmp(SignatureCheck, Signature, 4) == 0){
      //
      // Still need to determine the FileSystemGuid in EFI_FIRMWARE_VOLUME_HEADER equal to
      // EFI_FIRMWARE_FILE_SYSTEM2_GUID or EFI_FIRMWARE_FILE_SYSTEM3_GUID.
      // Turn back 28 bytes to find the GUID.
      //
      memcpy (Ffs2Guid, &FvHeader->FileSystemGuid, 16);

      //
      // Compare GUID.
      //
      for (Index = 0; Index < 16; Index ++) {
        if (Ffs2Guid[Index] != mFirmwareFileSystem2Guid[Index]) {
          break;
        }
      }
    if (Index != 16) {
      for (Index = 0; Index < 16; Index ++) {
          if (Ffs2Guid[Index] != mFirmwareFileSystem3Guid[Index]) {
            break;
          }
        }
    }

      //
      // Here we found an FV.
      //
      if ((Index == 16) && ((FdBuffer + FvHeader->FvLength) <= FdBufferEnd)) {
        if (FirstMatch) {
          LocalFdData->Fv->ImageAddress = (UINTN)((UINT8 *)FdBuffer - (UINT8 *)FdBufferOri);
          CurrentFv                     = LocalFdData->Fv;
          CurrentFv->FvNext             = NULL;
          //
          // Store the FV name by found sequence
          //
          sprintf(CurrentFv->FvName, "FV%d", FvCount);

          FirstMatch = FALSE;
          } else {
            NewFoundFv = (FV_INFORMATION *) malloc (sizeof (FV_INFORMATION));
            if (NewFoundFv == NULL) {
              Error ("FMMT", 0, 0002, "Error searching FVs in the input fd", "Allocate memory error");
              free (LocalFdData->Fv);
              free (LocalFdData);
              free (FdBuffer);
              return EFI_OUT_OF_RESOURCES;
            }

            LibInitializeFvStruct (NewFoundFv);

            //
            // Need to turn back 0x2c bytes
            //
            NewFoundFv->ImageAddress = (UINTN)((UINT8 *)FdBuffer - (UINT8 *)FdBufferOri);

            //
            // Store the FV name by found sequence
            //
            sprintf(NewFoundFv->FvName, "FV%d", FvCount);

            //
            // Value it to NULL for found FV usage.
            //
            NewFoundFv->FvNext       = NULL;
            CurrentFv->FvNext        = NewFoundFv;

            //
            // Make the CurrentFv point to next FV.
            //
            CurrentFv                = CurrentFv->FvNext;
          }

        FvCount ++;
        FdBuffer = FdBuffer + FvHeader->FvLength;
      } else {
        FdBuffer ++;
      }

    } else {
      FdBuffer ++;
    }
  }

  LocalFdData->Size = FdSize;

  *FdData = LocalFdData;

  free (FdBufferOri);

  return EFI_SUCCESS;
}

UINTN
GetFreeOffset (
  IN   VOID   *InputFv
)
{
  UINTN FreeOffset;
  UINTN Offset;
  EFI_STATUS Status;
  EFI_FFS_FILE_HEADER2 *CurrentFile;

  Offset = 0;
  CurrentFile = NULL;
  FreeOffset = 0;
  do {
    FreeOffset = (UINTN)ALIGN_POINTER(Offset, 8);
    Status = FvBufFindNextFile(InputFv, &Offset, (VOID **)&CurrentFile);
    if (Status == EFI_NOT_FOUND) {
      CurrentFile = NULL;
      break;
    }
    else if (EFI_ERROR(Status)) {
      return Status;
    }
  } while (CurrentFile != NULL);

  return FreeOffset;
}

/**

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


  @param[in ]   InputFile       The file that contains the FV image.
  @param[out]   FvSize          The size of the FV.
  @param[out]   ErasePolarity   The FV erase polarity.

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

**/
EFI_STATUS
LibReadFvHeader (
  IN   VOID                       *InputFv,
  IN   BOOLEAN                    ViewFlag,
  IN   UINT8                      FvLevel,
  IN   UINT8                      FvCount,
  IN   CHAR8                      *FvName
  )
{
  EFI_FIRMWARE_VOLUME_HEADER     *VolumeHeader;
  CHAR8                          *BlankSpace;
  CHAR8                          *FvUiName;
  EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader;

  BlankSpace = NULL;
  FvUiName = NULL;

  //
  // Check input parameters
  //
  if (InputFv == NULL) {
    Error (__FILE__, __LINE__, 0, "FMMT application error", "invalid parameter to function");
    return EFI_INVALID_PARAMETER;
  }

  //
  // Read the header
  //
  VolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *) InputFv;


  BlankSpace = LibConstructBlankChar((FvLevel)*2);

  if (BlankSpace == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return EFI_OUT_OF_RESOURCES;
  }


  if (ViewFlag) {
    if ((FvLevel -1) == 0) {
      printf ("\n%s :\n", FvName);
    } else {
      printf ("%sChild FV named FV%d of %s\n", BlankSpace, FvCount, FvName);
    }
  }

  //
  // Print FV header information
  //
  if (ViewFlag) {
    printf ("\n%sAttributes:            %X\n", BlankSpace, (unsigned) VolumeHeader->Attributes);
    printf ("%sTotal Volume Size:     0x%08X\n", BlankSpace, (unsigned) VolumeHeader->FvLength);
    printf ("%sFree Volume Size:      0x%08X\n", BlankSpace, (unsigned) (VolumeHeader->FvLength - GetFreeOffset(InputFv)));
  }

  if (ViewFlag && VolumeHeader->ExtHeaderOffset != 0) {
    FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)((UINT8 *)VolumeHeader + VolumeHeader->ExtHeaderOffset);
    printf("%sFvNameGuid:            %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
           BlankSpace,
           FvExtHeader->FvName.Data1,
           FvExtHeader->FvName.Data2,
           FvExtHeader->FvName.Data3,
           FvExtHeader->FvName.Data4[0],
           FvExtHeader->FvName.Data4[1],
           FvExtHeader->FvName.Data4[2],
           FvExtHeader->FvName.Data4[3],
           FvExtHeader->FvName.Data4[4],
           FvExtHeader->FvName.Data4[5],
           FvExtHeader->FvName.Data4[6],
           FvExtHeader->FvName.Data4[7]);
    LibExtractFvUiName(FvExtHeader, &FvUiName);
    if (FvUiName != NULL && FvLevel == 1) {
      printf("%sFV UI Name:            %s\n\n", BlankSpace, FvUiName);
    }
    free(FvUiName);
  }
  free (BlankSpace);
  return EFI_SUCCESS;
}

/*
  Get size info from FV file.

  @param[in]
  @param[out]

  @retval

*/
EFI_STATUS
LibGetFvSize (
  IN   FILE                       *InputFile,
  OUT  UINT32                     *FvSize
  )
{

  UINTN                          BytesRead;
  UINT32                         Size;
  EFI_FV_BLOCK_MAP_ENTRY         BlockMap;

  BytesRead = 0;
  Size      = 0;

  if (InputFile == NULL || FvSize == NULL) {
    Error (__FILE__, __LINE__, 0, "FMMT application error", "invalid parameter to function");
    return EFI_INVALID_PARAMETER;
  }

  fseek (InputFile, sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY), SEEK_CUR);
  do {
    fread (&BlockMap, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
    BytesRead += sizeof (EFI_FV_BLOCK_MAP_ENTRY);

    if (BlockMap.NumBlocks != 0) {
      Size += BlockMap.NumBlocks * BlockMap.Length;
    }
  } while (!(BlockMap.NumBlocks == 0 && BlockMap.Length == 0));


  *FvSize = Size;

  return EFI_SUCCESS;
}

/**

  Clears out all files from the Fv buffer in memory

  @param[in]    Fv - Address of the Fv in memory

  @return       EFI_STATUS

**/
EFI_STATUS
FvBufGetSize (
  IN  VOID   *Fv,
  OUT UINTN  *Size
  )
{
  EFI_FIRMWARE_VOLUME_HEADER *hdr;
  EFI_FV_BLOCK_MAP_ENTRY     *blk;

  *Size = 0;
  hdr   = (EFI_FIRMWARE_VOLUME_HEADER*)Fv;
  blk   = hdr->BlockMap;

  while (blk->Length != 0 || blk->NumBlocks != 0) {
    *Size = *Size + (blk->Length * blk->NumBlocks);
    if (*Size >= 0x40000000) {
    //
      // If size is greater than 1GB, then assume it is corrupted
      //
      return EFI_VOLUME_CORRUPTED;
    }
    blk++;
  }

  if (*Size == 0) {
    //
    // If size is 0, then assume the volume is corrupted
    //
    return EFI_VOLUME_CORRUPTED;
  }

  return EFI_SUCCESS;
}
/**

  Iterates through the files contained within the firmware volume

  @param[in]    Fv  - Address of the Fv in memory
  @param[in]    Key - Should be 0 to get the first file.  After that, it should be
                      passed back in without modifying it's contents to retrieve
                      subsequent files.
  @param[in]    File- Output file pointer
                      File == NULL - invalid parameter
                      otherwise - *File will be update to the location of the file

  @return       EFI_STATUS
                EFI_NOT_FOUND
                EFI_VOLUME_CORRUPTED

**/
EFI_STATUS
FvBufFindNextFile (
  IN     VOID      *Fv,
  IN OUT UINTN     *Key,
  OUT    VOID      **File
  )
{
  EFI_FIRMWARE_VOLUME_HEADER *hdr;
  EFI_FFS_FILE_HEADER        *fhdr;
  EFI_FIRMWARE_VOLUME_EXT_HEADER  *FwVolExtHeader;
  EFI_FVB_ATTRIBUTES_2       FvbAttributes;
  UINTN                      fsize;
  EFI_STATUS                 Status;
  UINTN                      fvSize;

  hdr = (EFI_FIRMWARE_VOLUME_HEADER*)Fv;
  fhdr = NULL;

  if (Fv == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status = FvBufGetSize (Fv, &fvSize);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (*Key == 0) {
    if (hdr->ExtHeaderOffset != 0) {
      //
      // Searching for files starts on an 8 byte aligned boundary after the end of the Extended Header if it exists.
      //
      FwVolExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)((UINT8 *)hdr + hdr->ExtHeaderOffset);
      *Key = (UINTN)hdr->ExtHeaderOffset + FwVolExtHeader->ExtHeaderSize;
      *Key = (UINTN)ALIGN_POINTER(*Key, 8);
    } else {
      *Key = hdr->HeaderLength;
    }
  }

  FvbAttributes = hdr->Attributes;

  for(
      *Key = (UINTN)ALIGN_POINTER (*Key, 8);
      (*Key + sizeof (*fhdr)) < fvSize;
      *Key = (UINTN)ALIGN_POINTER (*Key, 8)
    ) {
    fhdr = (EFI_FFS_FILE_HEADER*) ((UINT8*)hdr + *Key);
    fsize = GetFfsFileLength (fhdr);
    if (!EFI_TEST_FFS_ATTRIBUTES_BIT(
          FvbAttributes,
          fhdr->State,
          EFI_FILE_HEADER_VALID
        ) ||
        EFI_TEST_FFS_ATTRIBUTES_BIT(
          FvbAttributes,
          fhdr->State,
          EFI_FILE_HEADER_INVALID
        )
      ) {
      *Key = *Key + 1;
      continue;
    } else if(
        EFI_TEST_FFS_ATTRIBUTES_BIT(
          FvbAttributes,
          fhdr->State,
          EFI_FILE_MARKED_FOR_UPDATE
        ) ||
        EFI_TEST_FFS_ATTRIBUTES_BIT(
          FvbAttributes,
          fhdr->State,
          EFI_FILE_DELETED
        )
      ) {
      *Key = *Key + fsize;
      continue;
    } else if (EFI_TEST_FFS_ATTRIBUTES_BIT(
          FvbAttributes,
          fhdr->State,
          EFI_FILE_DATA_VALID
        )
      ) {
      *File = (UINT8*)hdr + *Key;
      *Key = *Key + fsize;
      return EFI_SUCCESS;
    }

    *Key = *Key + 1;
  }

  return EFI_NOT_FOUND;
}
/**

  Get firmware information. Including the FV headers,

  @param[in]    Fv            - Firmware Volume to get information from

  @return       EFI_STATUS

**/
EFI_STATUS
LibGetFvInfo (
  IN     VOID                         *Fv,
  IN OUT FV_INFORMATION               *CurrentFv,
  IN     CHAR8                        *FvName,
  IN     UINT8                        Level,
  IN     ENCAP_INFO_DATA              **CurrentFvEncapData,
  IN     UINT32                       *FfsCount,
  IN OUT UINT8                        *FvCount,
  IN     BOOLEAN                      ViewFlag,
  IN     BOOLEAN                      IsChildFv
  )
{
  EFI_STATUS                  Status;
  UINTN                       NumberOfFiles;
  BOOLEAN                     ErasePolarity;
  UINTN                       FvSize;
  EFI_FFS_FILE_HEADER2        *CurrentFile;
  UINTN                       Key;
  ENCAP_INFO_DATA             *LocalEncapData;
  EFI_FIRMWARE_VOLUME_EXT_HEADER *ExtHdrPtr;
  EFI_FIRMWARE_VOLUME_HEADER *FvHdr;

  NumberOfFiles  = 0;
  Key            = 0;
  LocalEncapData = NULL;
  CurrentFile    = NULL;
  FvHdr = (EFI_FIRMWARE_VOLUME_HEADER *)Fv;


  Level += 1;
  *FvCount += 1;
  CurrentFv->FvLevel += 1;

  Status = FvBufGetSize (Fv, &FvSize);

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

  Status = LibReadFvHeader (Fv, ViewFlag, CurrentFv->FvLevel, *FvCount - 1, CurrentFv->FvName);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 0003, "error parsing FV image", "Header is invalid");
    return EFI_ABORTED;
  }

  if (!IsChildFv) {
    //
    // Write FV header information into CurrentFv struct.
    //
    CurrentFv->FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) malloc (FvHdr->HeaderLength);

    if (CurrentFv->FvHeader == NULL) {
      Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
      return EFI_ABORTED;
    }

    //
    // Get the FV Header information
    //
    memcpy(CurrentFv->FvHeader, Fv, FvHdr->HeaderLength);
    CurrentFv->FvExtHeader = NULL;
    CurrentFv->FvUiName = NULL;

    //
    // Exist Extend FV header.
    //
    if (CurrentFv->FvHeader->ExtHeaderOffset != 0){
      ExtHdrPtr = (VOID *)((UINTN)Fv + CurrentFv->FvHeader->ExtHeaderOffset);
      CurrentFv->FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) malloc (ExtHdrPtr->ExtHeaderSize);

      if (CurrentFv->FvExtHeader == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      //
      // Get the FV extended Header information
      //
      memcpy (CurrentFv->FvExtHeader, (VOID *)((UINTN)Fv + CurrentFv->FvHeader->ExtHeaderOffset), ExtHdrPtr->ExtHeaderSize);
      LibExtractFvUiName(CurrentFv->FvExtHeader, &CurrentFv->FvUiName);
    }
  }

  //
  // Put encapsulate information into structure.
  //
  LocalEncapData = *CurrentFvEncapData;
  if (LocalEncapData == NULL && !IsChildFv) {
    //
    // First time in, the root FV
    //
    LocalEncapData = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));
    CurrentFv->EncapData = LocalEncapData;
    if (CurrentFv->EncapData == NULL) {
      Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
      return EFI_ABORTED;
    }

    CurrentFv->EncapData->FvExtHeader = NULL;
    CurrentFv->EncapData->Depex = NULL;
    CurrentFv->EncapData->DepexLen = 0;
    CurrentFv->EncapData->UiNameSize = 0;
    CurrentFv->EncapData->Level = Level;
    CurrentFv->EncapData->Type  = FMMT_ENCAP_TREE_FV;
    CurrentFv->EncapData->Data  = (EFI_FIRMWARE_VOLUME_HEADER *) malloc (sizeof (EFI_FIRMWARE_VOLUME_HEADER));
    CurrentFv->EncapData->FvId  = *FvCount;

    if (CurrentFv->EncapData->Data == NULL) {
      Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
      return EFI_ABORTED;
    }

    memcpy (CurrentFv->EncapData->Data, Fv, sizeof (EFI_FIRMWARE_VOLUME_HEADER));

    if (((EFI_FIRMWARE_VOLUME_HEADER *)(CurrentFv->EncapData->Data))->ExtHeaderOffset != 0) {
      ExtHdrPtr = (VOID *)((UINTN)Fv + ((EFI_FIRMWARE_VOLUME_HEADER *)(CurrentFv->EncapData->Data))->ExtHeaderOffset);
      CurrentFv->EncapData->FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *) malloc (ExtHdrPtr->ExtHeaderSize);

      if (CurrentFv->EncapData->FvExtHeader == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      //
      // Get the FV extended Header information
      //
      memcpy(CurrentFv->EncapData->FvExtHeader, (VOID *)((UINTN)Fv + ((EFI_FIRMWARE_VOLUME_HEADER *)(CurrentFv->EncapData->Data))->ExtHeaderOffset), ExtHdrPtr->ExtHeaderSize);
    }

    CurrentFv->EncapData->NextNode  = NULL;
  CurrentFv->EncapData->RightNode  = NULL;
  } else if (LocalEncapData == NULL) {
    return EFI_ABORTED;
  } else if (IsChildFv) {

      LocalEncapData = *CurrentFvEncapData;
      while (LocalEncapData->NextNode != NULL) {
        LocalEncapData = LocalEncapData->NextNode;
      }

      //
      // Construct the new ENCAP_DATA
      //
      LocalEncapData->NextNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));

      if (LocalEncapData->NextNode == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      LocalEncapData           = LocalEncapData->NextNode;

      LocalEncapData->Level = Level;
      LocalEncapData->Type  = FMMT_ENCAP_TREE_FV;
      LocalEncapData->Data  = (EFI_FIRMWARE_VOLUME_HEADER *) malloc (sizeof (EFI_FIRMWARE_VOLUME_HEADER));
      LocalEncapData->FvExtHeader = NULL;
      LocalEncapData->Depex = NULL;
      LocalEncapData->DepexLen = 0;
      LocalEncapData->UiNameSize = 0;
      LocalEncapData->FvId  = *FvCount;
      if (LocalEncapData->Data == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      memcpy (LocalEncapData->Data, Fv, sizeof (EFI_FIRMWARE_VOLUME_HEADER));

      if (((EFI_FIRMWARE_VOLUME_HEADER *)(LocalEncapData->Data))->ExtHeaderOffset != 0) {
        ExtHdrPtr = (VOID *)((UINTN)Fv + ((EFI_FIRMWARE_VOLUME_HEADER *)(LocalEncapData->Data))->ExtHeaderOffset);
        LocalEncapData->FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)malloc(ExtHdrPtr->ExtHeaderSize);

        if (LocalEncapData->FvExtHeader == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }

        //
        // Get the FV extended Header information
        //
        memcpy(LocalEncapData->FvExtHeader, (VOID *)((UINTN)Fv + ((EFI_FIRMWARE_VOLUME_HEADER *)(LocalEncapData->Data))->ExtHeaderOffset), ExtHdrPtr->ExtHeaderSize);
      }

      LocalEncapData->NextNode  = NULL;
      LocalEncapData->RightNode  = NULL;

  }


  //
  // Get the first file
  //
  Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
  if (Status == EFI_NOT_FOUND) {
    CurrentFile = NULL;
  } else if (EFI_ERROR (Status)) {
    Error ("FMMT", 0, 0003, "error parsing FV image", "cannot find the first file in the FV image");
    return Status;
  }

  while (CurrentFile != NULL) {

    //
    // Increment the number of files counter
    //
    NumberOfFiles++;

    //
    // Store FFS file Header information
    //
    CurrentFv->FfsHeader[*FfsCount].Attributes       = CurrentFile->Attributes;
    CurrentFv->FfsHeader[*FfsCount].IntegrityCheck   = CurrentFile->IntegrityCheck;
    CurrentFv->FfsHeader[*FfsCount].Name             = CurrentFile->Name;
    CurrentFv->FfsHeader[*FfsCount].Size[0]          = CurrentFile->Size[0];
    CurrentFv->FfsHeader[*FfsCount].Size[1]          = CurrentFile->Size[1];
    CurrentFv->FfsHeader[*FfsCount].Size[2]          = CurrentFile->Size[2];
    CurrentFv->FfsHeader[*FfsCount].State            = CurrentFile->State;
    CurrentFv->FfsHeader[*FfsCount].Type             = CurrentFile->Type;
    CurrentFv->FfsHeader[*FfsCount].ExtendedSize     = CurrentFile->ExtendedSize;
    CurrentFv->FfsAttuibutes[*FfsCount].Offset = Key - GetFfsFileLength ((EFI_FFS_FILE_HEADER *) CurrentFile);

    CurrentFv->FfsAttuibutes[*FfsCount].FvLevel = CurrentFv->FvLevel;
     if (CurrentFv->FvLevel > CurrentFv->MulFvLevel) {
      CurrentFv->MulFvLevel = CurrentFv->FvLevel;
   }
    //
    // Display info about this file
    //
    Status = LibGetFileInfo (Fv, CurrentFile, ErasePolarity, CurrentFv, FvName, Level, &LocalEncapData, FfsCount, FvCount, ViewFlag);
    if (EFI_ERROR (Status)) {
      Error ("FMMT", 0, 0003, "error parsing FV image", "failed to parse a file in the FV");
      return Status;
    }

    //
    // Get the next file
    //
    Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
    if (Status == EFI_NOT_FOUND) {
      CurrentFile = NULL;
    } else if (EFI_ERROR (Status)) {
      Error ("FMMT", 0, 0003, "error parsing FV image", "cannot find the next file in the FV image");
      return Status;
    }
  }

  if (IsChildFv) {
      if (CurrentFv->FvLevel > 1) {
          CurrentFv->FvLevel -= 1;
      }
  }
  return EFI_SUCCESS;
}

/**

  TODO: Add function description

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

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

**/
EFI_STATUS
LibGetFileInfo (
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage,
  EFI_FFS_FILE_HEADER2        *CurrentFile,
  BOOLEAN                     ErasePolarity,
  FV_INFORMATION              *CurrentFv,
  CHAR8                       *FvName,
  UINT8                       Level,
  ENCAP_INFO_DATA             **CurrentFvEncapData,
  UINT32                      *FfsCount,
  UINT8                       *FvCount,
  BOOLEAN                     ViewFlag
  )
{
  UINT32              FileLength;
  UINT8               FileState;
  UINT8               Checksum;
  EFI_FFS_FILE_HEADER2 BlankHeader;
  EFI_STATUS          Status;
  UINT8               GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
  ENCAP_INFO_DATA     *LocalEncapData;
  BOOLEAN             EncapDataNeedUpdateFlag;
  BOOLEAN             IsGeneratedFfs;
  UINT32              FfsFileHeaderSize;

  Status = EFI_SUCCESS;

  LocalEncapData  = NULL;
  EncapDataNeedUpdateFlag = TRUE;
  IsGeneratedFfs   = FALSE;

  FfsFileHeaderSize = GetFfsHeaderLength  ((EFI_FFS_FILE_HEADER *) CurrentFile);
  FileLength        = GetFfsFileLength ((EFI_FFS_FILE_HEADER *) CurrentFile);

  //
  // Check if we have free space
  //
  if (ErasePolarity) {
    memset (&BlankHeader, -1, FfsFileHeaderSize);
  } else {
    memset (&BlankHeader, 0, FfsFileHeaderSize);
  }

  //
  // Is this FV blank?
  //
  if (memcmp (&BlankHeader, CurrentFile, FfsFileHeaderSize) == 0) {
    return EFI_SUCCESS;
  }

  //
  // Print file information.
  //
  FileState = GetFileState (ErasePolarity, (EFI_FFS_FILE_HEADER *)CurrentFile);
  PrintGuidToBuffer (&(CurrentFile->Name), GuidBuffer, PRINTED_GUID_BUFFER_SIZE, FALSE);
  if (FileState == EFI_FILE_DATA_VALID) {
    //
    // Calculate header checksum
    //
    Checksum  = CalculateSum8 ((UINT8 *) CurrentFile, FfsFileHeaderSize);
    Checksum  = (UINT8) (Checksum - CurrentFile->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - CurrentFile->State);
    if (Checksum != 0) {
      Error ("FMMT", 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum", GuidBuffer);
      return EFI_ABORTED;
    }

    if (CurrentFile->Attributes & FFS_ATTRIB_CHECKSUM) {
      //
      // Calculate file checksum
      //
      Checksum  = CalculateSum8 ((UINT8 *) ((UINTN)CurrentFile + FfsFileHeaderSize), FileLength - FfsFileHeaderSize);
      Checksum  = Checksum + CurrentFile->IntegrityCheck.Checksum.File;
      if (Checksum != 0) {
        Error ("FMMT", 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid file checksum", GuidBuffer);
        return EFI_ABORTED;
      }
    } else {
      if (CurrentFile->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
        Error ("FMMT", 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;
      }
    }
  } else {
    Error ("FMMT", 0, 0003, "error parsing FFS file", "FFS file with Guid %s has the invalid/unrecognized file state bits", GuidBuffer);
    return EFI_ABORTED;
  }

  Level += 1;

  if ((CurrentFile->Type != EFI_FV_FILETYPE_ALL) && (CurrentFile->Type != EFI_FV_FILETYPE_FFS_PAD)) {

    //
    // Put in encapsulate data information.
    //
    LocalEncapData = *CurrentFvEncapData;
    if (LocalEncapData->NextNode != NULL) {
      LocalEncapData = LocalEncapData->NextNode;
      EncapDataNeedUpdateFlag = FALSE;
      while (LocalEncapData->RightNode != NULL) {
        LocalEncapData = LocalEncapData->RightNode;
      }
    }

    if (EncapDataNeedUpdateFlag) {
      //
      // Construct the new ENCAP_DATA
      //
      LocalEncapData->NextNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));

      if (LocalEncapData->NextNode == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      LocalEncapData        = LocalEncapData->NextNode;

      LocalEncapData->Level = Level;
      LocalEncapData->Type  = FMMT_ENCAP_TREE_FFS;
      LocalEncapData->FvExtHeader = NULL;
      LocalEncapData->Depex = NULL;
      LocalEncapData->DepexLen = 0;
      LocalEncapData->UiNameSize = 0;
      //
      // Store the header of FFS file.
      //
      LocalEncapData->Data     = malloc (FfsFileHeaderSize);
      if (LocalEncapData->Data == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      memcpy (LocalEncapData->Data, CurrentFile, FfsFileHeaderSize);
      LocalEncapData->FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)malloc(sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER));
      if (LocalEncapData->FvExtHeader == NULL) {
        Error(NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
        }
      LocalEncapData->FvExtHeader->FvName.Data1 = CurrentFile->Name.Data1;
      LocalEncapData->FvExtHeader->FvName.Data2 = CurrentFile->Name.Data2;
      LocalEncapData->FvExtHeader->FvName.Data3 = CurrentFile->Name.Data3;
      LocalEncapData->FvExtHeader->FvName.Data4[0] = CurrentFile->Name.Data4[0];
      LocalEncapData->FvExtHeader->FvName.Data4[1] = CurrentFile->Name.Data4[1];
      LocalEncapData->FvExtHeader->FvName.Data4[2] = CurrentFile->Name.Data4[2];
      LocalEncapData->FvExtHeader->FvName.Data4[3] = CurrentFile->Name.Data4[3];
      LocalEncapData->FvExtHeader->FvName.Data4[4] = CurrentFile->Name.Data4[4];
      LocalEncapData->FvExtHeader->FvName.Data4[5] = CurrentFile->Name.Data4[5];
      LocalEncapData->FvExtHeader->FvName.Data4[6] = CurrentFile->Name.Data4[6];
      LocalEncapData->FvExtHeader->FvName.Data4[7] = CurrentFile->Name.Data4[7]; 
      LocalEncapData->NextNode = NULL;
    LocalEncapData->RightNode = NULL;
    }else{
      //
      // Construct the new ENCAP_DATA
      //
      LocalEncapData->RightNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));

      if (LocalEncapData->RightNode == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      LocalEncapData        = LocalEncapData->RightNode;

      LocalEncapData->Level = Level;
      LocalEncapData->Type  = FMMT_ENCAP_TREE_FFS;
    LocalEncapData->FvExtHeader = NULL;
      LocalEncapData->Depex = NULL;
      LocalEncapData->DepexLen = 0;
      LocalEncapData->UiNameSize = 0;
      //
      // Store the header of FFS file.
      //
      LocalEncapData->Data     = malloc (FfsFileHeaderSize);
      if (LocalEncapData->Data == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }

      memcpy (LocalEncapData->Data, CurrentFile, FfsFileHeaderSize);
      LocalEncapData->FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)malloc(sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER));
      if (LocalEncapData->FvExtHeader == NULL) {
        Error(NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }
      LocalEncapData->FvExtHeader->FvName.Data1 = CurrentFile->Name.Data1;
      LocalEncapData->FvExtHeader->FvName.Data2 = CurrentFile->Name.Data2;
      LocalEncapData->FvExtHeader->FvName.Data3 = CurrentFile->Name.Data3;
      LocalEncapData->FvExtHeader->FvName.Data4[0] = CurrentFile->Name.Data4[0];
      LocalEncapData->FvExtHeader->FvName.Data4[1] = CurrentFile->Name.Data4[1];
      LocalEncapData->FvExtHeader->FvName.Data4[2] = CurrentFile->Name.Data4[2];
      LocalEncapData->FvExtHeader->FvName.Data4[3] = CurrentFile->Name.Data4[3];
      LocalEncapData->FvExtHeader->FvName.Data4[4] = CurrentFile->Name.Data4[4];
      LocalEncapData->FvExtHeader->FvName.Data4[5] = CurrentFile->Name.Data4[5];
      LocalEncapData->FvExtHeader->FvName.Data4[6] = CurrentFile->Name.Data4[6];
      LocalEncapData->FvExtHeader->FvName.Data4[7] = CurrentFile->Name.Data4[7];
      LocalEncapData->RightNode = NULL;
      LocalEncapData->NextNode = NULL;
    }

   if ( CurrentFile->Type == EFI_FV_FILETYPE_RAW){
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag){
        LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
      }
    } else if( CurrentFile->Type == EFI_FV_FILETYPE_FFS_PAD){
      //EFI_FV_FILETYPE_FFS_PAD
    } else {
    //
    // All other files have sections
    //
    Status = LibParseSection (
      (UINT8 *) ((UINTN) CurrentFile + FfsFileHeaderSize),
      FileLength - FfsFileHeaderSize,
      CurrentFv,
      FvName,
      CurrentFile,
      Level,
      &LocalEncapData,
      Level,
      FfsCount,
      FvCount,
      ViewFlag,
      ErasePolarity,
      &IsGeneratedFfs
      );
    }
    if (EFI_ERROR (Status)) {
      printf ("ERROR: Parsing the FFS file.\n");
      return Status;
    }
  }


  return EFI_SUCCESS;
}

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';
}

/**

  Parses EFI Sections, if the view flag turn on, then will collect FFS section information
  and extract FFS files.

  @param[in]      SectionBuffer - Buffer containing the section to parse.
  @param[in]      BufferLength  - Length of SectionBuffer
  @param[in, out] CurrentFv
  @param[in]      FvName
  @param[in]      CurrentFile
  @param[in]      Level
  @param[in, out] FfsCount
  @param[in]      ViewFlag
  @param[in]      ErasePolarity

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

--*/
EFI_STATUS
LibParseSection (
  UINT8                  *SectionBuffer,
  UINT32                 BufferLength,
  FV_INFORMATION         *CurrentFv,
  CHAR8                  *FvName,
  EFI_FFS_FILE_HEADER2   *CurrentFile,
  UINT8                  Level,
  ENCAP_INFO_DATA        **CurrentFvEncapData,
  UINT8                  FfsLevel,
  UINT32                 *FfsCount,
  UINT8                  *FvCount,
  BOOLEAN                ViewFlag,
  BOOLEAN                ErasePolarity,
  BOOLEAN                *IsFfsGenerated
  )
{
  UINT32              ParsedLength;
  UINT8               *Ptr;
  UINT32              SectionLength;
  UINT32              UiSectionLength;
  EFI_SECTION_TYPE    Type;
  EFI_STATUS          Status;
  CHAR8               *ExtractionTool;
  CHAR8               *ToolInputFile;
  CHAR8               *ToolOutputFile;
  CHAR8               *SystemCommandFormatString;
  CHAR8               *SystemCommand;
  UINT8               *ToolOutputBuffer;
  UINT32              ToolOutputLength;
  CHAR16              *UIName;
  UINT32              UINameSize;
  BOOLEAN             HasDepexSection;
  UINT32              NumberOfSections;
  ENCAP_INFO_DATA     *LocalEncapData;
  ENCAP_INFO_DATA     *LocalEncapDataTemp;
  CHAR8               *BlankChar;
  UINT8               *UncompressedBuffer;
  UINT32              UncompressedLength;
  UINT8               *CompressedBuffer;
  UINT32              CompressedLength;
  UINT8               CompressionType;
  DECOMPRESS_FUNCTION DecompressFunction;
  GETINFO_FUNCTION    GetInfoFunction;
  UINT32              DstSize;
  UINT32              ScratchSize;
  UINT8               *ScratchBuffer;
  BOOLEAN             EncapDataNeedUpdata;
  CHAR8               *TempDir;
  CHAR8               *ToolInputFileFullName;
  CHAR8               *ToolOutputFileFullName;
  UINT8               LargeHeaderOffset;
  UINT16              GuidAttr;
  UINT16              DataOffset;
  CHAR8               *UIFileName;
  CHAR8               *ToolInputFileName;
  CHAR8               *ToolOutputFileName;

  DataOffset                 = 0;
  GuidAttr                   = 0;
  ParsedLength               = 0;
  ToolOutputLength           = 0;
  UINameSize                 = 0;
  NumberOfSections           = 0;
  UncompressedLength         = 0;
  CompressedLength           = 0;
  CompressionType            = 0;
  DstSize                    = 0;
  ScratchSize                = 0;
  Ptr                        = NULL;
  ExtractionTool             = NULL;
  ToolInputFile              = NULL;
  ToolOutputFile             = NULL;
  SystemCommand              = NULL;
  SystemCommandFormatString  = NULL;
  ToolOutputBuffer           = NULL;
  UIName                     = NULL;
  LocalEncapData             = NULL;
  LocalEncapDataTemp         = NULL;
  BlankChar                  = NULL;
  UncompressedBuffer         = NULL;
  CompressedBuffer           = NULL;
  ScratchBuffer              = NULL;
  TempDir                    = NULL;
  ToolInputFileFullName      = NULL;
  ToolOutputFileFullName     = NULL;
  ToolInputFileName          = NULL;
  ToolOutputFileFullName     = NULL;
  HasDepexSection            = FALSE;
  EncapDataNeedUpdata        = TRUE;
  LargeHeaderOffset          = 0;


  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;
    }
  //
  //If Size is 0xFFFFFF then ExtendedSize contains the size of the section.
  //
    if (SectionLength == 0xffffff) {
    SectionLength     = ((EFI_COMMON_SECTION_HEADER2 *) Ptr)->ExtendedSize;
    LargeHeaderOffset = sizeof (EFI_COMMON_SECTION_HEADER2) - sizeof (EFI_COMMON_SECTION_HEADER);
  }

    switch (Type) {

    case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:

      EncapDataNeedUpdata = TRUE;

      Level ++;
      NumberOfSections ++;

      CurrentFv->FfsAttuibutes[*FfsCount].IsLeaf = FALSE;
      CurrentFv->FfsAttuibutes[*FfsCount].IsFvStart = TRUE;
      //
      // Put in encapsulate data information.
      //
      LocalEncapData = *CurrentFvEncapData;
      if (LocalEncapData->NextNode != NULL) {
        LocalEncapData = LocalEncapData->NextNode;
        while (LocalEncapData->RightNode != NULL) {
          LocalEncapData = LocalEncapData->RightNode;
        }
      }

      if (EncapDataNeedUpdata) {
        //
        // Put in this is an FFS with FV section
        //

        //
        // Construct the new ENCAP_DATA
        //
        LocalEncapData->NextNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));

        if (LocalEncapData->NextNode == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }

        LocalEncapData        = LocalEncapData->NextNode;

        LocalEncapData->Level = Level;
        LocalEncapData->Type  = FMMT_ENCAP_TREE_FV_SECTION;

        //
        // We don't need additional data for encapsulate this FFS but type.
        //
        LocalEncapData->Data        = NULL;
        LocalEncapData->FvExtHeader = NULL;
        LocalEncapData->NextNode    = NULL;
    LocalEncapData->RightNode = NULL;
        LocalEncapData->Depex = NULL;
        LocalEncapData->DepexLen = 0;
        LocalEncapData->UiNameSize = 0;
        LocalEncapData->FvId  = *FvCount;
      }

     //
     //save parent level FFS file's GUID name
     //
     LocalEncapDataTemp = CurrentFv->EncapData;
     while (LocalEncapDataTemp->NextNode != NULL) {
         if (LocalEncapDataTemp->Level == FfsLevel) {
           while (LocalEncapDataTemp->RightNode != NULL) {
             LocalEncapDataTemp = LocalEncapDataTemp->RightNode;
           }
             if (LocalEncapDataTemp != NULL && LocalEncapDataTemp->FvExtHeader == NULL) {
                 LocalEncapDataTemp->FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)malloc(sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER));
                 if (LocalEncapDataTemp->FvExtHeader == NULL) {
                     Error(NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
                     return EFI_ABORTED;
                 }

                 if (*FfsCount >= 1) {
                    if ((memcmp(&CurrentFv->FfsAttuibutes[*FfsCount - 1].GuidName, &(LocalEncapDataTemp->FvExtHeader->FvName), sizeof(EFI_GUID)) == 0)) {
                        memcpy(LocalEncapDataTemp->UiName, CurrentFv->FfsAttuibutes[*FfsCount - 1].UiName, _MAX_PATH);
                        LocalEncapDataTemp->UiNameSize = CurrentFv->FfsAttuibutes[*FfsCount - 1].UiNameSize;
                        LocalEncapDataTemp->DepexLen = CurrentFv->FfsAttuibutes[*FfsCount - 1].DepexLen;
                        LocalEncapDataTemp->Depex = malloc (LocalEncapDataTemp->DepexLen);
                        if (LocalEncapDataTemp->Depex == NULL) {
                            Error(NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
                            return EFI_ABORTED;
                        }
                        memcpy(LocalEncapDataTemp->Depex, CurrentFv->FfsAttuibutes[*FfsCount - 1].Depex, LocalEncapDataTemp->DepexLen);
                    }
                 }
             }
             break;
         }
         LocalEncapDataTemp = LocalEncapDataTemp->NextNode;
     }

      Status = LibGetFvInfo ((UINT8*)((EFI_FIRMWARE_VOLUME_IMAGE_SECTION*)Ptr + 1) + LargeHeaderOffset, CurrentFv, FvName, Level, &LocalEncapData, FfsCount, FvCount, ViewFlag, TRUE);
      if (EFI_ERROR (Status)) {
        Error ("FMMT", 0, 0003, "printing of FV section contents failed", NULL);
        return EFI_SECTION_ERROR;
      }
      if (*FfsCount >= 1) {
        CurrentFv->FfsAttuibutes[*FfsCount -1].IsFvEnd = TRUE;
      }
      break;

    case EFI_SECTION_COMPRESSION:
      Level ++;
      NumberOfSections ++;

      EncapDataNeedUpdata = TRUE;
      //
      // Put in encapsulate data information.
      //
      LocalEncapData = *CurrentFvEncapData;
      if (LocalEncapData->NextNode != NULL) {
        EncapDataNeedUpdata = FALSE;
        while (LocalEncapData->RightNode != NULL) {
          LocalEncapData = LocalEncapData->RightNode;
        }
      }

      if (EncapDataNeedUpdata) {
        //
        // Put in this is an FFS with FV section
        //

        //
        // Construct the new ENCAP_DATA
        //
        LocalEncapData->NextNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));

        if (LocalEncapData->NextNode == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
          }

        LocalEncapData        = LocalEncapData->NextNode;

        LocalEncapData->Level = Level;
        LocalEncapData->Type  = FMMT_ENCAP_TREE_COMPRESS_SECTION;

        //
        // Store the compress type
        //
        LocalEncapData->Data     = malloc (sizeof (UINT8));

        if (LocalEncapData->Data == NULL) {
          Error ("FMMT", 0, 0003, "Allocate memory failed", NULL);
          return EFI_OUT_OF_RESOURCES;
        }

        *(UINT8 *)LocalEncapData->Data     = ((EFI_COMPRESSION_SECTION *) (Ptr + LargeHeaderOffset))->CompressionType;
        LocalEncapData->FvExtHeader = NULL;
        LocalEncapData->NextNode = NULL;
    LocalEncapData->RightNode = NULL;
      } else {
        LocalEncapData->RightNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));
        if (LocalEncapData->RightNode == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }
        LocalEncapData        = LocalEncapData->RightNode;

        LocalEncapData->Level = Level;
        LocalEncapData->Type  = FMMT_ENCAP_TREE_COMPRESS_SECTION;

        //
        // Store the compress type
        //
        LocalEncapData->Data     = malloc (sizeof (UINT8));

        if (LocalEncapData->Data == NULL) {
          Error ("FMMT", 0, 0003, "Allocate memory failed", NULL);
          return EFI_OUT_OF_RESOURCES;
        }

        *(UINT8 *)LocalEncapData->Data     = ((EFI_COMPRESSION_SECTION *) (Ptr + LargeHeaderOffset))->CompressionType;
        LocalEncapData->FvExtHeader = NULL;
        LocalEncapData->NextNode = NULL;
        LocalEncapData->RightNode = NULL;

      }

      //
      // Process compressed section
      //
      CurrentFv->FfsAttuibutes[*FfsCount].IsLeaf = FALSE;

      UncompressedBuffer  = NULL;
      CompressedLength    = SectionLength - sizeof (EFI_COMPRESSION_SECTION) - LargeHeaderOffset;
      UncompressedLength  = ((EFI_COMPRESSION_SECTION *) (Ptr + LargeHeaderOffset))->UncompressedLength;
      CompressionType     = ((EFI_COMPRESSION_SECTION *) (Ptr + LargeHeaderOffset))->CompressionType;

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

        UncompressedBuffer = Ptr + sizeof (EFI_COMPRESSION_SECTION) + LargeHeaderOffset;
      } else if (CompressionType == EFI_STANDARD_COMPRESSION) {
        GetInfoFunction     = EfiGetInfo;
        DecompressFunction  = EfiDecompress;

        CompressedBuffer  = Ptr + sizeof (EFI_COMPRESSION_SECTION) + LargeHeaderOffset;

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

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

        ScratchBuffer       = malloc (ScratchSize);
        if (ScratchBuffer == NULL) {
          Error ("FMMT", 0, 0003, "Allocate memory failed", NULL);
          return EFI_OUT_OF_RESOURCES;
        }
        UncompressedBuffer  = malloc (UncompressedLength);
        if (UncompressedBuffer == NULL) {
          Error ("FMMT", 0, 0003, "Allocate memory failed", NULL);
          free (ScratchBuffer);
          return EFI_OUT_OF_RESOURCES;
        }
        //
        // Decompress the section.
        //
        Status = DecompressFunction (
                  CompressedBuffer,
                  CompressedLength,
                  UncompressedBuffer,
                  UncompressedLength,
                  ScratchBuffer,
                  ScratchSize
                  );
        free (ScratchBuffer);
        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0003, "decompress failed", NULL);
          free (UncompressedBuffer);
          return EFI_SECTION_ERROR;
        }
      } else {
        Error ("FMMT", 0, 0003, "unrecognized compression type", "type 0x%X", CompressionType);
        return EFI_SECTION_ERROR;
      }

      Status = LibParseSection (  UncompressedBuffer,
                                  UncompressedLength,
                                  CurrentFv,
                                  FvName,
                                  CurrentFile,
                                  Level,
                                  &LocalEncapData,
                                  FfsLevel,
                                  FfsCount,
                                  FvCount,
                                  ViewFlag,
                                  ErasePolarity,
                                  IsFfsGenerated);

      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:
      //
      // Process GUID defined
      // looks up the appropriate tool to use for extracting
      // a GUID defined FV section.
      //
      Level ++;
      NumberOfSections++;
      EncapDataNeedUpdata = TRUE;
      //
      // Put in encapsulate data information.
      //
      LocalEncapData = *CurrentFvEncapData;
      if (LocalEncapData->NextNode != NULL) {
        EncapDataNeedUpdata = FALSE;
        while (LocalEncapData->RightNode != NULL) {
          LocalEncapData = LocalEncapData->RightNode;
        }
      }
      GuidAttr = ((EFI_GUID_DEFINED_SECTION *)(Ptr + LargeHeaderOffset))->Attributes;
      DataOffset = ((EFI_GUID_DEFINED_SECTION *)(Ptr + LargeHeaderOffset))->DataOffset;

      if ((ViewFlag) && ((GuidAttr & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0)) {
        ToolOutputBuffer = Ptr + DataOffset;
        ToolOutputLength = SectionLength - DataOffset;
        Status = LibParseSection(
                ToolOutputBuffer,
                ToolOutputLength,
                CurrentFv,
                FvName,
                CurrentFile,
                Level,
                &LocalEncapData,
                FfsLevel,
                FfsCount,
                FvCount,
                ViewFlag,
                ErasePolarity,
                IsFfsGenerated
                );
        if (EFI_ERROR(Status)) {
          Error(NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL);
          return EFI_SECTION_ERROR;
        }
        break;
      }

      if (EncapDataNeedUpdata)  {

        //
        // Put in this is an FFS with FV section
        //

        //
        // Construct the new ENCAP_DATA
        //
        LocalEncapData->NextNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));

        if (LocalEncapData->NextNode == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }

        LocalEncapData        = LocalEncapData->NextNode;

        LocalEncapData->Level = Level;
        LocalEncapData->Type  = FMMT_ENCAP_TREE_GUIDED_SECTION;
        LocalEncapData->Depex = NULL;
        LocalEncapData->DepexLen = 0;
        LocalEncapData->UiNameSize = 0;
        //
        // We don't need additional data for encapsulate this FFS but type.
        // include DataOffset + Attributes
        //

        LocalEncapData->Data     = (EFI_GUID *) malloc (sizeof (EFI_GUID) + 4);

        if (LocalEncapData->Data == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }

        //
        // include guid attribute and dataoffset
        //
        memcpy (LocalEncapData->Data, Ptr + LargeHeaderOffset + OFFSET_OF (EFI_GUID_DEFINED_SECTION, SectionDefinitionGuid), sizeof (EFI_GUID) + 4);

        LocalEncapData->FvExtHeader = NULL;
        LocalEncapData->NextNode = NULL;
    LocalEncapData->RightNode = NULL;
      } else {
        LocalEncapData->RightNode = (ENCAP_INFO_DATA *) malloc (sizeof (ENCAP_INFO_DATA));
        if (LocalEncapData->RightNode == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
          }
        LocalEncapData        = LocalEncapData->RightNode;
        LocalEncapData->Level = Level;
        LocalEncapData->Type  = FMMT_ENCAP_TREE_GUIDED_SECTION;
        LocalEncapData->Depex = NULL;
        LocalEncapData->DepexLen = 0;
        LocalEncapData->UiNameSize = 0;
        //
        // We don't need additional data for encapsulate this FFS but type.
        // include DataOffset + Attributes
        //

        LocalEncapData->Data     = (EFI_GUID *) malloc (sizeof (EFI_GUID) + 4);

        if (LocalEncapData->Data == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }

        //
        // include guid attribute and dataoffset
        //
        memcpy (LocalEncapData->Data, Ptr + LargeHeaderOffset + OFFSET_OF (EFI_GUID_DEFINED_SECTION, SectionDefinitionGuid), sizeof (EFI_GUID) + 4);

        LocalEncapData->FvExtHeader = NULL;
        LocalEncapData->NextNode = NULL;
    LocalEncapData->RightNode = NULL;
      }

      CurrentFv->FfsAttuibutes[*FfsCount].IsLeaf = FALSE;

      ExtractionTool =
        LookupGuidedSectionToolPath (
          mParsedGuidedSectionTools,
          &((EFI_GUID_DEFINED_SECTION *) (Ptr + LargeHeaderOffset))->SectionDefinitionGuid
          );

      if (ExtractionTool != NULL && ((GuidAttr & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) != 0)) {

        TempDir = getcwd (NULL, _MAX_PATH);
        if (strlen (TempDir) + strlen(OS_SEP_STR) + strlen (TEMP_DIR_NAME) > _MAX_PATH - 1) {
          Error("FMMT", 0, 1001, "The directory is too long.", "");
          free (ExtractionTool);
          return EFI_SECTION_ERROR;
        }
        strncat (TempDir, OS_SEP_STR, _MAX_PATH - strlen(TempDir) - 1);
        strncat (TempDir, TEMP_DIR_NAME, _MAX_PATH - strlen(TempDir) - 1);
        mkdir(TempDir, S_IRWXU | S_IRWXG | S_IRWXO);
        ToolInputFile  = GenTempFile ();
        if (ToolInputFile == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          free (ExtractionTool);
          return EFI_OUT_OF_RESOURCES;
        }
        ToolOutputFile = GenTempFile ();
        if (ToolOutputFile == NULL) {
          free (ToolInputFile);
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          free (ExtractionTool);
          return EFI_OUT_OF_RESOURCES;
        }
        ToolInputFileName = strrchr(ToolInputFile, OS_SEP);
        if (ToolInputFileName == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ExtractionTool);
          return EFI_ABORTED;
        }
        ToolOutputFileName = strrchr(ToolOutputFile, OS_SEP);
        if (ToolOutputFileName == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ExtractionTool);
          return EFI_ABORTED;
        }

        ToolInputFileFullName   = malloc (strlen("%s%s") + strlen(TempDir) + strlen(ToolInputFileName) + 1);
        if (ToolInputFileFullName == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ExtractionTool);
          Error ("FMMT", 0, 0003, "Allocate memory failed", NULL);
          return EFI_OUT_OF_RESOURCES;
        }
        ToolOutputFileFullName  = malloc (strlen("%s%s") + strlen(TempDir) + strlen(ToolOutputFileName) + 1);

        if (ToolOutputFileFullName == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ToolInputFileFullName);
          free (ExtractionTool);
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_OUT_OF_RESOURCES;
        }

        sprintf (ToolInputFileFullName, "%s%s", TempDir, ToolInputFileName);
        sprintf (ToolOutputFileFullName, "%s%s", TempDir, ToolOutputFileName);

        //
        // Construction 'system' command string
        //
        SystemCommandFormatString = "%s -d -o \"%s\" \"%s\"";
        SystemCommand = malloc (
          strlen (SystemCommandFormatString) +
          strlen (ExtractionTool) +
          strlen (ToolInputFileFullName) +
          strlen (ToolOutputFileFullName) +
          1
          );
        if (SystemCommand == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ToolInputFileFullName);
          free (ToolOutputFileFullName);
          free (ExtractionTool);
          return EFI_ABORTED;
        }
        sprintf (
          SystemCommand,
          "%s -d -o \"%s\" \"%s\"",
          ExtractionTool,
          ToolOutputFileFullName,
          ToolInputFileFullName
          );
        free (ExtractionTool);
        ExtractionTool = NULL;

        Status = PutFileImage (
        ToolInputFileFullName,
        (CHAR8*) Ptr + ((EFI_GUID_DEFINED_SECTION *) (Ptr + LargeHeaderOffset))->DataOffset,
        SectionLength - ((EFI_GUID_DEFINED_SECTION *) (Ptr + LargeHeaderOffset))->DataOffset
        );

        if (HasDepexSection) {
          HasDepexSection = FALSE;
        }

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "unable to decoded GUIDED section", NULL);
          free (SystemCommand);
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ToolOutputFileFullName);
          remove (ToolInputFileFullName);
          free (ToolInputFileFullName);
          return EFI_SECTION_ERROR;
        }

        if (system (SystemCommand) != EFI_SUCCESS) {
          printf("Command failed: %s\n", SystemCommand);
          free (SystemCommand);
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ToolOutputFileFullName);
          remove (ToolInputFileFullName);
          free (ToolInputFileFullName);
          return EFI_ABORTED;
        }
        free (SystemCommand);
        remove (ToolInputFileFullName);
        free (ToolInputFile);
        free (ToolInputFileFullName);
        ToolInputFile = NULL;
        ToolInputFileFullName = NULL;


        Status = GetFileImage (
                   ToolOutputFileFullName,
                   (CHAR8 **)&ToolOutputBuffer,
                   &ToolOutputLength
                   );
        remove (ToolOutputFileFullName);
        free (ToolOutputFile);
        free (ToolOutputFileFullName);
        ToolOutputFile = NULL;
        ToolOutputFileFullName = NULL;

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "unable to read decoded GUIDED section", NULL);
          return EFI_SECTION_ERROR;
        }

        Status = LibParseSection (
                  ToolOutputBuffer,
                  ToolOutputLength,
                  CurrentFv,
                  FvName,
                  CurrentFile,
                  Level,
                  &LocalEncapData,
                  FfsLevel,
                  FfsCount,
                  FvCount,
                  ViewFlag,
                  ErasePolarity,
                  IsFfsGenerated
                  );
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL);
          return EFI_SECTION_ERROR;
        }
      } else if ((GuidAttr & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0){
          Status = LibParseSection (
            Ptr + ((EFI_GUID_DEFINED_SECTION *) (Ptr + LargeHeaderOffset))->DataOffset,
            SectionLength - ((EFI_GUID_DEFINED_SECTION *) (Ptr + LargeHeaderOffset))->DataOffset,
            CurrentFv,
            FvName,
            CurrentFile,
            Level,
            &LocalEncapData,
            FfsLevel,
            FfsCount,
            FvCount,
            ViewFlag,
            ErasePolarity,
            IsFfsGenerated
            );
          if (ExtractionTool != NULL) {
            free (ExtractionTool);
            ExtractionTool = NULL;
          }
          if (EFI_ERROR (Status)) {
            Error (NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL);
            return EFI_SECTION_ERROR;
          }
      }else {
        //
        // We don't know how to parse it now.
        //
        if (ExtractionTool != NULL) {
          free (ExtractionTool);
          ExtractionTool = NULL;
        }
        Error ("FMMT", 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 %s file.", mGuidToolDefinition);
        printf("  Its GUID is: ");
        PrintGuid(&(((EFI_GUID_DEFINED_SECTION *)(Ptr + LargeHeaderOffset))->SectionDefinitionGuid));
        return EFI_UNSUPPORTED;
      }
      break;

    //
    //Leaf sections
    //
    case EFI_SECTION_RAW:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag) {
        if (!*IsFfsGenerated) {
          LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
          *IsFfsGenerated = TRUE;
        }
      }

      break;
    case EFI_SECTION_PE32:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag) {
        if (!*IsFfsGenerated) {
          LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
          *IsFfsGenerated = TRUE;
        }
      }

      break;
    case EFI_SECTION_PIC:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag) {
        if (!*IsFfsGenerated) {
          LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
          *IsFfsGenerated = TRUE;
        }
      }

      break;
    case EFI_SECTION_TE:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag) {
        if (!*IsFfsGenerated) {
          LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
          *IsFfsGenerated = TRUE;
        }
      }
      break;

    case EFI_SECTION_COMPATIBILITY16:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag) {
        if (!*IsFfsGenerated) {
          LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
          *IsFfsGenerated = TRUE;
        }
      }
      break;

    case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      if (!ViewFlag) {
        if (!*IsFfsGenerated) {
          LibGenFfsFile(CurrentFile, CurrentFv, FvName, Level, FfsCount, ErasePolarity);
          *IsFfsGenerated = TRUE;
        }
      }
      break;

    case EFI_SECTION_VERSION:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      break;
    case EFI_SECTION_PEI_DEPEX:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      HasDepexSection = TRUE;
      CurrentFv->FfsAttuibutes[*FfsCount].Depex = malloc (SectionLength);
      memcpy(CurrentFv->FfsAttuibutes[*FfsCount].Depex, Ptr, SectionLength);
      CurrentFv->FfsAttuibutes[*FfsCount].DepexLen = SectionLength;
      break;
    case EFI_SECTION_DXE_DEPEX:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      HasDepexSection = TRUE;
      CurrentFv->FfsAttuibutes[*FfsCount].Depex = malloc (SectionLength);
      memcpy(CurrentFv->FfsAttuibutes[*FfsCount].Depex, Ptr, SectionLength);
      CurrentFv->FfsAttuibutes[*FfsCount].DepexLen = SectionLength;
      break;
    case EFI_SECTION_SMM_DEPEX:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;
      HasDepexSection = TRUE;
      CurrentFv->FfsAttuibutes[*FfsCount].Depex = malloc (SectionLength);
      memcpy(CurrentFv->FfsAttuibutes[*FfsCount].Depex, Ptr, SectionLength);
      CurrentFv->FfsAttuibutes[*FfsCount].DepexLen = SectionLength;
      break;

    case EFI_SECTION_USER_INTERFACE:
      NumberOfSections ++;
      CurrentFv->FfsAttuibutes[*FfsCount].Level = Level;

      UiSectionLength = GetLength (((EFI_USER_INTERFACE_SECTION *) Ptr)->CommonHeader.Size);
    if (UiSectionLength == 0xffffff) {
      UiSectionLength   = ((EFI_USER_INTERFACE_SECTION2 *) Ptr)->CommonHeader.ExtendedSize;
    UINameSize        = UiSectionLength - sizeof(EFI_COMMON_SECTION_HEADER2);
    } else {
      UINameSize = UiSectionLength - sizeof(EFI_COMMON_SECTION_HEADER);
    }

      UIName     = (CHAR16 *) malloc (UINameSize + 2);
      if (UIName != NULL) {
        memset (UIName, '\0', UINameSize + 2);
        if (UiSectionLength >= 0xffffff) {
          memcpy(UIName, ((EFI_USER_INTERFACE_SECTION2 *) Ptr)->FileNameString, UINameSize);
        } else {
          memcpy(UIName, ((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString, UINameSize);
        }
      } else {
        Error ("FMMT", 0, 0001, "Memory allocate error!", NULL);
        return EFI_OUT_OF_RESOURCES;
      }

      BlankChar = LibConstructBlankChar( CurrentFv->FvLevel * 2);
      if (BlankChar == NULL) {
        free(UIName);
        return EFI_OUT_OF_RESOURCES;
      }

      if (ViewFlag) {
        UIFileName = malloc (UINameSize + 2);
        if (UIFileName == NULL) {
          Error ("FMMT", 0, 4001, "Memory allocation fail!", NULL);
          free (UIName);
          free (BlankChar);
          return EFI_OUT_OF_RESOURCES;
        }
        Unicode2AsciiString (UIName, UIFileName);
        fprintf(stdout, "%sFile \"%s\"\n", BlankChar, UIFileName);
        free(UIFileName);
      }
      free (BlankChar);

      //
      // If Ffs file has been generated, then the FfsCount should decrease 1.
      //
      if (*IsFfsGenerated) {
        memcpy (CurrentFv->FfsAttuibutes[*FfsCount -1].UiName, UIName, UINameSize);
        CurrentFv->FfsAttuibutes[*FfsCount -1].UiNameSize = UINameSize;
      } else {
        memcpy (CurrentFv->FfsAttuibutes[*FfsCount].UiName, UIName, UINameSize);
        CurrentFv->FfsAttuibutes[*FfsCount].UiNameSize = UINameSize;
      }

      HasDepexSection = FALSE;
    free(UIName);
    UINameSize = 0;

      break;
    default:
      break;
    }

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

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


  return EFI_SUCCESS;
}

/*
  Generate the leaf FFS files.

*/
EFI_STATUS
LibGenFfsFile (
  EFI_FFS_FILE_HEADER2   *CurrentFile,
  FV_INFORMATION         *CurrentFv,
  CHAR8                  *FvName,
  UINT8                  Level,
  UINT32                 *FfsCount,
  BOOLEAN                ErasePolarity
)
{
  UINT32                      FfsFileSize;
  CHAR8                       *FfsFileName;
  FILE                        *FfsFile;
  CHAR8                       *TempDir;


  FfsFileSize   = 0;
  FfsFileName   = NULL;
  FfsFile       = NULL;
  TempDir       = NULL;

  TempDir = getcwd (NULL, _MAX_PATH);
  if (strlen (TempDir) + strlen(OS_SEP_STR) + strlen (TEMP_DIR_NAME) > _MAX_PATH - 1) {
    Error("FMMT", 0, 1001,  "The directory is too long.", "");
    return EFI_ABORTED;
  }
  strncat (TempDir, OS_SEP_STR, _MAX_PATH - strlen(TempDir) - 1);
  strncat (TempDir, TEMP_DIR_NAME, _MAX_PATH - strlen(TempDir) - 1);

  mkdir(TempDir, S_IRWXU | S_IRWXG | S_IRWXO);

  FfsFileName = (CHAR8 *) malloc (_MAX_PATH);
  if (FfsFileName == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return EFI_ABORTED;
  }
  memset (FfsFileName, '\0', _MAX_PATH);
  FfsFileSize = GetFfsFileLength ((EFI_FFS_FILE_HEADER *) CurrentFile);
  sprintf (
    (CHAR8 *)FfsFileName,
    "%s%cNum%d-%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X-Level%d",
    TempDir,
    OS_SEP,
    *FfsCount,
    (unsigned) CurrentFile->Name.Data1,
    CurrentFile->Name.Data2,
    CurrentFile->Name.Data3,
    CurrentFile->Name.Data4[0],
    CurrentFile->Name.Data4[1],
    CurrentFile->Name.Data4[2],
    CurrentFile->Name.Data4[3],
    CurrentFile->Name.Data4[4],
    CurrentFile->Name.Data4[5],
    CurrentFile->Name.Data4[6],
    CurrentFile->Name.Data4[7],
    Level
  );

  memcpy (CurrentFv->FfsAttuibutes[*FfsCount].FfsName, FfsFileName, strlen(FfsFileName));
  memcpy (&CurrentFv->FfsAttuibutes[*FfsCount].GuidName, &CurrentFile->Name, sizeof(EFI_GUID));
  //
  // Update current FFS files file state.
  //
  if (ErasePolarity) {
    CurrentFile->State = (UINT8)~(CurrentFile->State);
  }

  FfsFile = fopen (FfsFileName, "wb+");
  if (FfsFile == NULL) {
    Error ("FMMT", 0, 0003, "error writing FFS file", "cannot Create a new ffs file.");
    free(FfsFileName);
    return EFI_ABORTED;
  }

  if (fwrite (CurrentFile, 1, FfsFileSize, FfsFile) != FfsFileSize) {
     Error ("FMMT", 0, 0004, "error writing FFS file", "cannot Create a new ffs file.");
     fclose(FfsFile);
     free(FfsFileName);
     return EFI_ABORTED;
  }

  fclose(FfsFile);
  free(FfsFileName);
  FfsFileName = NULL;

  CurrentFv->FfsNumbers  = *FfsCount;

  *FfsCount += 1;

  if (ErasePolarity) {
    CurrentFile->State = (UINT8)~(CurrentFile->State);
  }

  return EFI_SUCCESS;
}


/**

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

  @param[in]      ActualSize      The size.
  @param[in]      Alignment       The desired alignment.

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

**/
UINT32
GetOccupiedSize (
  IN UINT32  ActualSize,
  IN UINT32  Alignment
  )
{
  UINT32  OccupiedSize;

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

  return OccupiedSize;
}



EFI_STATUS
LibDeleteAndRenameFfs(
  IN CHAR8*    DeleteFile,
  IN CHAR8*    NewFile
)
{
  CHAR8*                 SystemCommand;
  CHAR8*                 TemDir;
  SystemCommand             = NULL;

  if (DeleteFile == NULL ||
      NewFile    == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Delete the file of the specified extract FFS file.
  //
  SystemCommand = malloc (
    strlen (DEL_STR) +
    strlen (DeleteFile)  +
    1
    );
  if (SystemCommand == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return EFI_ABORTED;
  }

  sprintf (
    SystemCommand,
    DEL_STR,
    DeleteFile
    );

  if (system (SystemCommand) != EFI_SUCCESS) {
    free(SystemCommand);
    return EFI_ABORTED;
  }
  free(SystemCommand);

  TemDir = getcwd (NULL, _MAX_PATH);
  if (strlen (TemDir) + strlen(OS_SEP_STR) + strlen (TEMP_DIR_NAME) > _MAX_PATH - 1) {
    Error("FMMT", 0, 1001, "The directory is too long.", "");
     return EFI_ABORTED;
  }
  strncat (TemDir, OS_SEP_STR, _MAX_PATH - strlen(TemDir) - 1);
  strncat (TemDir, TEMP_DIR_NAME, _MAX_PATH - strlen(TemDir) - 1);

  mkdir(TemDir, S_IRWXU | S_IRWXG | S_IRWXO);
  //
  // Create a copy the new file.
  //

  SystemCommand = malloc (
    strlen (COPY_STR) +
    strlen (NewFile)    +
    strlen (DeleteFile) +
    1
    );
  if (SystemCommand == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return EFI_ABORTED;
  }

  sprintf (
    SystemCommand,
    COPY_STR,
    NewFile,
    DeleteFile
    );

  if (system (SystemCommand) != EFI_SUCCESS) {
  free(SystemCommand);
    return EFI_ABORTED;
  }
  free(SystemCommand);

  return EFI_SUCCESS;

}

/**
  Converts ASCII characters to Unicode.
  Assumes that the Unicode characters are only these defined in the ASCII set.

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

  The address to the ASCII string - same as AsciiStr.

**/
VOID
LibAscii2Unicode (
  IN   CHAR8          *String,
  OUT  CHAR16         *UniString
  )
{
  while (*String != '\0') {
    *(UniString++) = (CHAR16) *(String++);
    }
  //
  // End the UniString with a NULL.
  //
  *UniString = '\0';
}


EFI_STATUS
LibCreateGuidedSectionOriginalData(
  IN CHAR8*    FileIn,
  IN CHAR8*    ToolName,
  IN CHAR8*    FileOut
)
{
  CHAR8*                 SystemCommandFormatString;
  CHAR8*                 SystemCommand;

  SystemCommandFormatString = NULL;
  SystemCommand             = NULL;

  if (FileIn   == NULL ||
      ToolName == NULL ||
      FileOut  == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Delete the file of the specified extract FFS file.
  //
  SystemCommandFormatString = "%s -e \"%s\" -o \"%s\"";

  SystemCommand = malloc (
    strlen (SystemCommandFormatString) +
    strlen (FileIn)  +
    strlen (ToolName)  +
    strlen (FileOut)  +
    1
    );
  if (SystemCommand == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return EFI_ABORTED;
  }

  sprintf (
    SystemCommand,
    "%s -e \"%s\" -o \"%s\"",
    ToolName,
    FileIn,
    FileOut
    );

  if (system (SystemCommand) != EFI_SUCCESS) {
    printf("Command failed: %s\n", SystemCommand);
  free(SystemCommand);
    return EFI_ABORTED;
  }
  free(SystemCommand);

  return EFI_SUCCESS;
}

/**

   This function convert the FV header's attribute to a string. The converted string
   will be put into an INF file as the input of GenFV.

   @param[in]      Attr       FV header's attribute.
   @param[out]     InfFile    InfFile contain FV header attribute information.

   @retval         EFI_SUCCESS.
   @retval         EFI_INVLID_PARAMETER
   @retval         EFI_OUT_OF_RESOURCES

**/
EFI_STATUS
LibFvHeaderAttributeToStr (
  IN     EFI_FVB_ATTRIBUTES_2     Attr,
  IN     FILE*                  InfFile
)
{
  CHAR8     *LocalStr;

  LocalStr  = NULL;

  LocalStr = (CHAR8 *) malloc (1024 * 4);

  if (LocalStr == NULL) {
    printf ("Memory allocate error!\n");
    return EFI_OUT_OF_RESOURCES;
  }

  memset (LocalStr, '\0', 1024 * 4);

  if (Attr == 0 || InfFile  == NULL) {
    free (LocalStr);
    return EFI_INVALID_PARAMETER;
  }

  strncat (LocalStr, "[attributes] \n", sizeof("[attributes] \n"));

  if (Attr & EFI_FVB2_READ_DISABLED_CAP) {
    strncat (LocalStr, "EFI_READ_DISABLED_CAP = TRUE \n", sizeof ("EFI_READ_DISABLED_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_READ_ENABLED_CAP) {
    strncat (LocalStr, "EFI_READ_ENABLED_CAP = TRUE \n", sizeof ("EFI_READ_ENABLED_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_READ_STATUS) {
    strncat (LocalStr, "EFI_READ_STATUS = TRUE \n", sizeof ("EFI_READ_STATUS = TRUE \n"));
  }

  if (Attr & EFI_FVB2_WRITE_DISABLED_CAP) {
    strncat (LocalStr, "EFI_WRITE_DISABLED_CAP = TRUE \n", sizeof ("EFI_WRITE_DISABLED_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_WRITE_ENABLED_CAP) {
    strncat (LocalStr, "EFI_WRITE_ENABLED_CAP = TRUE \n", sizeof ("EFI_WRITE_ENABLED_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_WRITE_STATUS) {
    strncat (LocalStr, "EFI_WRITE_STATUS = TRUE \n", sizeof ("EFI_WRITE_STATUS = TRUE \n"));
  }

  if (Attr & EFI_FVB2_LOCK_CAP) {
    strncat (LocalStr, "EFI_LOCK_CAP = TRUE \n", sizeof ("EFI_LOCK_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_LOCK_STATUS) {
    strncat (LocalStr, "EFI_LOCK_STATUS = TRUE \n", sizeof ("EFI_LOCK_STATUS = TRUE \n"));
  }

  if (Attr & EFI_FVB2_STICKY_WRITE) {
    strncat (LocalStr, "EFI_STICKY_WRITE = TRUE \n", sizeof ("EFI_STICKY_WRITE = TRUE \n"));
  }

  if (Attr & EFI_FVB2_MEMORY_MAPPED) {
    strncat (LocalStr, "EFI_MEMORY_MAPPED = TRUE \n", sizeof ("EFI_MEMORY_MAPPED = TRUE \n"));
  }

  if (Attr & EFI_FVB2_ERASE_POLARITY) {
    strncat (LocalStr, "EFI_ERASE_POLARITY = 1 \n", sizeof ("EFI_ERASE_POLARITY = 1 \n"));
  }

  if (Attr & EFI_FVB2_READ_LOCK_CAP) {
    strncat (LocalStr, "EFI_READ_LOCK_CAP = TRUE \n", sizeof ("EFI_READ_LOCK_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_READ_LOCK_STATUS) {
    strncat (LocalStr, "EFI_READ_LOCK_STATUS = TRUE \n", sizeof ("EFI_READ_LOCK_STATUS = TRUE \n"));
  }

  if (Attr & EFI_FVB2_WRITE_LOCK_CAP) {
    strncat (LocalStr, "EFI_WRITE_LOCK_CAP = TRUE \n", sizeof ("EFI_WRITE_LOCK_CAP = TRUE \n"));
  }

  if (Attr & EFI_FVB2_WRITE_LOCK_STATUS) {
    strncat (LocalStr, "EFI_WRITE_LOCK_STATUS = TRUE \n", sizeof ("EFI_WRITE_LOCK_STATUS = TRUE \n"));
  }

  if (Attr & EFI_FVB2_LOCK_STATUS) {
    strncat (LocalStr, "EFI_READ_LOCK_STATUS = TRUE \n", sizeof ("EFI_READ_LOCK_STATUS = TRUE \n"));
  }

  //
  // Alignment
  //
  if (Attr & EFI_FVB2_ALIGNMENT_1) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_1 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_1 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_2) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_2 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_2 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_4) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_4 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_4 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_8) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_8 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_8 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_16) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_16 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_16 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_32) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_32 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_32 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_64) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_64 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_64 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_128) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_128 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_128 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_256) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_256 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_256 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_512) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_512 = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_512 = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_1K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_1K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_1K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_2K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_2K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_2K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_4K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_4K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_4K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_8K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_8K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_8K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_16K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_16K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_16K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_32K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_32K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_32K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_64K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_64K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_64K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_128K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_128K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_128K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_256K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_256K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_256K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_512K) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_512K = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_512K = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_1M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_1M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_1M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_2M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_2M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_2M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_4M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_4M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_4M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_8M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_8M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_8M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_16M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_16M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_16M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_32M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_32M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_32M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_64M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_64M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_64M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_128M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_128M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_128M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_256M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_256M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_256M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_512M) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_512M = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_512M = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_1G) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_1G = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_1G = TRUE \n"));
  } else if (Attr & EFI_FVB2_ALIGNMENT_2G) {
    strncat (LocalStr, "EFI_FVB2_ALIGNMENT_2G = TRUE \n", sizeof ("EFI_FVB2_ALIGNMENT_2G = TRUE \n"));
  }

  if (fwrite (LocalStr, 1, (size_t) strlen (LocalStr), InfFile) != (size_t) strlen (LocalStr)) {
    printf ("Error while writing data to %p file.", (void*)InfFile);
    free (LocalStr);
    return EFI_ABORTED;
  }

  free (LocalStr);

  return EFI_SUCCESS;
}


/**
   This function fill the FV inf files option field.

   @param[in]      BlockMap       FV header's attribute.
   @param[out]     InfFile    InfFile contain FV header attribute information.

   @retval         EFI_SUCCESS.
   @retval         EFI_INVLID_PARAMETER

**/
EFI_STATUS
LibFvHeaderOptionToStr (
  IN     EFI_FIRMWARE_VOLUME_HEADER  *FvHeader,
  IN     FILE*                   InfFile,
  IN     BOOLEAN                 IsRootFv
)
{
  CHAR8     *LocalStr;
  CHAR8     *TempStr;
  EFI_FV_BLOCK_MAP_ENTRY  *BlockMap;

  LocalStr     = NULL;
  TempStr      = NULL;

  if (FvHeader == NULL || InfFile  == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // This section will not over 1024 bytes and each line will never over 128 bytes.
  //
  LocalStr    = (CHAR8 *) malloc (1024);
  TempStr     = (CHAR8 *) malloc (128);

  if (LocalStr    == NULL ||
     TempStr   == NULL ) {
      if (LocalStr != NULL) {
        free (LocalStr);
      }
      if (TempStr != NULL) {
        free (TempStr);
      }
    printf ("Memory allocate error! \n");
    return EFI_OUT_OF_RESOURCES;
  }

  BlockMap = FvHeader->BlockMap;
  memset (LocalStr, '\0', 1024);
  memset (TempStr, '\0', 128);

  strncat (LocalStr, "[options] \n", sizeof("[Options] \n"));


  snprintf (TempStr, 128, "EFI_BLOCK_SIZE  = 0x%x \n", BlockMap->Length);
  strncat (LocalStr, TempStr, strlen(TempStr));

  if (IsRootFv) {
  snprintf (TempStr, 128, "EFI_NUM_BLOCKS  = 0x%x \n", BlockMap->NumBlocks);
  strncat (LocalStr, TempStr, strlen(TempStr));
  }

  if (fwrite (LocalStr, 1, (size_t) strlen (LocalStr), InfFile) != (size_t) strlen (LocalStr)) {
    printf ("Error while writing data to %p file.", (void*)InfFile);
    free (LocalStr);
    free (TempStr);
    return EFI_ABORTED;
  }

  free (LocalStr);
  free (TempStr);

  return EFI_SUCCESS;
}

/**
   This function fill the FV inf files option field.

   @param[in]      FfsName    Ffs file path/name.
   @param[out]     InfFile    InfFile contain FV header attribute information
   @param[in]      FirstIn    Is the first time call this function? If yes, should create [files] section.

   @retval         EFI_SUCCESS.
   @retval         EFI_INVLID_PARAMETER

**/
EFI_STATUS
LibAddFfsFileToFvInf (
  IN     CHAR8                   *FfsName,
  IN     FILE*                   InfFile,
  IN     BOOLEAN                 FirstIn
)
{

  CHAR8     *LocalStr;

  LocalStr     = NULL;

  if (FfsName == NULL || InfFile  == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (strlen(FfsName) == 0) {
    return EFI_SUCCESS;
  }

  LocalStr    = (CHAR8 *) malloc (_MAX_PATH);

  if (LocalStr == NULL) {
    printf ("Memory allocate error! \n");
    return EFI_OUT_OF_RESOURCES;
  }

  memset (LocalStr, '\0', _MAX_PATH);

  if (FirstIn) {
    sprintf (LocalStr, "[files] \nEFI_FILE_NAME = %s \n", FfsName);
  } else {
    sprintf (LocalStr, "EFI_FILE_NAME = %s \n", FfsName);
  }

  if (fwrite (LocalStr, 1, (size_t) strlen (LocalStr), InfFile) != (size_t) strlen (LocalStr)) {
    printf ("Error while writing data to %p file.", (void*)InfFile);
    free (LocalStr);
    return EFI_ABORTED;
  }

  free (LocalStr);

  return EFI_SUCCESS;
}


/**
  Convert EFI file to PE or TE section

  @param[in]   InputFilePath   .efi file, it's optional unless process PE/TE section.
  @param[in]   Type            PE or TE and UI/Version
  @param[in]   OutputFilePath  .te or .pe file
  @param[in]   UiString        String for generate UI section usage, this parameter is optional
                               unless Type is EFI_SECTION_USER_INTERFACE.
  @param[in]   VerString       String for generate Version section usage, this parameter is optional
                               unless Type is EFI_SECTION_VERSION.

  @retval EFI_SUCCESS

**/
EFI_STATUS
LibCreateFfsSection (
  IN FV_INFORMATION   *FvInFd,      OPTIONAL
  IN CHAR8*     InputFilePath,      OPTIONAL
  IN CHAR8*     Sections,           OPTIONAL
  IN UINT8      Type,
  IN CHAR8*     OutputFilePath,
  IN CHAR8*     UiString,           OPTIONAL
  IN CHAR8*     VerString,          OPTIONAL
  IN CHAR8*     GuidToolGuid,       OPTIONAL
  IN UINT16     GuidHeaderLength,
  IN UINT16     GuidAttr,
  IN CHAR8*     CompressType       OPTIONAL
  )
{
  //EFI_STATUS             Status;
  CHAR8*                 SystemCommandFormatString;
  CHAR8*                 SystemCommand;
  FILE                   *file;
  UINT8                  Buffer[4];
  int                    BitNum;
  int                    Position;
  UINT32                 AlignmentValue;
  //
  // Workaround for static code checkers.
  // Ensures the size of 'AlignmentStr' can hold all the digits of an
  // unsigned 32-bit integer plus the size unit character.
  //
  char                   AlignmentStr[16];

  SystemCommandFormatString = NULL;
  SystemCommand             = NULL;
  strcpy(AlignmentStr,"1");
  //
  // Call GenSec tool to generate FFS section.
  //

  //
  // -s SectionType.
  //
  if (Type != 0) {
    switch (Type) {
      //
      // Process compression section
      //
      case EFI_SECTION_COMPRESSION:
        SystemCommandFormatString = "GenSec -s %s -c %s  \"%s\" -o \"%s\"";
        SystemCommand = malloc (
          strlen (SystemCommandFormatString) +
          strlen (mSectionTypeName[Type]) +
          strlen (CompressType) +
          strlen (InputFilePath) +
          strlen (OutputFilePath) +
          1
          );
        if (SystemCommand == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }
        sprintf (
          SystemCommand,
          "GenSec -s %s -c %s  \"%s\" -o \"%s\"",
          mSectionTypeName[Type],
          CompressType,
          InputFilePath,
          OutputFilePath
          );

        if (system (SystemCommand) != EFI_SUCCESS) {
          free(SystemCommand);
          return EFI_ABORTED;
        }
        free(SystemCommand);
        break;

      //
      // Process GUID defined section
      //
      case EFI_SECTION_GUID_DEFINED:
        SystemCommandFormatString = "GenSec -s %s -g %s \"%s\" -o \"%s\" -r %s -r %s -l %d";
        SystemCommand = malloc (
          strlen (SystemCommandFormatString) +
          strlen (mSectionTypeName[Type]) +
          strlen (GuidToolGuid) +
          strlen (InputFilePath) +
          strlen (OutputFilePath) +
          strlen (mGuidSectionAttr[GuidAttr&0x01]) +
          strlen (mGuidSectionAttr[GuidAttr&0x02]) +
          4 +
          1
          );
        if (SystemCommand == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }
        sprintf (
          SystemCommand,
          "GenSec -s %s -g %s \"%s\" -o \"%s\" -r %s -r %s -l %d",
          mSectionTypeName[Type],
          GuidToolGuid,
          InputFilePath,
          OutputFilePath,
          mGuidSectionAttr[GuidAttr&0x01],
          mGuidSectionAttr[GuidAttr&0x02],
          GuidHeaderLength
          );

        if (system (SystemCommand) != EFI_SUCCESS) {
          free(SystemCommand);
          return EFI_ABORTED;
        }
        free(SystemCommand);
        break;

      case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:

        SystemCommandFormatString = "GenSec -s %s \"%s\" -o \"%s\"";
        SystemCommand = malloc (
          strlen (SystemCommandFormatString) +
          strlen (mSectionTypeName[Type]) +
          strlen (InputFilePath) +
          strlen (OutputFilePath) +
          1
          );
        if (SystemCommand == NULL) {
          Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
          return EFI_ABORTED;
        }
        sprintf (
          SystemCommand,
          "GenSec -s %s \"%s\" -o \"%s\"",
          mSectionTypeName[Type],
          InputFilePath,
          OutputFilePath
          );

        if (system (SystemCommand) != EFI_SUCCESS) {
          free(SystemCommand);
          return EFI_ABORTED;
        }
        free(SystemCommand);
        break;

      default:
        Error ("FMMT", 0, 0003, "Please specify the section type while call GenSec tool.", NULL);
        return EFI_UNSUPPORTED;
    }
  } else {
    //
    // Create Dummy section.
    //
    file = fopen(InputFilePath, "rb");
    if (file == NULL) {
      Error(NULL, 0, 0001, "Error opening the file", InputFilePath);
      return EFI_INVALID_PARAMETER;
    }
    // The Section Struct, 3 bits for Size, then 1 bit for Type
    if (fread(Buffer, 1, (size_t)(4), file) != (size_t)(4)) {
      fclose(file);
      return EFI_ABORTED;
    }
    if (*(Buffer + 3) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) {
      // The Section Struct, if size is not 0xFFFF, the length is 4
      Position = 4;
      // If Size is 0xFFFFFF then ExtendedSize contains the size of the section
      if ((*Buffer == 0xFF) && (*(Buffer + 1) == 0xFF) && (*(Buffer + 2) == 0xFF)) {
        Position = 8;
      }
      //Per EFI_FIRMWARE_VOLUME_HEADER struct, 0x2E bit is EFI_FVB_ATTRIBUTES_2 attr
      fseek(file, 0x2E + Position, SEEK_SET);
      BitNum = fgetc(file);
      AlignmentValue = 1 << (BitNum & 0x1F);
      if (AlignmentValue >= 0x400){
        if (AlignmentValue >= 0x10000){
          strcpy(AlignmentStr,"64K");
        }
        else{
          sprintf(AlignmentStr, "%d", AlignmentValue/0x400);
          strcat(AlignmentStr, "K");
        }
      }
      else{
        sprintf(AlignmentStr, "%d", AlignmentValue);
      }
      strcpy(FvInFd->AlignmentStr, AlignmentStr);
    }
    fclose(file);
    SystemCommandFormatString = "GenSec \"%s\" -o \"%s\" --sectionalign %s";
    SystemCommand = malloc (
      strlen (SystemCommandFormatString) +
      strlen (InputFilePath) +
      strlen (OutputFilePath) +
      4 +                                 // Alignment maximum length
      1
      );
    if (SystemCommand == NULL) {
      Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
      return EFI_ABORTED;
    }
    sprintf (
      SystemCommand,
      "GenSec \"%s\" -o \"%s\" --sectionalign %s",
      InputFilePath,
      OutputFilePath,
      AlignmentStr
      );

    if (system (SystemCommand) != EFI_SUCCESS) {
      free(SystemCommand);
      return EFI_ABORTED;
    }
    free(SystemCommand);

  }

  return EFI_SUCCESS;
}

/**
  Encapsulate FFSs to FV

  @param[in]   InputFilePath   Section file will be read into this FFS file. This option is required.
  @param[in]   OutputFilePath  The created PI firmware file name. This option is required.
  @param[in]   BlockSize       BlockSize is one HEX or DEC format value required by FV image.
  @param[in]   FileTakeSize

  @retval EFI_SUCCESS

**/
EFI_STATUS
LibEncapsulateFfsToFv (
  IN CHAR8*     InfFilePath,
  IN CHAR8*     InputFFSs,
  IN CHAR8*     OutputFilePath,
  IN CHAR8*     FvGuidName,
  IN BOOLEAN    IsLargeFile
  )
{

  CHAR8*                 SystemCommandFormatString;
  CHAR8*                 SystemCommand;
  CHAR8*                 FfsGuid = "8c8ce578-8a3d-4f1c-9935-896185c32dd3";

  if (IsLargeFile == TRUE) {
    FfsGuid = "5473c07a-3dcb-4dca-bd6f-1e9689e7349a";
  }

  SystemCommandFormatString = NULL;
  SystemCommand             = NULL;

  if (OutputFilePath  == NULL ||
      InfFilePath     == NULL ) {
    return EFI_INVALID_PARAMETER;
  }

  if (InfFilePath != NULL) {
    if (FvGuidName == NULL) {
      SystemCommandFormatString = "GenFv -i \"%s\" -g %s -o \"%s\"";

      SystemCommand = malloc (
        strlen (SystemCommandFormatString) +
        strlen (InfFilePath)   +
        strlen (FfsGuid) +
        strlen (OutputFilePath)  +
        1
        );
      if (SystemCommand == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }
      sprintf (
        SystemCommand,
        "GenFv -i \"%s\" -g %s -o \"%s\"",
        InfFilePath,          // -i
        FfsGuid,              // -g
        OutputFilePath        // -o
        );

      if (system (SystemCommand) != EFI_SUCCESS) {
        free(SystemCommand);
        return EFI_ABORTED;
      }

      free(SystemCommand);
    } else {
      //
      // Have FvGuidName in it.
      //
      SystemCommandFormatString = "GenFv -i \"%s\" -g %s -o \"%s\" --FvNameGuid %s";

      SystemCommand = malloc (
        strlen (SystemCommandFormatString) +
        strlen (InfFilePath)   +
        strlen (FfsGuid)  +
        strlen (OutputFilePath)  +
        strlen (FvGuidName) +
        1
        );
      if (SystemCommand == NULL) {
        Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
        return EFI_ABORTED;
      }
      sprintf (
        SystemCommand,
        "GenFv -i \"%s\" -g %s -o \"%s\" --FvNameGuid %s",
        InfFilePath,          // -i
        FfsGuid,              // -g
        OutputFilePath,       // -o
        FvGuidName            // FvNameGuid
        );

      if (system (SystemCommand) != EFI_SUCCESS) {
        free(SystemCommand);
        return EFI_ABORTED;
      }
      free(SystemCommand);

    }
  }

  return EFI_SUCCESS;
}

/**
  Encapsulate an FFS section file to an FFS file.

  @param[in]   Type            Type is one FV file type defined in PI spec, which is one type of EFI_FV_FILETYPE_RAW, EFI_FV_FILETYPE_FREEFORM,
                               EFI_FV_FILETYPE_SECURITY_CORE, EFI_FV_FILETYPE_PEIM, EFI_FV_FILETYPE_PEI_CORE, EFI_FV_FILETYPE_DXE_CORE,
                               EFI_FV_FILETYPE_DRIVER, EFI_FV_FILETYPE_APPLICATION, EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER,
                               EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE. This option is required.
  @param[in]   InputFilePath   Section file will be read into this FFS file. This option is required.
  @param[in]   OutputFilePath  The created PI firmware file name. This option is required.
  @param[in]   FileGuid        FileGuid is the unique identifier for this FFS file. This option is required.
  @param[in]   Fixed           Set fixed attribute in FFS file header to indicate that the file may not be moved from its present location.
  @param[in]   SectionAlign    FileAlign specifies FFS file alignment, which only support the following alignment: 8,16,128,512,1K,4K,32K,64K.

  @retval EFI_SUCCESS

**/
EFI_STATUS
LibEncapSectionFileToFFS (
  IN UINT8      Type,
  IN CHAR8*     InputFilePath,
  IN CHAR8*     OutputFilePath,
  IN EFI_GUID   FileGuid,
  IN BOOLEAN    Fixed,
  IN CHAR8*     SectionAlign
  )
{
  CHAR8*                 SystemCommandFormatString;
  CHAR8*                 SystemCommand;
  CHAR8*                 GuidStr;


  SystemCommandFormatString = NULL;
  SystemCommand             = NULL;
  GuidStr                   = NULL;

  GuidStr  = LibFmmtGuidToStr(&FileGuid);

  if (GuidStr == NULL) {
    return EFI_ABORTED;
  }


  //
  // -t  Type
  // -i  InputFilePath
  // -o  OutPutFilePath
  // -g  FileGuid
  // -x  Fixed
  // -n  SectionAlign
  //

  if (Fixed) {
    SystemCommandFormatString = "GenFfs -t %s -i \"%s\" -g %s -x -o \"%s\" -a %s";
    SystemCommand = malloc (
      strlen (SystemCommandFormatString) +
      strlen (mFfsFileType[Type]) +
      strlen (InputFilePath) +
      strlen (GuidStr) +
      strlen (OutputFilePath) +
      4 +
      1
      );
    if (SystemCommand == NULL) {
      Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
      free (GuidStr);
      return EFI_ABORTED;
    }
    sprintf (
      SystemCommand,
      "GenFfs -t %s -i \"%s\" -g %s -x -o \"%s\" -a %s",
      mFfsFileType[Type],     // -t
      InputFilePath,          // -i
      GuidStr,                // -g
      OutputFilePath,         // -o
      SectionAlign
      );

    free (GuidStr);
    if (system (SystemCommand) != EFI_SUCCESS) {
      free(SystemCommand);
      return EFI_ABORTED;
    }
    free(SystemCommand);
  } else {
    SystemCommandFormatString = "GenFfs -t %s -i \"%s\" -g %s -o \"%s\" -a %s";
    SystemCommand = malloc (
      strlen (SystemCommandFormatString) +
      strlen (mFfsFileType[Type]) +
      strlen (InputFilePath) +
      strlen (GuidStr) +
      strlen (OutputFilePath) +
      4 +
      1
      );
    if (SystemCommand == NULL) {
      free (GuidStr);
      Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
      return EFI_ABORTED;
    }
    sprintf (
      SystemCommand,
      "GenFfs -t %s -i \"%s\" -g %s -o \"%s\" -a %s",
      mFfsFileType[Type],     // -t
      InputFilePath,          // -i
      GuidStr,                // -g
      OutputFilePath,         // -o
      SectionAlign
      );

    free (GuidStr);
    if (system (SystemCommand) != EFI_SUCCESS) {
      free(SystemCommand);
      return EFI_ABORTED;
    }
    free(SystemCommand);
  }


  return EFI_SUCCESS;
}

EFI_STATUS
LibCreateNewFdCopy(
  IN CHAR8*    OldFd,
  IN CHAR8*    NewFd
)
{

  FILE*                       NewFdFile;
  FILE*                       OldFdFile;
  CHAR8                       *NewFdDir;
  CHAR8                       *OldFdDir;
  UINT64                      FdLength;
  UINT32                      Count;
  BOOLEAN                     UseNewDirFlag;
  CHAR8                       *Buffer;

  NewFdFile = NULL;
  OldFdFile = NULL;
  NewFdDir  = NULL;
  OldFdDir  = NULL;
  Count     = 0;
  UseNewDirFlag = FALSE;

  if (OldFd == NULL ||
    NewFd    == NULL) {
      return EFI_INVALID_PARAMETER;
  }


  NewFdDir = getcwd (NULL, _MAX_PATH);

  Count = strlen(NewFdDir);

  if (strlen(NewFd) > Count) {

    do {
      if (NewFdDir[Count-1] == NewFd[Count-1]) {
        Count--;
      } else {
        if (strlen(NewFdDir) + strlen (OS_SEP_STR) + strlen (NewFd) > _MAX_PATH -1) {
          return EFI_ABORTED;
        }
        strncat (NewFdDir,OS_SEP_STR, _MAX_PATH - strlen (NewFdDir) -1);
        strncat (NewFdDir,NewFd, _MAX_PATH - strlen (NewFdDir) -1);
        UseNewDirFlag = TRUE;
        break;
      }

    } while (Count != 1);

  }else {
    if (strlen(NewFdDir) + strlen (OS_SEP_STR) + strlen (NewFd) > _MAX_PATH -1) {
      return EFI_ABORTED;
    }
    strncat (NewFdDir,OS_SEP_STR, _MAX_PATH - strlen (NewFdDir) -1);
    strncat (NewFdDir,NewFd, _MAX_PATH - strlen (NewFdDir) -1);
    UseNewDirFlag = TRUE;
  }

  if (UseNewDirFlag) {
    NewFdFile = fopen (NewFdDir, "wb+");
    if (NewFdFile == NULL) {
      NewFdFile = fopen (NewFd, "wb+");
    }
  } else {
    NewFdFile = fopen (NewFd, "wb+");
  }
  // support network path file
  if (OldFd[0] == '\\' && OldFd[1] == '\\') {
    OldFdFile = fopen (OldFd, "rb");
  } else {
  UseNewDirFlag = FALSE;

  OldFdDir = getcwd (NULL, _MAX_PATH);

  Count = strlen(OldFdDir);

  if (strlen(OldFd) > Count) {

    do {
      if (OldFdDir[Count-1] == OldFd[Count-1]) {
        Count--;
      } else {
        if (strlen(OldFdDir) + strlen (OS_SEP_STR) + strlen (OldFd) > _MAX_PATH -1) {
          if (NewFdFile != NULL) {
            fclose(NewFdFile);
          }
          return EFI_ABORTED;
        }
        strncat (OldFdDir,OS_SEP_STR, _MAX_PATH - strlen (OldFdDir) -1);
        strncat (OldFdDir,OldFd, _MAX_PATH - strlen (OldFdDir) -1);
        UseNewDirFlag = TRUE;
        break;
      }

    } while (Count != 1);

  }else {
    if (strlen(OldFdDir) + strlen (OS_SEP_STR) + strlen (OldFd) > _MAX_PATH -1) {
      if (NewFdFile != NULL) {
        fclose(NewFdFile);
      }
      return EFI_ABORTED;
    }
    strncat (OldFdDir,OS_SEP_STR, _MAX_PATH - strlen (OldFdDir) -1);
    strncat (OldFdDir,OldFd, _MAX_PATH - strlen (OldFdDir) -1);
    UseNewDirFlag = TRUE;
  }

  if (UseNewDirFlag) {
    OldFdFile = fopen (OldFdDir, "rb+");
    if (OldFdFile == NULL) {
      OldFdFile = fopen (OldFd, "rb+");
    }
  } else {
    OldFdFile = fopen (OldFd, "rb+");
  }
  }

  if (NewFdFile == NULL) {
    Error ("FMMT", 0, 0003, "error Open FD file", "cannot Create a new FD file.");
    if (OldFdFile != NULL) {
      fclose (OldFdFile);
    }
    return EFI_ABORTED;
  }

  if (OldFdFile == NULL) {
    Error ("FMMT", 0, 0003, "error Open FD file", "cannot Create a new FD file.");
    if (NewFdFile != NULL) {
      fclose (NewFdFile);
    }
    return EFI_ABORTED;
  }


  fseek(OldFdFile,0,SEEK_SET);
  fseek(OldFdFile,0,SEEK_END);

  FdLength = ftell(OldFdFile);

  fseek(OldFdFile,0,SEEK_SET);
  fseek(NewFdFile,0,SEEK_SET);

  Buffer = malloc ((size_t)FdLength);

  if (Buffer == NULL)  {
    fclose(OldFdFile);
    fclose(NewFdFile);
    return EFI_ABORTED;
  }

  if (fread (Buffer, 1, (size_t) FdLength, OldFdFile) != (size_t) FdLength) {
    Error ("FMMT", 0, 0003, "error reading FD file %s", OldFd);
    free (Buffer);
    fclose(OldFdFile);
    fclose(NewFdFile);
    return EFI_ABORTED;
  }

  if (fwrite (Buffer, 1, (size_t) FdLength, NewFdFile) != (size_t) FdLength) {
    Error ("FMMT", 0, 0004, "error writing FD file", "cannot Create a new FD file.");
    free (Buffer);
    fclose(OldFdFile);
    fclose(NewFdFile);
    return EFI_ABORTED;
  }
  free (Buffer);
  fclose(OldFdFile);
  fclose (NewFdFile);

  return EFI_SUCCESS;
}


/**
  This function will assemble the filename, directory and extend and return the combined string.
  Like FileName = file1, Dir = c:\temp extend = txt, the output string will be:
  c:\temp\file1.txt.

  @param[in]
  @param[in]
  @param[in]

  @retrun     A string contain all the input information.

**/
CHAR8 *
LibFilenameStrExtended (
  IN CHAR8      *FileName,
  IN CHAR8      *Dir,
  IN CHAR8      *Extend
)
{
  CHAR8 *RetStr;

  RetStr = NULL;

  if (FileName == NULL) {
    return NULL;
  }

  if (Dir == NULL || Extend == NULL) {
    return FileName;
  }

  RetStr = (CHAR8 *) malloc (strlen (FileName) +
                             strlen (Dir) +
                             strlen (Extend) +
                             strlen ("%s%s.%s") +
                             1);
  if (RetStr == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return NULL;
  }

  memset (RetStr, '\0', (strlen (FileName) + strlen (Dir) + strlen (Extend) + strlen ("%s%s.%s") + 1));

  sprintf (RetStr, "%s%s.%s", Dir, FileName, Extend);

  return RetStr;
}

/**
  Delete a directory and files in it.

  @param[in]   DirName   Name of the directory need to be deleted.

  @return EFI_INVALID_PARAMETER
  @return EFI_SUCCESS
**/
EFI_STATUS
LibRmDir (
  IN  CHAR8*  DirName
)
{
  CHAR8*                 SystemCommand;

  SystemCommand             = NULL;


  if (DirName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (access (DirName, 0) == -1){
    return EFI_SUCCESS;
  }

  //
  // Delete a directory and files in it.
  //

  SystemCommand = malloc (
    strlen (RMDIR_STR) +
    strlen (DirName)     +
    1
    );
   if (SystemCommand == NULL) {
     Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
     return EFI_ABORTED;
   }

  sprintf (
    SystemCommand,
    RMDIR_STR,
    DirName
    );

  if (system (SystemCommand) != EFI_SUCCESS) {
    free(SystemCommand);
    return EFI_ABORTED;
  }
  free(SystemCommand);

  return EFI_SUCCESS;
}

/*
  Construct a set of blank chars based on the number.

  @param[in]   Count The number of blank chars.

  @return      A string contained the blank chars.

*/
CHAR8 *
LibConstructBlankChar (
  IN UINT8    Count
)
{
  CHAR8    *RetStr;
  UINT8    Index;

  Index  = 0;
  RetStr = NULL;

  RetStr = (CHAR8 *) malloc (Count +1);

  if (RetStr == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    return NULL;
  }

  memset (RetStr , '\0', Count + 1);

  for (Index=0; Index <= Count -1; Index ++) {
    RetStr[Index] = ' ';
  }

  return RetStr;

}

EFI_STATUS
LibGenExtFile(
CONST EFI_FIRMWARE_VOLUME_EXT_HEADER *ExtPtr,
FILE *InfFile
)
{
  CHAR8   *TempDir;
  FILE    *ExtFile;
  CHAR8   OutputExtFile[_MAX_PATH];
  CHAR8   Line[512];
  size_t  Len;

  TempDir = NULL;

  TempDir = getcwd(NULL, _MAX_PATH);
  if (strlen (TempDir) + strlen(OS_SEP_STR) + strlen (TEMP_DIR_NAME) > _MAX_PATH - 1) {
    Error("FMMT", 0, 1001,  "The directory is too long.", "");
    return EFI_ABORTED;
  }
  strncat (TempDir, OS_SEP_STR, _MAX_PATH - strlen (TempDir) -1);
  strncat (TempDir, TEMP_DIR_NAME, _MAX_PATH - strlen (TempDir) -1);

  mkdir(TempDir, S_IRWXU | S_IRWXG | S_IRWXO);

  sprintf(
    Line,
    "%c%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X%d.ext",
    OS_SEP,
    (unsigned)ExtPtr->FvName.Data1,
    ExtPtr->FvName.Data2,
    ExtPtr->FvName.Data3,
    ExtPtr->FvName.Data4[0],
    ExtPtr->FvName.Data4[1],
    ExtPtr->FvName.Data4[2],
    ExtPtr->FvName.Data4[3],
    ExtPtr->FvName.Data4[4],
    ExtPtr->FvName.Data4[5],
    ExtPtr->FvName.Data4[6],
    ExtPtr->FvName.Data4[7],
    ExtPtr->ExtHeaderSize
    );
  if (strlen (TempDir) + strlen (Line) > _MAX_PATH - 1) {
    Error("FMMT", 0, 1001,  "The directory is too long.", "");
    return EFI_ABORTED;
  }
  strncpy (OutputExtFile, TempDir, _MAX_PATH - 1);
  OutputExtFile[_MAX_PATH - 1] = 0;
  strncat (OutputExtFile, Line, _MAX_PATH - strlen (OutputExtFile) - 1);


  ExtFile = fopen(OutputExtFile, "wb+");
  if (ExtFile == NULL) {
    return EFI_ABORTED;
  }

  if (fwrite(ExtPtr, 1, ExtPtr->ExtHeaderSize, ExtFile) != ExtPtr->ExtHeaderSize) {
    fclose(ExtFile);
    return EFI_ABORTED;
  }

  fclose(ExtFile);

  strcpy (Line, "EFI_FV_EXT_HEADER_FILE_NAME = ");
  if (strlen (Line) + strlen (OutputExtFile) + 1 > sizeof(Line) / sizeof (CHAR8) - 1) {
    Error("FMMT", 0, 1001,  "The directory is too long.", "");
    return EFI_ABORTED;
  }
  strncat (Line, OutputExtFile, sizeof(Line) / sizeof (CHAR8) - strlen (Line) - 1);
  strncat (Line, "\n", sizeof(Line) / sizeof (CHAR8) - strlen (Line) - 1);
  Len = strlen(Line);
  if (fwrite(Line, 1, Len, InfFile) != Len) {
    return EFI_ABORTED;
  }

  return EFI_SUCCESS;
}

/**
  Delete a file.

  @param[in]   FileName   Name of the file need to be deleted.

  @return EFI_INVALID_PARAMETER
  @return EFI_SUCCESS
**/
EFI_STATUS
LibFmmtDeleteFile(
  IN   CHAR8    *FileName
)
{
  CHAR8*                 SystemCommand;
  CHAR8                  *TemDir;

  SystemCommand             = NULL;
  TemDir                    = NULL;


  if (FileName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  // if the FileName is not in TemDir, we don't need to delete.
  TemDir = getcwd (NULL, _MAX_PATH);
  if (*(TemDir + strlen(TemDir) - 1) == OS_SEP) {
    *(TemDir + strlen(TemDir) - 1) = '\0';
  }
  if (strlen (TemDir) + strlen (OS_SEP_STR) + strlen (TEMP_DIR_NAME) > _MAX_PATH - 1) {
    Error (NULL, 0, 2000, "Path: The current path is too long.", NULL);
    return EFI_ABORTED;
  }
  strncat (TemDir, OS_SEP_STR, _MAX_PATH - strlen (TemDir) - 1);
  strncat (TemDir, TEMP_DIR_NAME, _MAX_PATH - strlen (TemDir) - 1);
  if (strstr(FileName, TemDir) == NULL) {
    return EFI_SUCCESS;
  }

  //
  // Delete a file
  //

  SystemCommand = malloc (
    strlen (DEL_STR) +
    strlen (FileName)     +
    1
    );
   if (SystemCommand == NULL) {
     Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
     return EFI_ABORTED;
   }

  sprintf (
    SystemCommand,
    DEL_STR,
    FileName
    );

  if (system (SystemCommand) != EFI_SUCCESS) {
    free(SystemCommand);
    return EFI_ABORTED;
  }
  free(SystemCommand);

  return EFI_SUCCESS;

}


/**

  Convert a GUID to a string.


  @param[in]   Guid       - Pointer to GUID to print.


  @return The string after convert.

**/
CHAR8 *
LibFmmtGuidToStr (
  IN  EFI_GUID  *Guid
)
{
  CHAR8 * Buffer;

  Buffer = NULL;

  if (Guid == NULL) {
    printf ("The guid is NULL while convert guid to string! \n");
    return NULL;
  }

  Buffer = (CHAR8 *) malloc (36 + 1);

  if (Buffer == NULL) {
    printf ("Error while allocate resource! \n");
    return NULL;
  }
  memset (Buffer, '\0', 36 + 1);

  sprintf (
      Buffer,
      "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
      Guid->Data1,
      Guid->Data2,
      Guid->Data3,
      Guid->Data4[0],
      Guid->Data4[1],
      Guid->Data4[2],
      Guid->Data4[3],
      Guid->Data4[4],
      Guid->Data4[5],
      Guid->Data4[6],
      Guid->Data4[7]
      );

  return Buffer;
}


/**

  Free the whole Fd data structure.

  @param[in]  Fd  The pointer point to the Fd data structure.

**/
VOID
LibFmmtFreeFd (
  FIRMWARE_DEVICE *Fd
  )
{
  FV_INFORMATION   *CurrentFv;
  FV_INFORMATION   *TempFv;
  ENCAP_INFO_DATA  *EncapData1;
  ENCAP_INFO_DATA  *EncapData2;

  CurrentFv        = NULL;
  TempFv           = NULL;
  EncapData1       = NULL;
  EncapData2       = NULL;

  if (Fd == NULL) {
    return;
  }

  CurrentFv = Fd->Fv;

  do {
    TempFv = CurrentFv;
    CurrentFv = CurrentFv->FvNext;

    free (TempFv->FvHeader);

    if (TempFv->FvExtHeader != NULL) {
      free (TempFv->FvExtHeader);
    }
    if (TempFv->FvUiName) {
      free(TempFv->FvUiName);
    }

    //
    // Free encapsulate data;
    //
    EncapData1 = TempFv->EncapData;

    while (EncapData1 != NULL) {

      EncapData2 = EncapData1;
      EncapData1 = EncapData1->NextNode;

      if (EncapData2->Data != NULL) {
        free (EncapData2->Data);
      }
    if (EncapData2->FvExtHeader != NULL) {
      free(EncapData2->FvExtHeader);
    }
      free (EncapData2);
      EncapData2 = NULL;
    }

    EncapData1 = NULL;

    free (TempFv);
    TempFv = NULL;

  } while (CurrentFv != NULL);

  CurrentFv = NULL;
  free (Fd);
  Fd = NULL;

  return;
}

/**
  Generate the compressed section with specific type.
  Type could be EFI_STANDARD_COMPRESSION or EFI_NOT_COMPRESSED

  @param[in]  InputFileName    File name of the raw data.
  @param[in]  OutPutFileName   File name of the sectioned data.
  @param[in]  CompressionType  The compression type.

  @return  EFI_INVALID_PARAMETER
  @return  EFI_ABORTED
  @return  EFI_OUT_OF_RESOURCES
  @return  EFI_SUCCESS

**/
EFI_STATUS
LibGenCompressedSection (
  CHAR8         *InputFileName,
  CHAR8         *OutPutFileName,
  UINT8         CompressionType
)
{
  //FILE                        *UnCompressFile;
  //FILE                        *CompressedFile;
  //VOID                        *UnCompressedBuffer;
  //VOID                        *CompressedBuffer;
  //UINT32                      UnCompressedSize;
  //UINT32                      CompressedSize;
  //CHAR8                       *TempName;
  //CHAR8                       *TemDir;
  //EFI_STATUS                  Status;

  //UnCompressFile     = NULL;
  //CompressedFile     = NULL;
  //UnCompressedBuffer = NULL;
  //CompressedBuffer   = NULL;
  //TempName           = NULL;
  //TemDir             = NULL;
  //UnCompressedSize   = 0;
  //CompressedSize     = 0;

  if ( InputFileName == NULL ||
       OutPutFileName == NULL) {
    printf ("Error while generate compressed section!\n");
    return EFI_INVALID_PARAMETER;
  }

  if (CompressionType == EFI_STANDARD_COMPRESSION) {
    /*

    UnCompressFile = fopen (InputFileName, "rb");
    if (UnCompressFile == NULL) {
      printf ("Error while open file %s \n", InputFileName);
      return EFI_ABORTED;
    }

    TemDir = _getcwd (NULL, _MAX_PATH);
    sprintf(TemDir, "%s\\%s", TemDir, TEMP_DIR_NAME);

    TempName= LibFilenameStrExtended (strrchr(CloneString (tmpnam (NULL)),'\\'), TemDir, "comp");

    CompressedFile = fopen (TempName, "wb+");
    if (CompressedFile == NULL) {
      printf ("Error while open file %s \n", TempName);
      return EFI_ABORTED;
    }

    //
    // Get the original file size;
    //
    fseek(UnCompressFile,0,SEEK_SET);
    fseek(UnCompressFile,0,SEEK_END);

    UnCompressedSize = ftell(UnCompressFile);

    fseek(UnCompressFile,0,SEEK_SET);

    UnCompressedBuffer = malloc (UnCompressedSize);

    if (UnCompressedBuffer == NULL) {
      printf("Error while allocate memory! \n");
      return EFI_OUT_OF_RESOURCES;
    }

    CompressedBuffer = malloc (UnCompressedSize);

    if (CompressedBuffer == NULL) {
      printf("Error while allocate memory! \n");
      return EFI_OUT_OF_RESOURCES;
    }

    if (fread (UnCompressedBuffer, 1, (size_t) UnCompressedSize, UnCompressFile) == (size_t) UnCompressedSize) {
      CompressedSize = UnCompressedSize;

      Status = EfiCompress ( UnCompressedBuffer,
                             UnCompressedSize,
                             CompressedBuffer,
                             &CompressedSize);

      if (EFI_ERROR(Status)) {
        printf("Error while do compress operation! \n");
        return EFI_ABORTED;
      }

      if (CompressedSize > UnCompressedSize) {
        printf("Error while do compress operation! \n");
        return EFI_ABORTED;
      }
    } else {
      printf("Error while reading file %s! \n", InputFileName);
      return EFI_ABORTED;
    }

    //
    // Write the compressed data into output file
    //
    if (fwrite (CompressedBuffer, 1, (size_t) CompressedSize, CompressedFile) != (size_t) CompressedSize) {
      Error ("FMMT", 0, 0004, "error writing %s file", OutPutFileName);
      fclose(UnCompressFile);
      fclose (CompressedFile);
      return EFI_ABORTED;
    }

    fclose(UnCompressFile);
    fclose (CompressedFile);
    */

    //
    // Call GenSec tool to generate the compressed section.
    //
    LibCreateFfsSection(NULL, InputFileName, NULL, EFI_SECTION_COMPRESSION, OutPutFileName, NULL, NULL, NULL, 0, 0, "PI_STD");

  } else if (CompressionType == EFI_NOT_COMPRESSED) {

      LibCreateFfsSection(NULL, InputFileName, NULL, EFI_SECTION_COMPRESSION, OutPutFileName, NULL, NULL, NULL, 0, 0, "PI_NONE");

  } else {
    printf ("Error while generate compressed section, unknown compression type! \n");
    return EFI_INVALID_PARAMETER;
  }


  return EFI_SUCCESS;
}

EFI_STATUS
LibEncapNewFvFile(
  IN  FV_INFORMATION   *FvInFd,
  IN  CHAR8            *TemDir,
  IN  ENCAP_INFO_DATA  *CurrentEncapData,
  IN  UINT32           Level_Break,
  OUT FFS_INFORMATION  **OutputFile
)
{
  EFI_STATUS                  Status;
  UINT32                      ParentType;
  UINT8                       ParentLevel;
  UINT32                      Type;
  UINT8                       Level;
  CHAR8                       *InfFileName;
  FILE                        *InfFile;
  ENCAP_INFO_DATA             *LocalEncapData;
  ENCAP_INFO_DATA             *LocalEncapDataTemp;
  ENCAP_INFO_DATA             *LocalEncapDataNext;
  BOOLEAN                     FfsFoundFlag;
  UINT32                      Index;
  UINT32                      OuterIndex;
  CHAR8                       *ExtractionTool;
  BOOLEAN                     IsLastLevelFfs;
  BOOLEAN                     IsLeafFlagIgnore;
  BOOLEAN                     FirstInFlag;
  BOOLEAN                     OutputFileNameListFlag;
  CHAR8                       *InputFileName;
  CHAR8                       *OutputFileName;
  FFS_INFORMATION             *OutputFileNameList;
  FFS_INFORMATION             *ChildFileNameList;
  FFS_INFORMATION             *NewFileNameList;
  CHAR8                       *FvGuidName;
  UINT16                      GuidAttributes;
  UINT16                      GuidDataOffset;
  BOOLEAN                     IsRootFv;
  BOOLEAN                     IsLargeFile;
  UINT32                      EncapFvStart;
  UINT32                      EncapFvIndex;
  CHAR8                       *TmpFileName;
  FILE                        *TmpFile;
  FILE                        *InputFile;
  FILE                        *OutFile;
  UINT32                      InputFileSize;
  UINT32                      OutputFileSize;
  UINT32                      LargeFileSize;
  UINT8                       *Buffer = NULL;
  UINT8                       SectionHeader[4] = { 0x00, 0x00, 0x00, 0x00 };
  UINT32                      Id;
  UINT32                      SubFvId;
  UINT32                      header;
  UINT8                       AlignN;
  UINT8                       AlignV[1] = {0xFF};
  AlignN                      = 0;
  Id                          = 0;
  InputFileSize               = 0;
  EncapFvIndex                = 0;
  Index                       = 0;
  OuterIndex                  = 0;
  ParentType                  = 0;
  ParentLevel                 = 0;
  Type                        = 0;
  Level                       = 0;
  SubFvId                     = 0;
  FfsFoundFlag                = FALSE;
  LocalEncapDataTemp          = NULL;
  LocalEncapDataNext          = NULL;
  ExtractionTool              = NULL;
  InputFileName               = NULL;
  OutputFileName              = NULL;
  IsLastLevelFfs              = TRUE;
  IsLeafFlagIgnore            = FALSE;
  FirstInFlag                 = TRUE;
  FvGuidName                  = NULL;
  OutputFileNameListFlag      = TRUE;
  IsLargeFile                 = FALSE;
  OutputFileSize              = 0;
  LargeFileSize               = 0x1000000;


  OutputFileNameList = (FFS_INFORMATION *)malloc(sizeof(FV_INFORMATION));
  if (OutputFileNameList == NULL) {
    Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
    return EFI_OUT_OF_RESOURCES;
  }
  OutputFileNameList->FFSName = NULL;
  OutputFileNameList->InFvId = 0;
  OutputFileNameList->IsFFS = FALSE;
  OutputFileNameList->ParentLevel = 0;
  OutputFileNameList->Next = NULL;
  OutputFileNameList->UiNameSize = 0;
  OutputFileNameList->Depex = NULL;
  OutputFileNameList->DepexLen = 0;
  OutputFileNameList->FfsFoundFlag = FALSE;

  ChildFileNameList = (FFS_INFORMATION *)malloc(sizeof(FV_INFORMATION));
  if (ChildFileNameList == NULL) {
    Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
    return EFI_OUT_OF_RESOURCES;
  }
  ChildFileNameList->FFSName = NULL;
  ChildFileNameList->InFvId = 0;
  ChildFileNameList->ParentLevel = 0;
  ChildFileNameList->Next = NULL;
  ChildFileNameList->IsFFS = FALSE;
  ChildFileNameList->UiNameSize = 0;
  ChildFileNameList->Depex = NULL;
  ChildFileNameList->DepexLen = 0;
  ChildFileNameList->FfsFoundFlag = FALSE;
  //
  // Encapsulate from the lowest FFS file level.
  //
    LocalEncapData = CurrentEncapData;
    if (LocalEncapData == NULL) {
        LocalEncapData = FvInFd->EncapData;
    }
    Level = LocalEncapData->Level;
    Type = LocalEncapData->Type;

  if (CurrentEncapData == NULL) {
    LocalEncapData = FvInFd->EncapData;
    while (LocalEncapData != NULL) {
      if (LocalEncapData->Type == FMMT_ENCAP_TREE_FFS) {
        LocalEncapDataTemp = LocalEncapData->RightNode;
        while (LocalEncapDataTemp != NULL) {
            LocalEncapDataNext = LocalEncapDataTemp->NextNode;
            if (LocalEncapDataNext != NULL && LocalEncapDataNext->NextNode != NULL) {

                LibEncapNewFvFile(FvInFd, TemDir, LocalEncapDataTemp, 1, &ChildFileNameList);
                ChildFileNameList->ParentLevel = LocalEncapDataTemp->Level -1;
                if (FvInFd->ChildFvFFS == NULL) {
                    FvInFd->ChildFvFFS = ChildFileNameList;
                } else {
                    NewFileNameList = FvInFd->ChildFvFFS;
                    while (NewFileNameList->Next != NULL) {
                        NewFileNameList = NewFileNameList->Next;
                    }
                    NewFileNameList->Next = ChildFileNameList;
                }
            }
            LocalEncapDataTemp = LocalEncapDataTemp->RightNode;
        }
      }

      if (LocalEncapData->Level > Level) {
        if (LocalEncapData->Type == FMMT_ENCAP_TREE_FFS) {
            ParentLevel = Level;
            ParentType  = Type;
        }
        Level       = LocalEncapData->Level;
        Type        = LocalEncapData->Type;
      }
      LocalEncapData = LocalEncapData->NextNode;
    }
  } else {
    LocalEncapData = CurrentEncapData;
    while (LocalEncapData != NULL) {
        if (LocalEncapData->Level > Level) {
            if (LocalEncapData->Type == FMMT_ENCAP_TREE_FFS) {
                ParentLevel = Level;
                ParentType  = Type;
            }
            Level       = LocalEncapData->Level;
            Type        = LocalEncapData->Type;
        }
        LocalEncapData = LocalEncapData->NextNode;
    }
  }

  do {
    switch (ParentType) {
      case FMMT_ENCAP_TREE_FV:
        OutputFileNameListFlag = TRUE;
        EncapFvStart = 0;
    for(OuterIndex=0;OutputFileNameListFlag;OuterIndex++){
        //
        // Generate FV.inf attributes.
        //
        InfFileName = LibFilenameStrExtended (strrchr(GenTempFile (),OS_SEP), TemDir, "inf");
    FirstInFlag = TRUE;

        InfFile = fopen (InfFileName, "wt+");

        if (InfFile == NULL) {
          Error ("FMMT", 0, 0004, "Could not open inf file %s to store FV information! \n", "");
          free (OutputFileNameList);
          free (ChildFileNameList);
          return EFI_ABORTED;
        }

        if (CurrentEncapData == NULL) {
           LocalEncapData = FvInFd->EncapData;
        } else {
            LocalEncapData = CurrentEncapData;
        }

        while (LocalEncapData->NextNode != NULL) {
          if (LocalEncapData->Level == ParentLevel) {
             break;
          }
          LocalEncapData = LocalEncapData->NextNode;
        }

        if (((EFI_FIRMWARE_VOLUME_HEADER *)(LocalEncapData->Data))->ExtHeaderOffset != 0) {
          //
          // FV GUID Name memory allocation
          //
          FvGuidName = (CHAR8 *) malloc (255);

          if (FvGuidName == NULL) {
            Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
            fclose (InfFile);
            free (OutputFileNameList);
            free (ChildFileNameList);
            return EFI_ABORTED;
          }

          memset(FvGuidName, '\0', 255);

          sprintf(
            FvGuidName,
            "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
            LocalEncapData->FvExtHeader->FvName.Data1,
            LocalEncapData->FvExtHeader->FvName.Data2,
            LocalEncapData->FvExtHeader->FvName.Data3,
            LocalEncapData->FvExtHeader->FvName.Data4[0],
            LocalEncapData->FvExtHeader->FvName.Data4[1],
            LocalEncapData->FvExtHeader->FvName.Data4[2],
            LocalEncapData->FvExtHeader->FvName.Data4[3],
            LocalEncapData->FvExtHeader->FvName.Data4[4],
            LocalEncapData->FvExtHeader->FvName.Data4[5],
            LocalEncapData->FvExtHeader->FvName.Data4[6],
            LocalEncapData->FvExtHeader->FvName.Data4[7]
          );

        } else {
          FvGuidName = NULL;
        }


        if (ParentLevel == 1) {
          Status = LibFvHeaderOptionToStr((EFI_FIRMWARE_VOLUME_HEADER *)LocalEncapData->Data, InfFile, TRUE);
        } else {
          Status = LibFvHeaderOptionToStr((EFI_FIRMWARE_VOLUME_HEADER *)LocalEncapData->Data, InfFile, FALSE);
        }


        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "generate FV INF file [Options] section failed.");
          fclose (InfFile);
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }

        Status = LibFvHeaderAttributeToStr(((EFI_FIRMWARE_VOLUME_HEADER *)LocalEncapData->Data)->Attributes, InfFile);

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV header attribute failed");
          fclose (InfFile);
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }
        if (LocalEncapData->FvExtHeader != NULL) {
          Status = LibGenExtFile(LocalEncapData->FvExtHeader, InfFile);
          if (EFI_ERROR(Status)) {
            Error("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV EXT header failed");
            fclose (InfFile);
            free (OutputFileNameList);
            free (ChildFileNameList);
            return Status;
          }
          FvGuidName = NULL;
        }

        if (CurrentEncapData != NULL) {
            for (Index = 0; Index <= FvInFd->FfsNumbers; Index++) {
                if ((memcmp(&FvInFd->FfsAttuibutes[Index].GuidName, &(CurrentEncapData->FvExtHeader->FvName), sizeof(EFI_GUID)) == 0)) {
                    SubFvId = Index;
                    break;
                }
            }
        }
        //
        // Found FFSs from Fv structure.
        //
        FfsFoundFlag = FALSE;
        IsRootFv = FALSE;
        for (Index=0; Index <= FvInFd->FfsNumbers; Index++) {
            if (OutputFileNameList != NULL && OutputFileNameList->FFSName != NULL && OutputFileNameList->IsFFS == FALSE){
                break;
            }
            if (OutputFileNameList != NULL && OutputFileNameList->FFSName != NULL && OutputFileNameList->IsFFS == TRUE){
                if (Index == EncapFvIndex) {
                    if (FirstInFlag) {
                            Status = LibAddFfsFileToFvInf (OutputFileNameList->FFSName, InfFile, TRUE);
                            FirstInFlag = FALSE;
                        } else {
                            Status = LibAddFfsFileToFvInf (OutputFileNameList->FFSName, InfFile, FALSE);
                        }
                        if (EFI_ERROR (Status)) {
                            Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV inf file [files] section failed!");
                            fclose (InfFile);
                            free (OutputFileNameList);
                            free (ChildFileNameList);
                            return Status;
                        }
                }
            }

            NewFileNameList = FvInFd->ChildFvFFS;
            while (NewFileNameList != NULL && NewFileNameList -> FFSName != NULL) {
                if (NewFileNameList -> ParentLevel == ParentLevel && Index == NewFileNameList->InFvId && NewFileNameList->FfsFoundFlag==TRUE) {
                    if (FirstInFlag) {
                        Status = LibAddFfsFileToFvInf (NewFileNameList->FFSName, InfFile, TRUE);
                        FirstInFlag = FALSE;
                    } else {
                        Status = LibAddFfsFileToFvInf (NewFileNameList->FFSName, InfFile, FALSE);
                    }
                    if (EFI_ERROR (Status)) {
                        Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV inf file [files] section failed!");
                        fclose (InfFile);
                        free (OutputFileNameList);
                        free (ChildFileNameList);
                        return Status;
                    }
                }
                NewFileNameList = NewFileNameList->Next;
            }

            if (FvInFd->FfsAttuibutes[Index].IsHandle==TRUE) {
                continue;
            }
            if (SubFvId > 0 && Index < SubFvId) {
                continue;
            }

          //
          // For the last level FFS, the level below FFSs we should not care the IsLeaf Flag.
          //
          if (IsLastLevelFfs) {
            IsLeafFlagIgnore = TRUE;
            } else {
              IsLeafFlagIgnore = FvInFd->FfsAttuibutes[Index].IsLeaf;
            }

          if (FvInFd->FfsAttuibutes[Index].Level >= ParentLevel + 1 && IsLeafFlagIgnore) {
            if (FirstInFlag) {
        if (FvInFd->FfsAttuibutes[Index].Level < 0xFF) {
          FfsFoundFlag = TRUE;
          Status = LibAddFfsFileToFvInf (FvInFd->FfsAttuibutes[Index].FfsName, InfFile, TRUE);
          FirstInFlag = FALSE;
          FvInFd->FfsAttuibutes[Index].IsHandle=TRUE;
          EncapFvStart = Index;
        }

              if (EFI_ERROR (Status)) {
                  Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV inf file [files] section failed!");
                  fclose (InfFile);
                  free (OutputFileNameList);
                  free (ChildFileNameList);
                  return Status;
              }
              if (Index == 0) {
                // Root FV need to include all FFS files.
                IsRootFv = TRUE;
              }
            } else {
          if (FvInFd->FfsAttuibutes[Index].Level < 0xFF) {
          FfsFoundFlag = TRUE;
          Status = LibAddFfsFileToFvInf (FvInFd->FfsAttuibutes[Index].FfsName, InfFile, FALSE);
          FvInFd->FfsAttuibutes[Index].IsHandle=TRUE;
        }

                if (EFI_ERROR (Status)) {
                  Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV inf file [files] section failed!");
                  fclose (InfFile);
                  free (OutputFileNameList);
                  free (ChildFileNameList);
                  return Status;
                }
                if (Index == 0) {
                  // Root FV need to include all FFS files.
                  IsRootFv = TRUE;
                }
              }


      //avoid a FV contain too many ffs files
           if ((!IsRootFv) && (FvInFd->FfsAttuibutes[Index].FvLevel <= FvInFd->MulFvLevel) && (FvInFd->FfsAttuibutes[Index+1].FvLevel <= FvInFd->MulFvLevel) &&
               (FvInFd->FfsAttuibutes[Index].FvLevel != FvInFd->FfsAttuibutes[Index+1].FvLevel) && (ParentLevel != 1) && (FvInFd->FfsAttuibutes[Index].Level != FvInFd->FfsAttuibutes[Index+1].Level) &&
               FvInFd->FfsAttuibutes[Index].Level != 0xFF && FvInFd->FfsAttuibutes[Index+1].Level != 0xFF && FvInFd->FfsAttuibutes[Index+1].Level != 0x0){
        FvInFd->FfsAttuibutes[Index].Level = 0;
        break;
      }else{
        if (FvInFd->FfsAttuibutes[Index].Level != 0xFF){
          FvInFd->FfsAttuibutes[Index].Level = 0;
        }
      }

            }
          }
       // The Fv may has multiple level (> 2), when it is in the FvLevel == 2, we set the IsLastLevelFfs Flag
       if (Index <=FvInFd->FfsNumbers && FvInFd->FfsAttuibutes[Index].FvLevel <= FvInFd->MulFvLevel) {
           if (FvInFd->FfsAttuibutes[Index].FvLevel == 2) {
               IsLastLevelFfs = FALSE;
           }
       }
    if (!FfsFoundFlag){
      OutputFileNameListFlag = FALSE;
      if (OuterIndex > 0){
        fclose (InfFile);
        break;
      }
    }
        //
        // Create FV
        //
        fclose (InfFile);

        EncapFvIndex = EncapFvStart;

        OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "FV");

        Status = LibEncapsulateFfsToFv (InfFileName, NULL, OutputFileName, FvGuidName, IsLargeFile);

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV failed!");
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }

    OutputFileNameList->FFSName = (char *)malloc(strlen(OutputFileName)+1);
    if (OutputFileNameList->FFSName == NULL) {
      Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
      return EFI_OUT_OF_RESOURCES;
    } 
    memcpy((char *)OutputFileNameList->FFSName, (char *)OutputFileName, strlen(OutputFileName)+1);
      if (CurrentEncapData != NULL) {
        OutputFileNameList->InFvId = EncapFvIndex;
        if (EncapFvIndex > 0) {
            memcpy(OutputFileNameList->UiName,FvInFd->FfsAttuibutes[EncapFvIndex - 1].UiName, FvInFd->FfsAttuibutes[EncapFvIndex - 1].UiNameSize);
            OutputFileNameList->UiNameSize = FvInFd->FfsAttuibutes[EncapFvIndex - 1].UiNameSize;
            OutputFileNameList->Depex = FvInFd->FfsAttuibutes[EncapFvIndex - 1].Depex;
            OutputFileNameList->DepexLen = FvInFd->FfsAttuibutes[EncapFvIndex - 1].DepexLen;
            OutputFileNameList->FfsFoundFlag = FfsFoundFlag;
        }
      }
    }
        break;
      case FMMT_ENCAP_TREE_FFS:

        while(OutputFileNameList!= NULL && OutputFileNameList->FFSName != NULL){
          InputFileName  = OutputFileNameList->FFSName;
          OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "ffs");
          LocalEncapData = CurrentEncapData;
          if (LocalEncapData == NULL) {
            LocalEncapData = FvInFd->EncapData;
          }
          while (LocalEncapData->NextNode != NULL) {
            if (LocalEncapData->Level == ParentLevel) {
        for(;LocalEncapData->NextNode != NULL;) {
          if(LocalEncapData->FvExtHeader != NULL) {
            break;
          }
          LocalEncapData = LocalEncapData->NextNode;
        }
                break;
            }
            LocalEncapData = LocalEncapData->NextNode;
          }

          if (LocalEncapData->FvExtHeader == NULL) {
            Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FFS file failed!");
            free (OutputFileNameList);
            free (ChildFileNameList);
            return EFI_ABORTED;
          }

          if (OutputFileNameList->UiNameSize > 0) {
              TmpFileName = LibFilenameStrExtended(strrchr(GenTempFile (), OS_SEP), TemDir, "tmp");
              TmpFile = fopen(TmpFileName, "wb+");
              if (TmpFile == NULL) {
                 Error("FMMT", 0, 0004, "Could not open tmp file %s to store UI section information! \n", "");
                 free (OutputFileNameList);
                 free (ChildFileNameList);
                 return EFI_ABORTED;
              }
             header = (OutputFileNameList->UiNameSize+4) | (EFI_SECTION_USER_INTERFACE << 24);
             Index = 0;
             while (header) {
                 SectionHeader[Index] = header % 0x100;
                 header /= 0x100;
                 Index ++;
             }
             InputFile = fopen(InputFileName, "rb+");
             if (InputFile == NULL) {
               Error("FMMT", 0, 0004, "Could not open input file %s! \n", "");
               fclose(TmpFile);
               free (OutputFileNameList);
               free (ChildFileNameList);
               return EFI_ABORTED;
             }
             fseek(InputFile, 0, SEEK_SET);
             fseek(InputFile, 0, SEEK_END);
             InputFileSize = ftell(InputFile);
             fseek(InputFile, 0, SEEK_SET);

             Buffer = malloc(InputFileSize+OutputFileNameList->UiNameSize+4);
             memcpy(Buffer, (CHAR16 *)SectionHeader, 4);
             memcpy(Buffer + 4, (CHAR16 *)(OutputFileNameList->UiName), OutputFileNameList->UiNameSize);
             if (fread(Buffer+4+OutputFileNameList->UiNameSize, 1, InputFileSize, InputFile) != InputFileSize) {
               Error("FMMT", 0, 0004, "Could not open sec file %s to add UI section information! \n", "");
               fclose(TmpFile);
               fclose(InputFile);
               free(Buffer);
               free (OutputFileNameList);
               free (ChildFileNameList);
               return EFI_ABORTED;
             }
             fwrite(Buffer, 1, InputFileSize + OutputFileNameList->UiNameSize + 4, TmpFile);
             free(Buffer);
             fclose(TmpFile);
             fclose(InputFile);
             InputFileName = TmpFileName;
          }
          if (OutputFileNameList->DepexLen > 0) {
              TmpFileName = LibFilenameStrExtended(strrchr(GenTempFile (), OS_SEP), TemDir, "tmp");
              TmpFile = fopen(TmpFileName, "wb+");
              if (TmpFile == NULL) {
                  Error("FMMT", 0, 0004, "Could not open tmp file %s to store Depex section information! \n", "");
                  free (OutputFileNameList);
                  free (ChildFileNameList);
                  return EFI_ABORTED;
              }
              InputFile = fopen(InputFileName, "rb+");
              if (InputFile == NULL) {
                Error("FMMT", 0, 0004, "Could not open input file %s! \n", "");
                fclose(TmpFile);
                free (OutputFileNameList);
                free (ChildFileNameList);
                return EFI_ABORTED;
              }
              fseek(InputFile, 0, SEEK_SET);
              fseek(InputFile, 0, SEEK_END);
              InputFileSize = ftell(InputFile);
              fseek(InputFile, 0, SEEK_SET);
              // make sure the section is 4 byte align
              if (OutputFileNameList->DepexLen % 4 != 0) {
                  AlignN = 4 - OutputFileNameList->DepexLen % 4;
              }
              Buffer = malloc(InputFileSize + OutputFileNameList->DepexLen + AlignN);
              memcpy(Buffer, OutputFileNameList->Depex, OutputFileNameList->DepexLen);
              if (AlignN != 0) {
                  for (Index = 0; Index < AlignN; Index ++) {
                      memcpy(Buffer + OutputFileNameList->DepexLen + Index, AlignV, 1);
                  }
              }
              if (fread(Buffer + OutputFileNameList->DepexLen + AlignN, 1, InputFileSize, InputFile) != InputFileSize) {
                  Error("FMMT", 0, 0004, "Could not open sec file %s to add Depex section information! \n", "");
                  fclose(TmpFile);
                  fclose(InputFile);
                  free(Buffer);
                  free (OutputFileNameList);
                  free (ChildFileNameList);
                  return EFI_ABORTED;
              }
              fwrite(Buffer, 1, InputFileSize + OutputFileNameList->DepexLen + AlignN, TmpFile);
              free(Buffer);
              fclose(TmpFile);
              fclose(InputFile);
              InputFileName = TmpFileName;
         }
         for (Id = FvInFd->FfsNumbers; Id <= FvInFd->FfsNumbers; Id--) {
             if ((memcmp(&FvInFd->FfsAttuibutes[Id].GuidName, &(LocalEncapData->FvExtHeader->FvName), sizeof(EFI_GUID)) == 0)){
                 if (access(FvInFd->FfsAttuibutes[Id].FfsName, 0) != -1) {
                     Status = LibFmmtDeleteFile(FvInFd->FfsAttuibutes[Id].FfsName);
                     if (EFI_ERROR(Status)) {
                         Error("FMMT", 0, 0004, "error while encapsulate FD Image", "Delete the specified file failed!");
                         free (OutputFileNameList);
                         free (ChildFileNameList);
                         return Status;
                     }
                     memset(FvInFd->FfsAttuibutes[Id].FfsName, '\0', _MAX_PATH);
                     FvInFd->FfsAttuibutes[Id].Level = 0xFF;
                     break;
                 }
             }
         }
         if (LocalEncapData->NextNode != NULL) {
             LocalEncapDataTemp = LocalEncapData->NextNode;
             if ((LocalEncapDataTemp->Type == FMMT_ENCAP_TREE_GUIDED_SECTION) || (LocalEncapDataTemp->Type == FMMT_ENCAP_TREE_COMPRESS_SECTION)) {
                 Status = LibEncapSectionFileToFFS(EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, InputFileName, OutputFileName, LocalEncapData->FvExtHeader->FvName, FALSE, "1");
             }
             else{
                 Status = LibEncapSectionFileToFFS(EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, InputFileName, OutputFileName, LocalEncapData->FvExtHeader->FvName, FALSE, FvInFd->AlignmentStr);
             }
         }
         else{
             Status = LibEncapSectionFileToFFS(EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, InputFileName, OutputFileName, LocalEncapData->FvExtHeader->FvName, FALSE, FvInFd->AlignmentStr);
         }
      if (EFI_ERROR (Status)) {
        Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FFS file failed!");
        free (OutputFileNameList);
        free (ChildFileNameList);
        return Status;
      }
      free(LocalEncapData->FvExtHeader);
      LocalEncapData->FvExtHeader = NULL;
      OutputFileNameList->FFSName = (char *)malloc(strlen(OutputFileName)+1);
      if (OutputFileNameList->FFSName == NULL) {
          Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
          return EFI_OUT_OF_RESOURCES;
      }
      memcpy((char *)OutputFileNameList->FFSName, (char *)OutputFileName, strlen(OutputFileName)+1);
      OutputFileNameList->IsFFS = TRUE;
      if (OutputFileNameList->Next == NULL){
          break;
      }
      OutputFileNameList = OutputFileNameList->Next;
    }
        break;
      case FMMT_ENCAP_TREE_GUIDED_SECTION:
      while(OutputFileNameList!= NULL && OutputFileNameList->FFSName != NULL){
        //
        // Create the guided section original data, do compress operation.
        //
        InputFileName  = OutputFileNameList->FFSName;
        OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "compressed");

        //
        // Use the guided section header guid to find out compress application name.
        //
        LocalEncapData = CurrentEncapData;
          if (LocalEncapData == NULL) {
            LocalEncapData = FvInFd->EncapData;
        }
        while (LocalEncapData->NextNode != NULL) {
          if (LocalEncapData->Level == ParentLevel) {
            break;
          }
          LocalEncapData = LocalEncapData->NextNode;
        }

        ExtractionTool =
          LookupGuidedSectionToolPath (
            mParsedGuidedSectionTools,
            (EFI_GUID *)LocalEncapData->Data
            );
        GuidDataOffset = *(UINT16 *) ((UINT8 *) LocalEncapData->Data + sizeof (EFI_GUID));
        GuidAttributes = *(UINT16 *) ((UINT8 *) LocalEncapData->Data + sizeof (EFI_GUID) + sizeof (UINT16));

        Status = LibCreateGuidedSectionOriginalData (InputFileName, ExtractionTool, OutputFileName);

        if (EFI_ERROR (Status) || GuidDataOffset < sizeof (EFI_GUID_DEFINED_SECTION)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Compress guided data failed!");
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }

        GuidDataOffset = GuidDataOffset - sizeof (EFI_GUID_DEFINED_SECTION);
        InputFileName  = OutputFileName;
        OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "guided");

        Status = LibCreateFfsSection(NULL, InputFileName, NULL, EFI_SECTION_GUID_DEFINED, OutputFileName, NULL, NULL, LibFmmtGuidToStr((EFI_GUID *)LocalEncapData->Data), GuidDataOffset, GuidAttributes, NULL);
        OutFile = fopen(OutputFileName, "rb+");
        if (OutFile == NULL) {
            Error("FMMT", 0, 0004, "Could not open the file %s! \n", "");
            free (OutputFileNameList);
            free (ChildFileNameList);
            return EFI_ABORTED;
        }
        fseek(OutFile, 0, SEEK_SET);
        fseek(OutFile, 0, SEEK_END);
        OutputFileSize = ftell(OutFile);
        fclose(OutFile);
        if (OutputFileSize > LargeFileSize) {
            IsLargeFile = TRUE;
        }
    OutputFileNameList->FFSName = (char *)malloc(strlen(OutputFileName)+1);
    if (OutputFileNameList->FFSName == NULL) {
      Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
      return EFI_OUT_OF_RESOURCES;
    }
    memcpy((char *)OutputFileNameList->FFSName, (char *)OutputFileName, strlen(OutputFileName)+1);

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate guided section failed!");
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }
        if (OutputFileNameList->Next == NULL){
          break;
       }
       OutputFileNameList = OutputFileNameList->Next;
    }
        break;
      case FMMT_ENCAP_TREE_COMPRESS_SECTION:
        while(OutputFileNameList!= NULL && OutputFileNameList->FFSName != NULL){
          InputFileName  = OutputFileNameList->FFSName;

          OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "comsec");
          LocalEncapData = CurrentEncapData;
          if (LocalEncapData == NULL) {
            LocalEncapData = FvInFd->EncapData;
          }
          while (LocalEncapData->NextNode != NULL) {
            if (LocalEncapData->Level == ParentLevel) {
              break;
            }
            LocalEncapData = LocalEncapData->NextNode;
          }

          Status = LibGenCompressedSection (InputFileName, OutputFileName, *(UINT8 *)(LocalEncapData->Data));
      OutputFileNameList->FFSName = (char *)malloc(strlen(OutputFileName)+1);
      if (OutputFileNameList->FFSName == NULL) {
        Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
        return EFI_OUT_OF_RESOURCES;
      }
      memcpy((char *)OutputFileNameList->FFSName, (char *)OutputFileName, strlen(OutputFileName)+1);

          if (EFI_ERROR (Status)) {
            Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate compressed section failed!");
            free (OutputFileNameList);
            free (ChildFileNameList);
            return Status;
          }
          if (OutputFileNameList->Next == NULL){
            break;
          }
          OutputFileNameList = OutputFileNameList->Next;
        }
        break;
      case FMMT_ENCAP_TREE_FV_SECTION:
        while(OutputFileNameList!= NULL && OutputFileNameList->FFSName != NULL){
        InputFileName  = OutputFileNameList->FFSName;
        OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "sec");

        Status = LibCreateFfsSection(NULL, InputFileName, NULL, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, OutputFileName, NULL, NULL, NULL, 0, 0, NULL);

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV section failed!");
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }

        InputFileName  = OutputFileName;
        OutputFileName= LibFilenameStrExtended (strrchr(GenTempFile (), OS_SEP), TemDir, "sec");

        //
        // Make it alignment.
        //
        Status = LibCreateFfsSection(FvInFd, InputFileName, NULL, 0, OutputFileName, NULL, NULL, NULL, 0, 0, NULL);
        OutFile = fopen(OutputFileName, "rb+");
        if (OutFile == NULL) {
            Error("FMMT", 0, 0004, "Could not open the file %s! \n", "");
            free (OutputFileNameList);
            free (ChildFileNameList);
            return EFI_ABORTED;
        }
        fseek(OutFile, 0, SEEK_SET);
        fseek(OutFile, 0, SEEK_END);
        OutputFileSize = ftell(OutFile);
        fclose(OutFile);
        if (OutputFileSize > LargeFileSize) {
            IsLargeFile = TRUE;
        }

    OutputFileNameList->FFSName = (char *)malloc(strlen(OutputFileName)+1);
    if (OutputFileNameList->FFSName == NULL) {
      Error ("FMMT", 0, 0004, "Out of resource, memory allocation failed! \n", "");
      return EFI_OUT_OF_RESOURCES;
    }
    memcpy((char *)OutputFileNameList->FFSName, (char *)OutputFileName, strlen(OutputFileName)+1);

        if (EFI_ERROR (Status)) {
          Error ("FMMT", 0, 0004, "error while encapsulate FD Image", "Generate FV section failed!");
          free (OutputFileNameList);
          free (ChildFileNameList);
          return Status;
        }
        if (OutputFileNameList->Next == NULL){
           break;
        }
        OutputFileNameList = OutputFileNameList->Next;
    }
        break;
      default:
        for (Id = FvInFd->FfsNumbers; Id <= FvInFd->FfsNumbers; Id--) {
            if ((memcmp(&FvInFd->FfsAttuibutes[Id].GuidName, &(CurrentEncapData->FvExtHeader->FvName), sizeof(EFI_GUID)) == 0)){
                FvInFd->FfsAttuibutes[Id].IsHandle = TRUE;
                memcpy(OutputFileNameList->UiName, FvInFd->FfsAttuibutes[Id].UiName, FvInFd->FfsAttuibutes[Id].UiNameSize);
                OutputFileNameList->UiNameSize = FvInFd->FfsAttuibutes[Id].UiNameSize;
                OutputFileNameList->FFSName = FvInFd->FfsAttuibutes[Id].FfsName;
                OutputFileNameList->Depex = FvInFd->FfsAttuibutes[Id].Depex;
                OutputFileNameList->DepexLen = FvInFd->FfsAttuibutes[Id].DepexLen;
                OutputFileNameList->FfsFoundFlag = TRUE;
                OutputFileNameList->IsFFS = TRUE;
                OutputFileNameList->InFvId = Id;
                *OutputFile = OutputFileNameList;
                return EFI_SUCCESS;
            }
        }
    }

    if (CurrentEncapData == NULL) {
       LocalEncapData = FvInFd->EncapData;
    } else {
        if (OutputFileNameList != NULL && OutputFileNameList->FFSName != NULL && OutputFileNameList->IsFFS == TRUE) {
          *OutputFile = OutputFileNameList;
          return EFI_SUCCESS;
        }
        LocalEncapData = CurrentEncapData;
    }
    ParentLevel -= 1;

    while (LocalEncapData->NextNode != NULL) {
      if (LocalEncapData->Level == ParentLevel) {
       LocalEncapDataTemp = LocalEncapData->NextNode;
       if ((LocalEncapDataTemp != NULL) && (LocalEncapDataTemp->Level == ParentLevel)) {
           ParentType = LocalEncapDataTemp->Type;
           break;
        }
        ParentType = LocalEncapData->Type;
        break;
      }
      LocalEncapData = LocalEncapData->NextNode;
    }
  } while (ParentLevel != Level_Break);

  *OutputFile = OutputFileNameList;
  return EFI_SUCCESS;

}

EFI_STATUS
LibFindFvInEncapData (
  ENCAP_INFO_DATA *EncapData,
  UINT8 *Index
)
{
  ENCAP_INFO_DATA *LocalEncapData;
  LocalEncapData = EncapData;
  if (LocalEncapData == NULL) {
    Error("FMMT", 0, 0005, "error while find FV in Encapulate buffer", "Invalid parameters.");
    return EFI_INVALID_PARAMETER;
  }
  while (LocalEncapData != NULL) {
    if (LocalEncapData->RightNode != NULL) {
      LibFindFvInEncapData (LocalEncapData->RightNode, Index);
    }
    if (LocalEncapData->Type == FMMT_ENCAP_TREE_FV) {
      (*Index)++;
    }
    LocalEncapData = LocalEncapData->NextNode;
  }
    return EFI_SUCCESS;
}

EFI_STATUS
LibLocateFvViaFvId (
  IN     FIRMWARE_DEVICE     *FdData,
  IN     CHAR8               *FvId,
  IN OUT FV_INFORMATION      **FvInFd
)
{
  UINT8                       FvIndex1;
  UINT8                       FvIndex2;
  BOOLEAN                     FvFoundFlag;
  CHAR8*                      FvGuidName;
  ENCAP_INFO_DATA             *LocalEncapData;
  ENCAP_INFO_DATA             *LocalEncapDataRight;
  ENCAP_INFO_DATA             *LocalEncapDataNext;
  FvIndex1                    = 0;
  FvIndex2                    = 0;
  FvFoundFlag                 = FALSE;
  FvGuidName                  = NULL;
  LocalEncapDataNext          = NULL;
  LocalEncapDataRight         = NULL;

  if (FdData == NULL || FvId == NULL || FvInFd == NULL || FdData->Fv == NULL) {
    Error ("FMMT", 0, 0005, "error while find FV in FD", "Invalid parameters.");
    return EFI_INVALID_PARAMETER;
  }

  *FvInFd = FdData->Fv;

  if (strlen(FvId) < 3) {
    Error ("FMMT", 0, 0005, "error while find FV in FD", "Invalid FvId, please double check the FvId. You can use view operate to get the FvId information!");
    return EFI_ABORTED;
  }

  FvGuidName = (CHAR8 *) malloc (255);
  if (FvGuidName == NULL) {
    Error ("FMMT", 0, 0005, "Resource: Memory can't be allocated", NULL);
    return EFI_ABORTED;
  }
  memset(FvGuidName, '\0', 255);
  LocalEncapData = NULL;

  if (strlen(FvId) == 36) {
    while (FvInFd != NULL) {
      if (((*FvInFd)->FvExtHeader) != NULL) {
        sprintf(
          FvGuidName,
          "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
          (*FvInFd)->FvExtHeader->FvName.Data1,
          (*FvInFd)->FvExtHeader->FvName.Data2,
          (*FvInFd)->FvExtHeader->FvName.Data3,
          (*FvInFd)->FvExtHeader->FvName.Data4[0],
          (*FvInFd)->FvExtHeader->FvName.Data4[1],
          (*FvInFd)->FvExtHeader->FvName.Data4[2],
          (*FvInFd)->FvExtHeader->FvName.Data4[3],
          (*FvInFd)->FvExtHeader->FvName.Data4[4],
          (*FvInFd)->FvExtHeader->FvName.Data4[5],
          (*FvInFd)->FvExtHeader->FvName.Data4[6],
          (*FvInFd)->FvExtHeader->FvName.Data4[7]);
        if (strcmp(FvGuidName, FvId) == 0) {
          FvId = (*FvInFd)->FvName;
          break;
        }
      }
      if ((*FvInFd)->MulFvLevel > 1) {
        LocalEncapData = (*FvInFd) -> EncapData;
        LocalEncapData = LocalEncapData->NextNode;
        while (LocalEncapData != NULL) {
          if (LocalEncapData->RightNode != NULL) {
            LocalEncapDataRight = LocalEncapData->RightNode;
            while (LocalEncapDataRight !=NULL) {
              if (LocalEncapDataRight->NextNode != NULL) {
                LocalEncapDataNext = LocalEncapDataRight->NextNode;
                while (LocalEncapDataNext != NULL) {
                  if (LocalEncapDataNext->Type == FMMT_ENCAP_TREE_FV) {
                    if (LocalEncapDataNext->FvExtHeader != NULL) {
                      sprintf(
                          FvGuidName,
                          "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
                          LocalEncapDataNext->FvExtHeader->FvName.Data1,
                          LocalEncapDataNext->FvExtHeader->FvName.Data2,
                          LocalEncapDataNext->FvExtHeader->FvName.Data3,
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[0],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[1],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[2],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[3],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[4],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[5],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[6],
                          LocalEncapDataNext->FvExtHeader->FvName.Data4[7]);
                          if (strcmp(FvGuidName, FvId) == 0)
                          {
                            sprintf(FvId, "%s%d", "FV", LocalEncapDataNext->FvId - 1);
                            break;
                          }

                    }
                  }
                  LocalEncapDataNext = LocalEncapDataNext->NextNode;
                }
              }
              LocalEncapDataRight = LocalEncapDataRight->RightNode;
            }
          }
          if (LocalEncapData->Type == FMMT_ENCAP_TREE_FV) {
            if (LocalEncapData->FvExtHeader != NULL) {
              sprintf(
                FvGuidName,
                "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
                LocalEncapData->FvExtHeader->FvName.Data1,
                LocalEncapData->FvExtHeader->FvName.Data2,
                LocalEncapData->FvExtHeader->FvName.Data3,
                LocalEncapData->FvExtHeader->FvName.Data4[0],
                LocalEncapData->FvExtHeader->FvName.Data4[1],
                LocalEncapData->FvExtHeader->FvName.Data4[2],
                LocalEncapData->FvExtHeader->FvName.Data4[3],
                LocalEncapData->FvExtHeader->FvName.Data4[4],
                LocalEncapData->FvExtHeader->FvName.Data4[5],
                LocalEncapData->FvExtHeader->FvName.Data4[6],
                LocalEncapData->FvExtHeader->FvName.Data4[7]);

                if (strcmp(FvGuidName, FvId) == 0) {
                  sprintf(FvId, "%s%d", "FV", LocalEncapData->FvId - 1);
                  break;
                }
            }
          }
          LocalEncapData = LocalEncapData->NextNode;
        }
      }
      if ((*FvInFd)->FvNext == 0) {
        break;
      }
      *FvInFd = (*FvInFd)->FvNext;
    }
  }
  *FvInFd = FdData->Fv;
  FvIndex1 = (UINT8) atoi (FvId + 2);

  while (FvInFd != NULL) {
    if (((*FvInFd)->FvName) != NULL) {
      FvIndex2 = (UINT8) atoi ((*FvInFd)->FvName + 2);
      LocalEncapData = (*FvInFd)->EncapData;
      LibFindFvInEncapData (LocalEncapData, &FvIndex2);

      if ((FvIndex2 - 1 >= FvIndex1)) {
        FvFoundFlag = TRUE;
        break;
      }
      if ((*FvInFd)->FvNext == 0) {
        break;
      }
    }
    *FvInFd = (*FvInFd)->FvNext;
  }
  if (FvGuidName != NULL) {
    free (FvGuidName); 
  }
  //
  // The specified FV id has issue, can not find the FV in FD.
  //
  if (!FvFoundFlag) {
    Error ("FMMT", 0, 0005, "error while find FV in FD", "Invalid FvId, please double check the FvId. You can use view operate to get the FvId information!");
    return EFI_ABORTED;
  }

  return EFI_SUCCESS;

}

#define BUILD_IN_TOOL_COUNT 4

EFI_HANDLE
LibPreDefinedGuidedTools (
  VOID
)
{
  EFI_GUID            Guid;
  STRING_LIST         *Tool;
  GUID_SEC_TOOL_ENTRY *FirstGuidTool;
  GUID_SEC_TOOL_ENTRY *LastGuidTool;
  GUID_SEC_TOOL_ENTRY *NewGuidTool;
  UINT8               Index;
  EFI_STATUS          Status;

  CHAR8 PreDefinedGuidedTool[BUILD_IN_TOOL_COUNT][255] = {
    "a31280ad-481e-41b6-95e8-127f4c984779 TIANO TianoCompress",
    "ee4e5898-3914-4259-9d6e-dc7bd79403cf LZMA LzmaCompress",
    "fc1bcdb0-7d31-49aa-936a-a4600d9dd083 CRC32 GenCrc32",
    "3d532050-5cda-4fd0-879e-0f7f630d5afb BROTLI BrotliCompress"
  };

  Tool            = NULL;
  FirstGuidTool   = NULL;
  LastGuidTool    = NULL;
  NewGuidTool     = NULL;
  Index           = 0;

  for (Index = 0; Index < BUILD_IN_TOOL_COUNT; Index++) {
    Tool = SplitStringByWhitespace (PreDefinedGuidedTool[Index]);
    if ((Tool != NULL) &&
        (Tool->Count == 3)
       ) {
      Status = StringToGuid (Tool->Strings[0], &Guid);
      if (!EFI_ERROR (Status)) {
        NewGuidTool = malloc (sizeof (GUID_SEC_TOOL_ENTRY));
        if (NewGuidTool != NULL) {
          memcpy (&(NewGuidTool->Guid), &Guid, sizeof (Guid));
          NewGuidTool->Name = CloneString(Tool->Strings[1]);
          NewGuidTool->Path = CloneString(Tool->Strings[2]);
          NewGuidTool->Next = NULL;
        } else {
          printf ("Error while allocate resource! \n");
          FreeStringList (Tool);
          return NULL;
        }
        if (FirstGuidTool == NULL) {
          FirstGuidTool = NewGuidTool;
        } else {
          LastGuidTool->Next = NewGuidTool;
        }
        LastGuidTool = NewGuidTool;
      }
    } else {
      fprintf (stdout, "Error");
    }
    if (Tool != NULL) {
      FreeStringList (Tool);
      Tool = NULL;
    }
  }
  return FirstGuidTool;
}
