/*++

Copyright (c) 2004 - 2010, 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.             

Module Name:

  GenFfsFile.c

Abstract:

  This file contains functions required to generate a Firmware File System
  file.

--*/

#include "TianoCommon.h"
#include "EfiFirmwareFileSystem.h"
#include "EfiFirmwareVolumeHeader.h"
#include "EfiImageFormat.h"
#include "EfiImage.h"
#include "ParseInf.h"
#include "Compress.h"
#include "EfiCustomizedCompress.h"
#include "crc32.h"
#include "GenFfsFile.h"
#include <stdio.h>
#include <ctype.h>  // for isalpha()
//
// include file for _spawnv
//
#include <process.h>
#include <stdlib.h>
#include <string.h>
#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#include "SimpleFileParsing.h"

#define UTILITY_NAME    "GenFfsFile"
#define UTILITY_VERSION "v1.1"
#define MAX_ARRAY_SIZE  100

static
INT32
GetNextLine (
  OUT CHAR8       *Destination,
  IN FILE         *Package,
  IN OUT UINT32   *LineNumber
  );

static
void
CheckSlash (
  IN OUT CHAR8  *String,
  IN FILE       *In,
  IN OUT UINT32 *LineNumber
  );

static
INT32
FindSectionInPackage (
  IN CHAR8        *BuildDirectory,
  IN FILE         *OverridePackage,
  IN OUT UINT32   *LineNumber
  );

static
STATUS
ProcessCommandLineArgs (
  int     Argc,
  char    *Argv[]
  );

static
void
PrintUsage (
  void
  );

static
void
AddMacro (
  UINT8   *MacroString
  );

static
UINT8 *
GetMacroValue (
  UINT8   *MacroName
  );
    
static
void
FreeMacros (
  );

static
STATUS
ReplaceMacros (
  UINT8   *InputFile,
  UINT8   *OutputFile
  );
    
//
// Linked list to keep track of all macros
//
typedef struct _MACRO {
  struct _MACRO   *Next;
  UINT8           *Name;
  UINT8           *Value;
} MACRO;

//
// Keep globals in this structure
//
static struct {
  UINT8   BuildDirectory[_MAX_PATH];
  UINT8   PrimaryPackagePath[_MAX_PATH];
  UINT8   OverridePackagePath[_MAX_PATH];
  UINT8   OutputFilePath[_MAX_PATH];
  BOOLEAN Verbose;
  MACRO   *MacroList;
} mGlobals;

static EFI_GUID mZeroGuid = { 0 };

static UINT8  MinFfsDataAlignOverride = 0;

static
void
StripQuotes (
  IN OUT CHAR8 *String
  )
/*++

Routine Description:

  Removes quotes and/or whitespace from around a string

Arguments:

 String    - String to remove quotes from

Returns:

  None

--*/
{
  UINTN Index;
  UINTN Index2;
  UINTN StrLen;

  Index2  = strspn (String, "\" \t\n");
  StrLen  = strlen (String);

  for (Index = Index2; String[Index] != '\"', Index < StrLen; Index++) {
    String[Index - Index2] = String[Index];
  }

  String[Index - Index2] = 0;
}

static
void
PrintUsage (
  void
  )
/*++

Routine Description:

  Print Error / Help message.

Arguments:

  void

Returns:

  None

--*/
{
  int         Index;
  const char  *Str[] = {
    UTILITY_NAME" "UTILITY_VERSION" - Intel Generate FFS File Utility",
    "  Copyright (C), 2004 - 2009 Intel Corporation",
    
#if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
    "  Built from "UTILITY_BUILD", project of "UTILITY_VENDOR,
#endif
    "",
    "Usage:",
    "  "UTILITY_NAME" [OPTION]...",
    "Options:",
    "  -b BuildDirectory  Specifies the full path to the component build directory",
    "  -p1 P1Path         Specifies fully qualified file name to the primary package",
    "                     file. This file will normally exist in the same directory",
    "                     as the makefile for the component. Required.",
    "  -p2 P2Path         Specifies fully qualified file name to the override",
    "                     package. This file will normally exist in the build tip.",
    "                     Optional.",
    "  -d Name=Value      Add a macro definition for the package file. Optional.",
    "  -o OutputFile      Specifies the file name of output file. Optional.",
    "  -v                 Verbose. Optional.",
    NULL
  };
  for (Index = 0; Str[Index] != NULL; Index++) {
    fprintf (stdout, "%s\n", Str[Index]);
  }
}

static
INT32
TestComment (
  IN CHAR8  *String,
  IN FILE   *In
  )
/*++

Routine Description:

  Tests input string to see if it is a comment, and if so goes to the next line in the file that is not a comment

Arguments:

  String      - String to test

  In          - Open file to move pointer within

Returns:

  -1          - End of file reached
   0          - Not a comment
   1          - Comment bypassed

--*/
{
  CHAR8 CharBuffer;

  CharBuffer = 0;
  if ((String[0] == '/') && (String[1] == '/')) {
    while (CharBuffer != '\n') {
      fscanf (In, "%c", &CharBuffer);
      if (feof (In)) {
        return -1;
      }
    }
  } else {
    return 0;
  }

  return 1;
}

static
void
BreakString (
  IN CONST CHAR8 *Source,
  OUT CHAR8      *Destination,
  IN INTN        Direction
  )
/*++

Routine Description:

  Takes an input string and returns either the part before the =, or the part after the =, depending on direction

Arguments:

  Source      - String to break

  Destination - Buffer to place new string in

  Direction   - 0 to return all of source string before =
                1 to return all of source string after =

Returns:

  None

--*/
{
  UINTN Index;
  UINTN Index2;

  Index   = 0;
  Index2  = 0;

  if (strchr (Source, '=') == NULL) {
    strcpy (Destination, Source);

    return ;
  }

  if (Direction == 0) {
    //
    // return part of string before =
    //
    while (Source[Index] != '=') {
      Destination[Index] = Source[Index++];
    }

    Destination[Index] = 0;
  } else {
    //
    // return part of string after =
    //
    strcpy (Destination, strchr (Source, '=') + 1);
  }
}

static
INT32
GetNextLine (
  OUT CHAR8       *Destination,
  IN FILE         *Package,
  IN OUT UINT32   *LineNumber
  )
/*++

Routine Description:

  Gets the next non-commented line from the file

Arguments:

  Destination - Where to put string

  Package     - Package to get string from
  
  LineNumber  - The actual line number.

Returns:

  -1          - End of file reached
   0          - Success

--*/
{
  CHAR8 String[_MAX_PATH];
  fscanf (Package, "%s", &String);
  if (feof (Package)) {
    return -1;
  }

  while (TestComment (String, Package) == 1) {
    fscanf (Package, "%s", &String);
    if (feof (Package)) {
      return -1;
    }
  }

  strcpy (Destination, String);
  return 0;
}

static
VOID
CheckSlash (
  IN OUT CHAR8  *String,
  IN FILE       *In,
  IN OUT UINT32 *LineNumber
  )
/*++

Routine Description:

  Checks to see if string is line continuation character, if so goes to next valid line

Arguments:

  String      - String to test

  In          - Open file to move pointer within
  
  LineNumber  - The line number.

Returns:

  None

--*/
{
  CHAR8 ByteBuffer;
  ByteBuffer = 0;

  switch (String[0]) {

  case '\\':
    while (String[0] == '\\') {
      while (ByteBuffer != '\n') {
        fscanf (In, "%c", &ByteBuffer);
      }
      (*LineNumber)++;
      if (GetNextLine (String, In, LineNumber) == -1) {
        return ;
      }
    }
    break;

  case '\n':
    (*LineNumber)++;
    while (String[0] == '\n') {
      if (GetNextLine (String, In, LineNumber) == -1) {
        return ;
      }
    }
    break;

  default:
    break;

  }

}

static
INT32
FindSectionInPackage (
  IN CHAR8        *BuildDirectory,
  IN FILE         *OverridePackage,
  IN OUT UINT32   *LineNumber
  )
/*++

Routine Description:

  Finds the matching section within the package

Arguments:

  BuildDirectory  - name of section to find

  OverridePackage - Package file to search within
  
  LineNumber      - The line number.

Returns:

  -1          - End of file reached
   0          - Success

--*/
{
  CHAR8 String[_MAX_PATH];
  CHAR8 NewString[_MAX_PATH];
  String[0] = 0;

  while (strcmp (BuildDirectory, String) != 0) {
    if (GetNextLine (NewString, OverridePackage, LineNumber) != 0) {
      return -1;
    }

    if (NewString[0] == '[') {
      if (NewString[strlen (NewString) - 1] != ']') {
        //
        // have to construct string.
        //
        strcpy (String, NewString + 1);

        while (1) {
          fscanf (OverridePackage, "%s", &NewString);
          if (feof (OverridePackage)) {
            return -1;
          }

          if (NewString[0] != ']') {
            if (strlen (String) != 0) {
              strcat (String, " ");
            }

            strcat (String, NewString);
            if (String[strlen (String) - 1] == ']') {
              String[strlen (String) - 1] = 0;
              break;
            }
          } else {
            break;
          }
        }
      } else {
        NewString[strlen (NewString) - 1] = 0;
        strcpy (String, NewString + 1);
      }
    }
  }

  return 0;
}

static
EFI_STATUS
GenSimpleGuidSection (
  IN OUT UINT8  *FileBuffer,
  IN OUT UINT32 *BufferSize,
  IN UINT32     DataSize,
  IN EFI_GUID   SignGuid,
  IN UINT16     GuidedSectionAttributes
  )
