/*++

Copyright (c)  2004-2006 Intel Corporation. All rights reserved
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:

  FlashDefFile.c

Abstract:

  Utility for flash management in the Intel Platform Innovation Framework
  for EFI build environment.

--*/

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

#include <Common/UefiBaseTypes.h>
#include <Common/FirmwareVolumeHeader.h>
#include <Common/MultiPhase.h>

#include "EfiUtilityMsgs.h"
#include "FlashDefFile.h"
#include "SimpleFileParsing.h"
#include "Symbols.h"

//
// #include "TrackMallocFree.h"
//
#define WCHAR_T           char
#define MAX_STRING_LEN    256
#define MAX_NAME_LEN      128
#define BUFFER_SIZE       1024
#define MAX_ATTR_LEN      128
#define MAX_AREATYPE_LEN  128
#define COLUMN2_START     60
#define COLUMN3_START     70
//
// Information for each subregions defined in the fdf file will be saved in these
//
typedef struct _FLASH_SUBREGION_DESCRIPTION {
  struct _FLASH_SUBREGION_DESCRIPTION *Next;
  int                                 CreateHob;                  // to add to the auto-created HOB array
  WCHAR_T                             Name[MAX_NAME_LEN];         // each subregion within a region must have a unique name
  unsigned int                        Size;                       // size, in bytes, of this subregion
  unsigned int                        SizeLeft;                   // used when creating the image
  WCHAR_T                             Attributes[MAX_ATTR_LEN];   // subregion attributes used in the output HOB
  WCHAR_T                             AreaType[MAX_AREATYPE_LEN]; // subregion area type used in the output HOB
  EFI_GUID                            NameGuid;                   // used in the output HOB
  WCHAR_T                             NameGuidString[MAX_NAME_LEN];
  EFI_GUID                            AreaTypeGuid;               // used in the output HOB
  WCHAR_T                             AreaTypeGuidString[MAX_NAME_LEN];
  EFI_GUID                            FileSystemGuid;             // used in the output HOB
  WCHAR_T                             FileSystemGuidString[MAX_NAME_LEN];
} FLASH_SUBREGION_DESCRIPTION;

//
// Information for each block in a flash device will be saved in one of these.
// We'll also use it for region definitions.
//
typedef struct _FLASH_BLOCK_DESCRIPTION {
  struct _FLASH_BLOCK_DESCRIPTION *Next;                      // next block in the linked list
  WCHAR_T                         Name[MAX_NAME_LEN];         // each block must have a unique name
  unsigned int                    Size;                       // size, in bytes, of this block
  unsigned int                    SizeLeft;                   // for use when creating image
  unsigned int                    Flags;                      // user-defined flags for the block
  unsigned int                    Alignment;                  // power of 2 alignment
  WCHAR_T                         Attributes[MAX_ATTR_LEN];   // only used for Region definitions
  WCHAR_T                         AreaType[MAX_AREATYPE_LEN]; // only used for Region definitions
  EFI_GUID                        AreaTypeGuid;
  WCHAR_T                         AreaTypeGuidString[MAX_NAME_LEN];
  FLASH_SUBREGION_DESCRIPTION     *Subregions;
  FLASH_SUBREGION_DESCRIPTION     *LastSubregion;
} FLASH_BLOCK_DESCRIPTION;

//
// Information for each flash device will be saved in one of these
//
typedef struct _FLASH_DEVICE_DESCRIPTION {
  struct _FLASH_DEVICE_DESCRIPTION  *Next;              // next flash device in our linked list
  int                               ErasePolarity;      // erase polarity of the flash device
  unsigned int                      BaseAddress;        // base address of the flash device
  unsigned int                      Size;               // total size, in bytes, of the flash device
  WCHAR_T                           Name[MAX_NAME_LEN]; // name of the flash device
  FLASH_BLOCK_DESCRIPTION           *PBlocks;           // linked list of physical block descriptors
  FLASH_BLOCK_DESCRIPTION           *LastPBlock;        // last block in the linked list
  FLASH_BLOCK_DESCRIPTION           *Regions;           // linked list of flash region descriptors
  FLASH_BLOCK_DESCRIPTION           *LastRegion;        // last region in the linked list
} FLASH_DEVICE_DESCRIPTION;

//
// For image definitions, they can specify a file name or raw data bytes. Keep a linked list.
//
typedef struct _IMAGE_DEFINITION_ENTRY {
  struct _IMAGE_DEFINITION_ENTRY  *Next;
  WCHAR_T                         RegionName[MAX_NAME_LEN];
  WCHAR_T                         SubregionName[MAX_NAME_LEN];
  WCHAR_T                         Name[MAX_NAME_LEN]; // file or data name
  int                             IsRawData;          // non-zero if raw data bytes
  unsigned int                    RawDataSize;
  char                            *RawData;
  int                             Optional;           // optional file (don't include if it doesn't exist)
} IMAGE_DEFINITION_ENTRY;

//
// When we parse an image definition, save all the data for each in one of these
//
typedef struct _IMAGE_DEFINITION {
  struct _IMAGE_DEFINITION  *Next;
  WCHAR_T                   Name[MAX_NAME_LEN];
  IMAGE_DEFINITION_ENTRY    *Entries;
  IMAGE_DEFINITION_ENTRY    *LastEntry;
} IMAGE_DEFINITION;

typedef struct {
  char  *BufferStart;
  char  *BufferEnd;
  char  *BufferPos;
} BUFFER_DATA;

static const char               *CIncludeHeader = "/*++\n\n"
"  DO NOT EDIT -- file auto-generated by FlashMap utility\n\n""--*/\n""\n""#ifndef _FLASH_MAP_H_\n"
"#define _FLASH_MAP_H_\n\n";
//
//  "#include \"EfiFlashMap.h\"\n\n";
//
static const char               *CIncludeFooter = "#endif // #ifndef _FLASH_MAP_H_\n\n";

static const char               *CFlashMapDataFileHeader = "/*++\n\n"
"  DO NOT EDIT -- file auto-generated by FlashMap utility\n\n""--*/\n""\n";

static FLASH_DEVICE_DESCRIPTION *mFlashDevices      = NULL;
static IMAGE_DEFINITION         *mImageDefinitions  = NULL;

//
// Local function prototypes
//
static
BUFFER_DATA                     *
CreateBufferData (
  VOID
  );

static
BOOLEAN
AddBufferDataByte (
  BUFFER_DATA *Buffer,
  char        Data
  );

static
void
FreeBufferData (
  BUFFER_DATA *Buffer,
  BOOLEAN     FreeData
  );

static
char                            *
GetBufferData (
  BUFFER_DATA *Buffer,
  int         *BufferSize
  );

static
FLASH_SUBREGION_DESCRIPTION     *
ParseSubregionDefinition (
  unsigned int  SizeLeft
  );

void
FDFConstructor (
  VOID
  )
/*++

Routine Description:
  Initialization routine for the services that operate on a flash
  definition file.

Arguments:
  None.

Returns:
  NA

--*/
{
  mFlashDevices     = NULL;
  mImageDefinitions = NULL;
}

void
FDFDestructor (
  VOID
  )
/*++

Routine Description:
  Finalization/cleanup routine for the services that operate on a flash
  definition file.

Arguments:
  None.

Returns:
  NA

--*/
{
  FLASH_BLOCK_DESCRIPTION     *FBNext;
  FLASH_DEVICE_DESCRIPTION    *FDNext;
  IMAGE_DEFINITION            *IDNext;
  IMAGE_DEFINITION_ENTRY      *IDENext;
  FLASH_SUBREGION_DESCRIPTION *SubNext;
  //
  // Go through all our flash devices and free the memory
  //
  while (mFlashDevices != NULL) {
    //
    // Free the physical block definitions
    //
    while (mFlashDevices->PBlocks != NULL) {
      FBNext = mFlashDevices->PBlocks->Next;
      _free (mFlashDevices->PBlocks);
      mFlashDevices->PBlocks = FBNext;
    }
    //
    // Free the region definitions
    //
    while (mFlashDevices->Regions != NULL) {
      FBNext = mFlashDevices->Regions->Next;
      //
      // First free the subregion definitions
      //
      while (mFlashDevices->Regions->Subregions != NULL) {
        SubNext = mFlashDevices->Regions->Subregions->Next;
        _free (mFlashDevices->Regions->Subregions);
        mFlashDevices->Regions->Subregions = SubNext;
      }

      _free (mFlashDevices->Regions);
      mFlashDevices->Regions = FBNext;
    }

    FDNext = mFlashDevices->Next;
    _free (mFlashDevices);
    mFlashDevices = FDNext;
  }
  //
  // Free up the image definitions, and the data
  //
  while (mImageDefinitions != NULL) {
    //
    // Free the entries
    //
    while (mImageDefinitions->Entries != NULL) {
      IDENext = mImageDefinitions->Entries->Next;
      if (mImageDefinitions->Entries->RawData != NULL) {
        _free (mImageDefinitions->Entries->RawData);
      }

      _free (mImageDefinitions->Entries);
      mImageDefinitions->Entries = IDENext;
    }

    IDNext = mImageDefinitions->Next;
    _free (mImageDefinitions);
    mImageDefinitions = IDNext;
  }
}

STATUS
FDFParseFile (
  char    *FileName
  )
