/*++

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:

  HiiPack.c  

Abstract:

  Process HII export and pack files and create HII export files,
  dumps, or variable defaults packs.

--*/

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

#include "Tiano.h"
#include "EfiUtilityMsgs.h"
#include "ParseInf.h"
#include "EfiInternalFormRepresentation.h"
#include "HiiPack.h"
#include "Hii.h"
#include "IfrParse.h"
#include "StringParse.h"

#define UTILITY_VERSION "v1.0"
#define UTILITY_NAME    "HiiPack"
#define MAX_PATH        260

//
// We may have to create an empty IFR formset to provide a GUID for an HII
// export pack. Create a structure definition to make it easier.
//
#pragma pack(1)

typedef struct {
  EFI_HII_IFR_PACK      PackHeader;
  EFI_IFR_FORM_SET      Formset;
  EFI_IFR_END_FORM_SET  EndFormset;
} EMPTY_FORMSET_PACK;

#pragma pack()
//
// We'll store lists of file names from the command line in
// a linked list of these
//
typedef struct _FILE_NAME_LIST {
  struct _FILE_NAME_LIST  *Next;
  UINT8                   FileName[MAX_PATH];
  int                     Tag;  // used for whatever
} FILE_NAME_LIST;

//
// When merging HII export packs, we save HII data table headers in a linked
// list of these.
//
typedef struct _DATA_TABLE_HEADER_LIST {
  struct _DATA_TABLE_HEADER_LIST  *Next;
  EFI_HII_DATA_TABLE              DataTableHeader;
} DATA_TABLE_HEADER_LIST;
//
// Create some defines for the different operation modes supported by this utility
//
#define MODE_CREATE_HII_EXPORT  1
#define MODE_MERGE_HII_EXPORTS  2
#define MODE_EMIT_DEFAULTS      3
#define MODE_DUMP_HII_EXPORT    4
//
// Here's all our globals.
//
static struct {
  FILE_NAME_LIST  *PackFileNames;           // Input HII pack file names
  FILE_NAME_LIST  *HiiExportFileNames;      // Input files when merging
  CHAR8           OutputFileName[MAX_PATH]; // Output dump file
  BOOLEAN         MfgFlag;                  // From -mfg command line arg
  BOOLEAN         NoEmptyVarPacks;          // From -noemptyvarpacks command line arg
  BOOLEAN         NoVarPacks;               // From -novarpacks command line arg
  EFI_GUID        Guid;                     // Guid specified on command line
  BOOLEAN         GuidSpecified;
  BOOLEAN         DumpStrings;              // In dump mode, dump string data
  int             Verbose;
  int             Mode;                     // Mode this utility is operating in
} mGlobals;

static
void
Usage (
  VOID
  );

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

static
STATUS
DumpHiiExportFile (
  char    *HiiExportFileName,
  char    *OutputFileName
  );

static
void
DumpString (
  FILE    *OutFptr,
  int     StringIndex,
  CHAR16  *Str,
  int     Indent
  );

static
void
DumpStringPack (
  FILE                  *OutFptr,
  EFI_HII_STRING_PACK   *Pack,
  int                   BaseOffset,
  int                   Indent
  );

static
void
DumpVariablePacks (
  FILE                  *OutFptr,
  EFI_HII_VARIABLE_PACK *Pack,
  int                   NumPacks,
  int                   BaseOffset,
  int                   Indent
  );

static
void
TestDumpHiiPack (
  FILE    *OutFptr,
  char    *BufferStart,
  int     BufferSize
  );

static
void
DumpRawBytes (
  FILE                  *OutFptr,
  char                  *Buffer,
  int                   Count,
  int                   BaseOffset,
  int                   Indent
  );

static
void
DumpIfrPack (
  FILE                  *OutFptr,
  EFI_HII_IFR_PACK      *Pack,
  int                   BaseOffset,
  int                   Indent
  );

static
void
FreeGlobals (
  VOID
  );

static
STATUS
AddStringPack (
  EFI_HII_STRING_PACK   *PackHeader
  );

static
STATUS
ProcessHiiExportFile (
  char    *FileName,
  int     MfgDefaults
  );

static
STATUS
ProcessIfrFiles (
  FILE_NAME_LIST *FileName
  );

static
STATUS
EmitDefaults (
  FILE_NAME_LIST *HiiExportFiles,
  int            MfgDefaults,
  int            NoEmptyVarPacks
  );

static
STATUS
MergeHiiExports (
  FILE_NAME_LIST *HiiExportFiles,
  char           *OutputFileName,
  int            MfgDefaults,
  int            NoEmptyVarPacks
  );

void
GuidToString (
  EFI_GUID   *Guid,
  char       *Str
  );

static
CHAR16  *
AsciiToWchar (
  CHAR8 *Str
  );

static
STATUS
CreateHiiExport (
  char              *OutputFileName,
  EFI_GUID          *DummyFormsetGuid,
  FILE_NAME_LIST    *PackFiles,
  int               MfgDefaults
  );

int
main (
  int   Argc,
  char  *Argv[]
  )
/*++

Routine Description:

  Call the routine to parse the command-line options, then process the file.
  
Arguments:

  Standard C main() argc and argv.

Returns:

  0       if successful
  nonzero otherwise
  
--*/
// GC_TODO:    Argc - add argument and description to function comment
// GC_TODO:    ] - add argument and description to function comment
{
  STATUS  Status;
  //
  // Set the utility name for error reporting purposes
  //
  SetUtilityName (UTILITY_NAME);
  //
  // Process the command-line arguments
  //
  Status = ProcessArgs (Argc, Argv);
  if (Status != STATUS_SUCCESS) {
    return Status;
  }
  //
  // Switch based on whether we're dumping, merging, etc.
  //
  if (mGlobals.Mode == MODE_DUMP_HII_EXPORT) {
    if (mGlobals.Verbose) {
      fprintf (stdout, "Dumping HII export file %s => %s\n", mGlobals.HiiExportFileNames, mGlobals.OutputFileName);
    }

    DumpHiiExportFile (mGlobals.HiiExportFileNames->FileName, mGlobals.OutputFileName);
  } else if (mGlobals.Mode == MODE_CREATE_HII_EXPORT) {
    CreateHiiExport (mGlobals.OutputFileName, &mGlobals.Guid, mGlobals.PackFileNames, mGlobals.MfgFlag);
  } else if (mGlobals.Mode == MODE_MERGE_HII_EXPORTS) {
    MergeHiiExports (mGlobals.HiiExportFileNames, mGlobals.OutputFileName, mGlobals.MfgFlag, mGlobals.NoEmptyVarPacks);
  } else if (mGlobals.Mode == MODE_EMIT_DEFAULTS) {
    EmitDefaults (mGlobals.HiiExportFileNames, mGlobals.MfgFlag, mGlobals.NoEmptyVarPacks);
  }
  //
  //
  FreeGlobals ();
  IfrParseEnd ();
  StringEnd ();
  return GetUtilityStatus ();
}

/******************************************************************************/
static
STATUS
MergeHiiExports (
  FILE_NAME_LIST *HiiExportFiles,
  char           *OutputFileName,
  int            MfgDefaults,
  int            NoEmptyVarPacks
  )