/*++

Routine Description:

  add GUIDed section header for the data buffer.
  data stays in same location (overwrites source data).

Arguments:

  FileBuffer  - Buffer containing data to sign

  BufferSize  - On input, the size of FileBuffer. On output, the size of
                actual section data (including added section header).

  DataSize    - Length of data to Sign

  SignGuid    - Guid to be add.
  
  GuidedSectionAttributes - The section attribute.

Returns:

  EFI_SUCCESS           - Successful
  EFI_OUT_OF_RESOURCES  - Not enough resource.

--*/
{
  UINT32                    TotalSize;

  EFI_GUID_DEFINED_SECTION  GuidSectionHeader;
  UINT8                     *SwapBuffer;

  SwapBuffer = NULL;

  if (DataSize == 0) {
    *BufferSize = 0;

    return EFI_SUCCESS;
  }

  TotalSize = DataSize + sizeof (EFI_GUID_DEFINED_SECTION);
  GuidSectionHeader.CommonHeader.Type     = EFI_SECTION_GUID_DEFINED;
  GuidSectionHeader.CommonHeader.Size[0]  = (UINT8) (TotalSize & 0xff);
  GuidSectionHeader.CommonHeader.Size[1]  = (UINT8) ((TotalSize & 0xff00) >> 8);
  GuidSectionHeader.CommonHeader.Size[2]  = (UINT8) ((TotalSize & 0xff0000) >> 16);
  memcpy (&(GuidSectionHeader.SectionDefinitionGuid), &SignGuid, sizeof (EFI_GUID));
  GuidSectionHeader.Attributes  = GuidedSectionAttributes;
  GuidSectionHeader.DataOffset  = sizeof (EFI_GUID_DEFINED_SECTION);

  SwapBuffer                    = (UINT8 *) malloc (DataSize);
  if (SwapBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  memcpy (SwapBuffer, FileBuffer, DataSize);
  memcpy (FileBuffer, &GuidSectionHeader, sizeof (EFI_GUID_DEFINED_SECTION));
  memcpy (FileBuffer + sizeof (EFI_GUID_DEFINED_SECTION), SwapBuffer, DataSize);

  //
  // Make sure section ends on a DWORD boundary
  //
  while ((TotalSize & 0x03) != 0) {
    FileBuffer[TotalSize] = 0;
    TotalSize++;
  }

  *BufferSize = TotalSize;

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

  return EFI_SUCCESS;
}

static
EFI_STATUS
CompressSection (
  UINT8  *FileBuffer,
  UINT32 *BufferSize,
  UINT32 DataSize,
  CHAR8  *Type
  )
/*++

Routine Description:

  Compress the data and add section header for the compressed data.
  Compressed data (with section header) stays in same location as the source
  (overwrites source data).

Arguments:

  FileBuffer  - Buffer containing data to Compress

  BufferSize  - On input, the size of FileBuffer. On output, the size of
                actual compressed data (including added section header).
                When buffer is too small, this value indicates the size needed.

  DataSize    - The size of data to compress

  Type        - The compression type (not used currently).
                Assume EFI_HEAVY_COMPRESSION.

Returns:

  EFI_BUFFER_TOO_SMALL - Buffer size is too small.
  EFI_UNSUPPORTED      - Compress type can not be supported.
  EFI_SUCCESS          - Successful
  EFI_OUT_OF_RESOURCES - Not enough resource.

--*/
{
  EFI_STATUS              Status;
  UINT8                   *CompData;
  UINT32                  CompSize;
  UINT32                  TotalSize;
  EFI_COMPRESSION_SECTION CompressionSet;
  UINT8                   CompressionType;
  COMPRESS_FUNCTION       CompressFunction;

  Status            = EFI_SUCCESS;
  CompData          = NULL;
  CompSize          = 0;
  TotalSize         = 0;
  CompressFunction  = NULL;

  //
  // Get the compress type
  //
  if (_strcmpi (Type, "Dummy") == 0) {
    //
    // Added "Dummy" to keep backward compatibility.
    //
    CompressionType   = EFI_STANDARD_COMPRESSION;
    CompressFunction  = (COMPRESS_FUNCTION) TianoCompress;

  } else if (_strcmpi (Type, "LZH") == 0) {
    //
    // EFI standard compression (LZH)
    //
    CompressionType   = EFI_STANDARD_COMPRESSION;
    CompressFunction  = (COMPRESS_FUNCTION) TianoCompress;

  } else {
    //
    // Customized compression
    //
    Status = SetCustomizedCompressionType (Type);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    CompressionType   = EFI_CUSTOMIZED_COMPRESSION;
    CompressFunction  = (COMPRESS_FUNCTION) CustomizedCompress;
  }
  //
  // Compress the raw data
  //
  Status = CompressFunction (FileBuffer, DataSize, CompData, &CompSize);
  if (Status == EFI_BUFFER_TOO_SMALL) {
    CompData = malloc (CompSize);
    if (!CompData) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = CompressFunction (FileBuffer, DataSize, CompData, &CompSize);
  }

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

    return Status;
  }

  TotalSize = CompSize + sizeof (EFI_COMPRESSION_SECTION);

  //
  // Buffer too small?
  //
  if (TotalSize > *BufferSize) {
    *BufferSize = TotalSize;
    if (CompData != NULL) {
      free (CompData);
    }

    return EFI_BUFFER_TOO_SMALL;
  }
  //
  // Add the section header for the compressed data
  //
  CompressionSet.CommonHeader.Type    = EFI_SECTION_COMPRESSION;
  CompressionSet.CommonHeader.Size[0] = (UINT8) (TotalSize & 0xff);
  CompressionSet.CommonHeader.Size[1] = (UINT8) ((TotalSize & 0xff00) >> 8);
  CompressionSet.CommonHeader.Size[2] = (UINT8) ((TotalSize & 0xff0000) >> 16);
  CompressionSet.CompressionType      = CompressionType;
  CompressionSet.UncompressedLength   = DataSize;

  //
  // Copy header and data to the buffer
  //
  memcpy (FileBuffer, &CompressionSet, sizeof (EFI_COMPRESSION_SECTION));
  memcpy (FileBuffer + sizeof (CompressionSet), CompData, CompSize);

  //
  // Make sure section ends on a DWORD boundary
  //
  while ((TotalSize & 0x03) != 0) {
    FileBuffer[TotalSize] = 0;
    TotalSize++;
  }

  *BufferSize = TotalSize;

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

  return EFI_SUCCESS;
}

static
void
StripParens (
  IN OUT CHAR8 *String
  )
/*++

Routine Description:

  Removes Parenthesis from around a string

Arguments:

 String    - String to remove parens from

Returns:

  None

--*/
{
  INT32 Index;

  if (String[0] != '(') {
    return ;
  }

  for (Index = 1; String[Index] != ')'; Index++) {
    String[Index - 1] = String[Index];
    if (String[Index] == 0) {
      return ;
    }
  }

  String[Index - 1] = 0;

  return ;
}

static
void
StripEqualMark (
  IN OUT CHAR8 *String
  )
/*++

Routine Description:

  Removes Equal Mark from around a string

Arguments:

 String    - String to remove equal mark from

Returns:

  None

--*/
{
  INT32 Index;

  if (String[0] != '=' && String[strlen (String) - 1] != '=') {
    return ;
  }

  if (String[0] == '=') {

    for (Index = 1; String[Index] != 0; Index++) {
      String[Index - 1] = String[Index];
    }

    String[Index - 1] = 0;
  }

  if (String[strlen (String) - 1] == '=') {
    String[strlen (String) - 1] = 0;
  }

  return ;
}

static
void
SplitAttributesField (
  IN CHAR8       *Buffer,
  IN CHAR8       *AttributesArray[],
  IN OUT UINT32  *NumberOfAttributes
  )
/*
  NumberOfAttributes: on input, it specifies the current number of attributes
                      stored in AttributeArray.
                      on output, it is updated to the latest number of attributes
                      stored in AttributesArray.
*/
{
  UINT32  Index;
  UINT32  Index2;
  UINT32  z;
  CHAR8   *CharBuffer;

  CharBuffer  = NULL;
  CharBuffer  = (CHAR8 *) malloc (_MAX_PATH);
  ZeroMem (CharBuffer, _MAX_PATH);

  for (Index = 0, z = 0, Index2 = 0; Index < strlen (Buffer); Index++) {

    if (Buffer[Index] != '|') {
      CharBuffer[z] = Buffer[Index];
      z++;
    } else {

      CharBuffer[z] = 0;
      AttributesArray[*NumberOfAttributes + Index2] = CharBuffer;
      Index2++;

      //
      // allocate new char buffer for the next attributes string
      //
      CharBuffer = (CHAR8 *) malloc (_MAX_PATH);
      ZeroMem (CharBuffer, _MAX_PATH);
      z = 0;
    }
  }

  CharBuffer[z] = 0;
  //
  // record the last attributes string in the Buffer
  //
  AttributesArray[*NumberOfAttributes + Index2] = CharBuffer;
  Index2++;

  *NumberOfAttributes += Index2;

  return ;
}