/*++

Routine Description:
  Parse the specified flash definition file, saving the definitions in
  file-static variables for use by other functions.
  
Arguments:
  FileName    - name of the input flash definition text file.

Returns:
  STATUS_SUCCESS    - file parsed with no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered while parsing
  STATUS_ERROR      - errors were encountered while parsing
  
--*/
{
  FILE                        *Fptr;
  STATUS                      Status;
  unsigned int                Num;
  FLASH_DEVICE_DESCRIPTION    *FDDesc;
  FLASH_BLOCK_DESCRIPTION     *FBlockDesc;
  FLASH_BLOCK_DESCRIPTION     *TempBlockDesc;
  FLASH_SUBREGION_DESCRIPTION *Subregion;
  FLASH_SUBREGION_DESCRIPTION *TempSubregion;
  unsigned int                BlockSizeLeft;
  unsigned int                RegionSizeLeft;
  unsigned int                SubregionSizeLeft;
  int                         ErrorCount;
  int                         WarningCount;
  IMAGE_DEFINITION            *ImageDef;
  IMAGE_DEFINITION_ENTRY      *ImageDefEntry;
  IMAGE_DEFINITION_ENTRY      *TempImageDefEntry;
  BUFFER_DATA                 *BufferData;
  char                        Str[100];
  BOOLEAN                     PreviousComma;

  if ((Fptr = fopen (FileName, "r")) == NULL) {
    Error (NULL, 0, 0, FileName, "failed to open input flash definition file for reading");
    return STATUS_ERROR;
  }

  fclose (Fptr);
  Status        = STATUS_SUCCESS;
  ErrorCount    = 0;
  WarningCount  = 0;
  //
  // Initialize the simple-file-parsing routines
  //
  SFPInit ();
  //
  // Open the file
  //
  if ((Status = SFPOpenFile (FileName)) != STATUS_SUCCESS) {
    return Status;
  }
  //
  // Parse the file. Should start with a series of these:
  // FlashDevice {
  //   Name = "FLASH_1234", Size = 0x2004, BaseAddress = 0xFFF0000, ErasePolarity = 1,
  //   Block { Name = "BLOCK1",  Size = 0x1000, Flags = 0x0001 }
  //   Block { Name = "BLOCK2",  Size = 0x1004, Flags = 0x0002 }
  //   Region  { Name = "REGION_NAME", Size = 0x2004, Align= 4 }
  // }
  //
  while (SFPIsKeyword ("FlashDevice")) {
    //
    // Allocate memory for new flash device description block
    //
    FDDesc = (FLASH_DEVICE_DESCRIPTION *) _malloc (sizeof (FLASH_DEVICE_DESCRIPTION));
    if (FDDesc == NULL) {
      Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
      ErrorCount++;
      goto Done;
    }

    memset (FDDesc, 0, sizeof (FLASH_DEVICE_DESCRIPTION));
    //
    // Open brace -- warning if not there
    //
    if (!SFPIsToken ("{")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected {", NULL);
      WarningCount++;
    }
    //
    // Parse:  Name = "DeviceName",
    //
    if (!SFPIsKeyword ("Name")) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }

    if (!SFPGetQuotedString (FDDesc->Name, sizeof (FDDesc->Name))) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted name of flash device", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken (",")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following flash device name", NULL);
      WarningCount++;
    }
    //
    // Parse: Size = 0x20000,
    //
    if (!SFPIsKeyword ("Size")) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Size'", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }

    if (!SFPGetNumber (&FDDesc->Size)) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric Size value", NULL);
      ErrorCount++;
      goto Done;
    }
    //
    // Check for 0 size
    //
    if (FDDesc->Size == 0) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, FDDesc->Name, "Size field cannot be 0", NULL);
      ErrorCount++;
      goto Done;
    }

    SFPIsToken (",");
    //
    // Parse: BaseAddress = 0xFFF0000,
    //
    if (!SFPIsKeyword ("BaseAddress")) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'BaseAddress'", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }

    if (!SFPGetNumber (&FDDesc->BaseAddress)) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric value for BaseAddress", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken (",")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following BaseAddress value", NULL);
      WarningCount++;
    }
    //
    // Parse: ErasePolarity = 1,
    //
    if (!SFPIsKeyword ("ErasePolarity")) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'ErasePolarity'", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }

    if (!SFPGetNumber (&Num) || ((Num != 0) && (Num != 1))) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric erase polarity value 1 or 0", NULL);
      ErrorCount++;
      goto Done;
    }

    FDDesc->ErasePolarity = Num;
    if (!SFPIsToken (",")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following erase polarity value", NULL);
      WarningCount++;
    }
    //
    // Parse array of:
    //   Block {  Name = "BLOCK1", Size = 0x1000, Flags = 0x0001 }
    //
    // Keep track of size to make sure the sum of the physical blocks and region sizes do not
    // exceed the size of the flash device.
    //
    BlockSizeLeft   = FDDesc->Size;
    RegionSizeLeft  = FDDesc->Size;
    while (SFPIsKeyword ("Block")) {
      //
      // Allocate memory for a new physical block descriptor
      //
      FBlockDesc = (FLASH_BLOCK_DESCRIPTION *) _malloc (sizeof (FLASH_BLOCK_DESCRIPTION));
      if (FBlockDesc == NULL) {
        Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
        ErrorCount++;
        goto Done;
      }

      memset (FBlockDesc, 0, sizeof (FLASH_BLOCK_DESCRIPTION));
      //
      // Open brace -- warning if not there
      //
      if (!SFPIsToken ("{")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected {", NULL);
        WarningCount++;
      }
      //
      // Parse:  Name = "BlockName",
      //
      if (!SFPIsKeyword ("Name")) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
        WarningCount++;
      }

      if (!SFPGetQuotedString (FBlockDesc->Name, sizeof (FBlockDesc->Name))) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted name of physical block", NULL);
        ErrorCount++;
        goto Done;
      }
      //
      // Make sure there are no other physical block names with this same name
      //
      for (TempBlockDesc = FDDesc->PBlocks; TempBlockDesc != NULL; TempBlockDesc = TempBlockDesc->Next) {
        if (strcmp (TempBlockDesc->Name, FBlockDesc->Name) == 0) {
          Error (
            SFPGetFileName (),
            SFPGetLineNumber (),
            0,
            TempBlockDesc->Name,
            "physical block with this name already defined"
            );
          ErrorCount++;
        }
      }

      if (!SFPIsToken (",")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following physical block name", NULL);
        WarningCount++;
      }
      //
      // Parse: Size = 0x2000,
      //
      if (!SFPIsKeyword ("Size")) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Size'", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
        WarningCount++;
      }

      if (!SFPGetNumber (&FBlockDesc->Size)) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric Size value", NULL);
        ErrorCount++;
        goto Done;
      }
      //
      // Make sure the sum of physical blocks so far does not exceed flash device size
      //
      if (BlockSizeLeft < FBlockDesc->Size) {
        Error (
          SFPGetFileName (),
          SFPGetLineNumber (),
          0,
          "sum of physical block sizes exceeds flash device size",
          NULL
          );
        ErrorCount++;
      }

      BlockSizeLeft -= FBlockDesc->Size;
      SFPIsToken (",");
      //
      // Optional parse: Flags = 0xFFF0000,
      //
      if (SFPIsKeyword ("Flags")) {
        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetNumber (&FBlockDesc->Flags)) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric value for Flags", NULL);
          ErrorCount++;
          goto Done;
        }
      }

      if (!SFPIsToken ("}")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected PhysicalBlock closing brace '}'", NULL);
        WarningCount++;
      }
      //
      // Add the physical block descriptor to the end of the linked list
      //
      if (FDDesc->LastPBlock != NULL) {
        FDDesc->LastPBlock->Next = FBlockDesc;
      } else {
        FDDesc->PBlocks = FBlockDesc;
      }

      FDDesc->LastPBlock = FBlockDesc;
    }
    //
    // Make sure sum of sizes of physical blocks added up to size of flash device
    //
    if (BlockSizeLeft != 0) {
      Error (
        SFPGetFileName (),
        SFPGetLineNumber (),
        0,
        NULL,
        "sum of sizes of physical blocks (0x%08X) != flash device size (0x%08X) : delta = 0x%08X",
        FDDesc->Size - BlockSizeLeft,
        FDDesc->Size,
        BlockSizeLeft
        );
      ErrorCount++;
    }
    //
    // Parse array of:
    //   Region { Name = "REGION_1", Size = 0x2000, Flags = 0x1234, Alignment = 4, Attributes = "str", AreaType = "str" }
    //
    while (SFPIsKeyword ("Region")) {
      //
      // Allocate memory for a new physical block descriptor
      //
      FBlockDesc = (FLASH_BLOCK_DESCRIPTION *) _malloc (sizeof (FLASH_BLOCK_DESCRIPTION));
      if (FBlockDesc == NULL) {
        Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
        ErrorCount++;
        goto Done;
      }

      memset (FBlockDesc, 0, sizeof (FLASH_BLOCK_DESCRIPTION));
      //
      // Open brace -- warning if not there
      //
      if (!SFPIsToken ("{")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected {", NULL);
        WarningCount++;
      }
      //
      // Parse:  Name = "BlockName",
      //
      if (!SFPIsKeyword ("Name")) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
        WarningCount++;
      }

      if (!SFPGetQuotedString (FBlockDesc->Name, sizeof (FBlockDesc->Name))) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Region name", NULL);
        ErrorCount++;
        goto Done;
      }
      //
      // Make sure there are no other region names with this same name
      //
      for (TempBlockDesc = FDDesc->Regions; TempBlockDesc != NULL; TempBlockDesc = TempBlockDesc->Next) {
        if (strcmp (TempBlockDesc->Name, FBlockDesc->Name) == 0) {
          Error (
            SFPGetFileName (),
            SFPGetLineNumber (),
            0,
            TempBlockDesc->Name,
            "Region with this name already defined"
            );
          ErrorCount++;
        }
      }

      if (!SFPIsToken (",")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following Region name", NULL);
        WarningCount++;
      }
      //
      // Parse: Size = 0x2000,
      //
      if (!SFPIsKeyword ("Size")) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Size'", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
        WarningCount++;
      }

      if (!SFPGetNumber (&FBlockDesc->Size)) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric Size value", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken (",")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ','", NULL);
      }
      //
      // Make sure the sum of regions so far does not exceed flash device size
      //
      if (RegionSizeLeft < FBlockDesc->Size) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "sum of Region sizes exceeds flash device size", NULL);
        ErrorCount++;
      }

      RegionSizeLeft -= FBlockDesc->Size;
      //
      // Optional parse: Flags = 0xFFF0000,
      //
      if (SFPIsKeyword ("Flags")) {
        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetNumber (&FBlockDesc->Flags)) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric value for Flags", NULL);
          ErrorCount++;
          goto Done;
        }
        //
        // comma
        //
        if (!SFPIsToken (",")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ','", NULL);
        }
      }
      //
      // Optional parse: Alignment = 4
      //
      if (SFPIsKeyword ("Alignment")) {
        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetNumber (&FBlockDesc->Alignment)) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric Alignment value", NULL);
          ErrorCount++;
          goto Done;
        }
        //
        // comma
        //
        if (!SFPIsToken (",")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ','", NULL);
        }
      }
      //
      // Parse:  Attributes = "String",
      //
      if (!SFPIsKeyword ("Attributes")) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Attributes'", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
        WarningCount++;
      }

      if (!SFPGetQuotedString (FBlockDesc->Attributes, sizeof (FBlockDesc->Attributes))) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Attributes string", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken (",")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ','", NULL);
      }
      //
      // Parse:  AreaType = "String",
      //
      if (!SFPIsKeyword ("AreaType")) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'AreaType'", NULL);
        ErrorCount++;
        goto Done;
      }

      if (!SFPIsToken ("=")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
        WarningCount++;
      }

      if (!SFPGetQuotedString (FBlockDesc->AreaType, sizeof (FBlockDesc->AreaType))) {
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted AreaType string", NULL);
        ErrorCount++;
        goto Done;
      }

      PreviousComma = SFPIsToken (",");
      //
      // Parse optional attribute "AreaTypeGuid"
      //
      if (SFPIsKeyword ("AreaTypeGuid")) {
        //
        // Check for preceeding comma now
        //
        if (!PreviousComma) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' before 'AreaTypeGuid'", NULL);
          WarningCount++;
        }
        
        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }
        
        if (SFPGetQuotedString (FBlockDesc->AreaTypeGuidString, sizeof (FBlockDesc->AreaTypeGuidString))) {
          //
          // Nothing else to do
          //
        } else if (!SFPGetGuid (PARSE_GUID_STYLE_5_FIELDS, &FBlockDesc->AreaTypeGuid)) {
          Error (
            SFPGetFileName (),
            SFPGetLineNumber (),
            0,
            "expected AreaTypeGuid quoted string or GUID of form 12345678-1234-1234-1234-123456789ABC",
            NULL
            );
          ErrorCount++;
          goto Done;
        }
        PreviousComma = SFPIsToken (",");
      }

      //
      // Parse optional Subregion definitions
      //
      SubregionSizeLeft = FBlockDesc->Size;
      while (SFPIsToken ("Subregion")) {
        if (!PreviousComma) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' before 'Subregion'", NULL);
          WarningCount++;
          PreviousComma = TRUE;
        }

        Subregion = ParseSubregionDefinition (SubregionSizeLeft);
        if (Subregion == NULL) {
          ErrorCount++;
          goto Done;
        }

        SubregionSizeLeft -= Subregion->Size;
        //
        // Add it to the end of our list
        //
        if (FBlockDesc->Subregions == NULL) {
          FBlockDesc->Subregions = Subregion;
        } else {
          FBlockDesc->LastSubregion->Next = Subregion;
        }

        FBlockDesc->LastSubregion = Subregion;
        //
        // Make sure all subregion names are unique. We do this each time
        // through so that we catch the error immediately after it happens, in
        // which case the reported line number is at least close to where the
        // problem lies. We don't exit on the error because we can continue parsing
        // the script to perhaps catch other errors or warnings.
        //
        for (Subregion = FBlockDesc->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
          for (TempSubregion = Subregion->Next; TempSubregion != NULL; TempSubregion = TempSubregion->Next) {
            if (strcmp (Subregion->Name, TempSubregion->Name) == 0) {
              Error (SFPGetFileName (), SFPGetLineNumber (), 0, Subregion->Name, "duplicate Subregion name");
              ErrorCount++;
            }
          }
        }
      }

      if (!SFPIsToken ("}")) {
        Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected Region closing brace '}'", NULL);
        WarningCount++;
      }
      //
      // Add the region descriptor to the end of the linked list
      //
      if (FDDesc->LastRegion != NULL) {
        FDDesc->LastRegion->Next = FBlockDesc;
      } else {
        FDDesc->Regions = FBlockDesc;
      }

      FDDesc->LastRegion = FBlockDesc;
    }
    //
    // Make sure sum of sizes of regions adds up to size of flash device
    //
    if (RegionSizeLeft != 0) {
      Error (
        SFPGetFileName (),
        SFPGetLineNumber (),
        0,
        NULL,
        "sum of sizes of Regions (0x%08X) != flash device size (0x%08X) : delta = 0x%08X",
        FDDesc->Size - RegionSizeLeft,
        FDDesc->Size,
        RegionSizeLeft
        );
      ErrorCount++;
    }
    //
    // Look for closing brace
    //
    if (!SFPIsToken ("}")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected FlashDevice closing brace '}'", NULL);
      WarningCount++;
    }
    //
    // Add this flash description to the list
    //
    FDDesc->Next  = mFlashDevices;
    mFlashDevices = FDDesc;
  }

  while (SFPIsKeyword ("FlashDeviceImage")) {
    //
    // Allocate memory for a new FD image definition
    //
    ImageDef = (IMAGE_DEFINITION *) _malloc (sizeof (IMAGE_DEFINITION));
    if (ImageDef == NULL) {
      Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
      ErrorCount++;
      goto Done;
    }

    memset (ImageDef, 0, sizeof (IMAGE_DEFINITION));
    //
    // Open brace -- warning if not there
    //
    if (!SFPIsToken ("{")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected {", NULL);
      WarningCount++;
    }
    //
    // Parse:  Name = "ImageName",
    //
    if (!SFPIsKeyword ("Name")) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }

    if (!SFPGetQuotedString (ImageDef->Name, sizeof (ImageDef->Name))) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted name of image", NULL);
      ErrorCount++;
      goto Done;
    }

    if (!SFPIsToken (",")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following image name", NULL);
      WarningCount++;
    }

    while (1) {
      //
      // Parse: File { Name = "FV\FvOem.fv", Region = "REGION_OEM", Optional = TRUE }
      //
      if (SFPIsKeyword ("File")) {
        ImageDefEntry = (IMAGE_DEFINITION_ENTRY *) _malloc (sizeof (IMAGE_DEFINITION_ENTRY));
        if (ImageDefEntry == NULL) {
          Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
          ErrorCount++;
          goto Done;
        }

        memset (ImageDefEntry, 0, sizeof (IMAGE_DEFINITION_ENTRY));
        //
        // Open brace -- warning if not there
        //
        if (!SFPIsToken ("{")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected {", NULL);
          WarningCount++;
        }
        //
        // Parse: Name = "FileName.txt"
        //
        if (!SFPIsKeyword ("Name")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetQuotedString (ImageDefEntry->Name, sizeof (ImageDefEntry->Name))) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted name of file", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken (",")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following file name", NULL);
          WarningCount++;
        }
        //
        // Parse: Region = "REGION_NAME"
        //
        if (!SFPIsKeyword ("Region")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Region'", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetQuotedString (ImageDefEntry->RegionName, sizeof (ImageDefEntry->RegionName))) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Region name", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken (",")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following Region name", NULL);
          WarningCount++;
        }
        //
        // Parse optional: Subregion = "SUBREGION_NAME"
        //
        if (SFPIsKeyword ("Subregion")) {
          if (!SFPIsToken ("=")) {
            Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
            WarningCount++;
          }

          if (!SFPGetQuotedString (ImageDefEntry->SubregionName, sizeof (ImageDefEntry->SubregionName))) {
            Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Subegion name", NULL);
            ErrorCount++;
            goto Done;
          }

          if (!SFPIsToken (",")) {
            Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following Subregion name", NULL);
            WarningCount++;
          }
          //
          // For a given region, you can only place data using the region name, or the subregion names.
          // In other words, you can't say File1->Region1 and File2->Region1.Subregion1. Enforce that
          // here by checking that any previous entries with the same Region name had a Subregion specified
          // as well.
          //
          for (TempImageDefEntry = ImageDef->Entries;
               TempImageDefEntry != NULL;
               TempImageDefEntry = TempImageDefEntry->Next
              ) {
            if (strcmp (TempImageDefEntry->Name, ImageDefEntry->Name) == 0) {
              if (TempImageDefEntry->SubregionName[0] == 0) {
                Error (
                  SFPGetFileName (),
                  SFPGetLineNumber (),
                  0,
                  TempImageDefEntry->RegionName,
                  "data already placed on a region-basis in the region, can't place data using subregions"
                  );
                ErrorCount++;
              }
            }
          }
        }
        //
        // Optional parse: Optional = TRUE | FALSE
        //
        if (SFPIsKeyword ("Optional")) {
          if (!SFPIsToken ("=")) {
            Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
            WarningCount++;
          }

          if (!SFPIsKeyword ("TRUE")) {
            ImageDefEntry->Optional = 1;
          } else if (SFPIsKeyword ("FALSE")) {
            //
            // Already set to 0
            //
          } else {
            Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'TRUE' or 'FALSE'", NULL);
            ErrorCount++;
            goto Done;
          }

          SFPIsToken (",");
        }
        //
        // Closing brace
        //
        if (!SFPIsToken ("}")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '}' closing brace to File entry", NULL);
          ErrorCount++;
          goto Done;
        }
        //
        // Add the entry to the end of the list
        //
        if (ImageDef->LastEntry != NULL) {
          ImageDef->LastEntry->Next = ImageDefEntry;
        } else {
          ImageDef->Entries = ImageDefEntry;
        }

        ImageDef->LastEntry = ImageDefEntry;
      } else if (SFPIsKeyword ("RawData")) {
        //
        // Parse: RawData { Name = "PadBytes", Region = "REGION_1", Data = { 0x78, 0x56, 0x34, 0x12 }}
        //
        ImageDefEntry = (IMAGE_DEFINITION_ENTRY *) _malloc (sizeof (IMAGE_DEFINITION_ENTRY));
        if (ImageDefEntry == NULL) {
          Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
          ErrorCount++;
          goto Done;
        }

        memset (ImageDefEntry, 0, sizeof (IMAGE_DEFINITION_ENTRY));
        ImageDefEntry->IsRawData = 1;
        //
        // Open brace -- warning if not there
        //
        if (!SFPIsToken ("{")) {
          Warning (
            SFPGetFileName (),
            SFPGetLineNumber (),
            0,
            "expected '{' opening brace for RawData definition",
            NULL
            );
          WarningCount++;
        }
        //
        // Parse: Name = "PadBytes"
        //
        if (!SFPIsKeyword ("Name")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetQuotedString (ImageDefEntry->Name, sizeof (ImageDefEntry->Name))) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted name of raw data", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken (",")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following raw data name", NULL);
          WarningCount++;
        }
        //
        // Parse: Region = "REGION_NAME"
        //
        if (!SFPIsKeyword ("Region")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Region'", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPGetQuotedString (ImageDefEntry->RegionName, sizeof (ImageDefEntry->RegionName))) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Region name", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken (",")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following Region name", NULL);
          WarningCount++;
        }
        //
        // Parse optional: Subregion = "SUBREGION_NAME"
        //
        if (SFPIsKeyword ("Subregion")) {
          if (!SFPIsToken ("=")) {
            Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
            WarningCount++;
          }

          if (!SFPGetQuotedString (ImageDefEntry->SubregionName, sizeof (ImageDefEntry->SubregionName))) {
            Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Subegion name", NULL);
            ErrorCount++;
            goto Done;
          }

          if (!SFPIsToken (",")) {
            Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following Subregion name", NULL);
            WarningCount++;
          }
          //
          // For a given region, you can only place data using the region name, or the subregion names.
          // In other words, you can't say File1->Region1 and File2->Region1.Subregion1. Enforce that
          // here by checking that any previous entries with the same Region name had a Subregion specified
          // as well.
          //
          for (TempImageDefEntry = ImageDef->Entries;
               TempImageDefEntry != NULL;
               TempImageDefEntry = TempImageDefEntry->Next
              ) {
            if (strcmp (TempImageDefEntry->Name, ImageDefEntry->Name) == 0) {
              if (TempImageDefEntry->SubregionName[0] == 0) {
                Error (
                  SFPGetFileName (),
                  SFPGetLineNumber (),
                  0,
                  TempImageDefEntry->RegionName,
                  "data already placed on a region-basis in the region, can't place data using subregions"
                  );
                ErrorCount++;
              }
            }
          }
        }
        //
        // Parse: Data = { 0x78, 0x56, 0x34, 0x12 }
        //
        if (!SFPIsKeyword ("Data")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Data'", NULL);
          ErrorCount++;
          goto Done;
        }

        if (!SFPIsToken ("=")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
          WarningCount++;
        }

        if (!SFPIsToken ("{")) {
          Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '{' preceeding data list", NULL);
          WarningCount++;
        }

        if ((BufferData = CreateBufferData ()) == NULL) {
          ErrorCount++;
          goto Done;
        }
        //
        // Read bytes from input file until closing brace
        //
        while (!SFPIsToken ("}")) {
          if (!SFPGetNumber (&Num)) {
            SFPGetNextToken (Str, sizeof (Str));
            Error (SFPGetFileName (), SFPGetLineNumber (), 0, Str, "expected data value", Str);
            ErrorCount++;
            FreeBufferData (BufferData, TRUE);
            goto Done;
          } else {
            //
            // Only allow bytes
            //
            if (Num > 0xFF) {
              Error (SFPGetFileName (), SFPGetLineNumber (), 0, "only values 0-255 (0x00-0xFF) allowed", NULL);
              ErrorCount++;
              FreeBufferData (BufferData, TRUE);
              goto Done;
            }

            AddBufferDataByte (BufferData, (char) Num);
            SFPIsToken (",");
          }
        }
        //
        // Now get the data and save it in our image entry
        //
        ImageDefEntry->RawData = GetBufferData (BufferData, &ImageDefEntry->RawDataSize);
        FreeBufferData (BufferData, 0);
        //
        // Closing brace for RawData {}
        //
        if (!SFPIsToken ("}")) {
          Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '}' closing brace for RawData", NULL);
          ErrorCount++;
          goto Done;
        }
        //
        // Add the entry to the end of the list
        //
        if (ImageDef->LastEntry != NULL) {
          ImageDef->LastEntry->Next = ImageDefEntry;
        } else {
          ImageDef->Entries = ImageDefEntry;
        }

        ImageDef->LastEntry = ImageDefEntry;
      } else if (SFPIsToken ("}")) {
        //
        // Closing brace for FDImage {}
        //
        break;
      } else {
        SFPGetNextToken (Str, sizeof (Str));
        Error (SFPGetFileName (), SFPGetLineNumber (), 0, Str, "unrecognized token", Str);
        ErrorCount++;
        goto Done;
      }
    }
    //
    // Add this image definition to our global list
    //
    ImageDef->Next    = mImageDefinitions;
    mImageDefinitions = ImageDef;
  }
  //
  // Check for end-of-file
  //
  if (!SFPIsEOF ()) {
    SFPGetNextToken (Str, sizeof (Str));
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, Str, "expected end-of-file", Str);
    ErrorCount++;
  }