/*++

Routine Description:

  Given a linked list of input HII export pack files, read in the contents
  of each and create a single HII export pack that contains the contents
  of all the input files.
  
Arguments:

  HiiExportFiles    - pointer to linked list of input HII export pack file names
  OutputFileName    - name of output (merged) HII export file
  MfgDefaults       - non-zero to emit manufacturing defaults in output file
  NoEmptyVarPacks   - non-zero to not emit 0-length variable packs to the output file

Returns:

  STATUS_SUCCESS    - if successful
  STATUS_ERROR      - otherwise
  
--*/
{
  EFI_HII_HANDLE          HiiHandle;
  FILE                    *OutFptr;
  FILE                    *InFptr;
  STATUS                  Status;
  CHAR8                   *Buffer;
  int                     FileSize;
  int                     DataTableIndex;
  int                     Count;
  int                     NumDataTables;
  EFI_HII_EXPORT_TABLE    *HiiExportTableHeader;
  EFI_HII_EXPORT_TABLE    TempHiiExportTableHeader;
  EFI_HII_DATA_TABLE      *DataTableHeader;
  EFI_HII_STRING_PACK     *StringPack;
  EFI_HII_VARIABLE_PACK   *VarPack;
  EFI_HII_IFR_PACK        *IfrPack;
  EFI_GUID                HiiExportRevisionGuid = EFI_HII_PROTOCOL_GUID;
  EFI_GUID                PackageGuid;
  EFI_GUID                FormsetGuid;
  long                    DataTableHeaderOffset;
  DATA_TABLE_HEADER_LIST  *DataTableList;
  DATA_TABLE_HEADER_LIST  *LastDataTable;
  DATA_TABLE_HEADER_LIST  *TempDataTable;
  //
  // Init locals
  //
  HiiHandle     = FIRST_HII_PACK_HANDLE;
  Buffer        = NULL;
  InFptr        = NULL;
  OutFptr       = NULL;
  Status        = STATUS_ERROR;
  DataTableList = NULL;
  LastDataTable = NULL;
  //
  // Initialize our IFR parser and string routines
  //
  IfrParseInit ();
  StringInit ();
  //
  // Process each input HII export file
  //
  NumDataTables = 0;
  while (HiiExportFiles != NULL) {
    if (mGlobals.Verbose) {
      fprintf (stdout, "Processing file %s\n", HiiExportFiles->FileName);
    }
    //
    // Read in the entire file contents
    //
    if ((InFptr = fopen (HiiExportFiles->FileName, "rb")) == NULL) {
      Error (NULL, 0, 0, HiiExportFiles->FileName, "failed to open HII export file for reading");
      goto Done;
    }

    fseek (InFptr, 0, SEEK_END);
    FileSize = (int) ftell (InFptr);
    fseek (InFptr, 0, SEEK_SET);
    Buffer = (CHAR8 *) malloc (FileSize);
    if (Buffer == NULL) {
      Error (NULL, 0, 0, "memory allocation failure", NULL);
      goto Done;
    }

    if (fread (Buffer, FileSize, 1, InFptr) != 1) {
      Error (NULL, 0, 0, HiiExportFiles->FileName, "failed to read file contents");
      goto Done;
    }

    fclose (InFptr);
    InFptr                = NULL;
    HiiExportTableHeader  = (EFI_HII_EXPORT_TABLE *) Buffer;
    //
    // Walk all the data tables
    //
    DataTableHeader = (EFI_HII_DATA_TABLE *) (HiiExportTableHeader + 1);
    for (DataTableIndex = 0; DataTableIndex < (int) HiiExportTableHeader->NumberOfHiiDataTables; DataTableIndex++) {
      NumDataTables++;
      //
      // Make sure we're still pointing into our buffer
      //
      if (((char *) DataTableHeader < Buffer) || ((char *) DataTableHeader > Buffer + FileSize)) {
        Error (NULL, 0, 0, "bad data table size in input file", NULL);
        goto Done;
      }
      //
      // Save a copy of the data table header
      //
      TempDataTable = (DATA_TABLE_HEADER_LIST *) malloc (sizeof (DATA_TABLE_HEADER_LIST));
      if (TempDataTable == NULL) {
        Error (NULL, 0, 0, "memory allocation failure", NULL);
        goto Done;
      }

      memset ((void *) TempDataTable, 0, sizeof (DATA_TABLE_HEADER_LIST));
      memcpy (&TempDataTable->DataTableHeader, DataTableHeader, sizeof (EFI_HII_DATA_TABLE));
      if (DataTableList == NULL) {
        DataTableList = TempDataTable;
      } else {
        LastDataTable->Next = TempDataTable;
      }

      LastDataTable = TempDataTable;
      //
      // If there is an IFR pack, parse it
      //
      if (DataTableHeader->IfrDataOffset != 0) {
        if (IfrParsePack (
            HiiHandle,
            (EFI_HII_IFR_PACK *) ((char *) DataTableHeader + DataTableHeader->IfrDataOffset),
            &DataTableHeader->PackageGuid
            ) != STATUS_SUCCESS
            ) {
          goto Done;
        }
      }
      //
      // If there is string data, save it
      //
      if (DataTableHeader->StringDataOffset != 0) {
        Status = StringParsePack (
                  HiiHandle,
                  (EFI_HII_STRING_PACK *) ((char *) DataTableHeader + DataTableHeader->StringDataOffset),
                  NULL,
                  &DataTableHeader->PackageGuid
                  );
        if (Status != STATUS_SUCCESS) {
          goto Done;
        }
      }
      //
      // If there is device path data, process it
      //
      if (DataTableHeader->DevicePathOffset != 0) {
        Error (NULL, 0, 0, "application error", "%s contains unsupported device path data", HiiExportFiles->FileName);
        goto Done;
      }
      //
      // Next data pack
      //
      DataTableHeader = (EFI_HII_DATA_TABLE *) ((char *) DataTableHeader + DataTableHeader->DataTableSize);
      HiiHandle++;
    }

    free (Buffer);
    Buffer = NULL;
    //
    // Next input file
    //
    HiiExportFiles = HiiExportFiles->Next;
  }
  //
  // Now create defaults
  //
  if (IfrSetDefaults (MfgDefaults) != STATUS_SUCCESS) {
    goto Done;
  }
  //
  // Create and write the output HII export header
  //
  if ((OutFptr = fopen (OutputFileName, "wb")) == NULL) {
    Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
    goto Done;
  }

  memset ((void *) &TempHiiExportTableHeader, 0, sizeof (EFI_HII_EXPORT_TABLE));
  TempHiiExportTableHeader.NumberOfHiiDataTables = HiiHandle - FIRST_HII_PACK_HANDLE;
  memcpy (&TempHiiExportTableHeader.Revision, &HiiExportRevisionGuid, sizeof (EFI_GUID));
  if (fwrite ((void *) &TempHiiExportTableHeader, sizeof (EFI_HII_EXPORT_TABLE), 1, OutFptr) != 1) {
    Error (NULL, 0, 0, OutputFileName, "failed to write HII export table header to output file");
    goto Done;
  }
  //
  // Now go back through all the handles and create new data packs for each, writing out
  // the contents as we go.
  //
  HiiHandle = FIRST_HII_PACK_HANDLE;
  for (TempDataTable = DataTableList; TempDataTable != NULL; TempDataTable = TempDataTable->Next) {
    //
    // Write a data table header to the output file. We'll rewind the file and
    // write an updated one when we're done with this data set
    //
    DataTableHeaderOffset                         = ftell (OutFptr);
    TempDataTable->DataTableHeader.HiiHandle      = HiiHandle;
    TempDataTable->DataTableHeader.DataTableSize  = sizeof (EFI_HII_DATA_TABLE);
    //
    // We may change the number of variable data when merging export files, so init to 0
    //
    TempDataTable->DataTableHeader.NumberOfVariableData = 0;
    if (fwrite ((void *) &TempDataTable->DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
      Error (NULL, 0, 0, OutputFileName, "failed to write HII data table header to output file");
      goto Done;
    }
    //
    // Get the string pack if any
    //
    Status = StringGetPack (HiiHandle, &StringPack, &FileSize, &Count, &FormsetGuid, &PackageGuid);
    if (Status == STATUS_SUCCESS) {
      TempDataTable->DataTableHeader.StringDataOffset = TempDataTable->DataTableHeader.DataTableSize;
      TempDataTable->DataTableHeader.DataTableSize += FileSize;
      //
      // TempDataTable->DataTableHeader.NumberOfLanguages should be unchanged
      //
      if (fwrite ((void *) StringPack, FileSize, 1, OutFptr) != 1) {
        Error (NULL, 0, 0, "failed to write string pack to output file", NULL);
        goto Done;
      }
    }
    //
    // Get the IFR pack
    //
    Status = IfrGetIfrPack (HiiHandle, &IfrPack, &FormsetGuid);
    if (Status == STATUS_SUCCESS) {
      //
      // Write the IFR pack, followed by the variable packs
      //
      TempDataTable->DataTableHeader.IfrDataOffset = TempDataTable->DataTableHeader.DataTableSize;
      TempDataTable->DataTableHeader.DataTableSize += IfrPack->Header.Length;
      if (fwrite ((void *) IfrPack, IfrPack->Header.Length, 1, OutFptr) != 1) {
        Error (NULL, 0, 0, "failed to write IFR pack to output file", NULL);
        goto Done;
      }
      //
      // If this is just a formset stub, then don't write the variable packs
      //
      if (IfrPack->Header.Length != sizeof (EMPTY_FORMSET_PACK)) {
        //
        // Go through all the variable packs and see if they're referenced by this IFR
        //
        Count = 0;
        do {
          Status = IfrGetVarPack (Count, &VarPack);
          if (Status == STATUS_SUCCESS) {
            //
            // Check for variable data length of 0
            //
            if ((NoEmptyVarPacks == 0) ||
                ((VarPack->Header.Length - sizeof (EFI_HII_VARIABLE_PACK) - VarPack->VariableNameLength) != 0)
                ) {
              //
              // See if it's referenced by this IFR
              //
              if (IfrReferencesVarPack (HiiHandle, VarPack) == STATUS_SUCCESS) {
                if (TempDataTable->DataTableHeader.VariableDataOffset == 0) {
                  TempDataTable->DataTableHeader.VariableDataOffset = TempDataTable->DataTableHeader.DataTableSize;
                }

                TempDataTable->DataTableHeader.DataTableSize += VarPack->Header.Length;
                TempDataTable->DataTableHeader.NumberOfVariableData++;
                if (fwrite ((void *) VarPack, VarPack->Header.Length, 1, OutFptr) != 1) {
                  Error (NULL, 0, 0, "failed to write variable pack to output file", NULL);
                  goto Done;
                }

              }
            }
          }

          Count++;
        } while (Status == STATUS_SUCCESS);
      }

      Status = STATUS_SUCCESS;
    }
    //
    // Get the device path pack
    //
    //
    // Rewind the file and write the updated data table header.
    //
    fseek (OutFptr, DataTableHeaderOffset, SEEK_SET);
    if (fwrite ((void *) &TempDataTable->DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
      Error (NULL, 0, 0, OutputFileName, "failed to write HII data table header to output file");
      goto Done;
    }

    fseek (OutFptr, 0, SEEK_END);
    HiiHandle++;
  }

  Status = STATUS_SUCCESS;
Done:
  IfrParseEnd ();
  StringEnd ();
  if (Buffer != NULL) {
    free (Buffer);
  }

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

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

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

  return Status;
}

/******************************************************************************/
static
STATUS
CreateHiiExport (
  char              *OutputFileName,
  EFI_GUID          *DummyFormsetGuid,
  FILE_NAME_LIST    *PackFiles,
  int               MfgDefaults
  )
/*++

Routine Description:

  Given a linked list of HII pack file names, walk the list to
  process them and create a single HII export file.
  
Arguments:

  OutputFileName    - name of output HII export file to create
  DummyFormsetGuid  - IFR formsets contain a GUID which is used in many 
                      places while processing data tables. If we were not
                      given an IFR pack, then we'll create a stub IFR
                      pack using this GUID as the formset GUID.
  PackFiles         - linked list of HII pack files to process
  MfgDefaults       - when creating variable packs (via IFR pack processing),
                      use manufacturing defaults rather than standard defaults

Returns:

  STATUS_SUCCESS    - if successful
  STATUS_ERROR      - otherwise
  
--*/
{
  STATUS                      Status;
  EMPTY_FORMSET_PACK          EmptyFormset;
  EFI_HII_DATA_TABLE          DataTableHeader;
  EFI_HII_EXPORT_TABLE        ExportTableHeader;
  long                        DataTableHeaderOffset;
  long                        FileSize;
  FILE                        *OutFptr;
  FILE                        *InFptr;
  FILE_NAME_LIST              *TempFile;
  EFI_GUID                    HiiExportRevisionGuid = EFI_HII_PROTOCOL_GUID;
  EFI_GUID                    TempGuid;
  EFI_GUID                    PackageGuid;
  char                        *Buffer;
  EFI_HII_VARIABLE_PACK       *VarPack;
  EFI_HII_IFR_PACK            *IfrPack;
  EFI_HII_STRING_PACK_HEADER  *StringPack;
  EFI_HII_STRING_PACK_HEADER  TerminatorStringPack;
  int                         NumIfr;
  int                         NumStrings;
  int                         Index;
  int                         VarPackIndex;
  //
  // If no input HII pack files, then why are we here? Should have been caught when
  // args were processed though.
  //
  if (PackFiles == NULL) {
    Error (NULL, 0, 0, "no input pack files specified", NULL);
    return STATUS_ERROR;
  }

  InFptr  = NULL;
  Status  = STATUS_ERROR;
  Buffer  = NULL;
  //
  // Open the output file for writing
  //
  if ((OutFptr = fopen (OutputFileName, "wb")) == NULL) {
    Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
    goto Done;
  }
  //
  // Figure out how many data tables we are going to need. We'll create one
  // data table if no more than one IFR, or we'll create one data table per IFR,
  // and then one for strings if multiple IFR
  //
  NumIfr      = 0;
  NumStrings  = 0;
  for (TempFile = PackFiles; TempFile != NULL; TempFile = TempFile->Next) {
    if (TempFile->Tag == EFI_HII_IFR) {
      NumIfr++;
    } else if (TempFile->Tag == EFI_HII_STRING) {
      NumStrings++;
    }
  }
  //
  // Three possibilities:
  //  1) No IFR, so create one data table that contains only strings and an empty formset
  //  2) Only 1 IFR, so create an export table with one data table that contains the IFR
  //     and all the strings
  //  3) Multiple IFR, so create a data table for each IFR and another data table with
  //     all the strings.
  //
  // Initialize the export table header and write it out
  //
  memset ((void *) &ExportTableHeader, 0, sizeof (EFI_HII_EXPORT_TABLE));
  if (NumIfr < 2) {
    ExportTableHeader.NumberOfHiiDataTables = 1;
  } else {
    //
    // One data table per IFR, plus one for strings (if any).
    //
    ExportTableHeader.NumberOfHiiDataTables = (UINT16) NumIfr;
    if (NumStrings != 0) {
      ExportTableHeader.NumberOfHiiDataTables++;
    }
  }
  //
  // Init the GUID in the HII export table header
  //
  memcpy (&ExportTableHeader.Revision, &HiiExportRevisionGuid, sizeof (EFI_GUID));
  if (fwrite ((void *) &ExportTableHeader, sizeof (EFI_HII_EXPORT_TABLE), 1, OutFptr) != 1) {
    Error (NULL, 0, 0, OutputFileName, "failed to write HII export table header to output file");
    goto Done;
  }
  //
  // *****************************************************************************************
  //
  //  CASE 1 - No IFR => one data table that contains only strings and an empty formset.
  //           No variable data.
  //
  //  CASE 2 - Only 1 IFR => create an export table with one data table that contains the IFR
  //           and all the strings plus variable data
  //
  //  CASE 3 - Multiple IFR => create a data table for each IFR and another data table with
  //           all the strings. Each IFR data table has variable data if applicable.
  //
  // *****************************************************************************************
  //
  // If the user did not give us an IFR file, then we'll have to create an empty formset
  // and emit it to the output file. In this case, we need a formset GUID on the command
  // line.
  //
  if ((NumIfr == 0) && (mGlobals.GuidSpecified == 0)) {
    //
    //    Warning (NULL, 0, 0, "using NULL GUID for empty formset", "specify -g GUID on the command line if desired");
    //
    memset ((void *) &PackageGuid, 0, sizeof (EFI_GUID));
  } else if (mGlobals.GuidSpecified) {
    //
    // Use it for the package GUID
    //
    memcpy (&PackageGuid, &mGlobals.Guid, sizeof (EFI_GUID));
  }
  //
  // Init the data table header.
  // Write out the blank data table header. Save the offset so we can
  // write an updated version at the end of processing.
  //
  memset ((void *) &DataTableHeader, 0, sizeof (EFI_HII_DATA_TABLE));
  DataTableHeaderOffset     = ftell (OutFptr);
  DataTableHeader.HiiHandle = FIRST_HII_PACK_HANDLE;
  if (mGlobals.Verbose) {
    fprintf (stdout, "writing data table (first time) to offset 0x%X\n", ftell (OutFptr));
  }

  if (fwrite ((void *) &DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
    Error (NULL, 0, 0, "failed to write Data Table Header to output file", NULL);
    goto Done;
  }
  //
  // Set the data table size, then write out all the string packs
  //
  DataTableHeader.DataTableSize = sizeof (EFI_HII_DATA_TABLE);
  //
  // Write out the string files to a single data record
  //
  for (TempFile = PackFiles; TempFile != NULL; TempFile = TempFile->Next) {
    //
    // Continue to next file if it's not a string pack file
    //
    if (TempFile->Tag != EFI_HII_STRING) {
      continue;
    }
    //
    // Set the offset in the header if this is the first string pack
    //
    if (DataTableHeader.StringDataOffset == 0) {
      DataTableHeader.StringDataOffset = DataTableHeader.DataTableSize;
    }

    if ((InFptr = fopen (TempFile->FileName, "rb")) == NULL) {
      Error (NULL, 0, 0, TempFile->FileName, "failed to open input string pack file for reading");
      goto Done;
    }
    //
    // Get the file size, then read it into a buffer
    //
    fseek (InFptr, 0, SEEK_END);
    FileSize = ftell (InFptr);
    fseek (InFptr, 0, SEEK_SET);
    Buffer = (char *) malloc (FileSize);
    if (Buffer == NULL) {
      Error (NULL, 0, 0, TempFile->FileName, "memory allocation failure reading in file contents");
      goto Done;
    }

    if (fread (Buffer, FileSize, 1, InFptr) != 1) {
      Error (NULL, 0, 0, TempFile->FileName, "failed to read file contents");
      goto Done;
    }

    fclose (InFptr);
    InFptr = NULL;
    //
    // Verify that it's actually a string pack
    //
    StringPack = (EFI_HII_STRING_PACK_HEADER *) Buffer;
    while ((char *) StringPack < Buffer + FileSize) {
      if (StringPack->Header.Type != EFI_HII_STRING) {
        Error (NULL, 0, 0, TempFile->FileName, "file does not consist entirely of string packs");
        goto Done;
      }

      if (StringPack->Header.Length == 0) {
        break;
      }

      DataTableHeader.NumberOfLanguages++;
      DataTableHeader.DataTableSize += StringPack->Header.Length;
      //
      // Write the string pack to the output file
      //
      if (mGlobals.Verbose) {
        fprintf (stdout, "writing string pack to offset 0x%X\n", ftell (OutFptr));
      }

      if (fwrite (StringPack, StringPack->Header.Length, 1, OutFptr) != 1) {
        Error (NULL, 0, 0, TempFile->FileName, "failed to write string pack to output file");
        goto Done;
      }
      //
      // Sanity check that adding the length advances us (no wrap)
      //
      if ((char *) StringPack + StringPack->Header.Length <= (char *) StringPack) {
        Error (NULL, 0, 0, TempFile->FileName, "invalid pack size in file");
        goto Done;
      }

      StringPack = (EFI_HII_STRING_PACK_HEADER *) ((char *) StringPack + StringPack->Header.Length);
    }
    //
    // Free up buffer, go to next input string pack file
    //
    free (Buffer);
    Buffer = NULL;
  }
  //
  // Write a null-terminator string pack if we had any string packs at all
  //
  if (DataTableHeader.StringDataOffset != 0) {
    memset (&TerminatorStringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
    TerminatorStringPack.Header.Length  = 0;
    TerminatorStringPack.Header.Type    = EFI_HII_STRING;
    if (mGlobals.Verbose) {
      fprintf (stdout, "writing terminator string pack to offset 0x%X\n", ftell (OutFptr));
    }

    if (fwrite (&TerminatorStringPack, sizeof (EFI_HII_STRING_PACK_HEADER), 1, OutFptr) != 1) {
      Error (NULL, 0, 0, "failed to write string pack terminator to output file", NULL);
      goto Done;
    }

    DataTableHeader.DataTableSize += sizeof (EFI_HII_STRING_PACK_HEADER);
  }
  //
  // Parse all the IFR packs, then get the GUID from the first
  // one so we can use it for the package GUID if necessary.
  //
  memcpy (&PackageGuid, &mGlobals.Guid, sizeof (EFI_GUID));
  if (NumIfr != 0) {
    IfrParseInit ();
    if (ProcessIfrFiles (PackFiles) != STATUS_SUCCESS) {
      goto Done;
    }
    //
    // Set variable defaults in all variable packs
    //
    IfrSetDefaults (MfgDefaults);
    //
    // Get the GUID from the first IFR pack if the user did not specify a GUID on
    // the command line.
    //
    if (mGlobals.GuidSpecified == 0) {
      if (IfrGetIfrPack (FIRST_HII_PACK_HANDLE, &IfrPack, &PackageGuid) != STATUS_SUCCESS) {
        Error (NULL, 0, 0, "internal application error", "failed to retrieve IFR pack after parsing");
        goto Done;
      }
    }
  }
  //
  // Set the package GUID in the data table header.
  //
  memcpy (&DataTableHeader.PackageGuid, &PackageGuid, sizeof (EFI_GUID));
  //
  // If no IFR, then create and write the empty formset. Otherwise
  // parse the IFR and emit it and the variable data for it.
  //
  if (NumIfr == 0) {
    memset ((void *) &EmptyFormset, 0, sizeof (EMPTY_FORMSET_PACK));
    EmptyFormset.PackHeader.Header.Type   = EFI_HII_IFR;
    EmptyFormset.PackHeader.Header.Length = sizeof (EMPTY_FORMSET_PACK);
    //
    // Formset Opcode
    //
    EmptyFormset.Formset.Header.OpCode  = EFI_IFR_FORM_SET_OP;
    EmptyFormset.Formset.Header.Length  = (UINT8) sizeof (EFI_IFR_FORM_SET);
    memcpy (&EmptyFormset.Formset.Guid, &PackageGuid, sizeof (EFI_GUID));
    //
    // EndFormset Opcode
    //
    EmptyFormset.EndFormset.Header.OpCode = EFI_IFR_END_FORM_SET_OP;
    EmptyFormset.EndFormset.Header.Length = (UINT8) sizeof (EFI_IFR_END_FORM_SET);
    DataTableHeader.IfrDataOffset         = DataTableHeader.DataTableSize;
    if (mGlobals.Verbose) {
      fprintf (stdout, "writing stub IFR formset to to offset 0x%X\n", ftell (OutFptr));
    }

    if (fwrite (&EmptyFormset, sizeof (EMPTY_FORMSET_PACK), 1, OutFptr) != 1) {
      Error (NULL, 0, 0, OutputFileName, "failed to write formset stub to output file");
      goto Done;
    }

    DataTableHeader.DataTableSize += sizeof (EMPTY_FORMSET_PACK);
    //
    // Go back and re-write the data table header, reposition to the end, then return.
    //
    fseek (OutFptr, DataTableHeaderOffset, SEEK_SET);
    if (mGlobals.Verbose) {
      fprintf (stdout, "writing data table (second time) to offset 0x%X\n", ftell (OutFptr));
    }

    if (fwrite ((void *) &DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
      Error (NULL, 0, 0, "failed to write Data Table Header to output file", NULL);
      goto Done;
    }

    fseek (OutFptr, 0, SEEK_END);
    if (mGlobals.Verbose) {
      fprintf (
        stdout,
        "final file offset=0x%X DataTableHeader.DataTableSize=0x%X\n",
        ftell (OutFptr),
        DataTableHeader.DataTableSize
        );
    }
  } else if (NumIfr == 1) {
    //
    // They gave us one input IFR file. We parsed it above, so get each one
    // and emit the IFR and each variable pack it references.
    // Update the data pack header for the IFR pack, then write the IFR pack data
    //
    DataTableHeader.IfrDataOffset = DataTableHeader.DataTableSize;
    if (IfrGetIfrPack (FIRST_HII_PACK_HANDLE, &IfrPack, &TempGuid) != STATUS_SUCCESS) {
      Error (NULL, 0, 0, "internal application error", "failed to retrieve IFR pack after parsing");
      goto Done;
    }

    if (mGlobals.Verbose) {
      fprintf (stdout, "writing IFR pack to 0x%X\n", ftell (OutFptr));
    }

    if (fwrite (IfrPack, IfrPack->Header.Length, 1, OutFptr) != 1) {
      Error (NULL, 0, 0, OutputFileName, "failed to write IFR pack to output file");
      goto Done;
    }

    DataTableHeader.DataTableSize += IfrPack->Header.Length;
    //
    // Now go through all the variable packs discovered during IFR processing
    // and write them to the output file
    //
    if (mGlobals.NoVarPacks == 0) {
      Index = 0;
      do {
        Status = IfrGetVarPack (Index, &VarPack);
        if (Status == STATUS_SUCCESS) {
          //
          // If this is the first variable pack, then update the "offset
          // to variable data" in the data table header
          //
          if (Index == 0) {
            DataTableHeader.VariableDataOffset = DataTableHeader.DataTableSize;
          }

          DataTableHeader.DataTableSize += VarPack->Header.Length;
          DataTableHeader.NumberOfVariableData++;
          if (fwrite ((void *) VarPack, VarPack->Header.Length, 1, OutFptr) != 1) {
            Error (NULL, 0, 0, OutputFileName, "failed to write variable pack to output file");
            goto Done;
          }

          Index++;
        }
      } while (Status == STATUS_SUCCESS);
    }
    //
    // Reposition in the output file and write the updated data table header
    //
    fseek (OutFptr, DataTableHeaderOffset, SEEK_SET);
    if (fwrite ((void *) &DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
      Error (NULL, 0, 0, "failed to write Data Table Header to output file", NULL);
      goto Done;
    }

    fseek (OutFptr, 0, SEEK_END);
  } else {
    //
    // Multiple IFR input files. Close out the current data table (strings)
    // if applicable. Then retrieve each parsed IFR pack and create a data pack
    // that contains the IFR (one per data set) and the variable packs that
    // the given IFR form references.
    //
    if (NumStrings != 0) {
      fseek (OutFptr, DataTableHeaderOffset, SEEK_SET);
      if (fwrite ((void *) &DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
        Error (NULL, 0, 0, "failed to write Data Table Header to output file", NULL);
        goto Done;
      }

      fseek (OutFptr, 0, SEEK_END);
    } else {
      //
      // No strings, so back up over the data table header we wrote because we assumed
      // at least one string pack.
      //
      fseek (OutFptr, DataTableHeaderOffset, SEEK_SET);
    }
    //
    // Now go through all the IFR packs and write them out, along with variable
    // data referenced by each. Note that multiple IFR forms can refer to the
    // same variables, so the same variable data could be duplicated in multiple
    // data packs.
    //
    Index = FIRST_HII_PACK_HANDLE;
    while (IfrGetIfrPack (Index, &IfrPack, &TempGuid) == STATUS_SUCCESS) {
      //
      // Initialize the data table header
      //
      memset (&DataTableHeader, 0, sizeof (EFI_HII_DATA_TABLE));
      memcpy (&DataTableHeader.PackageGuid, &PackageGuid, sizeof (EFI_GUID));
      //
      // If we didn't have strings, then the HiiHandle should be just Index,
      // rather than Index+1. But since the HiiHandle is not required to start
      // with 1, we'll let it be Index+1.
      //
      DataTableHeader.HiiHandle     = (EFI_HII_HANDLE) (Index + 1);
      DataTableHeader.DataTableSize = sizeof (EFI_HII_DATA_TABLE);
      //
      // Save the file offset of the data table header so we can write an updated
      // version later.
      //
      DataTableHeaderOffset = ftell (OutFptr);
      if (mGlobals.Verbose) {
        fprintf (stdout, "writing data table header to 0x%X\n", ftell (OutFptr));
      }

      if (fwrite ((void *) &DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
        Error (NULL, 0, 0, "failed to write Data Table Header to output file", NULL);
        goto Done;
      }

      DataTableHeader.IfrDataOffset = DataTableHeader.DataTableSize;
      if (fwrite (IfrPack, IfrPack->Header.Length, 1, OutFptr) != 1) {
        Error (NULL, 0, 0, OutputFileName, "failed to write IFR pack to output file");
        goto Done;
      }

      DataTableHeader.DataTableSize += IfrPack->Header.Length;
      //
      // Go through all the variable packs and see if this IFR references each. If the
      // IFR does reference it, then add the variable pack to the output.
      //
      if (mGlobals.NoVarPacks == 0) {
        VarPackIndex = 0;
        while (IfrGetVarPack (VarPackIndex, &VarPack) == STATUS_SUCCESS) {
          //
          // See if the IFR references this variable pack
          //
          if (IfrReferencesVarPack (Index, VarPack) == STATUS_SUCCESS) {
            //
            // If this is the first variable pack, then set the offset in
            // the data table header.
            //
            if (DataTableHeader.VariableDataOffset == 0) {
              DataTableHeader.VariableDataOffset = DataTableHeader.DataTableSize;
            }
            //
            // Write the variable pack
            //
            if (fwrite (VarPack, VarPack->Header.Length, 1, OutFptr) != 1) {
              Error (NULL, 0, 0, OutputFileName, "failed to write variable pack to output file");
              goto Done;
            }

            DataTableHeader.NumberOfVariableData++;
            DataTableHeader.DataTableSize += VarPack->Header.Length;
          }

          VarPackIndex++;
        }
      }
      //
      // Write the updated data table header
      //
      fseek (OutFptr, DataTableHeaderOffset, SEEK_SET);
      if (fwrite ((void *) &DataTableHeader, sizeof (EFI_HII_DATA_TABLE), 1, OutFptr) != 1) {
        Error (NULL, 0, 0, "failed to write Data Table Header to output file", NULL);
        goto Done;
      }

      fseek (OutFptr, 0, SEEK_END);
      //
      // Next IFR pack
      //
      Index++;
    }
  }

  Status = STATUS_SUCCESS;
Done:
  IfrParseEnd ();
  StringEnd ();
  if (Buffer != NULL) {
    free (Buffer);
  }

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

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

  return Status;
}

/******************************************************************************/
static
STATUS
ProcessIfrFiles (
  FILE_NAME_LIST  *FileName
  )
/*++

Routine Description:

  Given a linked list of pack file names, read in each IFR pack file
  and process the contents.
  
Arguments:

  FileName    - pointer to linked list of input pack file names

Returns:

  STATUS_SUCCESS    - if successful
  STATUS_ERROR      - otherwise
  
--*/
{
  FILE                *InFptr;
  char                *Buffer;
  long                BufferSize;
  STATUS              Status;
  STATUS              IfrStatus;
  int                 Handle;
  EFI_GUID            FormsetGuid;
  EFI_HII_PACK_HEADER *PackHeader;
  //
  // Process each input IFR file
  //
  Status  = STATUS_ERROR;
  Handle  = 1;
  InFptr  = NULL;
  Buffer  = NULL;
  while (FileName != NULL) {
    //
    // Only process IFR pack files
    //
    if (FileName->Tag != EFI_HII_IFR) {
      FileName = FileName->Next;
      continue;
    }
    //
    // Open the input file, then read the contents
    //
    if ((InFptr = fopen (FileName->FileName, "rb")) == NULL) {
      Error (NULL, 0, 0, FileName->FileName, "failed to open input IFR file");
      goto Done;
    }

    fseek (InFptr, 0, SEEK_END);
    BufferSize = ftell (InFptr);
    fseek (InFptr, 0, SEEK_SET);
    Buffer = (char *) malloc (BufferSize);
    if (Buffer == NULL) {
      Error (NULL, 0, 0, "memory allocation failure", NULL);
      goto Done;
    }

    if (fread (Buffer, BufferSize, 1, InFptr) != 1) {
      Error (NULL, 0, 0, FileName->FileName, "failed to read file contents");
      goto Done;
    }

    fclose (InFptr);
    InFptr = NULL;
    //
    // Check the buffer contents -- better be an IFR pack
    //
    if (BufferSize < sizeof (EFI_HII_PACK_HEADER)) {
      Error (NULL, 0, 0, FileName->FileName, "file is not large enough to contain an IFR pack");
      goto Done;
    }

    PackHeader = (EFI_HII_PACK_HEADER *) Buffer;
    if (PackHeader->Type != EFI_HII_IFR) {
      Error (NULL, 0, 0, FileName->FileName, "file does not appear to be an IFR pack");
      goto Done;
    }
    //
    // Process the contents
    //
    memset ((void *) &FormsetGuid, 0, sizeof (EFI_GUID));
    IfrStatus = IfrParsePack (Handle, (EFI_HII_IFR_PACK *) PackHeader, &FormsetGuid);
    if (IfrStatus != STATUS_SUCCESS) {
      goto Done;
    }

    Handle++;
    free (Buffer);
    Buffer    = NULL;
    FileName  = FileName->Next;
  }

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

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

  return Status;
}

static
STATUS
EmitDefaults (
  FILE_NAME_LIST  *HiiExportFiles,
  int             MfgDefaults,
  int             NoEmptyVarPacks
  )
/*++

Routine Description:

  Given a linked list of HII export files, read in each file,
  process the contents, and then emit variable packs.
  
Arguments:

  HiiExportFiles  - linked list of HII export files to process
  MfgDefaults     - emit manufacturing defaults
  NoEmptyVarPacks - don't emit variable packs if they are 0-length

Returns:

  STATUS_SUCCESS    - if successful
  STATUS_ERROR      - otherwise
  
--*/
{
  int                   HiiHandle;
  FILE                  *OutFptr;
  FILE                  *InFptr;
  EFI_HII_VARIABLE_PACK *VarPack;
  CHAR8                 OutFileName[MAX_PATH];
  CHAR8                 GuidString[100];
  STATUS                Status;
  CHAR8                 *Buffer;
  int                   FileSize;
  int                   DataTableIndex;
  EFI_HII_EXPORT_TABLE  *HiiExportTableHeader;
  EFI_HII_DATA_TABLE    *DataTableHeader;
  //
  // Init locals
  //
  HiiHandle = FIRST_HII_PACK_HANDLE;
  Buffer    = NULL;
  InFptr    = NULL;
  OutFptr   = NULL;
  Status    = STATUS_ERROR;
  //
  // Initialize our IFR parser
  //
  IfrParseInit ();
  //
  // Process each input HII export file
  //
  while (HiiExportFiles != NULL) {
    if (mGlobals.Verbose) {
      fprintf (stdout, "Processing file %s\n", HiiExportFiles->FileName);
    }
    //
    // Read in the entire file contents
    //
    if ((InFptr = fopen (HiiExportFiles->FileName, "rb")) == NULL) {
      Error (NULL, 0, 0, HiiExportFiles->FileName, "failed to open HII export file for reading");
      goto Done;
    }

    fseek (InFptr, 0, SEEK_END);
    FileSize = (int) ftell (InFptr);
    fseek (InFptr, 0, SEEK_SET);
    Buffer = (CHAR8 *) malloc (FileSize);
    if (Buffer == NULL) {
      Error (NULL, 0, 0, "memory allocation failure", NULL);
      goto Done;
    }

    if (fread (Buffer, FileSize, 1, InFptr) != 1) {
      Error (NULL, 0, 0, HiiExportFiles->FileName, "failed to read file contents");
      goto Done;
    }

    fclose (InFptr);
    InFptr                = NULL;
    HiiExportTableHeader  = (EFI_HII_EXPORT_TABLE *) Buffer;
    //
    // Walk all the data tables
    //
    DataTableHeader = (EFI_HII_DATA_TABLE *) (HiiExportTableHeader + 1);
    for (DataTableIndex = 0; DataTableIndex < (int) HiiExportTableHeader->NumberOfHiiDataTables; DataTableIndex++) {
      //
      // Make sure we're still pointing into our buffer
      //
      if (((char *) DataTableHeader < Buffer) || ((char *) DataTableHeader > Buffer + FileSize)) {
        Error (NULL, 0, 0, "bad data table size in input file", NULL);
        goto Done;
      }
      //
      // If there is an IFR pack, parse it
      //
      HiiHandle++;
      if (DataTableHeader->IfrDataOffset != 0) {
        if (IfrParsePack (
            HiiHandle,
            (EFI_HII_IFR_PACK *) ((char *) DataTableHeader + DataTableHeader->IfrDataOffset),
            &DataTableHeader->PackageGuid
            ) != STATUS_SUCCESS
            ) {
          goto Done;
        }
      }
      //
      // Next data pack
      //
      DataTableHeader = (EFI_HII_DATA_TABLE *) ((char *) DataTableHeader + DataTableHeader->DataTableSize);
    }

    free (Buffer);
    Buffer = NULL;
    //
    // Next input file
    //
    HiiExportFiles = HiiExportFiles->Next;
  }
  //
  // Now create defaults
  //
  if (IfrSetDefaults (MfgDefaults) != STATUS_SUCCESS) {
    goto Done;
  }
  //
  // Now retrieve each variable pack and write it out to a GUID-VarName.hpk file
  //
  HiiHandle = 0;
  do {
    Status = IfrGetVarPack (HiiHandle, &VarPack);
    if (Status == STATUS_SUCCESS) {
      //
      // Check for variable data length of 0
      //
      if ((NoEmptyVarPacks == 0) ||
          ((VarPack->Header.Length - sizeof (EFI_HII_VARIABLE_PACK) - VarPack->VariableNameLength) != 0)
          ) {
        //
        // Open the output file and write the variable pack
        //
        GuidToString (&VarPack->VariableGuid, GuidString);
        if (MfgDefaults) {
          sprintf (
            OutFileName,
            "%s-%S-MfgDefaults%s",
            GuidString,
            (CHAR16 *) (VarPack + 1),
            DEFAULT_HII_PACK_FILENAME_EXTENSION
            );
        } else {
          sprintf (
            OutFileName,
            "%s-%S-Defaults%s",
            GuidString,
            (CHAR16 *) (VarPack + 1),
            DEFAULT_HII_PACK_FILENAME_EXTENSION
            );
        }

        if (mGlobals.Verbose) {
          fprintf (
            stdout,
            "Creating %svariable defaults pack file %s\n",
            MfgDefaults ? "manufacturing " : "",
            OutFileName
            );
        }

        if ((OutFptr = fopen (OutFileName, "wb")) == NULL) {
          Error (NULL, 0, 0, OutFileName, "failed to open output file for writing", NULL);
          goto Done;
        }

        if (fwrite ((void *) VarPack, VarPack->Header.Length, 1, OutFptr) != 1) {
          Error (NULL, 0, 0, OutFileName, "failed to write defaults to output file");
          goto Done;
        }

        fclose (OutFptr);
        OutFptr = NULL;
      } else {
        //
        // Print a message that we skipped one if in verbose mode
        //
        if (mGlobals.Verbose) {
          GuidToString (&VarPack->VariableGuid, GuidString);
          if (MfgDefaults) {
            sprintf (
              OutFileName,
              "%s-%S-MfgDefaults%s",
              GuidString,
              (CHAR16 *) (VarPack + 1),
              DEFAULT_HII_PACK_FILENAME_EXTENSION
              );
          } else {
            sprintf (
              OutFileName,
              "%s-%S-Defaults%s",
              GuidString,
              (CHAR16 *) (VarPack + 1),
              DEFAULT_HII_PACK_FILENAME_EXTENSION
              );
          }

          fprintf (
            stdout,
            "Skipping 0-length %svariable defaults pack file %s\n",
            MfgDefaults ? "manufacturing " : "",
            OutFileName
            );
        }
      }
    }

    HiiHandle++;
  } while (Status == STATUS_SUCCESS);
  Status = STATUS_SUCCESS;
Done:
  IfrParseEnd ();
  if (Buffer != NULL) {
    free (Buffer);
  }

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

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

  return Status;
}

static
void
FreeGlobals (
  VOID
  )
/*++

Routine Description:

  Free up an memory we allocated so we can exit cleanly
  
Arguments:

Returns: NA

--*/
{
  FILE_NAME_LIST  *Next;
  //
  // Free up input pack file names
  //
  while (mGlobals.PackFileNames != NULL) {
    Next = mGlobals.PackFileNames->Next;
    free (mGlobals.PackFileNames);
    mGlobals.PackFileNames = Next;
  }
  //
  // Free up input HII export file names
  //
  while (mGlobals.HiiExportFileNames != NULL) {
    Next = mGlobals.HiiExportFileNames->Next;
    free (mGlobals.HiiExportFileNames);
    mGlobals.HiiExportFileNames = Next;
  }
}

static
STATUS
DumpHiiExportFile (
  char    *HiiExportFileName,
  char    *OutputFileName
  )
/*++

Routine Description:

  Dump the contents of an HII export file for debug purposes
  
Arguments:

  HiiExportFileName - name of input HII export file
  OutputFileName    - name of output file to dump contents

Returns: 
  STATUS_SUCCESS  - no problems
  STATUS_ERROR    - problems encountered processing the file

--*/
{
  FILE                *InFptr;

  FILE                *OutFptr;
  char                *Buffer;
  char                *BufferStart;
  char                *BufferEnd;
  int                 BufferSize;
  STATUS              Status;
  char                GuidString[100];
  int                 Counter;
  int                 NumberOfTables;
  EFI_HII_DATA_TABLE  *DataTableHeader;
  EFI_GUID              HiiExportRevisionGuid = EFI_HII_PROTOCOL_GUID;
  //
  // Init locals
  //
  InFptr      = NULL;
  OutFptr     = NULL;
  BufferStart = NULL;
  Status      = STATUS_ERROR;
  //
  // Open the input file
  //
  if ((InFptr = fopen (HiiExportFileName, "rb")) == NULL) {
    Error (NULL, 0, 0, HiiExportFileName, "failed to open input HII export file for reading");
    goto Done;
  }
  //
  // Open the output file
  //
  if ((OutFptr = fopen (OutputFileName, "w")) == NULL) {
    Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
    goto Done;
  }
  //
  // Get the file size, then allocate a buffer and read in the file contents.
  //
  fseek (InFptr, 0, SEEK_END);
  BufferSize = (int) ftell (InFptr);
  fseek (InFptr, 0, SEEK_SET);
  BufferStart = (char *) malloc (BufferSize);
  if (BufferStart == NULL) {
    Error (NULL, 0, 0, "memory allocation failure", NULL);
    goto Done;
  }

  if (fread (BufferStart, BufferSize, 1, InFptr) != 1) {
    Error (NULL, 0, 0, HiiExportFileName, "error reading file contents");
    goto Done;
  }

  fclose (InFptr);
  InFptr = NULL;
  //
  // Crude check of the input data -- check the size and GUID
  //
  if (BufferSize < sizeof (EFI_HII_EXPORT_TABLE)) {
    Error (NULL, 0, 0, HiiExportFileName, "files not large enough to contain an HII export table header");
    goto Done;
  }

  if (memcmp (&((EFI_HII_EXPORT_TABLE *) BufferStart)->Revision, &HiiExportRevisionGuid, sizeof (EFI_GUID)) != 0) {
    Error (NULL, 0, 0, HiiExportFileName, "invalid HII export revision GUID -- is this an HII export file?");
    //
    // See if it's a HII pack file
    //
    TestDumpHiiPack (OutFptr, BufferStart, BufferSize);
    goto Done;
  }
  //
  // Now walk the export data
  //
  Buffer    = BufferStart;
  BufferEnd = BufferStart + BufferSize;
  //
  // Dump the header
  //
  fprintf (OutFptr, "HII dump of file %s\n\n", HiiExportFileName);
  NumberOfTables = ((EFI_HII_EXPORT_TABLE *) Buffer)->NumberOfHiiDataTables;
  fprintf (OutFptr, "Number of data tables:  %d\n", NumberOfTables);
  GuidToString (&((EFI_HII_EXPORT_TABLE *) Buffer)->Revision, GuidString);
  fprintf (OutFptr, "HII export revision:    %s\n", GuidString);
  //
  // Now walk the data tables
  //
  Buffer += sizeof (EFI_HII_EXPORT_TABLE);
  for (Counter = 0; Counter < NumberOfTables; Counter++) {
    DataTableHeader = (EFI_HII_DATA_TABLE *) Buffer;
    fprintf (OutFptr, "----------------------------------------------------------\n");
    fprintf (OutFptr, "  DataTable at offset 0x%08X\n", (int) Buffer - (int) BufferStart);
    fprintf (OutFptr, "    HII Handle:                            0x%08X\n", DataTableHeader->HiiHandle);
    GuidToString (&DataTableHeader->PackageGuid, GuidString);
    fprintf (OutFptr, "    Package GUID:                          %s\n", GuidString);
    fprintf (OutFptr, "    Data table size:                       0x%08X\n", DataTableHeader->DataTableSize);
    fprintf (OutFptr, "    IFR data offset:                       0x%08X\n", DataTableHeader->IfrDataOffset);
    fprintf (OutFptr, "    String data offset:                    0x%08X\n", DataTableHeader->StringDataOffset);
    fprintf (OutFptr, "    Variable data offset:                  0x%08X\n", DataTableHeader->VariableDataOffset);
    fprintf (OutFptr, "    Device path offset:                    0x%08X\n", DataTableHeader->DevicePathOffset);
    fprintf (OutFptr, "    Number of variable data:               0x%08X\n", DataTableHeader->NumberOfVariableData);
    fprintf (OutFptr, "    Number of languages:                   0x%08X\n", DataTableHeader->NumberOfLanguages);
    //
    // Dump strings
    //
    if (DataTableHeader->StringDataOffset != 0) {
      DumpStringPack (
        OutFptr,
        (EFI_HII_STRING_PACK *) ((char *) DataTableHeader + DataTableHeader->StringDataOffset),
        DataTableHeader->StringDataOffset,
        6
        );
    }
    //
    // Dump IFR
    //
    if (DataTableHeader->IfrDataOffset != 0) {
      DumpIfrPack (
        OutFptr,
        (EFI_HII_IFR_PACK *) ((char *) DataTableHeader + DataTableHeader->IfrDataOffset),
        DataTableHeader->IfrDataOffset,
        6
        );
    }
    //
    // Dump variables
    //
    if (DataTableHeader->VariableDataOffset != 0) {
      DumpVariablePacks (
        OutFptr,
        (EFI_HII_VARIABLE_PACK *) ((char *) DataTableHeader + DataTableHeader->VariableDataOffset),
        DataTableHeader->NumberOfVariableData,
        DataTableHeader->VariableDataOffset,
        6
        );
    }
    //
    // Dump device path
    //
    //
    // Check position before advancing
    //
    if ((Buffer + DataTableHeader->DataTableSize < Buffer) || (Buffer + DataTableHeader->DataTableSize > BufferEnd)) {
      Error (NULL, 0, 0, HiiExportFileName, "bad data table size at offset 0x%X", (int) Buffer - (int) BufferStart);
      goto Done;
    }

    Buffer += DataTableHeader->DataTableSize;
  }

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

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

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

  return Status;
}

static
void
DumpIfrPack (
  FILE                  *OutFptr,
  EFI_HII_IFR_PACK      *Pack,
  int                   BaseOffset,
  int                   Indent
  )
/*++

Routine Description:

  Dump the contents of an IFR pack for debug purposes
  
Arguments:

  OutFptr         - file pointer to which to dump the output
  Pack            - pointer to IFR pack to dump
  BaseOffset      - offset from which Pack starts in its parent data table
  Indent          - indent this many spaces when printing text to OutFptr

Returns: 
  NA

--*/
{
  EFI_IFR_FORM_SET  *IfrFormSet;
  char              GuidString[100];
  if (Pack->Header.Type != EFI_HII_IFR) {
    Error (NULL, 0, 0, "found non-IFR pack type at IFR data offset", NULL);
    return ;
  }

  fprintf (OutFptr, "%*cIFR pack at offset      0x%08X\n", Indent, ' ', BaseOffset);
  fprintf (OutFptr, "%*c  Length                0x%08X\n", Indent, ' ', Pack->Header.Length);
  //
  // Get the GUID from the formset
  //
  IfrFormSet = (EFI_IFR_FORM_SET *) (Pack + 1);
  GuidToString (&IfrFormSet->Guid, GuidString);
  fprintf (OutFptr, "%*c  Variable GUID         %s\n", Indent, ' ', GuidString);
  //
  // Print the IFR formset size, with a note indicating if it's a min (likely stub)
  // formset
  //
  if (Pack->Header.Length == sizeof (EMPTY_FORMSET_PACK)) {
    fprintf (
      OutFptr,
      "%*c  IFR formset size      0x%08X (empty formset)\n",
      Indent,
      ' ',
      Pack->Header.Length - sizeof (EFI_HII_IFR_PACK)
      );
  } else {
    fprintf (
      OutFptr,
      "%*c  IFR formset size      0x%08X\n",
      Indent,
      ' ',
      Pack->Header.Length - sizeof (EFI_HII_IFR_PACK)
      );
  }
  //
  // Dump raw bytes -- not much use
  //
}

static
void
DumpVariablePacks (
  FILE                  *OutFptr,
  EFI_HII_VARIABLE_PACK *Pack,
  int                   NumPacks,
  int                   BaseOffset,
  int                   Indent
  )
/*++

Routine Description:

  Dump the contents of an IFR pack for debug purposes
  
Arguments:

  OutFptr         - file pointer to which to dump the output
  Pack            - pointer to variable pack to dump
  NumPacks        - number of packs in Pack[] array
  BaseOffset      - offset from which Pack starts in its parent data table
  Indent          - indent this many spaces when printing text to OutFptr

Returns: 
  NA

--*/
{
  int   Count;

  int   Len;
  char  GuidString[100];

  for (Count = 0; Count < NumPacks; Count++) {
    if (Pack->Header.Type != EFI_HII_VARIABLE) {
      Error (NULL, 0, 0, "found non-variable pack type in variable pack array", NULL);
      return ;
    }

    fprintf (OutFptr, "%*cVariable pack at offset 0x%08X\n", Indent, ' ', BaseOffset);
    fprintf (OutFptr, "%*c  Length                0x%08X\n", Indent, ' ', Pack->Header.Length);
    GuidToString (&Pack->VariableGuid, GuidString);
    fprintf (OutFptr, "%*c  Variable GUID         %s\n", Indent, ' ', GuidString);
    fprintf (OutFptr, "%*c  Variable Name         %S\n", Indent, ' ', (CHAR16 *) (Pack + 1));
    Len = sizeof (EFI_HII_VARIABLE_PACK) + Pack->VariableNameLength;
    fprintf (OutFptr, "%*c  Variable Size         0x%08X\n", Indent, ' ', Pack->Header.Length - Len);
    //
    // Dump raw bytes
    //
    DumpRawBytes (OutFptr, (char *) Pack + Len, Pack->Header.Length - Len, Len, Indent + 2);
    BaseOffset += Pack->Header.Length;
    Pack = (EFI_HII_VARIABLE_PACK *) ((char *) Pack + Pack->Header.Length);
  }
}

static
void
DumpStringPack (
  FILE                  *OutFptr,
  EFI_HII_STRING_PACK   *Pack,
  int                   BaseOffset,
  int                   Indent
  )
/*++

Routine Description:

  Dump the contents of a string pack array for debug purposes
  
Arguments:

  OutFptr         - file pointer to which to dump the output
  Pack            - pointer to string pack array to dump
  BaseOffset      - offset from which Pack starts in its parent data table
  Indent          - indent this many spaces when printing text to OutFptr

Returns: 
  NA

--*/
{
  int     Count;
  int     *IndexPtr;
  CHAR16  *WCPtr;
  //
  // String pack array is terminated with a zero-length string pack
  //
  while (Pack->Header.Length > 0) {
    if (Pack->Header.Type != EFI_HII_STRING) {
      Error (NULL, 0, 0, "found non-string pack type in string pack array", NULL);
      return ;
    }

    fprintf (OutFptr, "%*cString pack at offset   0x%08X\n", Indent, ' ', BaseOffset);
    fprintf (OutFptr, "%*c  Length                0x%08X\n", Indent, ' ', Pack->Header.Length);
    fprintf (
      OutFptr,
      "%*c  Language              %S\n",
      Indent,
      ' ',
      (CHAR16 *) ((char *) Pack + Pack->LanguageNameString)
      );
    fprintf (
      OutFptr,
      "%*c  Printable Language    %S\n",
      Indent,
      ' ',
      (CHAR16 *) ((char *) Pack + Pack->PrintableLanguageName)
      );
    fprintf (OutFptr, "%*c  Number of strings     0x%08X\n", Indent, ' ', Pack->NumStringPointers);
    fprintf (OutFptr, "%*c  Attributes            0x%08X\n", Indent, ' ', Pack->Attributes);
    IndexPtr = (int *) (Pack + 1);
    //
    // Dump string data
    //
    if (mGlobals.DumpStrings) {
      for (Count = 0; Count < (int) Pack->NumStringPointers; Count++) {
        fprintf (OutFptr, "%*c    String 0x%04X: ", Indent, ' ', Count);
        //
        // Print raw hex bytes
        //
        for (WCPtr = (CHAR16 *) ((char *) Pack +*IndexPtr); *WCPtr != 0; WCPtr++) {
          fprintf (OutFptr, "%02X ", (unsigned int) *WCPtr);
        }

        fprintf (OutFptr, "00\n");
        IndexPtr++;
      }
    }

    BaseOffset += Pack->Header.Length;
    Pack = (EFI_HII_STRING_PACK *) ((char *) Pack + Pack->Header.Length);
  }
}

static
void
TestDumpHiiPack (
  FILE    *OutFptr,
  char    *Buffer,
  int     BufferSize
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  OutFptr     - GC_TODO: add argument description
  Buffer      - GC_TODO: add argument description
  BufferSize  - GC_TODO: add argument description

Returns:

  GC_TODO: add return values

--*/
{
  EFI_HII_PACK_HEADER *PackHeader;

  PackHeader = (EFI_HII_PACK_HEADER *) Buffer;
  //
  // Check size match
  //
  if (PackHeader->Length != (unsigned int) BufferSize) {
    return ;
  }
  //
  // Check type
  //
  switch (PackHeader->Type) {
  case EFI_HII_STRING:
    fprintf (stdout, "Dumping as string pack\n");
    DumpStringPack (OutFptr, (EFI_HII_STRING_PACK *) Buffer, 0, 2);
    break;

  case EFI_HII_IFR:
    fprintf (stdout, "Dumping as IFR pack\n");
    DumpIfrPack (OutFptr, (EFI_HII_IFR_PACK *) Buffer, 0, 2);
    break;

  case EFI_HII_VARIABLE:
    fprintf (stdout, "Dumping as IFR pack\n");
    DumpVariablePacks (OutFptr, (EFI_HII_VARIABLE_PACK *) Buffer, 1, 0, 2);
    break;
  }
}

static
void
DumpRawBytes (
  FILE                  *OutFptr,
  char                  *Buffer,
  int                   Count,
  int                   BaseOffset,
  int                   Indent
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  OutFptr     - GC_TODO: add argument description
  Buffer      - GC_TODO: add argument description
  Count       - GC_TODO: add argument description
  BaseOffset  - GC_TODO: add argument description
  Indent      - GC_TODO: add argument description

Returns:

  GC_TODO: add return values

--*/
{
  int Counter;

  for (Counter = 0; Counter < Count; Counter++) {
    if ((Counter & 0xF) == 0) {
      if (Counter != 0) {
        fprintf (OutFptr, "\n%*c%08X ", Indent, ' ', Counter);
      } else {
        fprintf (OutFptr, "\n%*c%08X ", Indent, ' ', Counter);
      }
    }

    fprintf (OutFptr, "%02X ", (unsigned int) (unsigned char) *Buffer);
    Buffer++;
  }

  fprintf (OutFptr, "\n");
}

void
GuidToString (
  EFI_GUID   *Guid,
  char       *Str
  )
/*++

Routine Description:

  Given a pointer to a GUID, sprint the value into a string
  
Arguments:

  Guid   - pointer to input GUID
  Str    - pointer to outgoing printed GUID value

Returns:
  NA
  
--*/
{
  sprintf (
    Str,
    "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
    Guid->Data1,
    Guid->Data2,
    Guid->Data3,
    Guid->Data4[0],
    Guid->Data4[1],
    Guid->Data4[2],
    Guid->Data4[3],
    Guid->Data4[4],
    Guid->Data4[5],
    Guid->Data4[6],
    Guid->Data4[7]
    );
}

int
FindFilesCallback (
  char *FoundFileName
  )
/*++

Routine Description:

  Callback function used to get files matching a file mask. This
  function is called when the command-line arguments to this utility
  are parsed and the user specified "-s Path FileMask" to process
  all HII export files in Path and its subdirectories that match
  FileMask.
  
Arguments:

  FoundFileName - name of file found.

Returns:
  non-zero    - caller should halt processing
  zero        - no problems while processing FoundFileName
  
--*/
{
  FILE_NAME_LIST  *FileName;

  FILE_NAME_LIST  *TempFileName;

  FileName = (FILE_NAME_LIST *) malloc (sizeof (FILE_NAME_LIST));
  if (FileName == NULL) {
    Error (NULL, 0, 0, "memory allocation failure", NULL);
    return STATUS_ERROR;
  }

  memset ((void *) FileName, 0, sizeof (FILE_NAME_LIST));
  strcpy (FileName->FileName, FoundFileName);
  if (mGlobals.HiiExportFileNames == NULL) {
    mGlobals.HiiExportFileNames = FileName;
  } else {
    //
    // Add to the end of the list
    //
    for (TempFileName = mGlobals.HiiExportFileNames; TempFileName->Next != NULL; TempFileName = TempFileName->Next)
      ;
    TempFileName->Next = FileName;
  }

  return 0;
}

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

Routine Description:

  Process the command line arguments
  
Arguments:

  As per standard C main()

Returns:

  STATUS_SUCCESS    - if successful
  STATUS_ERROR      - otherwise
  
--*/
// GC_TODO:    Argc - add argument and description to function comment
// GC_TODO:    ] - add argument and description to function comment
{
  FILE_NAME_LIST      *FileName;

  FILE_NAME_LIST      *TempFileName;
  FILE                *InFptr;
  EFI_HII_PACK_HEADER PackHeader;

  memset ((void *) &mGlobals, 0, sizeof (mGlobals));
  //
  // Skip program name
  //
  Argc--;
  Argv++;

  if (Argc == 0) {
    Usage ();
    return STATUS_ERROR;
  }
  //
  // First arg must be one of create, merge, defaults, or dump
  //
  if (_stricmp (Argv[0], "create") == 0) {
    mGlobals.Mode = MODE_CREATE_HII_EXPORT;
  } else if (_stricmp (Argv[0], "merge") == 0) {
    mGlobals.Mode = MODE_MERGE_HII_EXPORTS;
  } else if (_stricmp (Argv[0], "defaults") == 0) {
    mGlobals.Mode = MODE_EMIT_DEFAULTS;
  } else if (_stricmp (Argv[0], "dump") == 0) {
    mGlobals.Mode = MODE_DUMP_HII_EXPORT;
  } else if (strcmp (Argv[0], "-?") == 0) {
    Usage ();
    return STATUS_ERROR;
  } else {
    Error (NULL, 0, 0, Argv[0], "unrecognized mode");
    return STATUS_ERROR;
  }

  Argv++;
  Argc--;
  //
  // Process until no more args.
  //
  while (Argc > 0) {
    if (_stricmp (Argv[0], "-o") == 0) {
      //
      // -o option to specify the output file
      //
      if ((Argc <= 1) || (Argv[1][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing output file name");
        return STATUS_ERROR;
      }

      if (mGlobals.OutputFileName[0] == 0) {
        mGlobals.OutputFileName[MAX_PATH - 1] = 0;
        strncpy (mGlobals.OutputFileName, Argv[1], MAX_PATH - 1);
      } else {
        Error (UTILITY_NAME, 0, 0, Argv[1], "-o option already specified with '%s'", mGlobals.OutputFileName);
        return STATUS_ERROR;
      }

      Argv++;
      Argc--;
    } else if (_stricmp (Argv[0], "-mfg") == 0) {
      mGlobals.MfgFlag = 1;
    } else if (_stricmp (Argv[0], "-g") == 0) {
      //
      // -g option to specify the guid
      //
      if ((Argc <= 1) || (Argv[1][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing GUID");
        return STATUS_ERROR;
      }

      StringToGuid (Argv[1], &mGlobals.Guid);
      mGlobals.GuidSpecified = 1;
      Argv++;
      Argc--;
    } else if (_stricmp (Argv[0], "-v") == 0) {
      mGlobals.Verbose = 1;
    } else if (_stricmp (Argv[0], "-p") == 0) {
      //
      // -p option to specify an input pack file. Only valid for 'create' mode
      //
      if (mGlobals.Mode != MODE_CREATE_HII_EXPORT) {
        Error (NULL, 0, 0, Argv[0], "option only valid in 'create' mode");
        return STATUS_ERROR;
      }

      if ((Argc <= 1) || (Argv[1][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing pack file name");
        return STATUS_ERROR;
      }
      //
      // Consume arguments until next -arg or end
      //
      do {
        Argv++;
        Argc--;
        //
        // Open the file, read the pack header, and figure out what type of
        // HII pack it is.
        //
        if ((InFptr = fopen (Argv[0], "rb")) == NULL) {
          Error (NULL, 0, 0, Argv[0], "failed to open input HII pack file for reading");
          return STATUS_ERROR;
        }

        if (fread (&PackHeader, sizeof (EFI_HII_PACK_HEADER), 1, InFptr) != 1) {
          Error (NULL, 0, 0, Argv[0], "failed to read pack header from input HII pack file");
          fclose (InFptr);
          return STATUS_ERROR;
        }

        fclose (InFptr);
        if ((PackHeader.Type != EFI_HII_STRING) &&
            (PackHeader.Type != EFI_HII_IFR) &&
            (PackHeader.Type != EFI_HII_VARIABLE)
            ) {
          Error (NULL, 0, 0, Argv[0], "unsupported HII pack type 0x%X", (unsigned int) PackHeader.Type);
          return STATUS_ERROR;
        }
        //
        // Add this file name to our list of pack files
        //
        FileName = (FILE_NAME_LIST *) malloc (sizeof (FILE_NAME_LIST));
        if (FileName == NULL) {
          Error (NULL, 0, 0, "memory allocation failure", NULL);
          return STATUS_ERROR;
        }

        memset ((void *) FileName, 0, sizeof (FILE_NAME_LIST));
        FileName->Tag = (int) PackHeader.Type;
        strcpy (FileName->FileName, Argv[0]);
        if (mGlobals.PackFileNames == NULL) {
          mGlobals.PackFileNames = FileName;
        } else {
          //
          // Add to the end of the list
          //
          for (TempFileName = mGlobals.PackFileNames; TempFileName->Next != NULL; TempFileName = TempFileName->Next)
            ;
          TempFileName->Next = FileName;
        }
      } while ((Argc > 1) && (Argv[1][0] != '-'));
    } else if (_stricmp (Argv[0], "-noemptyvarpacks") == 0) {
      mGlobals.NoEmptyVarPacks = 1;
    } else if (_stricmp (Argv[0], "-novarpacks") == 0) {
      mGlobals.NoVarPacks = 1;
    } else if (_stricmp (Argv[0], "-x") == 0) {
      //
      // -x option to specify an input HII export file name. Not valid for 'create' mode
      //
      if (mGlobals.Mode == MODE_CREATE_HII_EXPORT) {
        Error (NULL, 0, 0, Argv[0], "option is not valid in 'create' mode");
        return STATUS_ERROR;
      }

      if ((Argc <= 1) || (Argv[1][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing HII export input file name");
        return STATUS_ERROR;
      }
      //
      // Consume arguments until next -arg or end
      //
      do {
        Argv++;
        Argc--;
        //
        // Add this file name to our list of export files
        //
        FileName = (FILE_NAME_LIST *) malloc (sizeof (FILE_NAME_LIST));
        if (FileName == NULL) {
          Error (NULL, 0, 0, "memory allocation failure", NULL);
          return STATUS_ERROR;
        }

        memset ((void *) FileName, 0, sizeof (FILE_NAME_LIST));
        strcpy (FileName->FileName, Argv[0]);
        if (mGlobals.HiiExportFileNames == NULL) {
          mGlobals.HiiExportFileNames = FileName;
        } else {
          //
          // Add to the end of the list
          //
          for (TempFileName = mGlobals.HiiExportFileNames;
               TempFileName->Next != NULL;
               TempFileName = TempFileName->Next
              )
            ;
          TempFileName->Next = FileName;
        }
      } while ((Argc > 1) && (Argv[1][0] != '-'));
    } else if (_stricmp (Argv[0], "-dumpstrings") == 0) {
      mGlobals.DumpStrings = 1;
    } else if (_stricmp (Argv[0], "-s") == 0) {
      //
      // -s option to specify input HII export files using a path and file mask.
      // Only valid in merge mode
      //
      if (mGlobals.Mode != MODE_MERGE_HII_EXPORTS) {
        Error (NULL, 0, 0, Argv[0], "option only valid in 'merge' mode");
        return STATUS_ERROR;
      }

      if ((Argc <= 1) || (Argv[1][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing root directory name");
        return STATUS_ERROR;
      }

      if ((Argc <= 2) || (Argv[2][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing file mask");
        return STATUS_ERROR;
      }
      //
      // Call our function to process the directory and file mask. If
      // the directory does not start with c:\, then prepend cwd to it.
      //
      if (FindFiles (Argv[1], Argv[2], FindFilesCallback)) {
        Error (NULL, 0, 0, "failed to process matching files", "%s\\%s", Argv[1], Argv[2]);
        return STATUS_ERROR;
      }

      Argv += 2;
      Argc -= 2;
    } else if (_stricmp (Argv[0], "-p") == 0) {
      //
      // -p option to specify an input pack file. Only valid for 'create' mode
      //
      if (mGlobals.Mode != MODE_CREATE_HII_EXPORT) {
        Error (NULL, 0, 0, Argv[0], "option only valid in 'create' mode");
        return STATUS_ERROR;
      }

      if ((Argc <= 1) || (Argv[1][0] == '-')) {
        Error (UTILITY_NAME, 0, 0, Argv[0], "missing pack file name");
        return STATUS_ERROR;
      }
      //
      // Consume arguments until next -arg or end
      //
      do {
        Argv++;
        Argc--;
        //
        // Open the file, read the pack header, and figure out what type of
        // HII pack it is.
        //
        if ((InFptr = fopen (Argv[0], "rb")) == NULL) {
          Error (NULL, 0, 0, Argv[0], "failed to open input HII pack file for reading");
          return STATUS_ERROR;
        }

        if (fread (&PackHeader, sizeof (EFI_HII_PACK_HEADER), 1, InFptr) != 1) {
          Error (NULL, 0, 0, Argv[0], "failed to read pack header from input HII pack file");
          fclose (InFptr);
          return STATUS_ERROR;
        }

        fclose (InFptr);
        if ((PackHeader.Type != EFI_HII_STRING) &&
            (PackHeader.Type != EFI_HII_IFR) &&
            (PackHeader.Type != EFI_HII_VARIABLE)
            ) {
          Error (NULL, 0, 0, Argv[0], "unsupported HII pack type 0x%X", (unsigned int) PackHeader.Type);
          return STATUS_ERROR;
        }
        //
        // Add this file name to our list of pack files
        //
        FileName = (FILE_NAME_LIST *) malloc (sizeof (FILE_NAME_LIST));
        if (FileName == NULL) {
          Error (NULL, 0, 0, "memory allocation failure", NULL);
          return STATUS_ERROR;
        }

        memset ((void *) FileName, 0, sizeof (FILE_NAME_LIST));
        FileName->Tag = (int) PackHeader.Type;
        strcpy (FileName->FileName, Argv[0]);
        if (mGlobals.PackFileNames == NULL) {
          mGlobals.PackFileNames = FileName;
        } else {
          //
          // Add to the end of the list
          //
          for (TempFileName = mGlobals.PackFileNames; TempFileName->Next != NULL; TempFileName = TempFileName->Next)
            ;
          TempFileName->Next = FileName;
        }
      } while ((Argc > 1) && (Argv[1][0] != '-'));
    } else {
      Error (NULL, 0, 0, Argv[0], "unrecognized option");
      return STATUS_ERROR;
    }

    Argv++;
    Argc--;
  }
  //
  // All modes except 'defaults' requires an output file name
  //
  if (mGlobals.Mode != MODE_EMIT_DEFAULTS) {
    if (mGlobals.OutputFileName[0] == 0) {
      Error (NULL, 0, 0, "must specify '-o OutputFileName'", NULL);
      return STATUS_ERROR;
    }
    //
    // If merging, then you have to specify at least one HII export files.
    // We support specifying only one file in case you want to take an export file
    // and emit a copy with different (for example, manufacturing) defaults.
    //
    if (mGlobals.Mode == MODE_MERGE_HII_EXPORTS) {
      if (mGlobals.HiiExportFileNames == NULL) {
        Error (NULL, 0, 0, "must specify at least one HII export file in 'merge' mode", NULL);
        return STATUS_ERROR;
      }
    } else if (mGlobals.Mode == MODE_CREATE_HII_EXPORT) {
      //
      // Must have specified at least one HII pack file
      //
      if (mGlobals.PackFileNames == NULL) {
        Error (NULL, 0, 0, "must specify at least one input HII pack file in 'create' mode", NULL);
        return STATUS_ERROR;
      }
    }
  } else {
    //
    // Must have specified an input HII export file name
    //
    if (mGlobals.HiiExportFileNames == NULL) {
      Error (NULL, 0, 0, "must specify at least one '-x HiiExportFileName'", NULL);
      return STATUS_ERROR;
    }
  }

  return STATUS_SUCCESS;
}

static
void
Usage (
  VOID
  )
/*++

Routine Description:

  Print usage information for this utility.
  
Arguments:

  None.

Returns:

  Nothing.
  
--*/
{
  int          Index;
  const char   *Str[] = {
    UTILITY_NAME" "UTILITY_VERSION" - Create/Dump HII Database Files Utility",
    "  Copyright (C), 2004 - 2008 Intel Corporation",
#if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
    "  Built from "UTILITY_BUILD", project of "UTILITY_VENDOR,
#endif
    "",
    "Usage:",
    "  "UTILITY_NAME " [MODE] [OPTION]",
    "Modes:",
    "  create     create an HII export file from one or more HII pack files",
    "  merge      merge two or more HII export files into one HII export file",
    "  defaults   emit variable defaults from an input HII export file",
    "  dump       ASCII dump the contents of an HII export file",
    "Options for all modes:",
    "  -o FileName write output to FileName",
    "  -mfg        use manufacturing defaults from IFR rather than standard defaults",
    "  -g GUID     use GUID for a package GUID in the data tables where applicable",
    "  -v          verbose operation",
    "Options for 'create' mode:",
    "  -p PackFileName(s)  include contents of HII pack file PackFileName",
    "                      in the output file",
    "  -novarpacks         don't emit variable packs to the output file",
    "Options for 'merge' mode:",
    "  -x HiiExportFileName(s)  include contents of HII export file",
    "                           HiiExportFileName in the output file",
    "  -s Path FileMask         include all matching HII export files in Path",
    "                           and its subdirectories in the output file.",
    "                           If Path does not begin with the form C:\\, then",
    "                           it is assumed to be relative to the current working",
    "                           directory. FileMask may contain wildcard characters.",
    "Options for 'defaults' mode:",
    "  -x HiiExportFileName     emit defaults from all variables referenced",
    "                           in input file HiiExportFileName",
    "  -noemptyvarpacks         don't emit variable packs for 0-length variables",
    "Options for 'dump' mode:",
    "  -x HiiExportFileName     dump contents of input file HiiExportFileName",
    "  -dumpstrings             dump string data",
    NULL
  };
  for (Index = 0; Str[Index] != NULL; Index++) {
    fprintf (stdout, "%s\n", Str[Index]);
  }
}