static
INT32
GetToolArguments (
  CHAR8       *ToolArgumentsArray[],
  FILE        *Package,
  CHAR8       **PtrInputFileName,
  CHAR8       **PtrOutputFileName,
  EFI_GUID    *Guid,
  UINT16      *GuidedSectionAttributes
  )
{
  CHAR8       Buffer[_MAX_PATH];
  BOOLEAN     ArgumentsFlag;
  BOOLEAN     InputFlag;
  BOOLEAN     OutputFlag;
  BOOLEAN     GuidFlag;
  BOOLEAN     AttributesFlag;
  UINT32      argc;
  UINT32      Index2;
  UINT32      z;
  CHAR8       *CharBuffer;
  INT32       ReturnValue;
  EFI_STATUS  Status;

  CHAR8       *AttributesArray[MAX_ARRAY_SIZE];
  UINT32      NumberOfAttributes;
  CHAR8       *InputFileName;
  CHAR8       *OutputFileName;
  UINT32      LineNumber;
  Buffer[_MAX_PATH];

  ArgumentsFlag   = FALSE;
  InputFlag       = FALSE;
  OutputFlag      = FALSE;
  GuidFlag        = FALSE;
  AttributesFlag  = FALSE;
  //
  // Start at 1, since ToolArgumentsArray[0]
  // is the program name.
  //
  argc            = 1;
  Index2              = 0;

  z                   = 0;
  ReturnValue         = 0;
  NumberOfAttributes  = 0;
  InputFileName       = NULL;
  OutputFileName      = NULL;

  ZeroMem (Buffer, _MAX_PATH);
  ZeroMem (AttributesArray, sizeof (CHAR8 *) * MAX_ARRAY_SIZE);
  LineNumber = 0;
  while (Buffer[0] != ')') {

    if (GetNextLine (Buffer, Package, &LineNumber) != -1) {
      CheckSlash (Buffer, Package, &LineNumber);
      StripEqualMark (Buffer);
    } else {
      Error (NULL, 0, 0, "failed to get next line from package file", NULL);
      return -1;
    }

    if (Buffer[0] == ')') {
      break;
    } else if (_strcmpi (Buffer, "ARGS") == 0) {

      ArgumentsFlag   = TRUE;
      AttributesFlag  = FALSE;
      continue;

    } else if (_strcmpi (Buffer, "INPUT") == 0) {

      InputFlag       = TRUE;
      ArgumentsFlag   = FALSE;
      AttributesFlag  = FALSE;
      continue;

    } else if (_strcmpi (Buffer, "OUTPUT") == 0) {

      OutputFlag      = TRUE;
      ArgumentsFlag   = FALSE;
      AttributesFlag  = FALSE;
      continue;

    } else if (_strcmpi (Buffer, "GUID") == 0) {

      GuidFlag        = TRUE;
      ArgumentsFlag   = FALSE;
      AttributesFlag  = FALSE;
      //
      // fetch the GUID for the section
      //
      continue;

    } else if (_strcmpi (Buffer, "ATTRIBUTES") == 0) {

      AttributesFlag  = TRUE;
      ArgumentsFlag   = FALSE;
      //
      // fetch the GUIDed Section's Attributes
      //
      continue;

    } else if (_strcmpi (Buffer, "") == 0) {
      continue;
    }
    //
    // get all command arguments into ToolArgumentsArray
    //
    if (ArgumentsFlag) {

      StripEqualMark (Buffer);

      CharBuffer = (CHAR8 *) malloc (_MAX_PATH);
      if (CharBuffer == NULL) {
        goto ErrorExit;
      }

      ZeroMem (CharBuffer, sizeof (_MAX_PATH));

      ToolArgumentsArray[argc] = CharBuffer;

      strcpy (ToolArgumentsArray[argc], Buffer);

      argc += 1;
      ToolArgumentsArray[argc] = NULL;
      continue;
    }

    if (InputFlag) {

      StripEqualMark (Buffer);

      InputFileName = (CHAR8 *) malloc (_MAX_PATH);
      if (InputFileName == NULL) {
        goto ErrorExit;
      }

      ZeroMem (InputFileName, sizeof (_MAX_PATH));

      strcpy (InputFileName, Buffer);

      InputFlag = FALSE;
      continue;
    }

    if (OutputFlag) {

      StripEqualMark (Buffer);

      OutputFileName = (CHAR8 *) malloc (_MAX_PATH);
      if (OutputFileName == NULL) {
        goto ErrorExit;
      }

      ZeroMem (OutputFileName, sizeof (_MAX_PATH));

      strcpy (OutputFileName, Buffer);

      OutputFlag = FALSE;
      continue;
    }

    if (GuidFlag) {

      StripEqualMark (Buffer);

      Status = StringToGuid (Buffer, Guid);
      if (EFI_ERROR (Status)) {
        ReturnValue = -1;
        goto ErrorExit;
      }

      GuidFlag = FALSE;
    }

    if (AttributesFlag) {

      StripEqualMark (Buffer);

      //
      // there might be no space between each attribute in the statement,
      // split them aside and return each attribute string
      // in the AttributesArray
      //
      SplitAttributesField (Buffer, AttributesArray, &NumberOfAttributes);
    }
  }
  //
  // ReplaceVariableInBuffer (ToolArgumentsArray,&i,"INPUT",InputVariable,j);
  // ReplaceVariableInBuffer (ToolArgumentsArray,&i,"OUTPUT",&TargetFileName,1);
  //
  for (z = 0; z < NumberOfAttributes; z++) {
    if (_strcmpi (AttributesArray[z], "PROCESSING_REQUIRED") == 0) {
      *GuidedSectionAttributes |= EFI_GUIDED_SECTION_PROCESSING_REQUIRED;
    } else if (_strcmpi (AttributesArray[z], "AUTH_STATUS_VALID") == 0) {
      *GuidedSectionAttributes |= EFI_GUIDED_SECTION_AUTH_STATUS_VALID;
    }
  }

ErrorExit:

  for (Index2 = 0; Index2 < MAX_ARRAY_SIZE; Index2++) {
    if (AttributesArray[Index2] == NULL) {
      break;
    }

    free (AttributesArray[Index2]);
  }

  *PtrInputFileName   = InputFileName;
  *PtrOutputFileName  = OutputFileName;

  return ReturnValue;
}

static
INT32
ProcessScript (
  IN OUT UINT8   *FileBuffer,
  IN FILE        *Package,
  IN CHAR8       *BuildDirectory,
  IN BOOLEAN     ForceUncompress
  )
/*++

Routine Description:

  Signs the section, data stays in same location

Arguments:

  FileBuffer  - Data Buffer

  Package     - Points to curly brace in Image Script

  BuildDirectory     - Name of the source directory parameter
  
  ForceUncompress   - Whether to force uncompress.

Returns:

  Number of bytes added to file buffer
  -1 on error

--*/
{
  EFI_STATUS  Status;
  UINT32      Size;
  UINT32      OldSize;
  UINT32      Adjust;
  UINT16      TeStrippedSize;
  CHAR8       Buffer[_MAX_PATH];
  CHAR8       Type[_MAX_PATH];
  CHAR8       FileName[_MAX_PATH];
  INT32       Index3;
  INT32       Index2;
  UINT32      ReturnValue;
  UINT8       ByteBuffer;
  FILE        *InFile;
  UINT32      SourceDataSize;
  CHAR8       *ToolArgumentsArray[MAX_ARRAY_SIZE];
  CHAR8       *OutputFileName;
  CHAR8       *InputFileName;
  CHAR8       ToolName[_MAX_PATH];
  FILE        *OutputFile;
  FILE        *InputFile;
  UINT8       Temp;
  int         returnint;
  UINT32      LineNumber;
  BOOLEAN     IsError;
  EFI_GUID    SignGuid;
  UINT16      GuidedSectionAttributes;
  UINT8       *TargetFileBuffer;

  OutputFileName          = NULL;
  InputFileName           = NULL;
  OutputFile              = NULL;
  InputFile               = NULL;
  IsError                 = FALSE;
  GuidedSectionAttributes = 0;
  TargetFileBuffer        = NULL;

  Size                    = 0;
  LineNumber              = 0;
  Buffer[0]               = 0;
  for (Index3 = 0; Index3 < MAX_ARRAY_SIZE; ++Index3) {
    ToolArgumentsArray[Index3] = NULL;
  }

  while (Buffer[0] != '}') {
    if (GetNextLine (Buffer, Package, &LineNumber) != -1) {
      CheckSlash (Buffer, Package, &LineNumber);
    } else {
      printf ("ERROR in IMAGE SCRIPT!\n");
      IsError = TRUE;
      goto Done;
    }

    if (_strcmpi (Buffer, "Compress") == 0) {
      //
      // Handle compress
      //
      //
      // read compression type
      //
      if (GetNextLine (Buffer, Package, &LineNumber) != -1) {
        CheckSlash (Buffer, Package, &LineNumber);
      }

      StripParens (Buffer);
      strcpy (Type, Buffer);
      //
      // build buffer
      //
      while (Buffer[0] != '{') {
        if (GetNextLine (Buffer, Package, &LineNumber) != -1) {
          CheckSlash (Buffer, Package, &LineNumber);
        }
      }

      ReturnValue = ProcessScript (&FileBuffer[Size], Package, BuildDirectory, ForceUncompress);
      if (ReturnValue == -1) {
        IsError = TRUE;
        goto Done;
      }
      //
      // Call compress routine on buffer.
      // Occasionally, compressed data + section header would
      // be largere than the source and EFI_BUFFER_TOO_SMALL is
      // returned from CompressSection()
      //
      SourceDataSize = ReturnValue;

      if (!ForceUncompress) {

        Status = CompressSection (
                  &FileBuffer[Size],
                  &ReturnValue,
                  SourceDataSize,
                  Type
                  );

        if (Status == EFI_BUFFER_TOO_SMALL) {
          Status = CompressSection (
                    &FileBuffer[Size],
                    &ReturnValue,
                    SourceDataSize,
                    Type
                    );
        }

        if (EFI_ERROR (Status)) {
          IsError = TRUE;
          goto Done;
        }
      }

      Size += ReturnValue;

    } else if (_strcmpi (Buffer, "Tool") == 0) {

      ZeroMem (ToolName, _MAX_PATH);
      ZeroMem (ToolArgumentsArray, sizeof (CHAR8 *) * MAX_ARRAY_SIZE);
      ZeroMem (&SignGuid, sizeof (EFI_GUID));

      //
      // handle signing Tool
      //
      while (Buffer[0] != '(') {
        if (GetNextLine (Buffer, Package, &LineNumber) != -1) {
          CheckSlash (Buffer, Package, &LineNumber);
        }
      }

      if (_strcmpi (Buffer, "(") == 0) {
        if (GetNextLine (Buffer, Package, &LineNumber) != -1) {
          CheckSlash (Buffer, Package, &LineNumber);
        }
      }

      StripParens (Buffer);
      strcpy (ToolName, Buffer);
      ToolArgumentsArray[0] = ToolName;

      //
      // read ARGS
      //
      if (GetToolArguments (
            ToolArgumentsArray,
            Package,
            &InputFileName,
            &OutputFileName,
            &SignGuid,
            &GuidedSectionAttributes
            ) == -1) {
        IsError = TRUE;
        goto Done;
      }
      //
      // if the tool need input file,
      // dump the file buffer to the specified input file.
      //
      if (InputFileName != NULL) {
        InputFile = fopen (InputFileName, "wb");
        if (InputFile == NULL) {
          Error (NULL, 0, 0, InputFileName, "failed to open output file for writing");
          IsError = TRUE;
          goto Done;
        }

        fwrite (FileBuffer, sizeof (UINT8), Size, InputFile);
        fclose (InputFile);
        InputFile = NULL;
        free (InputFileName);
        InputFileName = NULL;
      }
      //
      // dispatch signing tool
      //
      returnint = _spawnv (_P_WAIT, ToolName, ToolArgumentsArray);
      if (returnint != 0) {
        Error (NULL, 0, 0, ToolName, "external tool failed");
        IsError = TRUE;
        goto Done;
      }
      //
      // if the tool has output file,
      // dump the output file to the file buffer
      //
      if (OutputFileName != NULL) {

        OutputFile = fopen (OutputFileName, "rb");
        if (OutputFile == NULL) {
          Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
          IsError = TRUE;
          goto Done;
        }

        TargetFileBuffer  = &FileBuffer[Size];
        SourceDataSize    = Size;

        fread (&Temp, sizeof (UINT8), 1, OutputFile);
        while (!feof (OutputFile)) {
          FileBuffer[Size++] = Temp;
          fread (&Temp, sizeof (UINT8), 1, OutputFile);
        }

        while ((Size & 0x03) != 0) {
          FileBuffer[Size] = 0;
          Size++;
        }

        SourceDataSize = Size - SourceDataSize;

        fclose (OutputFile);
        OutputFile = NULL;
        free (OutputFileName);
        OutputFileName = NULL;

        if (CompareGuid (&SignGuid, &mZeroGuid) != 0) {
          ReturnValue = SourceDataSize;
          Status = GenSimpleGuidSection (
                    TargetFileBuffer,
                    &ReturnValue,
                    SourceDataSize,
                    SignGuid,
                    GuidedSectionAttributes
                    );
          if (EFI_ERROR (Status)) {
            IsError = TRUE;
            goto Done;
          }

          Size = ReturnValue;
        }
      }

    } else if (Buffer[0] != '}') {
      //
      // if we are here, we should see either a file name,
      // or a }.
      //
      Index3      = 0;
      FileName[0] = 0;
      //
      // Prepend the build directory to the file name if the
      // file name does not already contain a full path.
      //
      if (!isalpha (Buffer[0]) || (Buffer[1] != ':')) {
        sprintf (FileName, "%s\\", BuildDirectory);
      }
      
      while (Buffer[Index3] != '\n') {
        if (Buffer[Index3] == 0) {
          break;
        } else {
          Index2              = strlen (FileName);
          FileName[Index2++]  = Buffer[Index3++];
          FileName[Index2]    = 0;
        }
      }

      InFile = fopen (FileName, "rb");
      if (InFile == NULL) {
        Error (NULL, 0, 0, FileName, "failed to open file for reading");
        IsError = TRUE;
        goto Done;
      }

      OldSize = Size;
      fread (&ByteBuffer, sizeof (UINT8), 1, InFile);
      while (!feof (InFile)) {
        FileBuffer[Size++] = ByteBuffer;
        fread (&ByteBuffer, sizeof (UINT8), 1, InFile);
      }

      fclose (InFile);
      InFile = NULL;

      //
      // Adjust the TE Section for IPF so that the function entries are 16-byte aligned.
      //
      if (Size - OldSize >= sizeof (EFI_COMMON_SECTION_HEADER) + sizeof (EFI_TE_IMAGE_HEADER) &&
          ((EFI_COMMON_SECTION_HEADER *) &FileBuffer[OldSize])->Type == EFI_SECTION_TE        &&
          ((EFI_TE_IMAGE_HEADER *) &FileBuffer[OldSize + 4])->Machine == EFI_IMAGE_MACHINE_IA64) {
        TeStrippedSize = ((EFI_TE_IMAGE_HEADER *) &FileBuffer[OldSize + 4])->StrippedSize;
        Adjust = TeStrippedSize - (OldSize + sizeof (EFI_COMMON_SECTION_HEADER) + sizeof (EFI_TE_IMAGE_HEADER));
        Adjust &= 15;
        if (Adjust > 0) {
          memmove (&FileBuffer[OldSize + Adjust], &FileBuffer[OldSize], Size - OldSize);
          //
          // Pad with RAW Section type
          //
          *(UINT32 *)&FileBuffer[OldSize] = 0x19000000 | Adjust;
          Size += Adjust;
          //
          // Make sure the Data alignment in FFS header is no less than 1 (16-byte aligned)
          //
          MinFfsDataAlignOverride = 1;
        }
      }

      //
      // Make sure section ends on a DWORD boundary
      //
      while ((Size & 0x03) != 0) {
        FileBuffer[Size] = 0;
        Size++;
      }

    }
  }

Done:
  for (Index3 = 1; Index3 < MAX_ARRAY_SIZE; Index3++) {
    if (ToolArgumentsArray[Index3] == NULL) {
      break;
    }

    free (ToolArgumentsArray[Index3]);
  }

  if (IsError) {
    return -1;
  }

  return Size;

}