Done:
  SFPCloseFile ();
  if (ErrorCount != 0) {
    return STATUS_ERROR;
  } else if (WarningCount != 0) {
    return STATUS_WARNING;
  }

  return STATUS_SUCCESS;
}

static
FLASH_SUBREGION_DESCRIPTION *
ParseSubregionDefinition (
  unsigned int  SizeLeft
  )
/*++
  
Routine Description:

  Parse Subregion definitions from the input flash definition file. Format:

    Subregion {
      CreateHob       = TRUE,
      Name            = "FOO",
      Size            = 0xA000,
      Attributes      = "EFI_FLASH_AREA_SUBFV | EFI_FLASH_AREA_MEMMAPPED_FV",
      AreaType        = "EFI_FLASH_AREA_EFI_VARIABLES",
      NameGuid        = 12345678-1234-5678-AAAA-BBBBCCCCDDDD (or "EFI_SOME_GUID"),
      AreaTypeGuid    = 11111111-2222-3333-4444-1, (or "EFI_SOME_GUID") (optional)
      FileSystemGuid  = 11111111-2222-3333-4444-1, (or "EFI_SOME_GUID") (optional)
    }

    NOTE: The caller has already parsed the "Subregion" token, so start with the opening brace.

Arguments:
   
   SizeLeft   - in the flash definition file, a Region can be broken up into
                one or more subregions. As we parse the subregion definitions,
                the caller keeps track of how much space is left in the region
                that we're parsing subregions for. SizeLeft is that size, and
                so the size of the subregion we're now parsing better not
                exceed the size left.
  Returns:

    NULL    - unrecoverable errors detected while parsing the subregion definition

    pointer to a subregion definition created from the parsed subregion

--*/
{
  FLASH_SUBREGION_DESCRIPTION *Subregion;
  int                         ErrorCount;
  int                         WarningCount;
  unsigned int                Number;
  BOOLEAN                     PreviousComma;
  //
  // Allocate memory for the new subregion descriptor
  //
  ErrorCount    = 0;
  WarningCount  = 0;
  Subregion     = (FLASH_SUBREGION_DESCRIPTION *) _malloc (sizeof (FLASH_SUBREGION_DESCRIPTION));
  if (Subregion == NULL) {
    Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
    ErrorCount++;
    goto Done;
  }

  memset (Subregion, 0, sizeof (FLASH_SUBREGION_DESCRIPTION));
  //
  // Open brace -- warning if not there
  //
  if (!SFPIsToken ("{")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected {", NULL);
    WarningCount++;
  }
  //
  // Parse:  CreateHob = TRUE | FALSE,
  //
  if (!SFPIsKeyword ("CreateHob")) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'CreateHob'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken ("=")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
    WarningCount++;
  }

  if (SFPIsToken ("TRUE")) {
    Subregion->CreateHob = 1;
  } else if (SFPIsToken ("FALSE")) {
    //
    // Subregion->CreateHob = 0; -- not required since we did a memset earlier
    //
  } else {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'TRUE' or 'FALSE'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken (",")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' following CreateHob value", NULL);
    WarningCount++;
  }
  //
  // Parse:  Name = "Name",
  //
  if (!SFPIsKeyword ("Name")) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Name'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken ("=")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
    WarningCount++;
  }

  if (!SFPGetQuotedString (Subregion->Name, sizeof (Subregion->Name))) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected Subregion name", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken (",")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected comma following Region name", NULL);
    WarningCount++;
  }
  //
  // Parse: Size = 0x2000,
  //
  if (!SFPIsKeyword ("Size")) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Size'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken ("=")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
    WarningCount++;
  }

  if (!SFPGetNumber (&Subregion->Size)) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected numeric Size value", NULL);
    ErrorCount++;
    goto Done;
  }

  //
  // Check that the size does not exceed the size left passed in
  //
  if (Subregion->Size > SizeLeft) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "sum of Subregion sizes exceeds Region size", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken (",")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' following Size value", NULL);
  }
  //
  // Parse:  Attributes = Number | "String",
  //
  if (!SFPIsKeyword ("Attributes")) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'Attributes'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken ("=")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
    WarningCount++;
  }

  if (SFPGetNumber (&Number)) {
    sprintf (Subregion->Attributes, "0x%X", Number);
  } else if (!SFPGetQuotedString (Subregion->Attributes, sizeof (Subregion->Attributes))) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted Attributes string", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken (",")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ','", NULL);
  }
  //
  // Parse:  AreaType = Number | "String",
  // AreaType is a UINT8, so error if it exceeds the size
  //
  if (!SFPIsKeyword ("AreaType")) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'AreaType'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken ("=")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
    WarningCount++;
  }

  if (SFPGetNumber (&Number)) {
    if (Number > 0xFF) {
      Error (SFPGetFileName (), SFPGetLineNumber (), 0, "AreaType value exceeds 255", NULL);
      ErrorCount++;
    }

    sprintf (Subregion->AreaType, "0x%X", Number & 0x00FF);
  } else if (!SFPGetQuotedString (Subregion->AreaType, sizeof (Subregion->AreaType))) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected quoted AreaType string", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken (",")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' following AreaType value", NULL);
  }
  //
  // Parse the three GUIDs (last two are optional)
  //
  //    NameGuid        = 12345678-1234-5678-AAAA-BBBBCCCCDDDD, (or "EFI_SOME_GUID")
  //    AreaTypeGuid    = 11111111-2222-3333-4444-1, (or "EFI_SOME_GUID")
  //    FileSysteGuid   = 11111111-2222-3333-4444-1, (or "EFI_SOME_GUID")
  //
  if (!SFPIsKeyword ("NameGuid")) {
    Error (SFPGetFileName (), SFPGetLineNumber (), 0, "expected 'NameGuid'", NULL);
    ErrorCount++;
    goto Done;
  }

  if (!SFPIsToken ("=")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
    WarningCount++;
  }
  //
  // Allow a GUID or a quoted string identifier, which we'll just copy as a string
  //
  if (SFPGetQuotedString (Subregion->NameGuidString, sizeof (Subregion->NameGuidString))) {
    //
    // Nothing else to do
    //
  } else if (!SFPGetGuid (PARSE_GUID_STYLE_5_FIELDS, &Subregion->NameGuid)) {
    Error (
      SFPGetFileName (),
      SFPGetLineNumber (),
      0,
      "expected NameGuid quoted string or GUID of form 12345678-1234-1234-1234-123456789ABC",
      NULL
      );
    ErrorCount++;
    goto Done;
  }
  //
  // Comma following NameGuid is optional if they don't specify AreaTypeGuid or FileSystemGuid
  //
  PreviousComma = SFPIsToken (",");
  if (SFPIsKeyword ("AreaTypeGuid")) {
    //
    // Check for preceeding comma now
    //
    if (!PreviousComma) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' before 'AreaTypeGuid'", NULL);
      WarningCount++;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }

    if (SFPGetQuotedString (Subregion->AreaTypeGuidString, sizeof (Subregion->AreaTypeGuidString))) {
      //
      // Nothing else to do
      //
    } else if (!SFPGetGuid (PARSE_GUID_STYLE_5_FIELDS, &Subregion->AreaTypeGuid)) {
      Error (
        SFPGetFileName (),
        SFPGetLineNumber (),
        0,
        "expected AreaTypeGuid quoted string or GUID of form 12345678-1234-1234-1234-123456789ABC",
        NULL
        );
      ErrorCount++;
      goto Done;
    }

    PreviousComma = SFPIsToken (",");
  }

  if (SFPIsKeyword ("FileSystemGuid")) {
    //
    // Check for preceeding comma now
    //
    if (!PreviousComma) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected ',' before 'FileSystemGuid'", NULL);
      WarningCount++;
    }

    if (!SFPIsToken ("=")) {
      Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected '='", NULL);
      WarningCount++;
    }
    //
    // Allow a GUID or a quoted string identifier, which we'll just copy as a string
    //
    if (SFPGetQuotedString (Subregion->FileSystemGuidString, sizeof (Subregion->FileSystemGuidString))) {
      //
      // Nothing else to do
      //
    } else if (!SFPGetGuid (PARSE_GUID_STYLE_5_FIELDS, &Subregion->FileSystemGuid)) {
      Error (
        SFPGetFileName (),
        SFPGetLineNumber (),
        0,
        "expected FileSystemGuid quoted string or GUID of form 12345678-1234-1234-1234-123456789ABC",
        NULL
        );
      ErrorCount++;
      goto Done;
    }

    SFPIsToken (",");
  }
  //
  // Look for subregion closing brace
  //
  if (!SFPIsToken ("}")) {
    Warning (SFPGetFileName (), SFPGetLineNumber (), 0, "expected Subregion closing brace '}'", NULL);
    WarningCount++;
  }