static
UINT8
StringToType (
  IN CHAR8 *String
  )
/*++

Routine Description:

  Converts File Type String to value.  EFI_FV_FILETYPE_ALL indicates that an
  unrecognized file type was specified.

Arguments:

  String    - File type string

Returns:

  File Type Value

--*/
{
  if (_strcmpi (String, "EFI_FV_FILETYPE_RAW") == 0) {
    return EFI_FV_FILETYPE_RAW;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_FREEFORM") == 0) {
    return EFI_FV_FILETYPE_FREEFORM;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_SECURITY_CORE") == 0) {
    return EFI_FV_FILETYPE_SECURITY_CORE;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_PEI_CORE") == 0) {
    return EFI_FV_FILETYPE_PEI_CORE;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_DXE_CORE") == 0) {
    return EFI_FV_FILETYPE_DXE_CORE;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_PEIM") == 0) {
    return EFI_FV_FILETYPE_PEIM;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_DRIVER") == 0) {
    return EFI_FV_FILETYPE_DRIVER;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER") == 0) {
    return EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_APPLICATION") == 0) {
    return EFI_FV_FILETYPE_APPLICATION;
  }

  if (_strcmpi (String, "EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE") == 0) {
    return EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE;
  }

  return EFI_FV_FILETYPE_ALL;
}

static
UINT32
AdjustFileSize (
  IN UINT8  *FileBuffer,
  IN UINT32 FileSize
  )
/*++

Routine Description:
  Adjusts file size to insure sectioned file is exactly the right length such
  that it ends on exactly the last byte of the last section.  ProcessScript()
  may have padded beyond the end of the last section out to a 4 byte boundary.
  This padding is stripped.

Arguments:
  FileBuffer  - Data Buffer - contains a section stream
  FileSize    - Size of FileBuffer as returned from ProcessScript()

Returns:
  Corrected size of file.

--*/
{
  UINT32                    TotalLength;
  UINT32                    CurrentLength;
  UINT32                    SectionLength;
  UINT32                    SectionStreamLength;
  EFI_COMMON_SECTION_HEADER *SectionHeader;
  EFI_COMMON_SECTION_HEADER *NextSectionHeader;

  TotalLength         = 0;
  CurrentLength       = 0;
  SectionStreamLength = FileSize;

  SectionHeader       = (EFI_COMMON_SECTION_HEADER *) FileBuffer;

  while (TotalLength < SectionStreamLength) {
    SectionLength = *((UINT32 *) SectionHeader->Size) & 0x00ffffff;
    TotalLength += SectionLength;

    if (TotalLength == SectionStreamLength) {
      return TotalLength;
    }
    //
    // Move to the next byte following the section...
    //
    SectionHeader = (EFI_COMMON_SECTION_HEADER *) ((UINT8 *) SectionHeader + SectionLength);
    CurrentLength = (UINTN) SectionHeader - (UINTN) FileBuffer;

    //
    // Figure out where the next section begins
    //
    NextSectionHeader = (EFI_COMMON_SECTION_HEADER *) ((UINT8 *) SectionHeader + 3);
    NextSectionHeader = (EFI_COMMON_SECTION_HEADER *) ((UINTN) NextSectionHeader &~ (UINTN) 3);
    TotalLength += (UINTN) NextSectionHeader - (UINTN) SectionHeader;
    SectionHeader = NextSectionHeader;
  }

  return CurrentLength;
}

static
INT32
MainEntry (
  INT32     argc,
  CHAR8     *argv[],
  BOOLEAN   ForceUncompress
  )
/*++

Routine Description:

  MainEntry function.

Arguments:

  argc            - Number of command line parameters.
  argv            - Array of pointers to command line parameter strings.
  ForceUncompress - If TRUE, force to do not compress the sections even if compression
                    is specified in the script. Otherwise, FALSE.

Returns:
  STATUS_SUCCESS  - Function exits successfully.
  STATUS_ERROR    - Some error occurred during execution.

--*/
{
  FILE                    *PrimaryPackage;
  FILE                    *OverridePackage;
  FILE                    *Out;
  CHAR8                   BaseName[_MAX_PATH];
  EFI_GUID                FfsGuid;
  CHAR8                   GuidString[_MAX_PATH];
  EFI_FFS_FILE_HEADER     FileHeader;
  CHAR8                   FileType[_MAX_PATH];
  EFI_FFS_FILE_ATTRIBUTES FfsAttrib;
  EFI_FFS_FILE_ATTRIBUTES FfsAttribDefined;
  UINT64                  FfsAlignment;
  UINT32                  FfsAlignment32;
  CHAR8                   InputString[_MAX_PATH];
  BOOLEAN                 ImageScriptInOveride;
  UINT32                  FileSize;
  UINT8                   *FileBuffer;
  EFI_STATUS              Status;
  UINT32                  LineNumber;
#if (PI_SPECIFICATION_VERSION < 0x00010000)  
  EFI_FFS_FILE_TAIL       TailValue;
#endif
  BaseName[0]       = 0;
  FileType[0]       = 0;
  FfsAttrib         = 0;
  FfsAttribDefined  = 0;
  FfsAlignment      = 0;
  FfsAlignment32    = 0;
  PrimaryPackage    = NULL;
  Out               = NULL;
  OverridePackage   = NULL;
  FileBuffer        = NULL;

  strcpy (GuidString, "00000000-0000-0000-0000-000000000000");
  Status = StringToGuid (GuidString, &FfsGuid);
  if (Status != 0) {
    Error (NULL, 0, 0, GuidString, "error parsing GUID string");
    return STATUS_ERROR;
  }

  GuidString[0]         = 0;
  ImageScriptInOveride  = FALSE;
  //
  // Initialize the simple file parsing routines. Then open
  // the primary package file for parsing.
  //
  SFPInit ();
  if (SFPOpenFile (mGlobals.PrimaryPackagePath) != STATUS_SUCCESS) {
    Error (NULL, 0, 0, mGlobals.PrimaryPackagePath, "unable to open primary package file");
    goto Done;
  }
  //
  // First token in the file must be "PACKAGE.INF"
  //
  if (!SFPIsToken ("PACKAGE.INF")) {
    Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 'PACKAGE.INF'", NULL);
    goto Done;
  }
  //
  // Find the [.] section
  //
  if (!SFPSkipToToken ("[.]")) {
    Error (mGlobals.PrimaryPackagePath, 1, 0, "could not locate [.] section in package file", NULL);
    goto Done;
  }
  //
  // Start parsing the data. The algorithm is essentially the same for each keyword:
  //   1. Identify the keyword
  //   2. Verify that the keyword/value pair has not already been defined
  //   3. Set some flag indicating that the keyword/value pair has been defined
  //   4. Skip over the "="
  //   5. Get the value, which may be a number, TRUE, FALSE, or a string.
  //
  while (1) {
    if (SFPIsToken ("BASE_NAME")) {
      //
      // Found BASE_NAME, format:
      //   BASE_NAME = MyBaseName
      //
      if (BaseName[0] != 0) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "BASE_NAME already defined", NULL);
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (!SFPGetNextToken (BaseName, sizeof (BaseName))) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected valid base name", NULL);
        goto Done;
      }
    } else if (SFPIsToken ("IMAGE_SCRIPT")) {
      //
      // Found IMAGE_SCRIPT. Break out and process below.
      //
      break;
    } else if (SFPIsToken ("FFS_FILEGUID")) {
      //
      // found FILEGUID, format:
      //   FFS_FILEGUID = F7845C4F-EDF5-42C5-BD8F-A02AF63DD93A
      //
      if (GuidString[0] != 0) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "FFS_FILEGUID already defined", NULL);
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (SFPGetGuidToken (GuidString, sizeof (GuidString)) != TRUE) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected file GUID", NULL);
        goto Done;
      }

      Status = StringToGuid (GuidString, &FfsGuid);
      if (Status != 0) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected valid file GUID", NULL);
        goto Done;
      }
    } else if (SFPIsToken ("FFS_FILETYPE")) {
      //
      // ***********************************************************************
      //
      // Found FFS_FILETYPE, format:
      //  FFS_FILETYPE = EFI_FV_FILETYPE_APPLICATION
      //
      if (FileType[0] != 0) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "FFS_FILETYPE previously defined", NULL);
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (!SFPGetNextToken (FileType, sizeof (FileType))) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected valid FFS_FILETYPE", NULL);
        goto Done;
      }
    }