Done:
  //
  // If any errors were encountered, then delete the subregion definition
  //
  if (ErrorCount != 0) {
    _free (Subregion);
    Subregion = NULL;
  }

  return Subregion;
}

STATUS
FDFCreateCIncludeFile (
  char      *FlashDeviceName,
  char      *FileName
  )
/*++

Routine Description:
  Create a header file with #define definitions per an already-parsed
  flash definition file.

Arguments:
  FlashDeviceName - name of flash device (from the flash definition file)
                    to use
  FileName        - name of output file to create

Returns:
  STATUS_SUCCESS    - no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered
  STATUS_ERROR      - errors were encountered

--*/
{
  FILE                        *OutFptr;
  FLASH_BLOCK_DESCRIPTION     *FBlock;
  FLASH_DEVICE_DESCRIPTION    *FDev;
  FLASH_SUBREGION_DESCRIPTION *Subregion;
  unsigned int                Offset;
  unsigned int                SubregionOffset;
  int                         CreateHobs;
  //
  // Find the definition we're supposed to use
  //
  for (FDev = mFlashDevices; FDev != NULL; FDev = FDev->Next) {
    if (strcmp (FDev->Name, FlashDeviceName) == 0) {
      break;
    }
  }

  if (FDev == NULL) {
    Error (NULL, 0, 0, NULL, FlashDeviceName, "flash device not found in flash definitions");
    return STATUS_ERROR;
  }

  if ((OutFptr = fopen (FileName, "w")) == NULL) {
    Error (NULL, 0, 0, FileName, "failed to open output file for writing");
    return STATUS_ERROR;
  }
  //
  // Write a header
  //
  fprintf (OutFptr, CIncludeHeader);
  //
  // Write flash block base and size defines
  //
  fprintf (OutFptr, "#define FLASH_BASE                                          0x%08X\n", FDev->BaseAddress);
  fprintf (OutFptr, "#define FLASH_SIZE                                          0x%08X\n\n", FDev->Size);
  //
  // Write flash regions base, size and offset defines
  //
  Offset      = 0;
  CreateHobs  = 0;
  for (FBlock = FDev->Regions; FBlock != NULL; FBlock = FBlock->Next) {
    fprintf (
      OutFptr,
      "#define FLASH_REGION_%s_BASE              %*c0x%08X\n",
      FBlock->Name,
      COLUMN2_START - 40 - strlen (FBlock->Name),
      ' ',
      Offset + FDev->BaseAddress
      );
    fprintf (
      OutFptr,
      "#define FLASH_REGION_%s_SIZE              %*c0x%08X\n",
      FBlock->Name,
      COLUMN2_START - 40 - strlen (FBlock->Name),
      ' ',
      FBlock->Size
      );
    fprintf (
      OutFptr,
      "#define FLASH_REGION_%s_OFFSET            %*c0x%08X\n",
      FBlock->Name,
      COLUMN2_START - 40 - strlen (FBlock->Name),
      ' ',
      Offset
      );
    //
    // Create defines for any subregions
    //
    SubregionOffset = 0;
    for (Subregion = FBlock->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
      fprintf (
        OutFptr,
        "#define FLASH_REGION_%s_SUBREGION_%s_BASE     %*c0x%08X\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 43 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        FDev->BaseAddress + Offset + SubregionOffset
        );
      fprintf (
        OutFptr,
        "#define FLASH_REGION_%s_SUBREGION_%s_SIZE     %*c0x%08X\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 43 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        Subregion->Size
        );
      fprintf (
        OutFptr,
        "#define FLASH_REGION_%s_SUBREGION_%s_OFFSET   %*c0x%08X\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 43 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        Offset + SubregionOffset
        );
      SubregionOffset += Subregion->Size;
      if (Subregion->CreateHob != 0) {
        CreateHobs = 1;
      }
    }

    Offset += FBlock->Size;
  }
  //
  // Now create a #define for the flash map data definition
  //
  fprintf (OutFptr, "\n\n#define EFI_FLASH_AREA_DATA_DEFINITION \\\n");
  //
  // Emit entry for each region
  //
  Offset = 0;
  for (FBlock = FDev->Regions; FBlock != NULL; FBlock = FBlock->Next) {
    fprintf (OutFptr, "  /* %s region */\\\n", FBlock->Name);
    fprintf (OutFptr, "  {\\\n");
    fprintf (OutFptr, "    FLASH_REGION_%s_BASE,\\\n", FBlock->Name);
    fprintf (OutFptr, "    FLASH_REGION_%s_SIZE,\\\n", FBlock->Name);
    fprintf (OutFptr, "    %s,\\\n", FBlock->Attributes);
    fprintf (OutFptr, "    %s,\\\n", FBlock->AreaType);
    fprintf (OutFptr, "    0, 0, 0,\\\n");
    //
    // The AreaTypeGuid may have been specified in the input flash definition file as a GUID, or
    // as a quoted string. Do the right one.
    //
    if (FBlock->AreaTypeGuidString[0] != 0) {
      fprintf (OutFptr, "    %s, \\\n", FBlock->AreaTypeGuidString);
    } else {
      fprintf (
        OutFptr,
        "    { 0x%08X, 0x%04X, 0x%04X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X },\\\n",
        FBlock->AreaTypeGuid.Data1,
        (unsigned int) FBlock->AreaTypeGuid.Data2,
        (unsigned int) FBlock->AreaTypeGuid.Data3,
        (unsigned int) FBlock->AreaTypeGuid.Data4[0],
        (unsigned int) FBlock->AreaTypeGuid.Data4[1],
        (unsigned int) FBlock->AreaTypeGuid.Data4[2],
        (unsigned int) FBlock->AreaTypeGuid.Data4[3],
        (unsigned int) FBlock->AreaTypeGuid.Data4[4],
        (unsigned int) FBlock->AreaTypeGuid.Data4[5],
        (unsigned int) FBlock->AreaTypeGuid.Data4[6],
        (unsigned int) FBlock->AreaTypeGuid.Data4[7]
        );
    }
    fprintf (OutFptr, "  },\\\n");
  }

  fprintf (OutFptr, "\n\n");
  //
  // Now walk the list again to create the EFI_HOB_FLASH_MAP_ENTRY_TYPE definition
  //
  if (CreateHobs != 0) {
    fprintf (OutFptr, "//\n// EFI_HOB_FLASH_MAP_ENTRY_TYPE definition\n//\n");
    fprintf (OutFptr, "#define EFI_HOB_FLASH_MAP_ENTRY_TYPE_DATA_DEFINITION");
    for (FBlock = FDev->Regions; FBlock != NULL; FBlock = FBlock->Next) {
      //
      // See if the block has subregions, and that the CreateHobs flag is set
      // for any of them.
      //
      CreateHobs = 0;
      for (Subregion = FBlock->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
        if (Subregion->CreateHob != 0) {
          CreateHobs = 1;
          break;
        }
      }
      //
      // If any of the subregions had the CreateHobs flag set, then create the entries in the
      // output file
      //
      if (CreateHobs != 0) {
        for (Subregion = FBlock->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
          if (Subregion->CreateHob != 0) {
            fprintf (OutFptr, " \\\n");
            fprintf (OutFptr, "  /* %s.%s Subregion */\\\n", FBlock->Name, Subregion->Name);
            fprintf (OutFptr, "  {\\\n");
            fprintf (OutFptr, "    {EFI_HOB_TYPE_GUID_EXTENSION,\\\n");
            fprintf (OutFptr, "    sizeof (EFI_HOB_FLASH_MAP_ENTRY_TYPE ),\\\n");
            fprintf (OutFptr, "    0},\\\n");
            //
            // The NameGuid may have been specified in the input flash definition file as a GUID, or
            // as a quoted string. Do the right one.
            //
            if (Subregion->NameGuidString[0] != 0) {
              fprintf (OutFptr, "    %s, \\\n", Subregion->NameGuidString);
            } else {
              fprintf (
                OutFptr,
                "    { 0x%08X, 0x%04X, 0x%04X, {0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}},\\\n",
                Subregion->NameGuid.Data1,
                (unsigned int) Subregion->NameGuid.Data2,
                (unsigned int) Subregion->NameGuid.Data3,
                (unsigned int) Subregion->NameGuid.Data4[0],
                (unsigned int) Subregion->NameGuid.Data4[1],
                (unsigned int) Subregion->NameGuid.Data4[2],
                (unsigned int) Subregion->NameGuid.Data4[3],
                (unsigned int) Subregion->NameGuid.Data4[4],
                (unsigned int) Subregion->NameGuid.Data4[5],
                (unsigned int) Subregion->NameGuid.Data4[6],
                (unsigned int) Subregion->NameGuid.Data4[7]
                );
            }

            fprintf (OutFptr, "    {0, 0, 0},\\\n");
            fprintf (OutFptr, "    %s,\\\n", Subregion->AreaType);
            //
            // The AreaTypeGuid may have been specified in the input flash definition file as a GUID, or
            // as a quoted string. Do the right one.
            //
            if (Subregion->AreaTypeGuidString[0] != 0) {
              fprintf (OutFptr, "    %s, \\\n", Subregion->AreaTypeGuidString);
            } else {
              fprintf (
                OutFptr,
                "    { 0x%08X, 0x%04X, 0x%04X, {0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}},\\\n",
                Subregion->AreaTypeGuid.Data1,
                (unsigned int) Subregion->AreaTypeGuid.Data2,
                (unsigned int) Subregion->AreaTypeGuid.Data3,
                (unsigned int) Subregion->AreaTypeGuid.Data4[0],
                (unsigned int) Subregion->AreaTypeGuid.Data4[1],
                (unsigned int) Subregion->AreaTypeGuid.Data4[2],
                (unsigned int) Subregion->AreaTypeGuid.Data4[3],
                (unsigned int) Subregion->AreaTypeGuid.Data4[4],
                (unsigned int) Subregion->AreaTypeGuid.Data4[5],
                (unsigned int) Subregion->AreaTypeGuid.Data4[6],
                (unsigned int) Subregion->AreaTypeGuid.Data4[7]
                );
            }

            fprintf (OutFptr, "    1,\\\n");
            fprintf (OutFptr, "    {{\\\n");
            fprintf (OutFptr, "      %s,\\\n", Subregion->Attributes);
            fprintf (OutFptr, "      0,\\\n");
            fprintf (OutFptr, "      FLASH_REGION_%s_SUBREGION_%s_BASE,\\\n", FBlock->Name, Subregion->Name);
            fprintf (OutFptr, "      FLASH_REGION_%s_SUBREGION_%s_SIZE,\\\n", FBlock->Name, Subregion->Name);
            //
            // The FileSystemGuid may have been specified in the input flash definition file as a GUID, or
            // as a quoted string. Do the right one.
            //
            if (Subregion->FileSystemGuidString[0] != 0) {
              fprintf (OutFptr, "      %s, \\\n", Subregion->FileSystemGuidString);
            } else {
              fprintf (
                OutFptr,
                "      { 0x%08X, 0x%04X, 0x%04X, {0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}},\\\n",
                Subregion->FileSystemGuid.Data1,
                (unsigned int) Subregion->FileSystemGuid.Data2,
                (unsigned int) Subregion->FileSystemGuid.Data3,
                (unsigned int) Subregion->FileSystemGuid.Data4[0],
                (unsigned int) Subregion->FileSystemGuid.Data4[1],
                (unsigned int) Subregion->FileSystemGuid.Data4[2],
                (unsigned int) Subregion->FileSystemGuid.Data4[3],
                (unsigned int) Subregion->FileSystemGuid.Data4[4],
                (unsigned int) Subregion->FileSystemGuid.Data4[5],
                (unsigned int) Subregion->FileSystemGuid.Data4[6],
                (unsigned int) Subregion->FileSystemGuid.Data4[7]
                );
            }

            fprintf (OutFptr, "    }},\\\n");
            fprintf (OutFptr, "  },");
          }
        }
      }
    }

    fprintf (OutFptr, "\n\n");
  }

  //
  // Write the file's closing #endif
  //
  fprintf (OutFptr, CIncludeFooter);
  fclose (OutFptr);
  return STATUS_SUCCESS;
}