#if (PI_SPECIFICATION_VERSION < 0x00010000)    
    else if (SFPIsToken ("FFS_ATTRIB_HEADER_EXTENSION")) {
      //
      // ***********************************************************************
      //
      // Found: FFS_ATTRIB_HEADER_EXTENSION = FALSE
      // Spec says the bit is for future expansion, and must be false.
      //
      if (FfsAttribDefined & FFS_ATTRIB_HEADER_EXTENSION) {
        Error (
          mGlobals.PrimaryPackagePath,
          SFPGetLineNumber (),
          0,
          "FFS_ATTRIB_HEADER_EXTENSION previously defined",
          NULL
          );
        goto Done;
      }

      FfsAttribDefined |= FFS_ATTRIB_HEADER_EXTENSION;
      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (SFPIsToken ("TRUE")) {
        Error (
          mGlobals.PrimaryPackagePath,
          SFPGetLineNumber (),
          0,
          "only FFS_ATTRIB_HEADER_EXTENSION = FALSE is supported",
          NULL
          );
        goto Done;
      } else if (SFPIsToken ("FALSE")) {
        //
        // Default is FALSE
        //
      } else {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 'FALSE'", NULL);
        goto Done;
      }
    }
#else
    else if (SFPIsToken ("FFS_ATTRIB_FIXED")) {
      //
      // ***********************************************************************
      //
      // Found: FFS_ATTRIB_FIXED = TRUE | FALSE
      //
      if (FfsAttribDefined & FFS_ATTRIB_FIXED) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "FFS_ATTRIB_FIXED previously defined", NULL);
        goto Done;
      }

      FfsAttribDefined |= FFS_ATTRIB_FIXED;
      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (SFPIsToken ("TRUE")) {
        FfsAttrib |= FFS_ATTRIB_FIXED;
      } else if (SFPIsToken ("FALSE")) {
        //
        // Default is FALSE
        //
      } else {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 'TRUE' or 'FALSE'", NULL);
        goto Done;
      }
    } 
#endif
    else if (SFPIsToken ("FFS_ATTRIB_TAIL_PRESENT")) {
      //
      // ***********************************************************************
      //
      // Found: FFS_ATTRIB_TAIL_PRESENT = TRUE | FALSE
      //
      if (FfsAttribDefined & FFS_ATTRIB_TAIL_PRESENT) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "FFS_ATTRIB_TAIL_PRESENT previously defined", NULL);
        goto Done;
      }

      FfsAttribDefined |= FFS_ATTRIB_TAIL_PRESENT;
      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (SFPIsToken ("TRUE")) {
        FfsAttrib |= FFS_ATTRIB_TAIL_PRESENT;
      } else if (SFPIsToken ("FALSE")) {
        //
        // Default is FALSE
        //
      } else {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 'TRUE' or 'FALSE'", NULL);
        goto Done;
      }
    } else if (SFPIsToken ("FFS_ATTRIB_RECOVERY")) {
      //
      // ***********************************************************************
      //
      // Found: FFS_ATTRIB_RECOVERY = TRUE | FALSE
      //
      if (FfsAttribDefined & FFS_ATTRIB_RECOVERY) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "FFS_ATTRIB_RECOVERY previously defined", NULL);
        goto Done;
      }

      FfsAttribDefined |= FFS_ATTRIB_RECOVERY;
      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (SFPIsToken ("TRUE")) {
        FfsAttrib |= FFS_ATTRIB_RECOVERY;
      } else if (SFPIsToken ("FALSE")) {
        //
        // Default is FALSE
        //
      } else {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 'TRUE' or 'FALSE'", NULL);
        goto Done;
      }
    } else if (SFPIsToken ("FFS_ATTRIB_CHECKSUM")) {
      //
      // ***********************************************************************
      //
      // Found: FFS_ATTRIB_CHECKSUM = TRUE | FALSE
      //
      if (FfsAttribDefined & FFS_ATTRIB_CHECKSUM) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "FFS_ATTRIB_CHECKSUM previously defined", NULL);
        goto Done;
      }

      FfsAttribDefined |= FFS_ATTRIB_CHECKSUM;
      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (SFPIsToken ("TRUE")) {
        FfsAttrib |= FFS_ATTRIB_CHECKSUM;
      } else if (SFPIsToken ("FALSE")) {
        //
        // Default is FALSE
        //
      } else {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 'TRUE' or 'FALSE'", NULL);
        goto Done;
      }
    } else if (SFPIsToken ("FFS_ALIGNMENT") || SFPIsToken ("FFS_ATTRIB_DATA_ALIGNMENT")) {
      //
      // ***********************************************************************
      //
      // Found FFS_ALIGNMENT, formats:
      //   FFS_ALIGNMENT = 0-7
      //   FFS_ATTRIB_DATA_ALIGNMENT = 0-7
      //
      if (FfsAttribDefined & FFS_ATTRIB_DATA_ALIGNMENT) {
        Error (
          mGlobals.PrimaryPackagePath,
          SFPGetLineNumber (),
          0,
          "FFS_ALIGNMENT/FFS_ATTRIB_DATA_ALIGNMENT previously defined",
          NULL
          );
        goto Done;
      }

      FfsAttribDefined |= FFS_ATTRIB_DATA_ALIGNMENT;
      if (!SFPIsToken ("=")) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected '='", NULL);
        goto Done;
      }

      if (!SFPGetNumber (&FfsAlignment32)) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected numeric value for alignment", NULL);
        goto Done;
      }

      if (FfsAlignment32 > 7) {
        Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, "expected 0 <= alignment <= 7", NULL);
        goto Done;
      }

      FfsAttrib |= (((EFI_FFS_FILE_ATTRIBUTES) FfsAlignment32) << 3);
    } else {
      SFPGetNextToken (InputString, sizeof (InputString));
      Error (mGlobals.PrimaryPackagePath, SFPGetLineNumber (), 0, InputString, "unrecognized/unexpected token");
      goto Done;
    }
  }
  //
  // Close the primary package file
  //
  SFPCloseFile ();
  //
  // TODO: replace code below with basically a copy of the code above. Don't
  // forget to reset the FfsAttribDefined variable first. Also, you'll need
  // to somehow keep track of whether or not the basename is defined multiple
  // times in the override package. Ditto on the file GUID.
  //
  if (mGlobals.OverridePackagePath[0] != 0) {
    OverridePackage = fopen (mGlobals.OverridePackagePath, "r");
    //
    // NOTE: For package override to work correctly, the code below must be modified to
    //       SET or CLEAR bits properly. For example, if the primary package set
    //       FFS_ATTRIB_CHECKSUM = TRUE, and the override set FFS_ATTRIB_CHECKSUM = FALSE, then
    //       we'd need to clear the bit below. Since this is not happening, I'm guessing that
    //       the override functionality is not being used, so should be made obsolete. If I'm
    //       wrong, and it is being used, then it needs to be fixed. Thus emit an error if it is
    //       used, and we'll address it then.  4/10/2003
    //
    Error (__FILE__, __LINE__, 0, "package override functionality is not implemented correctly", NULL);
    goto Done;
  } else {
    OverridePackage = NULL;
  }