STATUS
FDFCreateAsmIncludeFile (
  char      *FlashDeviceName,
  char      *FileName
  )
/*++

Routine Description:
  Create an assembly header file with equate definitions per an already-parsed
  flash definition file.

Arguments:
  FlashDeviceName - name of flash device (from the flash definition file)
                    to use
  FileName        - name of output file to create

Returns:
  STATUS_SUCCESS    - no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered
  STATUS_ERROR      - errors were encountered

--*/
{
  FILE                        *OutFptr;
  FLASH_BLOCK_DESCRIPTION     *FBlock;
  FLASH_DEVICE_DESCRIPTION    *FDev;
  unsigned int                Offset;
  FLASH_SUBREGION_DESCRIPTION *Subregion;
  unsigned int                SubregionOffset;
  //
  // Find the definition we're supposed to use
  //
  for (FDev = mFlashDevices; FDev != NULL; FDev = FDev->Next) {
    if (strcmp (FDev->Name, FlashDeviceName) == 0) {
      break;
    }
  }

  if (FDev == NULL) {
    Error (NULL, 0, 0, NULL, FlashDeviceName, "flash device not found in flash definitions");
    return STATUS_ERROR;
  }

  if ((OutFptr = fopen (FileName, "w")) == NULL) {
    Error (NULL, 0, 0, FileName, "failed to open output file for writing");
    return STATUS_ERROR;
  }
  //
  // Write a header
  //
  fprintf (OutFptr, "\n\n");
  //
  // Write flash block size and offset defines
  //
  fprintf (
    OutFptr,
    "FLASH_BASE                               %*cequ 0%08Xh\n",
    COLUMN2_START - 40,
    ' ',
    FDev->BaseAddress
    );
  fprintf (OutFptr, "FLASH_SIZE                               %*cequ 0%08Xh\n", COLUMN2_START - 40, ' ', FDev->Size);
  //
  // Write flash region size and offset defines
  //
  fprintf (OutFptr, "\n");
  Offset = 0;
  for (FBlock = FDev->Regions; FBlock != NULL; FBlock = FBlock->Next) {
    fprintf (
      OutFptr,
      "FLASH_REGION_%s_BASE   %*cequ 0%08Xh\n",
      FBlock->Name,
      COLUMN2_START - 20 - strlen (FBlock->Name),
      ' ',
      FDev->BaseAddress + Offset
      );
    fprintf (
      OutFptr,
      "FLASH_REGION_%s_SIZE   %*cequ 0%08Xh\n",
      FBlock->Name,
      COLUMN2_START - 20 - strlen (FBlock->Name),
      ' ',
      FBlock->Size
      );
    fprintf (
      OutFptr,
      "FLASH_REGION_%s_OFFSET %*cequ 0%08Xh\n",
      FBlock->Name,
      COLUMN2_START - 20 - strlen (FBlock->Name),
      ' ',
      Offset
      );
    //
    // Create defines for any subregions
    //
    SubregionOffset = 0;
    for (Subregion = FBlock->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
      fprintf (
        OutFptr,
        "FLASH_REGION_%s_SUBREGION_%s_BASE     %*cequ 0%08Xh\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 39 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        FDev->BaseAddress + Offset + SubregionOffset
        );
      fprintf (
        OutFptr,
        "FLASH_REGION_%s_SUBREGION_%s_SIZE     %*cequ 0%08Xh\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 39 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        Subregion->Size
        );
      fprintf (
        OutFptr,
        "FLASH_REGION_%s_SUBREGION_%s_OFFSET   %*cequ 0%08Xh\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 39 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        Offset + SubregionOffset
        );
      SubregionOffset += Subregion->Size;
    }

    Offset += FBlock->Size;
  }

  //
  // Write closing \n
  //
  fprintf (OutFptr, "\n\n");
  fclose (OutFptr);
  return STATUS_SUCCESS;
}

STATUS
FDFCreateSymbols (
  char      *FlashDeviceName
  )
/*++

Routine Description:
  Using the given flash device name, add symbols to the global symbol table. This
  allows other functions to use the symbol definitions for other purposes.

Arguments:
  FlashDeviceName - name of flash device (from the flash definition file)
                    to use

Returns:
  STATUS_SUCCESS    - no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered
  STATUS_ERROR      - errors were encountered

--*/
{
  FLASH_BLOCK_DESCRIPTION     *FBlock;
  FLASH_DEVICE_DESCRIPTION    *FDev;
  unsigned int                Offset;
  char                        SymName[120];
  char                        SymValue[120];
  FLASH_SUBREGION_DESCRIPTION *Subregion;
  unsigned int                SubregionOffset;
  //
  // Find the definition we're supposed to use
  //
  for (FDev = mFlashDevices; FDev != NULL; FDev = FDev->Next) {
    if (strcmp (FDev->Name, FlashDeviceName) == 0) {
      break;
    }
  }

  if (FDev == NULL) {
    Error (NULL, 0, 0, NULL, FlashDeviceName, "flash device not found in flash definitions");
    return STATUS_ERROR;
  }

  sprintf (SymValue, "0x%08X", FDev->BaseAddress);
  SymbolAdd ("FLASH_BASE", SymValue, 0);
  sprintf (SymValue, "0x%08X", FDev->Size);
  SymbolAdd ("FLASH_SIZE", SymValue, 0);
  //
  // Add flash block size and offset defines
  //
  // Offset = 0;
  // for (FBlock = FDev->PBlocks; FBlock != NULL; FBlock = FBlock->Next) {
  //  sprintf (SymName, "FLASH_BLOCK_%s_BASE", FBlock->Name);
  //  sprintf (SymValue, "0x%08X", FDev->BaseAddress + Offset);
  //  SymbolAdd (SymName, SymValue, 0);
  //  sprintf (SymName, "FLASH_BLOCK_%s_SIZE", FBlock->Name);
  //  sprintf (SymValue, "0x%08X", FBlock->Size);
  //  SymbolAdd (SymName, SymValue, 0);
  //  sprintf (SymName, "FLASH_BLOCK_%s_OFFSET", FBlock->Name);
  //  sprintf (SymValue, "0x%08X", Offset);
  //  SymbolAdd (SymName, SymValue, 0);
  //  Offset += FBlock->Size;
  // }
  //
  // Add flash region block base, size, and offset defines
  //
  Offset = 0;
  for (FBlock = FDev->Regions; FBlock != NULL; FBlock = FBlock->Next) {
    sprintf (SymName, "FLASH_REGION_%s_BASE", FBlock->Name);
    sprintf (SymValue, "0x%08X", FDev->BaseAddress + Offset);
    SymbolAdd (SymName, SymValue, 0);
    sprintf (SymName, "FLASH_REGION_%s_SIZE", FBlock->Name);
    sprintf (SymValue, "0x%08X", FBlock->Size);
    SymbolAdd (SymName, SymValue, 0);
    sprintf (SymName, "FLASH_REGION_%s_OFFSET", FBlock->Name);
    sprintf (SymValue, "0x%08X", Offset);
    SymbolAdd (SymName, SymValue, 0);
    //
    // Add subregion symbols
    //
    if (FBlock->Subregions != NULL) {
      SubregionOffset = 0;
      for (Subregion = FBlock->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
        sprintf (SymName, "FLASH_REGION_%s_SUBREGION_%s_BASE", FBlock->Name, Subregion->Name);
        sprintf (SymValue, "0x%08X", FDev->BaseAddress + Offset + SubregionOffset);
        SymbolAdd (SymName, SymValue, 0);
        sprintf (SymName, "FLASH_REGION_%s_SUBREGION_%s_SIZE", FBlock->Name, Subregion->Name);
        sprintf (SymValue, "0x%08X", Subregion->Size);
        SymbolAdd (SymName, SymValue, 0);
        sprintf (SymName, "FLASH_REGION_%s_SUBREGION_%s_OFFSET", FBlock->Name, Subregion->Name);
        sprintf (SymValue, "0x%08X", Offset + SubregionOffset);
        SymbolAdd (SymName, SymValue, 0);
        SubregionOffset += Subregion->Size;
      }
    }

    Offset += FBlock->Size;
  }

  return STATUS_SUCCESS;
}