#ifdef OVERRIDE_SUPPORTED
  if (OverridePackage != NULL) {
    //
    // Parse override package file
    //
    fscanf (OverridePackage, "%s", &InputString);
    if (_strcmpi (InputString, "PACKAGE.INF") != 0) {
      Error (mGlobals.OverridePackagePath, 1, 0, "invalid package file", "expected 'PACKAGE.INF'");
      goto Done;
    }
    //
    // Match [dir] to Build Directory
    //
    if (FindSectionInPackage (mGlobals.BuildDirectory, OverridePackage, &LineNumber) != 0) {
      Error (mGlobals.OverridePackagePath, 1, 0, mGlobals.BuildDirectory, "section not found in package file");
      goto Done;
    }

    InputString[0] = 0;
    while ((InputString[0] != '[') && (!feof (OverridePackage))) {
      if (GetNextLine (InputString, OverridePackage, &LineNumber) != -1) {
        if (InputString[0] != '[') {
here:
          if (_strcmpi (InputString, "BASE_NAME") == 0) {
            //
            // found BASE_NAME, next is = and string.
            //
            fscanf (OverridePackage, "%s", &InputString);
            CheckSlash (InputString, OverridePackage, &LineNumber);
            if (strlen (InputString) == 1) {
              //
              // string is just =
              //
              fscanf (OverridePackage, "%s", &InputString);
              CheckSlash (InputString, OverridePackage, &LineNumber);
              strcpy (BaseName, InputString);
            } else {
              BreakString (InputString, InputString, 1);
              strcpy (BaseName, InputString);
            }
          } else if (_strcmpi (InputString, "IMAGE_SCRIPT") == 0) {
            //
            // found IMAGE_SCRIPT, come back later to process it
            //
            ImageScriptInOveride = TRUE;
            fscanf (OverridePackage, "%s", &InputString);
          } else if (_strcmpi (InputString, "FFS_FILEGUID") == 0) {
            //
            // found FILEGUID, next is = and string.
            //
            fscanf (OverridePackage, "%s", &InputString);
            CheckSlash (InputString, OverridePackage, &LineNumber);
            if (strlen (InputString) == 1) {
              //
              // string is just =
              //
              fscanf (OverridePackage, "%s", &InputString);
              CheckSlash (InputString, OverridePackage, &LineNumber);
              Status = StringToGuid (InputString, &FfsGuid);
              if (Status != 0) {
                Error (mGlobals.OverridePackagePath, 1, 0, InputString, "bad FFS_FILEGUID format");
                goto Done;
              }
            } else {
              BreakString (InputString, InputString, 1);
              Status = StringToGuid (InputString, &FfsGuid);
              if (Status != 0) {
                Error (mGlobals.OverridePackagePath, 1, 0, InputString, "bad FFS_FILEGUID format");
                goto Done;
              }
            }
          } else if (_strcmpi (InputString, "FFS_FILETYPE") == 0) {
            //
            // found FILETYPE, next is = and string.
            //
            fscanf (OverridePackage, "%s", &InputString);
            CheckSlash (InputString, OverridePackage, &LineNumber);
            if (strlen (InputString) == 1) {
              //
              // string is just =
              //
              fscanf (OverridePackage, "%s", &InputString);
              CheckSlash (InputString, OverridePackage, &LineNumber);
              strcpy (FileType, InputString);
            } else {
              BreakString (InputString, InputString, 1);
              strcpy (FileType, InputString);
            }

          } else if (_strcmpi (InputString, "FFS_ATTRIB_RECOVERY") == 0) {
            //
            // found FFS_ATTRIB_RECOVERY, next is = and string.
            //
            fscanf (OverridePackage, "%s", &InputString);
            CheckSlash (InputString, OverridePackage, &LineNumber);
            if (strlen (InputString) == 1) {
              //
              // string is just =
              //
              fscanf (OverridePackage, "%s", &InputString);
              CheckSlash (InputString, OverridePackage, &LineNumber);
              if (_strcmpi (InputString, "TRUE") == 0) {
                FfsAttrib |= FFS_ATTRIB_RECOVERY;
              }
            } else {
              BreakString (InputString, InputString, 1);
              if (_strcmpi (InputString, "TRUE") == 0) {
                FfsAttrib |= FFS_ATTRIB_RECOVERY;
              }
            }
          } else if (_strcmpi (InputString, "FFS_ATTRIB_CHECKSUM") == 0) {
            //
            // found FFS_ATTRIB_CHECKSUM, next is = and string.
            //
            fscanf (OverridePackage, "%s", &InputString);
            CheckSlash (InputString, OverridePackage, &LineNumber);
            if (strlen (InputString) == 1) {
              //
              // string is just =
              //
              fscanf (OverridePackage, "%s", &InputString);
              CheckSlash (InputString, OverridePackage, &LineNumber);
              if (_strcmpi (InputString, "TRUE") == 0) {
                FfsAttrib |= FFS_ATTRIB_CHECKSUM;
              }
            } else {
              BreakString (InputString, InputString, 1);
              if (_strcmpi (InputString, "TRUE") == 0) {
                FfsAttrib |= FFS_ATTRIB_CHECKSUM;
              }
            }
          } else if (_strcmpi (InputString, "FFS_ALIGNMENT") == 0) {
            //
            // found FFS_ALIGNMENT, next is = and string.
            //
            fscanf (OverridePackage, "%s", &InputString);
            CheckSlash (InputString, OverridePackage, &LineNumber);
            if (strlen (InputString) == 1) {
              //
              // string is just =
              //
              fscanf (OverridePackage, "%s", &InputString);
              CheckSlash (InputString, OverridePackage, &LineNumber);
            } else {
              BreakString (InputString, InputString, 1);
            }

            AsciiStringToUint64 (InputString, FALSE, &FfsAlignment);
            if (FfsAlignment > 7) {
              Error (mGlobals.OverridePackagePath, 1, 0, InputString, "invalid FFS_ALIGNMENT value");
              goto Done;
            }

            FfsAttrib |= (((EFI_FFS_FILE_ATTRIBUTES) FfsAlignment) << 3);
          } else if (strchr (InputString, '=') != NULL) {
            BreakString (InputString, String, 1);
            fseek (OverridePackage, (-1 * (strlen (String) + 1)), SEEK_CUR);
            BreakString (InputString, InputString, 0);
            goto here;
          }
        }
      }
    }
  }