STATUS
FDFCreateImage (
  char      *FlashDeviceName,
  char      *ImageName,
  char      *FileName
  )
/*++

Routine Description:
  Create a flash image using the given device and image names.

Arguments:
  FlashDeviceName - name of flash device (from the flash definition file)
                    to use
  ImageName       - name of image (from the flash definition file) to create
  FileName        - name of output file to create

Returns:
  STATUS_SUCCESS    - no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered
  STATUS_ERROR      - errors were encountered

--*/
{
  STATUS                      Status;
  FILE                        *OutFptr;
  FLASH_BLOCK_DESCRIPTION     *RegionDef;
  FLASH_DEVICE_DESCRIPTION    *FDev;
  IMAGE_DEFINITION            *ImageDef;
  unsigned int                Offset;
  char                        *Buffer;
  FILE                        *InFptr;
  long                        FileSize;
  IMAGE_DEFINITION_ENTRY      *IDefEntry;
  FLASH_SUBREGION_DESCRIPTION *SubregionDef;
  //
  // Find the flash definition we're supposed to use
  //
  InFptr  = NULL;
  Status  = STATUS_ERROR;
  for (FDev = mFlashDevices; FDev != NULL; FDev = FDev->Next) {
    if (strcmp (FDev->Name, FlashDeviceName) == 0) {
      break;
    }
  }

  if (FDev == NULL) {
    Error (NULL, 0, 0, FlashDeviceName, "flash device not found in flash definitions");
    return STATUS_ERROR;
  }
  //
  // Find the image name we're supposed to create
  //
  for (ImageDef = mImageDefinitions; ImageDef != NULL; ImageDef = ImageDef->Next) {
    if (strcmp (ImageDef->Name, ImageName) == 0) {
      break;
    }
  }

  if (ImageDef == NULL) {
    Error (NULL, 0, 0, ImageName, "image definition not found in image definitions");
    return STATUS_ERROR;
  }
  //
  // Open the output file
  //
  if ((OutFptr = fopen (FileName, "wb")) == NULL) {
    Error (NULL, 0, 0, FileName, "failed to open output file for writing");
    return STATUS_ERROR;
  }
  //
  // Allocate a buffer to copy the input data to
  //
  Buffer = (char *) _malloc (FDev->Size);
  if (Buffer == NULL) {
    Error (NULL, 0, 0, (INT8 *) "failed to allocate memory", NULL);
    goto Done;
  }
  //
  // Set contents of buffer to the erased value
  //
  if (FDev->ErasePolarity) {
    memset (Buffer, 0xFF, FDev->Size);
  } else {
    memset (Buffer, 0, FDev->Size);
  }
  //
  // Set all region and subregion size-left fields to the size of the region/subregion
  //
  for (RegionDef = FDev->Regions; RegionDef != NULL; RegionDef = RegionDef->Next) {
    RegionDef->SizeLeft = RegionDef->Size;
    for (SubregionDef = RegionDef->Subregions; SubregionDef != NULL; SubregionDef = SubregionDef->Next) {
      SubregionDef->SizeLeft = SubregionDef->Size;
    }
  }
  //
  // Now go through the image list, read files into the buffer.
  //
  for (IDefEntry = ImageDef->Entries; IDefEntry != NULL; IDefEntry = IDefEntry->Next) {
    //
    // If it's a file name, open the file, get the size, find the corresponding
    // flash region it's in, and copy the data.
    //
    if (IDefEntry->IsRawData == 0) {
      if ((InFptr = fopen (IDefEntry->Name, "rb")) == NULL) {
        Error (NULL, 0, 0, IDefEntry->Name, "failed to open input file for reading");
        goto Done;
      }

      fseek (InFptr, 0, SEEK_END);
      FileSize = ftell (InFptr);
      fseek (InFptr, 0, SEEK_SET);
    } else {
      FileSize = IDefEntry->RawDataSize;
    }
    //
    // Find the region/subregion it's in, see if we have space left
    //
    Offset = 0;
    for (RegionDef = FDev->Regions; RegionDef != NULL; RegionDef = RegionDef->Next) {
      if (strcmp (RegionDef->Name, IDefEntry->RegionName) == 0) {
        break;
      }

      Offset += RegionDef->Size;
    }

    if (RegionDef == NULL) {
      Error (NULL, 0, 0, IDefEntry->RegionName, "Region name not found in FlashDevice %s definition", FDev->Name);
      goto Done;
    }

    //
    // Check for subregion
    //
    if (IDefEntry->SubregionName[0] != 0) {
      for (SubregionDef = RegionDef->Subregions; SubregionDef != NULL; SubregionDef = SubregionDef->Next) {
        if (strcmp (SubregionDef->Name, IDefEntry->SubregionName) == 0) {
          break;
        }

        Offset += SubregionDef->Size;
      }

      if (SubregionDef == NULL) {
        Error (
          NULL,
          0,
          0,
          IDefEntry->SubregionName,
          "Subregion name not found in FlashDevice %s.%s Region definition",
          FDev->Name,
          RegionDef->Name
          );
        goto Done;
      }
      //
      // Enough space in the subregion?
      //
      if (SubregionDef->SizeLeft < (unsigned int) FileSize) {
        Error (
          NULL,
          0,
          0,
          IDefEntry->Name,
          "insufficient space in Subregion (at least 0x%X additional bytes required)",
          FileSize - SubregionDef->SizeLeft
          );
        goto Done;
      }

      //
      // Read the file into the buffer if it's a file. Otherwise copy the raw data
      //
      if (IDefEntry->IsRawData == 0) {
        if (fread (Buffer + Offset + (SubregionDef->Size - SubregionDef->SizeLeft), FileSize, 1, InFptr) != 1) {
          Error (NULL, 0, 0, IDefEntry->Name, "failed to read file contents");
          goto Done;
        }

        fclose (InFptr);
        InFptr = NULL;
      } else {
        memcpy (
          Buffer + Offset + (SubregionDef->Size - SubregionDef->SizeLeft),
          IDefEntry->RawData,
          IDefEntry->RawDataSize
          );
      }

      SubregionDef->SizeLeft -= FileSize;
      //
      // Align based on the Region alignment requirements.
      //
      if (RegionDef->Alignment != 0) {
        while (((unsigned int) (SubregionDef->Size - SubregionDef->SizeLeft) &~RegionDef->Alignment) != 0) {
          if (SubregionDef->SizeLeft == 0) {
            break;
          }

          SubregionDef->SizeLeft--;
        }
      }
    } else {
      //
      // Placing data in a region. Check for enough space in the region left.
      //
      if (RegionDef->SizeLeft < (unsigned int) FileSize) {
        Error (
          NULL,
          0,
          0,
          IDefEntry->Name,
          "insufficient space in Region (at least 0x%X additional bytes required)",
          FileSize - RegionDef->SizeLeft
          );
        goto Done;
      }

      //
      // Read the file into the buffer if it's a file. Otherwise copy the raw data
      //
      if (IDefEntry->IsRawData == 0) {
        if (fread (Buffer + Offset + (RegionDef->Size - RegionDef->SizeLeft), FileSize, 1, InFptr) != 1) {
          Error (NULL, 0, 0, IDefEntry->Name, "failed to read file contents");
          goto Done;
        }

        fclose (InFptr);
        InFptr = NULL;
      } else {
        memcpy (Buffer + Offset + (RegionDef->Size - RegionDef->SizeLeft), IDefEntry->RawData, IDefEntry->RawDataSize);
      }

      RegionDef->SizeLeft -= FileSize;
      //
      // Align
      //
      if (RegionDef->Alignment != 0) {
        while (((unsigned int) (RegionDef->Size - RegionDef->SizeLeft) &~RegionDef->Alignment) != 0) {
          if (RegionDef->SizeLeft == 0) {
            break;
          }

          RegionDef->SizeLeft--;
        }
      }
    }
  }

  if (fwrite (Buffer, FDev->Size, 1, OutFptr) != 1) {
    Error (NULL, 0, 0, "failed to write buffer contents to output file", NULL);
    goto Done;
  }

  Status = STATUS_SUCCESS;
Done:
  if (InFptr != NULL) {
    fclose (InFptr);
  }

  if (Buffer != NULL) {
    _free (Buffer);
  }

  if (OutFptr != NULL) {
    fclose (OutFptr);
    if (Status == STATUS_ERROR) {
      remove (FileName);
    }
  }

  return Status;
}

STATUS
FDFCreateDscFile (
  char      *FlashDeviceName,
  char      *FileName
  )
/*++

Routine Description:
  Create a DSC-style output file with equates for flash management.

Arguments:
  FlashDeviceName - name of flash device (from the flash definition file)
                    to use
  FileName        - name of output file to create

Returns:
  STATUS_SUCCESS    - no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered
  STATUS_ERROR      - errors were encountered

--*/
{
  FILE                        *OutFptr;
  FLASH_BLOCK_DESCRIPTION     *FBlock;
  FLASH_DEVICE_DESCRIPTION    *FDev;
  unsigned int                Offset;
  FLASH_SUBREGION_DESCRIPTION *Subregion;
  unsigned int                SubregionOffset;
  //
  // Find the definition we're supposed to use
  //
  for (FDev = mFlashDevices; FDev != NULL; FDev = FDev->Next) {
    if (strcmp (FDev->Name, FlashDeviceName) == 0) {
      break;
    }
  }

  if (FDev == NULL) {
    Error (NULL, 0, 0, FlashDeviceName, "flash device not found in flash definitions");
    return STATUS_ERROR;
  }

  if ((OutFptr = fopen (FileName, "w")) == NULL) {
    Error (NULL, 0, 0, FileName, "failed to open output file for writing");
    return STATUS_ERROR;
  }
  //
  // Write the flash base address and size
  //
  fprintf (OutFptr, "\n");
  fprintf (OutFptr, "FLASH_BASE                                      = 0x%08X\n", FDev->BaseAddress);
  fprintf (OutFptr, "FLASH_SIZE                                      = 0x%08X\n\n", FDev->Size);
  //
  // Write flash block size and offset defines
  //
  Offset = 0;
  for (FBlock = FDev->Regions; FBlock != NULL; FBlock = FBlock->Next) {
    fprintf (
      OutFptr,
      "FLASH_REGION_%s_BASE          %*c= 0x%08X\n",
      FBlock->Name,
      COLUMN2_START - 40 - strlen (FBlock->Name),
      ' ',
      Offset + FDev->BaseAddress
      );
    fprintf (
      OutFptr,
      "FLASH_REGION_%s_SIZE          %*c= 0x%08X\n",
      FBlock->Name,
      COLUMN2_START - 40 - strlen (FBlock->Name),
      ' ',
      FBlock->Size
      );
    fprintf (
      OutFptr,
      "FLASH_REGION_%s_SIZE_BLOCKS   %*c= 0x%x\n",
      FBlock->Name,
      COLUMN2_START - 40 - strlen (FBlock->Name),
      ' ',
      (FBlock->Size)/(FDev->PBlocks->Size)
      );      
    //
    // Create defines for any subregions
    //
    SubregionOffset = 0;
    for (Subregion = FBlock->Subregions; Subregion != NULL; Subregion = Subregion->Next) {
      fprintf (
        OutFptr,
        "FLASH_REGION_%s_SUBREGION_%s_BASE     %*c= 0x%08X\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 48 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        FDev->BaseAddress + Offset + SubregionOffset
        );
      fprintf (
        OutFptr,
        "FLASH_REGION_%s_SUBREGION_%s_SIZE     %*c= 0x%08X\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 48 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        Subregion->Size
        );
      fprintf (
        OutFptr,
        "FLASH_REGION_%s_SUBREGION_%s_OFFSET   %*c= 0x%08X\n",
        FBlock->Name,
        Subregion->Name,
        COLUMN3_START - 48 - strlen (FBlock->Name) - strlen (Subregion->Name),
        ' ',
        Offset + SubregionOffset
        );

      SubregionOffset += Subregion->Size;
    }

    Offset += FBlock->Size;
  }
  //
  // Close file
  //
  fprintf (OutFptr, "\n");
  fclose (OutFptr);
  return STATUS_SUCCESS;
}


/*++

Routine Description:
  The following buffer management routines are used to encapsulate functionality
  for managing a growable data buffer.

Arguments:
  BUFFER_DATA     - structure that is used to maintain a data buffer

Returns:
  NA

--*/
static
BUFFER_DATA *
CreateBufferData (
  VOID
  )
/*++

Routine Description:

  Create a growable data buffer with default buffer length.

Arguments:

  None

Returns:

  NULL          - error occured during data buffer creation
  Not NULL      - the pointer to the newly created data buffer

--*/
{
  BUFFER_DATA *BD;
  BD = (BUFFER_DATA *) _malloc (sizeof (BUFFER_DATA));
  if (BD == NULL) {
    Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
    return NULL;
  }

  memset (BD, 0, sizeof (BUFFER_DATA));
  BD->BufferStart = (char *) _malloc (BUFFER_SIZE);
  if (BD->BufferStart == NULL) {
    _free (BD);
    Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
    return NULL;
  }

  BD->BufferEnd = BD->BufferStart + BUFFER_SIZE;
  BD->BufferPos = BD->BufferStart;
  return BD;
}

static
BOOLEAN
AddBufferDataByte (
  BUFFER_DATA *Buffer,
  char        Data
  )
/*++

Routine Description:

  Add a single byte to a growable data buffer, growing the buffer if required.

Arguments:

  Buffer  - pointer to the growable data buffer to add a single byte to
  Data    - value of the single byte data to be added

Returns:

  TRUE    - the single byte data was successfully added
  FALSE   - error occurred, the single byte data was not added

--*/
{
  int   Size;
  char  *NewBuffer;
  //
  // See if we have to grow the buffer
  //
  if (Buffer->BufferPos >= Buffer->BufferEnd) {
    Size      = (int) Buffer->BufferEnd - (int) Buffer->BufferStart;
    NewBuffer = (char *) _malloc (Size + BUFFER_SIZE);
    if (NewBuffer == NULL) {
      Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
      return FALSE;
    }

    memcpy (NewBuffer, Buffer->BufferStart, Size);
    _free (Buffer->BufferStart);
    Buffer->BufferStart = NewBuffer;
    Buffer->BufferPos   = Buffer->BufferStart + Size;
    Buffer->BufferEnd   = Buffer->BufferStart + Size + BUFFER_SIZE;
  }

  *Buffer->BufferPos = Data;
  Buffer->BufferPos++;
  return TRUE;
}

static
void
FreeBufferData (
  BUFFER_DATA *Buffer,
  BOOLEAN     FreeData
  )
/*++

Routine Description:

  Free memory used to manage a growable data buffer.

Arguments:

  Buffer    - pointer to the growable data buffer to be destructed
  FreeData  - TRUE, free memory containing the buffered data
              FALSE, do not free the buffered data memory

Returns:

  None

--*/
{
  if (Buffer != NULL) {
    if (FreeData && (Buffer->BufferStart != NULL)) {
      _free (Buffer->BufferStart);
    }

    _free (Buffer);
  }
}

static
char *
GetBufferData (
  BUFFER_DATA *Buffer,
  int         *BufferSize
  )
/*++

Routine Description:

  Return a pointer and size of the data in a growable data buffer.

Arguments:

  Buffer      - pointer to the growable data buffer
  BufferSize  - size of the data in the growable data buffer

Returns:

  pointer of the data in the growable data buffer

--*/
{
  *BufferSize = (int) Buffer->BufferPos - (int) Buffer->BufferStart;
  return Buffer->BufferStart;
}

STATUS
FDDiscover (
  char          *FDFileName,
  unsigned int  BaseAddr
  )
/*++

Routine Description:
  Walk a binary image and see if you find anything that looks like a
  firmware volume.

Arguments:
  FDFileName        - name of input FD image to parse
  BaseAddr          - base address of input FD image

Returns:
  STATUS_SUCCESS    - no errors or warnings
  STATUS_WARNING    - warnings, but no errors, were encountered
  STATUS_ERROR      - errors were encountered

NOTE:
  This routine is used for debug purposes only.

--*/
{
  FILE                        *InFptr;
  long                        FileSize;
  long                        Offset;
  EFI_FIRMWARE_VOLUME_HEADER  FVHeader;
  EFI_GUID
    FileSystemGuid = { 0x7A9354D9, 0x0468, 0x444a, 0x81, 0xCE, 0x0B, 0xF6, 0x17, 0xD8, 0x90, 0xDF };

  if ((InFptr = fopen (FDFileName, "rb")) == NULL) {
    Error (NULL, 0, 0, FDFileName, "failed to open file for reading");
    return STATUS_ERROR;
  }

  fseek (InFptr, 0, SEEK_END);
  FileSize = ftell (InFptr);
  fseek (InFptr, 0, SEEK_SET);
  Offset = 0;
  while (Offset < FileSize) {
    fseek (InFptr, Offset, SEEK_SET);
    //
    // Read the contents of the file, see if it's an FV header
    //
    if (fread (&FVHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER), 1, InFptr) == 1) {
      //
      // Check version and GUID
      //
      if ((FVHeader.Revision == EFI_FVH_REVISION) && (FVHeader.Signature == EFI_FVH_SIGNATURE)) {
        fprintf (stdout, "FV header at 0x%08X FVSize=0x%08X ", Offset + BaseAddr, (UINT32) FVHeader.FvLength);
        if (memcmp (&FVHeader.FileSystemGuid, &FileSystemGuid, sizeof (EFI_GUID)) == 0) {
          fprintf (stdout, "standard FFS file system\n");
        } else {
          fprintf (stdout, "non-standard FFS file system\n");
        }
      }
    }

    Offset += 16 * 1024;
  }

  fclose (InFptr);
  return STATUS_SUCCESS;
}