#endif // #ifdef OVERRIDE_SUPPORTED
  //
  // Require that they specified a file GUID at least, since that's how we're
  // naming the file.
  //
  if (GuidString[0] == 0) {
    Error (mGlobals.PrimaryPackagePath, 1, 0, "FFS_FILEGUID must be specified", NULL);
    return STATUS_ERROR;
  }
  //
  // Build Header and process image script
  //
  FileBuffer = (UINT8 *) malloc ((1024 * 1024 * 16) * sizeof (UINT8));
  if (FileBuffer == NULL) {
    Error (__FILE__, __LINE__, 0, "memory allocation failed", NULL);
    goto Done;
  }

  FileSize = 0;
  if (ImageScriptInOveride) {
#ifdef OVERRIDE_SUPPORTED
    rewind (OverridePackage);
    LineNumber = 0;
    FindSectionInPackage (mGlobals.BuildDirectory, OverridePackage, &LineNumber);
    while (_strcmpi (InputString, "IMAGE_SCRIPT") != 0) {
      GetNextLine (InputString, OverridePackage, &LineNumber);
      CheckSlash (InputString, OverridePackage, &LineNumber);
      if (strchr (InputString, '=') != NULL) {
        BreakString (InputString, InputString, 0);
      }
    }

    while (InputString[0] != '{') {
      GetNextLine (InputString, OverridePackage, &LineNumber);
      CheckSlash (InputString, OverridePackage, &LineNumber);
    }
    //
    // Found start of image script, process it
    //
    FileSize += ProcessScript (FileBuffer, OverridePackage, mGlobals.BuildDirectory, ForceUncompress);
    if (FileSize == -1) {
      Error (NULL, 0, 0, "failed to process script", NULL);
      goto Done;
    }

    if (StringToType (FileType) != EFI_FV_FILETYPE_RAW) {
      FileSize = AdjustFileSize (FileBuffer, FileSize);
    }

    if (BaseName[0] == '\"') {
      StripQuotes (BaseName);
    }

    if (mGlobals.OutputFilePath[0]) {
      //
      // Use user specified output file name
      //
      strcpy (InputString, mGlobals.OutputFilePath);
    } else {
      //
      // Construct the output file name according to FileType
      //
      if (BaseName[0] != 0) {
        sprintf (InputString, "%s-%s", GuidString, BaseName);
      } else {
        strcpy (InputString, GuidString);
      }

      switch (StringToType (FileType)) {

      case EFI_FV_FILETYPE_SECURITY_CORE:
        strcat (InputString, ".SEC");
        break;

      case EFI_FV_FILETYPE_PEIM:
      case EFI_FV_FILETYPE_PEI_CORE:
      case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
        strcat (InputString, ".PEI");
        break;

      case EFI_FV_FILETYPE_DRIVER:
      case EFI_FV_FILETYPE_DXE_CORE:
        strcat (InputString, ".DXE");
        break;

      case EFI_FV_FILETYPE_APPLICATION:
        strcat (InputString, ".APP");
        break;

      case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
        strcat (InputString, ".FVI");
        break;

      case EFI_FV_FILETYPE_RAW:
        strcat (InputString, ".RAW");
        break;

      case EFI_FV_FILETYPE_ALL:
        Error (mGlobals.OverridePackagePath, 1, 0, "invalid FFS file type for this utility", NULL);
        goto Done;

      default:
        strcat (InputString, ".FFS");
        break;
      }
    }

    if (ForceUncompress) {
      strcat (InputString, ".ORG");
    }
	
    Out = fopen (InputString, "wb");
    if (Out == NULL) {
      Error (NULL, 0, 0, InputString, "could not open output file for writing");
      goto Done;
    }
    //
    // create ffs header
    //
    memset (&FileHeader, 0, sizeof (EFI_FFS_FILE_HEADER));
    memcpy (&FileHeader.Name, &FfsGuid, sizeof (EFI_GUID));
    FileHeader.Type       = StringToType (FileType);
    if (((FfsAttrib & FFS_ATTRIB_DATA_ALIGNMENT) >> 3) < MinFfsDataAlignOverride) {
      FfsAttrib = (FfsAttrib & ~FFS_ATTRIB_DATA_ALIGNMENT) | (MinFfsDataAlignOverride << 3);
    }
    FileHeader.Attributes = FfsAttrib;
    //
    // Now FileSize includes the EFI_FFS_FILE_HEADER
    //
    FileSize += sizeof (EFI_FFS_FILE_HEADER);
    FileHeader.Size[0]  = (UINT8) (FileSize & 0xFF);
    FileHeader.Size[1]  = (UINT8) ((FileSize & 0xFF00) >> 8);
    FileHeader.Size[2]  = (UINT8) ((FileSize & 0xFF0000) >> 16);
    //
    // Fill in checksums and state, these must be zero for checksumming
    //
    // FileHeader.IntegrityCheck.Checksum.Header = 0;
    // FileHeader.IntegrityCheck.Checksum.File = 0;
    // FileHeader.State = 0;
    //
    FileHeader.IntegrityCheck.Checksum.Header = CalculateChecksum8 (
                                                  (UINT8 *) &FileHeader,
                                                  sizeof (EFI_FFS_FILE_HEADER)
                                                  );
    if (FileHeader.Attributes & FFS_ATTRIB_CHECKSUM) {
#if (PI_SPECIFICATION_VERSION < 0x00010000)  
      FileHeader.IntegrityCheck.Checksum.File = CalculateChecksum8 ((UINT8 *) &FileHeader, FileSize);
#else
      FileHeader.IntegrityCheck.Checksum.File = CalculateChecksum8 ((UINT8 *) ((UINTN)&FileHeader + sizeof (EFI_FFS_FILE_HEADER)), FileSize - sizeof (EFI_FFS_FILE_HEADER));
#endif
    } else {
      FileHeader.IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
    }

    FileHeader.State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;
    //
    // write header
    //
    if (fwrite (&FileHeader, sizeof (FileHeader), 1, Out) != 1) {
      Error (NULL, 0, 0, "failed to write file header to output file", NULL);
      goto Done;
    }
    //
    // write data
    //
    if (fwrite (FileBuffer, FileSize - sizeof (EFI_FFS_FILE_HEADER), 1, Out) != 1) {
      Error (NULL, 0, 0, "failed to write all bytes to output file", NULL);
      goto Done;
    }

    fclose (Out);
    Out = NULL;
#endif // #ifdef OVERRIDE_SUPPORTED
  } else {
    //
    // Open primary package file and process the IMAGE_SCRIPT section
    //
    PrimaryPackage = fopen (mGlobals.PrimaryPackagePath, "r");
    if (PrimaryPackage == NULL) {
      Error (NULL, 0, 0, mGlobals.PrimaryPackagePath, "unable to open primary package file");
      goto Done;
    }

    LineNumber = 1;
    FindSectionInPackage (".", PrimaryPackage, &LineNumber);
    while (_strcmpi (InputString, "IMAGE_SCRIPT") != 0) {
      GetNextLine (InputString, PrimaryPackage, &LineNumber);
      CheckSlash (InputString, PrimaryPackage, &LineNumber);
      if (strchr (InputString, '=') != NULL) {
        BreakString (InputString, InputString, 0);
      }
    }

    while (InputString[0] != '{') {
      GetNextLine (InputString, PrimaryPackage, &LineNumber);
      CheckSlash (InputString, PrimaryPackage, &LineNumber);
    }
    //
    // Found start of image script, process it
    //
    FileSize += ProcessScript (FileBuffer, PrimaryPackage, mGlobals.BuildDirectory, ForceUncompress);
    if (FileSize == -1) {
      Error (NULL, 0, 0, "failed to process script", NULL);
      goto Done;
    }

    if (StringToType (FileType) != EFI_FV_FILETYPE_RAW) {
      FileSize = AdjustFileSize (FileBuffer, FileSize);
    }

    if (BaseName[0] == '\"') {
      StripQuotes (BaseName);
    }

    if (mGlobals.OutputFilePath[0]) {
      //
      // Use user specified output file name
      //
      strcpy (InputString, mGlobals.OutputFilePath);
    } else {
      //
      // Construct the output file name according to FileType
      //
      if (BaseName[0] != 0) {
        sprintf (InputString, "%s-%s", GuidString, BaseName);
      } else {
        strcpy (InputString, GuidString);
      }

      switch (StringToType (FileType)) {

      case EFI_FV_FILETYPE_SECURITY_CORE:
        strcat (InputString, ".SEC");
        break;

      case EFI_FV_FILETYPE_PEIM:
      case EFI_FV_FILETYPE_PEI_CORE:
      case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
        strcat (InputString, ".PEI");
        break;

      case EFI_FV_FILETYPE_DRIVER:
      case EFI_FV_FILETYPE_DXE_CORE:
        strcat (InputString, ".DXE");
        break;

      case EFI_FV_FILETYPE_APPLICATION:
        strcat (InputString, ".APP");
        break;

      case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
        strcat (InputString, ".FVI");
        break;

      case EFI_FV_FILETYPE_RAW:
        strcat (InputString, ".RAW");
        break;

      case EFI_FV_FILETYPE_ALL:
        Error (mGlobals.PrimaryPackagePath, 1, 0, "invalid FFS file type for this utility", NULL);
        goto Done;

      default:
        strcat (InputString, ".FFS");
        break;
      }
    }

    if (ForceUncompress) {
      strcat (InputString, ".ORG");
    }

    Out = fopen (InputString, "wb");
    if (Out == NULL) {
      Error (NULL, 0, 0, InputString, "failed to open output file for writing");
      goto Done;
    }
    //
    // Initialize the FFS file header
    //
    memset (&FileHeader, 0, sizeof (EFI_FFS_FILE_HEADER));
    memcpy (&FileHeader.Name, &FfsGuid, sizeof (EFI_GUID));
    FileHeader.Type       = StringToType (FileType);
    if (((FfsAttrib & FFS_ATTRIB_DATA_ALIGNMENT) >> 3) < MinFfsDataAlignOverride) {
      FfsAttrib = (FfsAttrib & ~FFS_ATTRIB_DATA_ALIGNMENT) | (MinFfsDataAlignOverride << 3);
    }
    FileHeader.Attributes = FfsAttrib;
    //
    // From this point on FileSize includes the size of the EFI_FFS_FILE_HEADER
    //
    FileSize += sizeof (EFI_FFS_FILE_HEADER);
    //
    // If using a tail, then it adds two bytes
    //
    if (FileHeader.Attributes & FFS_ATTRIB_TAIL_PRESENT) {
      //
      // Tail is not allowed for pad and 0-length files
      //
      if ((FileHeader.Type == EFI_FV_FILETYPE_FFS_PAD) || (FileSize == sizeof (EFI_FFS_FILE_HEADER))) {
        Error (
          mGlobals.PrimaryPackagePath,
          1,
          0,
          "FFS_ATTRIB_TAIL_PRESENT=TRUE is invalid for PAD or 0-length files",
          NULL
          );
        goto Done;
      }

      FileSize += sizeof (EFI_FFS_FILE_TAIL);
    }

    FileHeader.Size[0]  = (UINT8) (FileSize & 0xFF);
    FileHeader.Size[1]  = (UINT8) ((FileSize & 0xFF00) >> 8);
    FileHeader.Size[2]  = (UINT8) ((FileSize & 0xFF0000) >> 16);
    //
    // Fill in checksums and state, they must be 0 for checksumming.
    //
    // FileHeader.IntegrityCheck.Checksum.Header = 0;
    // FileHeader.IntegrityCheck.Checksum.File = 0;
    // FileHeader.State = 0;
    //
    FileHeader.IntegrityCheck.Checksum.Header = CalculateChecksum8 (
                                                  (UINT8 *) &FileHeader,
                                                  sizeof (EFI_FFS_FILE_HEADER)
                                                  );
    if (FileHeader.Attributes & FFS_ATTRIB_CHECKSUM) {
      //
      // Cheating here.  Since the header checksums, just calculate the checksum of the body.
      // Checksum does not include the tail
      //
      if (FileHeader.Attributes & FFS_ATTRIB_TAIL_PRESENT) {
        FileHeader.IntegrityCheck.Checksum.File = CalculateChecksum8 (
                                                    FileBuffer,
                                                    FileSize - sizeof (EFI_FFS_FILE_HEADER) - sizeof (EFI_FFS_FILE_TAIL)
                                                    );
      } else {
        FileHeader.IntegrityCheck.Checksum.File = CalculateChecksum8 (
                                                    FileBuffer,
                                                    FileSize - sizeof (EFI_FFS_FILE_HEADER)
                                                    );
      }
    } else {
      FileHeader.IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
    }
    //
    // Set the state now. Spec says the checksum assumes the state is 0
    //
    FileHeader.State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;

#if (PI_SPECIFICATION_VERSION < 0x00010000)

    //
    // If there is a tail, then set it
    //
    if (FileHeader.Attributes & FFS_ATTRIB_TAIL_PRESENT) {
      TailValue = FileHeader.IntegrityCheck.TailReference;
      TailValue = (UINT16) (~TailValue);
      memcpy (
        (UINT8 *) FileBuffer + FileSize - sizeof (EFI_FFS_FILE_HEADER) - sizeof (EFI_FFS_FILE_TAIL),
        &TailValue,
        sizeof (TailValue)
        );
    }
#endif    
    //
    // Write the FFS file header
    //
    if (fwrite (&FileHeader, sizeof (FileHeader), 1, Out) != 1) {
      Error (NULL, 0, 0, "failed to write file header contents", NULL);
      goto Done;
    }
    //
    // Write data
    //
    if (fwrite (FileBuffer, FileSize - sizeof (EFI_FFS_FILE_HEADER), 1, Out) != 1) {
      Error (NULL, 0, 0, "failed to write file contents", NULL);
      goto Done;
    }
  }

Done:
  SFPCloseFile ();
  if (Out != NULL) {
    fclose (Out);
  }

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

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

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

  return GetUtilityStatus ();
}

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

Routine Description:

  Main function.

Arguments:

  argc - Number of command line parameters.
  argv - Array of pointers to parameter strings.

Returns:
  STATUS_SUCCESS - Utility exits successfully.
  STATUS_ERROR   - Some error occurred during execution.

--*/
{
  STATUS  Status;
  //
  // Set the name of our utility for error reporting purposes.
  //
  SetUtilityName (UTILITY_NAME);
  Status = ProcessCommandLineArgs (argc, argv);
  FreeMacros ();
  if (Status != STATUS_SUCCESS) {
    return Status;
  }

  Status = MainEntry (argc, argv, TRUE);
  if (Status == STATUS_SUCCESS) {
    MainEntry (argc, argv, FALSE);
  }
  //
  // If any errors were reported via the standard error reporting
  // routines, then the status has been saved. Get the value and
  // return it to the caller.
  //
  return GetUtilityStatus ();
}

static
STATUS
ProcessCommandLineArgs (
  int     Argc,
  char    *Argv[]
  )
/*++

Routine Description:
  Process the command line arguments.

Arguments:
  Argc - as passed in to main()
  Argv - as passed in to main()

Returns:
  STATUS_SUCCESS    - arguments all ok
  STATUS_ERROR      - problem with args, so caller should exit

--*/
{
  STATUS       Status;
  UINT8        *OriginalPrimaryPackagePath;
  UINT8        *OriginalOverridePackagePath;
  UINT8        *PackageName;
  
  //
  // If no args, then print usage instructions and return an error
  //
  if (Argc == 1) {
    PrintUsage ();
    return STATUS_ERROR;
  }
  
  OriginalPrimaryPackagePath = NULL;
  OriginalOverridePackagePath = NULL;
  memset (&mGlobals, 0, sizeof (mGlobals));
  Argc--;
  Argv++;
  while (Argc > 0) {
    if (_strcmpi (Argv[0], "-b") == 0) {
      //
      // OPTION: -b BuildDirectory
      // Make sure there is another argument, then save it to our globals.
      //
      if (Argc < 2) {
        Error (NULL, 0, 0, "-b option requires the build directory name", NULL);
        return STATUS_ERROR;
      }

      if (mGlobals.BuildDirectory[0]) {
        Error (NULL, 0, 0, Argv[0], "option can only be specified once");
        return STATUS_ERROR;
      }

      strcpy (mGlobals.BuildDirectory, Argv[1]);
      Argc--;
      Argv++;
    } else if (_strcmpi (Argv[0], "-p1") == 0) {
      //
      // OPTION: -p1 PrimaryPackageFile
      // Make sure there is another argument, then save it to our globals.
      //
      if (Argc < 2) {
        Error (NULL, 0, 0, Argv[0], "option requires the primary package file name");
        return STATUS_ERROR;
      }

      if (OriginalPrimaryPackagePath) {
        Error (NULL, 0, 0, Argv[0], "option can only be specified once");
        return STATUS_ERROR;
      }
      
      OriginalPrimaryPackagePath = Argv[1];
      Argc--;
      Argv++;
    } else if (_strcmpi (Argv[0], "-p2") == 0) {
      //
      // OPTION: -p2 OverridePackageFile
      // Make sure there is another argument, then save it to our globals.
      //
      if (Argc < 2) {
        Error (NULL, 0, 0, Argv[0], "option requires the override package file name");
        return STATUS_ERROR;
      }

      if (OriginalOverridePackagePath) {
        Error (NULL, 0, 0, Argv[0], "option can only be specified once");
        return STATUS_ERROR;
      }
      
      OriginalOverridePackagePath = Argv[1];
      Argc--;
      Argv++;
    } else if (_strcmpi (Argv[0], "-o") == 0) {
      //
      // OPTION: -o OutputFilePath
      // Make sure there is another argument, then save it to out globals.
      //
      if (Argc < 2) {
        Error (NULL, 0, 0, Argv[0], "option requires the output file name");
        return STATUS_ERROR;
      }
      if (mGlobals.OutputFilePath[0]) {
        Error (NULL, 0, 0, Argv[0], "option can only be specified once");
        return STATUS_ERROR;
      }

      strcpy (mGlobals.OutputFilePath, Argv[1]);
      Argc--;
      Argv++;
    } else if (_strcmpi (Argv[0], "-v") == 0) {
      //
      // OPTION: -v       verbose
      //
      mGlobals.Verbose = TRUE;
    } else if (_strcmpi (Argv[0], "-d") == 0) {
      //
      // OPTION: -d  name=value
      // Make sure there is another argument, then add it to our macro list.
      //
      if (Argc < 2) {
        Error (NULL, 0, 0, Argv[0], "option requires the macro definition");
        return STATUS_ERROR;
      }
      
      AddMacro (Argv[1]);
      Argc--;
      Argv++;
    } else if (_strcmpi (Argv[0], "-h") == 0) {
      //
      // OPTION: -h      help
      //
      PrintUsage ();
      return STATUS_ERROR;
    } else if (_strcmpi (Argv[0], "-?") == 0) {
      //
      // OPTION:  -?      help
      //
      PrintUsage ();
      return STATUS_ERROR;
    } else {
      Error (NULL, 0, 0, Argv[0], "unrecognized option");
      PrintUsage ();
      return STATUS_ERROR;
    }

    Argv++;
    Argc--;
  }

  //
  // Must have at least specified the build directory
  //
  if (!mGlobals.BuildDirectory[0]) {
    Error (NULL, 0, 0, "must specify build directory", NULL);
    return STATUS_ERROR;
  }
  
  //
  // Must have at least specified the package file name
  //
  if (OriginalPrimaryPackagePath == NULL) {
    Error (NULL, 0, 0, "must specify primary package file", NULL);
    return STATUS_ERROR;
  }

  PackageName = OriginalPrimaryPackagePath + strlen (OriginalPrimaryPackagePath);
  while ((*PackageName != '\\') && (*PackageName != '/') && 
         (PackageName != OriginalPrimaryPackagePath)) {
    PackageName--;
  }
  //
  // Skip the '\' or '/'
  //
  if (PackageName != OriginalPrimaryPackagePath) {
    PackageName++;
  }
  sprintf (mGlobals.PrimaryPackagePath, "%s\\%s.new", mGlobals.BuildDirectory, PackageName);
  Status = ReplaceMacros (OriginalPrimaryPackagePath, mGlobals.PrimaryPackagePath);
  if (Status == STATUS_WARNING) {
    //
    // No macro replacement, use the previous package file
    //
    strcpy (mGlobals.PrimaryPackagePath, OriginalPrimaryPackagePath);
  } else if (Status != STATUS_SUCCESS) {
    return Status;
  }
  
  if (OriginalOverridePackagePath != NULL) {
    PackageName = OriginalOverridePackagePath + strlen (OriginalOverridePackagePath);
    while ((*PackageName != '\\') && (*PackageName != '/') && 
           (PackageName != OriginalOverridePackagePath)) {
      PackageName--;
    }
    //
    // Skip the '\' or '/'
    //
    if (PackageName != OriginalOverridePackagePath) {
      PackageName++;
    }    
    sprintf (mGlobals.OverridePackagePath, "%s\\%s.new", mGlobals.BuildDirectory, PackageName);
    Status = ReplaceMacros (OriginalOverridePackagePath, mGlobals.OverridePackagePath);
    if (Status == STATUS_WARNING) {
      //
      // No macro replacement, use the previous package file
      //
      strcpy (mGlobals.OverridePackagePath, OriginalOverridePackagePath);
    } else if (Status != STATUS_SUCCESS) {
        return Status;
    }    
  }

  return STATUS_SUCCESS;
}

static
void
AddMacro (
  UINT8   *MacroString
  )
/*++

Routine Description:

  Add or override a macro definition.

Arguments:

  MacroString  - macro definition string: name=value

Returns:

  None

--*/  
{
  MACRO    *Macro;
  MACRO    *NewMacro;
  UINT8    *Value;
  
  //
  // Seperate macro name and value by '\0'
  //
  for (Value = MacroString; *Value && (*Value != '='); Value++);
  
  if (*Value == '=') {
    *Value = '\0';
    Value ++;
  }
  
  //
  // We now have a macro name and value. 
  // Look for an existing macro and overwrite it.
  //
  Macro = mGlobals.MacroList;
  while (Macro) {
    if (_strcmpi (MacroString, Macro->Name) == 0) {
      Macro->Value = Value;
      return;
    }

    Macro = Macro->Next;
  }
  
  //
  // Does not exist, create a new one
  //
  NewMacro = (MACRO *) malloc (sizeof (MACRO));
  memset ((UINT8 *) NewMacro, 0, sizeof (MACRO));
  NewMacro->Name   = MacroString;
  NewMacro->Value  = Value;

  //
  // Add it to the head of the list.
  //
  NewMacro->Next = mGlobals.MacroList;
  mGlobals.MacroList = NewMacro;
  
  return;
}

static
UINT8 *
GetMacroValue (
  UINT8   *MacroName
  )
/*++

Routine Description:

  Look up a macro.

Arguments:

  MacroName  - The name of macro

Returns:

  Pointer to the value of the macro if found
  NULL if the macro is not found

--*/   
{

  MACRO  *Macro;
  UINT8  *Value;

  //
  // Scan for macro
  //
  Macro = mGlobals.MacroList;
  while (Macro) {
    if (_strcmpi (MacroName, Macro->Name) == 0) {
      return Macro->Value;
    }
    Macro = Macro->Next;
  }
  
  //
  // Try environment variable
  //
  Value = getenv (MacroName);
  if (Value == NULL) {
    printf ("Environment variable %s not found!\n", MacroName);
  }   
  return Value;
}
  
static
void
FreeMacros (
  )
/*++

Routine Description:

  Free the macro list.

Arguments:

  None

Returns:

  None

--*/    
{
  MACRO    *Macro;
  MACRO    *NextMacro;
  
  Macro = mGlobals.MacroList;
  while (Macro) {
    NextMacro = Macro->Next;
    free (Macro);
    Macro = NextMacro;
  }
  mGlobals.MacroList = NULL;
  
  return;
}

static
STATUS
ReplaceMacros (
  UINT8   *InputFile,
  UINT8   *OutputFile
  )
/*++

Routine Description:

  Replace all the macros in InputFile to create the OutputFile.

Arguments:

  InputFile         - Input package file for macro replacement
  OutputFile        - Output package file after macro replacement

Returns:

  STATUS_SUCCESS    - Output package file is created successfully after the macro replacement.
  STATUS_WARNING    - Output package file is not created because of no macro replacement.
  STATUS_ERROR      - Some error occurred during execution.

--*/    
{
  FILE   *Fptr;
  UINT8  *SaveStart;
  UINT8  *FromPtr;
  UINT8  *ToPtr;
  UINT8  *Value;
  UINT8  *FileBuffer;
  UINTN  FileSize;
  
  //
  // Get the file size, and then read the entire thing into memory.
  // Allocate extra space for a terminator character.
  //
  if ((Fptr = fopen (InputFile, "r")) == NULL) {
    Error (NULL, 0, 0, InputFile, "can't open input file");
    return STATUS_ERROR;    
  }
  fseek (Fptr, 0, SEEK_END);
  FileSize = ftell (Fptr);
  fseek (Fptr, 0, SEEK_SET);
  FileBuffer = malloc (FileSize + 1);
  if (FileBuffer == NULL) {
    fclose (Fptr);
    Error (NULL, 0, 0, InputFile, "file buffer memory allocation failure");
    return STATUS_ERROR;
  }
  fread (FileBuffer, FileSize, 1, Fptr);
  FileBuffer[FileSize] = '\0';
  fclose (Fptr);
    
  //
  // Walk the entire file, replacing $(MACRO_NAME).
  //
  Fptr = NULL;
  FromPtr = FileBuffer;
  SaveStart = FromPtr;
  while (*FromPtr) {
    if ((*FromPtr == '$') && (*(FromPtr + 1) == '(')) {
      FromPtr += 2;
      for (ToPtr = FromPtr; *ToPtr && (*ToPtr != ')'); ToPtr++);
      if (*ToPtr) {
        //
        // Find an $(MACRO_NAME), replace it
        //
        *ToPtr = '\0';
        Value = GetMacroValue (FromPtr);
        *(FromPtr-2)= '\0';
        if (Fptr == NULL) {
          if ((Fptr = fopen (OutputFile, "w")) == NULL) {
            free (FileBuffer);
            Error (NULL, 0, 0, OutputFile, "can't open output file");
            return STATUS_ERROR;    
          }
        }
        if (Value != NULL) {
          fprintf (Fptr, "%s%s", SaveStart, Value);
        } else {
          fprintf (Fptr, "%s", SaveStart);
        }
        //
        // Continue macro replacement for the remaining string line
        //
        FromPtr = ToPtr+1;
        SaveStart = FromPtr;
        continue;
      } else {
        break;
      }
    } else {
      FromPtr++;
    }
  }
  if (Fptr != NULL) {
    fprintf (Fptr, "%s", SaveStart);
  }
  
  free (FileBuffer);
  if (Fptr != NULL) {
    fclose (Fptr);
    return STATUS_SUCCESS;
  } else {
    return STATUS_WARNING;
  }
}
