/**@file
  Entry and initialization module for the browser.

Copyright (c) 2006 - 2007 Intel Corporation. <BR>
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.

**/

#include "Setup.h"
#include "Ui.h"

FUNCTIION_KEY_SETTING gFunctionKeySettingTable[] = {
  //
  // Boot Manager
  //
  {
    {
      0x847bc3fe,
      0xb974,
      0x446d,
      {
        0x94,
        0x49,
        0x5a,
        0xd5,
        0x41,
        0x2e,
        0x99,
        0x3b
      }
    },
    NONE_FUNCTION_KEY_SETTING
  },
  //
  // Device Manager
  //
  {
    {
      0x3ebfa8e6,
      0x511d,
      0x4b5b,
      {
        0xa9,
        0x5f,
        0xfb,
        0x38,
        0x26,
        0xf,
        0x1c,
        0x27
      }
    },
    NONE_FUNCTION_KEY_SETTING
  },
  //
  // BMM Formset.
  //
  {
    {
      0x642237c7,
      0x35d4,
      0x472d,
      {
        0x83,
        0x65,
        0x12,
        0xe0,
        0xcc,
        0xf2,
        0x7a,
        0x22
      }
    },
    NONE_FUNCTION_KEY_SETTING
  },
  //
  // BMM File Explorer Formset.
  //
  {
    {
      0x1f2d63e1,
      0xfebd,
      0x4dc7,
      {
        0x9c,
        0xc5,
        0xba,
        0x2b,
        0x1c,
        0xef,
        0x9c,
        0x5b
      }
    },
    NONE_FUNCTION_KEY_SETTING
  },
};

STATIC
EFI_STATUS
InitializeBinaryStructures (
  IN  EFI_HII_HANDLE                           *Handle,
  IN  BOOLEAN                                  UseDatabase,
  IN  EFI_IFR_PACKET                           *Packet,
  IN  UINT8                                    *NvMapOverride,
  IN  UINTN                                    NumberOfIfrImages,
  EFI_FILE_FORM_TAGS                           **FileFormTagsHead
  );

STATIC
EFI_STATUS
InitializeTagStructures (
  IN  EFI_IFR_BINARY                            *BinaryData,
  OUT EFI_FILE_FORM_TAGS                        *FileFormTags
  );

STATIC
UI_MENU_OPTION        *
DisplayHomePage (
  IN UINTN                                    NumberOfIfrImages,
  IN EFI_FILE_FORM_TAGS                       *FileFormTagsHead,
  IN UINT8                                    *CallbackData
  );

STATIC
EFI_STATUS
GetIfrBinaryData (
  IN EFI_HII_PROTOCOL *Hii,
  IN EFI_HII_HANDLE   HiiHandle,
  IN EFI_IFR_PACKET   *Packet,
  IN EFI_IFR_BINARY   *BinaryData
  );

STATIC
EFI_STATUS
InstallPrint (
  VOID
  );

STATIC
EFI_STATUS
EFIAPI
SendForm (
  IN EFI_FORM_BROWSER_PROTOCOL        * This,
  IN BOOLEAN                          UseDatabase,
  IN EFI_HII_HANDLE                   * Handle,
  IN UINTN                            HandleCount,
  IN EFI_IFR_PACKET                   * Packet,
  IN EFI_HANDLE                       CallbackHandle,
  IN UINT8                            *NvMapOverride,
  IN EFI_SCREEN_DESCRIPTOR            *ScreenDimensions, OPTIONAL
  OUT BOOLEAN                         *ResetRequired OPTIONAL
  )
/*++

Routine Description:

  This is the routine which an external caller uses to direct the browser
  where to obtain it's information.

Arguments:

  UseDatabase -     If set to TRUE, then all information is retrieved from the HII database handle specified
                    If set to FALSE, then the passed in Packet and CallbackHandle is used and Handle is ignored

  Handle -          A pointer to an array of Handles.  If HandleCount > 1 we display a list of the formsets for the handles specified

  HandleCount -     The number of Handles specified in Handle.

  Packet -          Valid only if UseDatabase is FALSE.  Packet defines the pages being passed into
                    the browser.  This is composed of IFR data as well as String information.

  CallbackHandle -  The handle which contains the calling driver's EFI_FORM_CALLBACK_PROTOCOL interface.

  ScreenDimenions - This allows the browser to be called so that it occupies a portion of the physical screen instead of
                    dynamically determining the screen dimensions.

  NvMapOverride -   This buffer is used only when there is no NV variable to define the current settings and the caller
                    needs to provide to the browser the current settings for the "fake" NV variable.  If used, no saving
                    of an NV variable will be possible.  This parameter is also ignored if HandleCount > 1.

Returns:

--*/
{
  EFI_FORM_CALLBACK_PROTOCOL  *FormCallback;
  EFI_FILE_FORM_TAGS          *FileFormTagsHead;
  UI_MENU_OPTION              *Selection;
  UI_MENU_OPTION              *AltSelection;
  EFI_STATUS                  Status;
  BOOLEAN                     Callback;
  VOID                        *CallbackData;
  EFI_HII_HANDLE              BackupHandle;

  ZeroMem (&gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));

  gPreviousValue  = AllocatePool (0x1000);
  CallbackData    = AllocatePool (0x10000);
  ASSERT (gPreviousValue != NULL);
  ASSERT (CallbackData != NULL);

  do {
    //
    // Seed the dimensions in the global
    //
    gST->ConOut->QueryMode (
                  gST->ConOut,
                  gST->ConOut->Mode->Mode,
                  &gScreenDimensions.RightColumn,
                  &gScreenDimensions.BottomRow
                  );

    if (ScreenDimensions != NULL) {
      //
      // Check local dimension vs. global dimension.
      //
      if ((gScreenDimensions.RightColumn < ScreenDimensions->RightColumn) ||
          (gScreenDimensions.BottomRow < ScreenDimensions->BottomRow)
          ) {
        return EFI_INVALID_PARAMETER;
      } else {
        //
        // Local dimension validation.
        //
        if ((ScreenDimensions->RightColumn > ScreenDimensions->LeftColumn) &&
            (ScreenDimensions->BottomRow > ScreenDimensions->TopRow) &&
            ((ScreenDimensions->RightColumn - ScreenDimensions->LeftColumn) > 2) &&
            (
              (ScreenDimensions->BottomRow - ScreenDimensions->TopRow) > STATUS_BAR_HEIGHT +
            SCROLL_ARROW_HEIGHT *
            2 +
            FRONT_PAGE_HEADER_HEIGHT +
            FOOTER_HEIGHT +
            1
          )
            ) {
          CopyMem (&gScreenDimensions, ScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
        } else {
          return EFI_INVALID_PARAMETER;
        }
      }
    }

    gOptionBlockWidth = (CHAR16) ((gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3);
    gHelpBlockWidth   = gOptionBlockWidth;
    gPromptBlockWidth = gOptionBlockWidth;

    //
    // Initialize the strings for the browser, upon exit of the browser, the strings will be freed
    //
    InitializeBrowserStrings ();

    gFunctionKeySetting = DEFAULT_FUNCTION_KEY_SETTING;
    gClassOfVfr         = EFI_SETUP_APPLICATION_SUBCLASS;
    gResetRequired      = FALSE;
    gExitRequired       = FALSE;
    gSaveRequired       = FALSE;
    gNvUpdateRequired   = FALSE;
    gActiveIfr          = 0;
    gConsistencyId      = 0;
    gPriorMenuEntry     = 0;
    BackupHandle        = *Handle;
    gMenuRefreshHead    = NULL;
    ASSERT (CallbackData);
    ZeroMem (CallbackData, 0x10000);

    //
    // We can recurse through this and might need to re-allocate this particular buffer
    //
    if (gPreviousValue == NULL) {
      gPreviousValue = AllocatePool (0x1000);
      ASSERT (gPreviousValue != NULL);
    }

    Callback      = FALSE;
    FormCallback  = NULL;

    if (CallbackHandle != NULL) {
      //
      // Retrieve the Callback protocol interface
      //
      Status = gBS->HandleProtocol (
                      CallbackHandle,
                      &gEfiFormCallbackProtocolGuid,
                      (VOID **) &FormCallback
                      );

      if (EFI_ERROR (Status)) {
        FreePool (CallbackData);
        return Status;;
      }

      Callback = TRUE;
    }
    //
    // Initializes all the internal state structures for all IFR images in system
    //
    Status = InitializeBinaryStructures (Handle, UseDatabase, Packet, NvMapOverride, HandleCount, &FileFormTagsHead);

    if (EFI_ERROR (Status)) {
      FreePool (CallbackData);
      return Status;
    }
    //
    // Beginning of the Presentation of the Data
    //
    if (UseDatabase && (HandleCount > 1)) {
      Selection = DisplayHomePage (HandleCount, FileFormTagsHead, CallbackData);
    } else {
      //
      // If passing something specific, we know there is only one Ifr
      //
      Selection = AllocateZeroPool (sizeof (UI_MENU_OPTION));
      ASSERT (Selection != NULL);
      Selection->IfrNumber  = 0;
      Selection->Handle     = Handle[0];
      UiInitMenu ();
    }

    UiInitMenuList ();

    if (UseDatabase && (HandleCount > 1)) {
      if (Selection == NULL) {
        FreePool (CallbackData);
        return EFI_SUCCESS;
      }
    }
    //
    // Launch the setup browser with the user's selection information
    //
    AltSelection = SetupBrowser (Selection, Callback, FileFormTagsHead, CallbackData);

    //
    // If the caller cares about Reset status, we can return to the caller if something happened that required a reset
    //
    if (ResetRequired != NULL) {
      *ResetRequired = gResetRequired;
    }

    if (Callback && (AltSelection != NULL)) {
      if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
        Status = FormCallback->Callback (
                                FormCallback,
                                AltSelection->ThisTag->Key,
                                CallbackData,
                                (EFI_HII_CALLBACK_PACKET **) &Packet
                                );
      }
    }

    *Handle = BackupHandle;

    if (EFI_ERROR (Status)) {
      FreePool (CallbackData);
      return Status;
    }

    if (Callback && (AltSelection == NULL)) {
      FreePool (CallbackData);
      return Status;
    }

    if (UseDatabase && (HandleCount > 1)) {
    } else {

      if (gBinaryDataHead->UnRegisterOnExit) {
        Hii->RemovePack (Hii, Handle[0]);
      }

      if (Callback &&
        ((AltSelection->ThisTag->SubClass == EFI_FRONT_PAGE_SUBCLASS) ||
        (AltSelection->ThisTag->SubClass == EFI_SINGLE_USE_SUBCLASS))) {
        //
        // If this is the FrontPage, return after every selection
        //
        FreePool (Selection);
        UiFreeMenu ();

        //
        // Clean up the allocated data buffers
        //
        FreeData (FileFormTagsHead, NULL, NULL);

        gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
        gST->ConOut->ClearScreen (gST->ConOut);

        FreePool (CallbackData);
        return EFI_SUCCESS;
      }

      FreePool (Selection);
      UiFreeMenu ();

      //
      // Clean up the allocated data buffers
      //
      FreeData (FileFormTagsHead, NULL, NULL);

      gST->ConOut->ClearScreen (gST->ConOut);

      if (!Callback) {
        FreePool (CallbackData);
        return EFI_SUCCESS;
      }
    }

  } while (!EFI_ERROR (Status));

  FreePool (CallbackData);
  return Status;
}

EFI_STATUS
EFIAPI
InitializeSetup (
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
/*++

Routine Description:
  Initialize Setup

Arguments:
  (Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT)

Returns:
  EFI_SUCCESS - Setup loaded.
  other       - Setup Error

--*/
{
  EFI_STATUS                  Status;
  EFI_FORM_CONFIGURATION_DATA *FormData;
  EFI_FORM_BROWSER_PROTOCOL   *FormBrowser;
  EFI_HANDLE                  Handle;
  EFI_HII_PACKAGES            *PackageList;

  //
  // There will be only one FormConfig in the system
  // If there is another out there, someone is trying to install us
  // again.  Fail that scenario.
  //
  Status = gBS->LocateProtocol (
                  &gEfiFormBrowserProtocolGuid,
                  NULL,
                  (VOID **) &FormBrowser
                  );

  gFirstIn = TRUE;

  //
  // If there was no error, assume there is an installation and fail to load
  //
  if (!EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  FormData = AllocatePool (sizeof (EFI_FORM_CONFIGURATION_DATA));

  if (FormData == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Fill in HII data
  //
  FormData->Signature               = EFI_FORM_DATA_SIGNATURE;
  FormData->FormConfig.SendForm     = SendForm;
  FormData->FormConfig.CreatePopUp  = CreateDialog;

  //
  // There should only be one HII image
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiProtocolGuid,
                  NULL,
                  (VOID **) &FormData->Hii
                  );

  ASSERT_EFI_ERROR (Status);

  Hii         = FormData->Hii;

  PackageList = PreparePackages (1, &gEfiFormBrowserProtocolGuid, SetupBrowserStrings);

  Status      = Hii->NewPack (Hii, PackageList, &gHiiHandle);

  FreePool (PackageList);

  //
  // Install protocol interface
  //
  Handle = NULL;
  Status = gBS->InstallProtocolInterface (
                  &Handle,
                  &gEfiFormBrowserProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &FormData->FormConfig
                  );

  ASSERT_EFI_ERROR (Status);

  BannerData = AllocateZeroPool (sizeof (BANNER_DATA));
  ASSERT (BannerData != NULL);

  Status = InstallPrint ();
  return Status;
}

VOID
GetQuestionHeader (
  IN  EFI_TAG             *Tag,
  IN  UINT8               *RawFormSet,
  IN  UINT16              Index,
  IN  EFI_FILE_FORM_TAGS  *FileFormTags,
  IN  UINT16              CurrentVariable
  )
/*++

Routine Description:
  Initialize question tag's members.

Arguments:
  Tag              - Pointer of the current EFI_TAG structure.
  RawFormSet       - Pointer of the formset raw data.
  Index            - Offset of the current opcode in the Ifr raw data.
  FileFormTags     - Pointer of current EFI_FILE_FORM_TAGS structure.
  CurrentVariable  - Current variable number.

Returns:
  None.
--*/
{
  EFI_VARIABLE_DEFINITION *VariableDefinition;

  Tag->NumberOfLines  = 1;
  Tag->VariableNumber = CurrentVariable;
  CopyMem (&Tag->Id, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->QuestionId, sizeof (UINT16));
  CopyMem (&Tag->StorageStart, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->QuestionId, sizeof (UINT16));
  CopyMem (&Tag->StorageWidth, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->Width, sizeof (UINT8));
  CopyMem (&Tag->Text, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->Prompt, sizeof (UINT16));
  CopyMem (&Tag->Help, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->Help, sizeof (UINT16));

  VariableDefinition = FileFormTags->VariableDefinitions;

  for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
    //
    // Have we found the correct variable for the request?
    //
    if (CurrentVariable == VariableDefinition->VariableId) {
      if (VariableDefinition->VariableSize < (UINTN) (Tag->StorageStart + Tag->StorageWidth)) {
        VariableDefinition->VariableFakeSize = (UINT16) (VariableDefinition->VariableFakeSize + Tag->StorageWidth);
      }

      if (VariableDefinition->NvRamMap != NULL) {
        //
        // If it is an 8bit or 16bit width, then move it to Tag->Value, otherwise
        // we will never be looking for the data in Tag->Value (e.g. strings, password, etc)
        //
        if (Tag->StorageWidth == (UINT16) 1) {
          CopyMem (&Tag->Value, &VariableDefinition->NvRamMap[Tag->StorageStart], sizeof (UINT16));
        }

        if (Tag->StorageWidth == (UINT16) 2) {
          Index = (UINT16)
            (
              VariableDefinition->NvRamMap[Tag->StorageStart] +
              (VariableDefinition->NvRamMap[Tag->StorageStart + 1] * 0x100)
            );
          CopyMem (&Tag->Value, &Index, sizeof (UINT16));
        }
      } else {
        Index = 0;
        CopyMem (&Tag->Value, &Index, sizeof (UINT16));
      }
      break;
    }
  }
}

VOID
GetNumericHeader (
  IN  EFI_TAG             *Tag,
  IN  UINT8               *RawFormSet,
  IN  UINT16              Index,
  IN  UINT16              NumberOfLines,
  IN  EFI_FILE_FORM_TAGS  *FileFormTags,
  IN  UINT16              CurrentVariable
  )
/*++

Routine Description:
  Initialize numeric tag's members.

Arguments:
  Tag              - Pointer of the current EFI_TAG structure.
  RawFormSet       - Pointer of the formset raw data.
  Index            - Offset of the current opcode in the Ifr raw data.
  NumberOfLines    - Number of lines this opcode occupied.
  FileFormTags     - Pointer of current EFI_FILE_FORM_TAGS structure.
  CurrentVariable  - Current variable number.

Returns:
  None.
--*/
{
  EFI_VARIABLE_DEFINITION *VariableDefinition;

  Tag->NumberOfLines  = NumberOfLines;
  Tag->VariableNumber = CurrentVariable;
  CopyMem (&Tag->Id, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->QuestionId, sizeof (UINT16));
  CopyMem (&Tag->StorageStart, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->QuestionId, sizeof (UINT16));
  CopyMem (&Tag->StorageWidth, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->Width, sizeof (UINT8));
  CopyMem (&Tag->Text, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->Prompt, sizeof (UINT16));
  CopyMem (&Tag->Help, &((EFI_IFR_ONE_OF *) &RawFormSet[Index])->Help, sizeof (UINT16));
  CopyMem (&Tag->Minimum, &((EFI_IFR_NUMERIC *) &RawFormSet[Index])->Minimum, sizeof (UINT16));
  CopyMem (&Tag->Maximum, &((EFI_IFR_NUMERIC *) &RawFormSet[Index])->Maximum, sizeof (UINT16));
  CopyMem (&Tag->Step, &((EFI_IFR_NUMERIC *) &RawFormSet[Index])->Step, sizeof (UINT16));
  CopyMem (&Tag->Default, &((EFI_IFR_NUMERIC *) &RawFormSet[Index])->Default, sizeof (UINT16));
  Tag->ResetRequired  = (BOOLEAN) (((EFI_IFR_NUMERIC *) &RawFormSet[Index])->Flags & EFI_IFR_FLAG_RESET_REQUIRED);

  VariableDefinition  = FileFormTags->VariableDefinitions;

  for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
    //
    // Have we found the correct variable for the request?
    //
    if (CurrentVariable == VariableDefinition->VariableId) {
      if (VariableDefinition->VariableSize <= (UINTN) (Tag->StorageStart + Tag->StorageWidth)) {
        if (Tag->StorageWidth == 0) {
          VariableDefinition->VariableFakeSize = (UINT16) (VariableDefinition->VariableFakeSize + 2);
        } else {
          VariableDefinition->VariableFakeSize = (UINT16) (VariableDefinition->VariableFakeSize + Tag->StorageWidth);
        }
      }

      if (VariableDefinition->NvRamMap != NULL) {
        //
        // If it is an 8bit or 16bit width, then move it to Tag->Value, otherwise
        // we will never be looking for the data in Tag->Value (e.g. strings, password, etc)
        //
        if (Tag->StorageWidth == (UINT16) 1) {
          CopyMem (&Tag->Value, &VariableDefinition->NvRamMap[Tag->StorageStart], sizeof (UINT16));
        }

        if (Tag->StorageWidth == (UINT16) 2) {
          Index = (UINT16)
            (
              VariableDefinition->NvRamMap[Tag->StorageStart] +
                (VariableDefinition->NvRamMap[Tag->StorageStart + 1] * 0x100)
            );
          CopyMem (&Tag->Value, &Index, sizeof (UINT16));
        }
      } else {
        CopyMem (&Tag->Value, &Tag->Default, sizeof (UINT16));
      }
      break;
    }
  }
}

VOID
GetTagCount (
  IN      UINT8                                 *RawFormSet,
  IN OUT  UINT16                                *NumberOfTags
  )
{
  UINT16  Index;

  //
  // Assume on entry we are pointing to an OpCode - reasonably this should
  // be a FormOp since the purpose is to count the tags in a particular Form.
  //
  for (Index = 0; RawFormSet[Index] != EFI_IFR_END_FORM_OP;) {
    //
    // If we encounter the end of a form set, bail out
    //
    if (RawFormSet[Index] == EFI_IFR_END_FORM_SET_OP) {
      break;
    }
    //
    // We treat date/time internally as three op-codes
    //
    if (RawFormSet[Index] == EFI_IFR_DATE_OP || RawFormSet[Index] == EFI_IFR_TIME_OP) {
      *NumberOfTags = (UINT16) (*NumberOfTags + 3);
    } else {
      //
      // Assume that we could have no more tags than op-codes
      //
      (*NumberOfTags)++;
    }

    Index = (UINT16) (Index + RawFormSet[Index + 1]);
  }
  //
  // Increase the tag count by one so it is inclusive of the end_form_op
  //
  (*NumberOfTags)++;
}

STATIC
VOID
AddNextInconsistentTag (
  IN OUT  EFI_INCONSISTENCY_DATA  **InconsistentTagsPtr
  )
/*++

Routine Description:
  Initialize the next inconsistent tag data and add it to the inconsistent tag list.

Arguments:
 InconsistentTagsPtr   - Pointer of the inconsistent tag's pointer.

Returns:
  None.

--*/
{
  EFI_INCONSISTENCY_DATA  *PreviousInconsistentTags;
  EFI_INCONSISTENCY_DATA  *InconsistentTags;

  InconsistentTags = *InconsistentTagsPtr;
  //
  // We just hit the end of an inconsistent expression.  Let's allocate the ->Next structure
  //
  InconsistentTags->Next = AllocatePool (sizeof (EFI_INCONSISTENCY_DATA));
  ASSERT (InconsistentTags->Next != NULL);

  //
  // Preserve current Tag entry
  //
  PreviousInconsistentTags  = InconsistentTags;

  InconsistentTags          = InconsistentTags->Next;

  //
  // This will zero on the entry including the ->Next so I don't have to do it
  //
  ZeroMem (InconsistentTags, sizeof (EFI_INCONSISTENCY_DATA));

  //
  // Point our Previous field to the previous entry
  //
  InconsistentTags->Previous  = PreviousInconsistentTags;

  *InconsistentTagsPtr        = InconsistentTags;

  return ;
}

STATIC
EFI_STATUS
InitializeTagStructures (
  IN  EFI_IFR_BINARY                            *BinaryData,
  OUT EFI_FILE_FORM_TAGS                        *FileFormTags
  )
{
  EFI_STATUS              Status;
  UINT8                   *RawFormSet;
  UINT16                  Index;
  UINT16                  QuestionIndex;
  UINT16                  NumberOfTags;
  INT16                   CurrTag;
  UINT8                   TagLength;
  EFI_FORM_TAGS           *FormTags;
  EFI_FORM_TAGS           *SavedFormTags;
  EFI_INCONSISTENCY_DATA  *InconsistentTags;
  EFI_VARIABLE_DEFINITION *VariableDefinitions;
  UINTN                   Count;
  UINT16                  Class;
  UINT16                  SubClass;
  UINT16                  TempValue;
  UINT16                  CurrentVariable;
  UINT16                  CurrentVariable2;

  //
  // Initialize some Index variable and Status
  //
  Count             = 0;
  Class             = 0;
  SubClass          = 0;
  CurrentVariable   = 0;
  CurrentVariable2  = 0;
  QuestionIndex     = 0;
  NumberOfTags      = 1;
  Status            = EFI_SUCCESS;
  FormTags          = &FileFormTags->FormTags;
  FormTags->Next    = NULL;
  if (FileFormTags->InconsistentTags == NULL) {
    InconsistentTags = NULL;
  } else {
    InconsistentTags = FileFormTags->InconsistentTags;
  }

  if (FileFormTags->VariableDefinitions == NULL) {
    VariableDefinitions = NULL;
  } else {
    VariableDefinitions = FileFormTags->VariableDefinitions;
  }
  //
  // RawFormSet now points to the beginning of the forms portion of
  // the specific IFR Binary.
  //
  RawFormSet = (UINT8 *) BinaryData->FormBinary;

  //
  // Determine the number of tags for the first form
  //
  GetTagCount (&RawFormSet[0], &NumberOfTags);

  SavedFormTags = FormTags;

  if (FormTags->Tags != NULL) {
    do {
      //
      // Advance FormTags to the last entry
      //
      for (; FormTags->Next != NULL; FormTags = FormTags->Next)
        ;

      //
      // Walk through each of the tags and free the IntList allocation
      //
      for (Index = 0; Index < NumberOfTags; Index++) {
        if (FormTags->Tags[Index].IntList != NULL) {
          FreePool (FormTags->Tags[Index].IntList);
        }
      }

      FreePool (FormTags->Tags);

      ASSERT (FormTags->Next == NULL);

      FormTags->Tags  = NULL;

      FormTags        = SavedFormTags;

    } while (FormTags->Next != NULL);
  }

  Index = 0;

  //
  // Test for an allocated buffer.  If already allocated this is due to having called this routine
  // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
  // the tag structure with current values from the NV
  //
  if (FormTags->Tags == NULL) {
    //
    // Allocate memory for our tags on the first form
    //
    FormTags->Tags = AllocateZeroPool (NumberOfTags * sizeof (EFI_TAG));
    ASSERT (FormTags->Tags);
  }
  //
  // Test for an allocated buffer.  If already allocated this is due to having called this routine
  // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
  // the tag structure with current values from the NV
  //
  if (InconsistentTags == NULL) {
    //
    // We just hit the end of an inconsistent expression.  Let's allocate the ->Next structure
    //
    InconsistentTags = AllocateZeroPool (sizeof (EFI_INCONSISTENCY_DATA));
    ASSERT (InconsistentTags != NULL);

    FileFormTags->InconsistentTags = InconsistentTags;
  }

  ZeroMem (FormTags->Tags, NumberOfTags * sizeof (EFI_TAG));

  for (CurrTag = 0; RawFormSet[Index] != EFI_IFR_END_FORM_SET_OP; CurrTag++) {
    //
    // Operand = IFR OpCode
    //
    FormTags->Tags[CurrTag].Operand = RawFormSet[Index];

    //
    // Assume for now 0 lines occupied by this OpCode
    //
    FormTags->Tags[CurrTag].NumberOfLines = 0;

    FormTags->Tags[CurrTag].Class         = Class;
    FormTags->Tags[CurrTag].SubClass      = SubClass;

    //
    // Determine the length of the Tag so we can later skip to the next tag in the form
    //
    TagLength = RawFormSet[Index + 1];
    //
    // get the length
    //
    // Operate on the Found OpCode
    //
    switch (RawFormSet[Index]) {

    case EFI_IFR_FORM_OP:
      //
      // If there was no variable op-code defined, create a dummy entry for one
      //
      if (FileFormTags->VariableDefinitions == NULL) {
        FileFormTags->VariableDefinitions = AllocateZeroPool (sizeof (EFI_VARIABLE_DEFINITION));
        ASSERT (FileFormTags->VariableDefinitions != NULL);
        IfrToFormTag (
          RawFormSet[Index],
          &FormTags->Tags[CurrTag],
          (VOID *) &RawFormSet[Index],
          FileFormTags->VariableDefinitions
          );
      } else {
        IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      }
      break;

    case EFI_IFR_SUBTITLE_OP:
    case EFI_IFR_TEXT_OP:
    case EFI_IFR_REF_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      break;

    case EFI_IFR_VARSTORE_OP:
      if (FileFormTags->VariableDefinitions == NULL) {
        VariableDefinitions = AllocateZeroPool (sizeof (EFI_VARIABLE_DEFINITION));
        ASSERT (VariableDefinitions != NULL);
        FileFormTags->VariableDefinitions = VariableDefinitions;
      }

      IfrToFormTag (
        RawFormSet[Index],
        &FormTags->Tags[CurrTag],
        (VOID *) &RawFormSet[Index],
        FileFormTags->VariableDefinitions
        );
      break;

    case EFI_IFR_VARSTORE_SELECT_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      CopyMem (&CurrentVariable, &((EFI_IFR_VARSTORE_SELECT *) &RawFormSet[Index])->VarId, sizeof (UINT16));
      CurrentVariable2 = CurrentVariable;
      break;

    case EFI_IFR_VARSTORE_SELECT_PAIR_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      CopyMem(&CurrentVariable, &((EFI_IFR_VARSTORE_SELECT_PAIR *)&RawFormSet[Index])->VarId, sizeof (UINT16));
      CopyMem (
        &CurrentVariable2,
        &((EFI_IFR_VARSTORE_SELECT_PAIR *) &RawFormSet[Index])->SecondaryVarId,
        sizeof (UINT16)
        );
      break;

    case EFI_IFR_END_FORM_OP:
      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (FormTags->Next == NULL) {
        //
        // We just hit the end of a form.  Let's allocate the ->Next structure
        //
        FormTags->Next = AllocatePool (sizeof (EFI_FORM_TAGS));
        ASSERT (FormTags->Next);
      }

      FormTags = FormTags->Next;
      ZeroMem (FormTags, sizeof (EFI_FORM_TAGS));

      //
      // Reset the tag count to one
      //
      NumberOfTags = 1;

      //
      // Reset the CurrTag value (it will be incremented, after this case statement
      // so set to a negative one so that we get the desired effect.)  Fish can beat me later.
      //
      CurrTag = -1;

      //
      // Determine the number of tags after this form.  If this is the last
      // form, then we will count the endformset and preserve that information
      // in the tag structure.
      //
      GetTagCount (&RawFormSet[Index + TagLength], &NumberOfTags);

      //
      // Allocate memory for our tags
      //
      FormTags->Tags = AllocateZeroPool (NumberOfTags * sizeof (EFI_TAG));
      ASSERT (FormTags->Tags);
      break;

    //
    // Two types of tags constitute the One Of question: a one-of header and
    // several one-of options.
    //
    case EFI_IFR_ONE_OF_OP:
    case EFI_IFR_ORDERED_LIST_OP:
      GetQuestionHeader (&FormTags->Tags[CurrTag], RawFormSet, Index, FileFormTags, CurrentVariable);

      //
      // Store away the CurrTag since what follows will be the answer that we
      // need to place into the appropriate location in the tag array
      //
      //
      // record for setting default later
      //
      QuestionIndex = (UINT16) CurrTag;
      break;

    case EFI_IFR_ONE_OF_OPTION_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      FormTags->Tags[QuestionIndex].Flags = ((EFI_IFR_ONE_OF_OPTION *) &RawFormSet[Index])->Flags;
      CopyMem (
        &FormTags->Tags[QuestionIndex].Key,
        &((EFI_IFR_ONE_OF_OPTION *) &RawFormSet[Index])->Key,
        sizeof (UINT16)
        );
      FormTags->Tags[QuestionIndex].ResetRequired = (BOOLEAN) (FormTags->Tags[QuestionIndex].Flags & EFI_IFR_FLAG_RESET_REQUIRED);
      break;

    case EFI_IFR_CHECKBOX_OP:
      GetQuestionHeader (&FormTags->Tags[CurrTag], RawFormSet, Index, FileFormTags, CurrentVariable);
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      break;

    case EFI_IFR_NUMERIC_OP:
      GetNumericHeader (&FormTags->Tags[CurrTag], RawFormSet, Index, (UINT16) 1, FileFormTags, CurrentVariable);
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      break;

    case EFI_IFR_DATE_OP:
      //
      // Date elements come in as a Year, Month, Day.  We need to process them as a country-based
      // Order.  It is much easier to do it here than anywhere else.
      //
      // For US standards - we want Month/Day/Year, thus we advance "Index" +1, +2, +0 while CurrTag is +0, +1, +2
      //
      GetNumericHeader (
        &FormTags->Tags[CurrTag],
        RawFormSet,
        (UINT16) (Index + TagLength),
        (UINT16) 0,
        FileFormTags,
        CurrentVariable
        );

      //
      // The current language selected + the Date operand
      //
      FormTags->Tags[CurrTag + 1].Operand = RawFormSet[Index];
      GetNumericHeader (
        &FormTags->Tags[CurrTag + 1],
        RawFormSet,
        (UINT16) (Index + TagLength + RawFormSet[Index + TagLength + 1]),
        (UINT16) 0,
        FileFormTags,
        CurrentVariable
        );

      //
      // The current language selected + the Date operand
      //
      FormTags->Tags[CurrTag + 2].Operand = RawFormSet[Index];
      GetNumericHeader (&FormTags->Tags[CurrTag + 2], RawFormSet, Index, (UINT16) 1, FileFormTags, CurrentVariable);

      CurrTag   = (INT16) (CurrTag + 2);

      Index     = (UINT16) (Index + TagLength);
      //
      // get the length
      //
      TagLength = RawFormSet[Index + 1];
      Index     = (UINT16) (Index + TagLength);
      //
      // get the length
      //
      TagLength = RawFormSet[Index + 1];
      break;

    case EFI_IFR_TIME_OP:
      GetNumericHeader (&FormTags->Tags[CurrTag], RawFormSet, Index, (UINT16) 0, FileFormTags, CurrentVariable);

      if (Count == 2) {
        //
        // Override the GetQuestionHeader information - date/time are treated very differently
        //
        FormTags->Tags[CurrTag].NumberOfLines = 1;
        Count = 0;
      } else {
        //
        // The premise is that every date/time op-code have 3 elements, the first 2 have 0 lines
        // associated with them, and the third has 1 line to allow to space beyond the choice.
        //
        Count++;
      }
      break;

    case EFI_IFR_PASSWORD_OP:
    case EFI_IFR_STRING_OP:
      GetQuestionHeader (&FormTags->Tags[CurrTag], RawFormSet, Index, FileFormTags, CurrentVariable);
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      break;

    case EFI_IFR_SUPPRESS_IF_OP:
    case EFI_IFR_GRAYOUT_IF_OP:
      InconsistentTags->Operand = ((EFI_IFR_INCONSISTENT *) &RawFormSet[Index])->Header.OpCode;
      gConsistencyId++;

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //
      InconsistentTags->QuestionId1 = INVALID_OFFSET_VALUE;
      InconsistentTags->QuestionId2 = INVALID_OFFSET_VALUE;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_FORM_SET_OP:
      CopyMem (
        &FormTags->Tags[CurrTag].GuidValue,
        &((EFI_IFR_FORM_SET *) &RawFormSet[Index])->Guid,
        sizeof (EFI_GUID)
        );
      CopyMem (
        &FormTags->Tags[CurrTag].CallbackHandle,
        &((EFI_IFR_FORM_SET *) &RawFormSet[Index])->CallbackHandle,
        sizeof (EFI_PHYSICAL_ADDRESS)
        );
      CopyMem (&FormTags->Tags[CurrTag].Class, &((EFI_IFR_FORM_SET *) &RawFormSet[Index])->Class, sizeof (UINT8));
      CopyMem (
        &FormTags->Tags[CurrTag].SubClass,
        &((EFI_IFR_FORM_SET *) &RawFormSet[Index])->SubClass,
        sizeof (UINT8)
        );
      CopyMem (
        &FormTags->Tags[CurrTag].NvDataSize,
        &((EFI_IFR_FORM_SET *) &RawFormSet[Index])->NvDataSize,
        sizeof (UINT16)
        );
      Class     = ((EFI_IFR_FORM_SET *) &RawFormSet[Index])->Class;
      SubClass  = ((EFI_IFR_FORM_SET *) &RawFormSet[Index])->SubClass;
      //
      // If the formset has a size value, that means someone must be using this, so create a variable
      // We also shall reserve the formid of 0 for this specific purpose.
      //
      if ((FileFormTags->VariableDefinitions == NULL) && (FormTags->Tags[CurrTag].NvDataSize > 0)) {
        FileFormTags->VariableDefinitions = AllocateZeroPool (sizeof (EFI_VARIABLE_DEFINITION));
        ASSERT (FileFormTags->VariableDefinitions != NULL);
        IfrToFormTag (
          RawFormSet[Index],
          &FormTags->Tags[CurrTag],
          (VOID *) &RawFormSet[Index],
          FileFormTags->VariableDefinitions
          );
      } else {
        IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);
      }
      break;

    case EFI_IFR_BANNER_OP:
      if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
        TempValue = 0;
        CopyMem (&TempValue, &((EFI_IFR_BANNER *) &RawFormSet[Index])->Alignment, sizeof (UINT8));
        //
        // If this is the special timeout value, we will dynamically figure out where to put it
        // Also the least significant byte refers to the TimeOut desired.
        //
        if (TempValue == EFI_IFR_BANNER_TIMEOUT) {
          CopyMem (&FrontPageTimeOutTitle, &((EFI_IFR_BANNER *) &RawFormSet[Index])->Title, sizeof (UINT16));
          if (FrontPageTimeOutValue != (INT16) -1) {
            CopyMem (&FrontPageTimeOutValue, &((EFI_IFR_BANNER *) &RawFormSet[Index])->LineNumber, sizeof (UINT16));
          }
          break;
        }

        CopyMem (
          &BannerData->Banner[((EFI_IFR_BANNER *) &RawFormSet[Index])->LineNumber][
          ((EFI_IFR_BANNER *) &RawFormSet[Index])->Alignment],
          &((EFI_IFR_BANNER *) &RawFormSet[Index])->Title,
          sizeof (STRING_REF)
          );
      }
      break;

    case EFI_IFR_INCONSISTENT_IF_OP:
      CopyMem (
        &FormTags->Tags[CurrTag].Text,
        &((EFI_IFR_INCONSISTENT *) &RawFormSet[Index])->Popup,
        sizeof (UINT16)
        );
      gConsistencyId++;

      InconsistentTags->Operand = ((EFI_IFR_INCONSISTENT *) &RawFormSet[Index])->Header.OpCode;
      CopyMem (&InconsistentTags->Popup, &((EFI_IFR_INCONSISTENT *) &RawFormSet[Index])->Popup, sizeof (UINT16));

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //
      InconsistentTags->QuestionId1     = INVALID_OFFSET_VALUE;
      InconsistentTags->QuestionId2     = INVALID_OFFSET_VALUE;

      InconsistentTags->VariableNumber  = CurrentVariable;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_EQ_ID_VAL_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);

      InconsistentTags->Operand = ((EFI_IFR_EQ_ID_VAL *) &RawFormSet[Index])->Header.OpCode;
      CopyMem (&InconsistentTags->Value, &((EFI_IFR_EQ_ID_VAL *) &RawFormSet[Index])->Value, sizeof (UINT16));
      CopyMem (
        &InconsistentTags->QuestionId1,
        &((EFI_IFR_EQ_ID_VAL *) &RawFormSet[Index])->QuestionId,
        sizeof (UINT16)
        );

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //
      InconsistentTags->Width               = FormTags->Tags[CurrTag].StorageWidth;
      InconsistentTags->QuestionId2         = INVALID_OFFSET_VALUE;
      InconsistentTags->ConsistencyId       = gConsistencyId;
      FormTags->Tags[CurrTag].ConsistencyId = gConsistencyId;

      InconsistentTags->VariableNumber      = CurrentVariable;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_EQ_VAR_VAL_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);

      InconsistentTags->Operand = ((EFI_IFR_EQ_VAR_VAL *) &RawFormSet[Index])->Header.OpCode;
      CopyMem (&InconsistentTags->Value, &((EFI_IFR_EQ_VAR_VAL *) &RawFormSet[Index])->Value, sizeof (UINT16));
      CopyMem (
        &InconsistentTags->QuestionId1,
        &((EFI_IFR_EQ_VAR_VAL *) &RawFormSet[Index])->VariableId,
        sizeof (UINT16)
        );

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //
      InconsistentTags->QuestionId2         = INVALID_OFFSET_VALUE;
      InconsistentTags->ConsistencyId       = gConsistencyId;
      FormTags->Tags[CurrTag].ConsistencyId = gConsistencyId;

      InconsistentTags->VariableNumber      = CurrentVariable;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_EQ_ID_ID_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);

      InconsistentTags->Operand = ((EFI_IFR_EQ_ID_ID *) &RawFormSet[Index])->Header.OpCode;
      CopyMem (
        &InconsistentTags->QuestionId1,
        &((EFI_IFR_EQ_ID_ID *) &RawFormSet[Index])->QuestionId1,
        sizeof (UINT16)
        );
      CopyMem (
        &InconsistentTags->QuestionId2,
        &((EFI_IFR_EQ_ID_ID *) &RawFormSet[Index])->QuestionId2,
        sizeof (UINT16)
        );

      InconsistentTags->Width               = FormTags->Tags[CurrTag].StorageWidth;
      InconsistentTags->ConsistencyId       = gConsistencyId;
      FormTags->Tags[CurrTag].ConsistencyId = gConsistencyId;

      InconsistentTags->VariableNumber      = CurrentVariable;
      InconsistentTags->VariableNumber2     = CurrentVariable2;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_AND_OP:
    case EFI_IFR_OR_OP:
    case EFI_IFR_NOT_OP:
    case EFI_IFR_GT_OP:
    case EFI_IFR_GE_OP:
    case EFI_IFR_TRUE_OP:
    case EFI_IFR_FALSE_OP:
      InconsistentTags->Operand = ((EFI_IFR_NOT *) &RawFormSet[Index])->Header.OpCode;

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //

      //
      // Reserve INVALID_OFFSET_VALUE - 1 for TRUE or FALSE because they are inconsistency tags also, but
      // have no coresponding id. The examination of id is needed by evaluating boolean expression.
      //
      if (RawFormSet[Index] == EFI_IFR_TRUE_OP ||
          RawFormSet[Index] == EFI_IFR_FALSE_OP) {
        InconsistentTags->QuestionId1         = INVALID_OFFSET_VALUE - 1;
      } else {
        InconsistentTags->QuestionId1         = INVALID_OFFSET_VALUE;
      }
      InconsistentTags->QuestionId2         = INVALID_OFFSET_VALUE;
      InconsistentTags->ConsistencyId       = gConsistencyId;
      FormTags->Tags[CurrTag].ConsistencyId = gConsistencyId;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_EQ_ID_LIST_OP:
      IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);

      InconsistentTags->Operand = ((EFI_IFR_EQ_ID_LIST *) &RawFormSet[Index])->Header.OpCode;
      CopyMem (
        &InconsistentTags->QuestionId1,
        &((EFI_IFR_EQ_ID_LIST *) &RawFormSet[Index])->QuestionId,
        sizeof (UINT16)
        );
      CopyMem (
        &InconsistentTags->ListLength,
        &((EFI_IFR_EQ_ID_LIST *) &RawFormSet[Index])->ListLength,
        sizeof (UINT16)
        );
      InconsistentTags->ValueList = FormTags->Tags[CurrTag].IntList;

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //
      InconsistentTags->Width               = FormTags->Tags[CurrTag].StorageWidth;
      InconsistentTags->QuestionId2         = INVALID_OFFSET_VALUE;
      InconsistentTags->ConsistencyId       = gConsistencyId;
      FormTags->Tags[CurrTag].ConsistencyId = gConsistencyId;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_END_IF_OP:
      InconsistentTags->Operand = ((EFI_IFR_END_EXPR *) &RawFormSet[Index])->Header.OpCode;

      //
      // Since this op-code doesn't use the next field(s), initialize them with something invalid.
      // Unfortunately 0 is a valid offset value for a QuestionId
      //
      InconsistentTags->QuestionId1 = INVALID_OFFSET_VALUE;
      InconsistentTags->QuestionId2 = INVALID_OFFSET_VALUE;

      //
      // Test for an allocated buffer.  If already allocated this is due to having called this routine
      // once for sizing of the NV storage.  We then loaded the NV variable and can correctly initialize
      // the tag structure with current values from the NV
      //
      if (InconsistentTags->Next == NULL) {
        AddNextInconsistentTag (&InconsistentTags);
        break;
      }

      InconsistentTags = InconsistentTags->Next;
      break;

    case EFI_IFR_END_ONE_OF_OP:
      break;

    default:
      break;
    }
    //
    // End of switch
    //
    // Per spec., we ignore ops that we don't know how to deal with.  Skip to next tag
    //
    Index = (UINT16) (Index + TagLength);
  }
  //
  // End of Index
  //
  // When we eventually exit, make sure we mark the last tag with an op-code
  //
  FormTags->Tags[CurrTag].Operand = RawFormSet[Index];

  IfrToFormTag (RawFormSet[Index], &FormTags->Tags[CurrTag], (VOID *) &RawFormSet[Index], NULL);

  //
  // Place this as an end of the database marker
  //
  InconsistentTags->Operand = 0xFF;

  //
  // This is the Head of the linked list of pages.  Each page is an array of tags
  //
  FormTags          = &FileFormTags->FormTags;
  InconsistentTags  = FileFormTags->InconsistentTags;

  for (; InconsistentTags->Operand != 0xFF;) {
    if (InconsistentTags->QuestionId1 != INVALID_OFFSET_VALUE) {
      //
      // Search the tags for the tag which corresponds to this ID
      //
      for (CurrTag = 0; FormTags->Tags[0].Operand != EFI_IFR_END_FORM_SET_OP; CurrTag++) {
        //
        // If we hit the end of a form, go to the next set of Tags.
        // Remember - EndFormSet op-codes sit on their own page after an end form.
        //
        if (FormTags->Tags[CurrTag].Operand == EFI_IFR_END_FORM_OP) {
          //
          // Reset the CurrTag value (it will be incremented, after this case statement
          // so set to a negative one so that we get the desired effect.)  Fish can beat me later.
          //
          CurrTag   = -1;
          FormTags  = FormTags->Next;
          continue;
        }

        if (FormTags->Tags[CurrTag].Id == InconsistentTags->QuestionId1) {
          FormTags->Tags[CurrTag].Consistency++;
        }
      }
    }

    FormTags = &FileFormTags->FormTags;

    if (InconsistentTags->QuestionId2 != INVALID_OFFSET_VALUE) {
      //
      // Search the tags for the tag which corresponds to this ID
      //
      for (CurrTag = 0; FormTags->Tags[CurrTag].Operand != EFI_IFR_END_FORM_SET_OP; CurrTag++) {
        //
        // If we hit the end of a form, go to the next set of Tags.
        // Remember - EndFormSet op-codes sit on their own page after an end form.
        //
        if (FormTags->Tags[CurrTag].Operand == EFI_IFR_END_FORM_OP) {
          //
          // Reset the CurrTag value (it will be incremented, after this case statement
          // so set to a negative one so that we get the desired effect.)  Fish can beat me later.
          //
          CurrTag   = -1;
          FormTags  = FormTags->Next;
          continue;
        }

        if (FormTags->Tags[CurrTag].Id == InconsistentTags->QuestionId2) {
          FormTags->Tags[CurrTag].Consistency++;
        }
      }
    }

    InconsistentTags = InconsistentTags->Next;
  }

  return Status;
}

VOID
InitPage (
  VOID
  )
{
  CHAR16  *HomePageString;
  CHAR16  *HomeEscapeString;

  //
  // Displays the Header and Footer borders
  //
  DisplayPageFrame ();

  HomePageString    = GetToken (STRING_TOKEN (HOME_PAGE_TITLE), gHiiHandle);
  HomeEscapeString  = GetToken (STRING_TOKEN (HOME_ESCAPE_STRING), gHiiHandle);

  gST->ConOut->SetAttribute (gST->ConOut, EFI_YELLOW | EFI_BRIGHT);
  //
  //  PrintStringAt ((gScreenDimensions.RightColumn - GetStringWidth(HomePageString)/2)/2, 1, HomePageString);
  //
  PrintStringAt (
    (gScreenDimensions.RightColumn + gScreenDimensions.LeftColumn - GetStringWidth (HomePageString) / 2) / 2,
    1,
    HomePageString
    );
  PrintAt (
    gScreenDimensions.LeftColumn + 2,
    gScreenDimensions.BottomRow - 3,
    (CHAR16 *) L"%c%c%s",
    ARROW_UP,
    ARROW_DOWN,
    gMoveHighlight
    );
  PrintAt (
    gScreenDimensions.RightColumn - (GetStringWidth (HomeEscapeString) / 2) - 2,
    gScreenDimensions.BottomRow - 3,
    (CHAR16 *) L" %s",
    HomeEscapeString
    );
  gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
  FreePool (HomeEscapeString);
  FreePool (HomePageString);

  return ;
}

CHAR16 *
GetToken (
  IN  STRING_REF                              Token,
  IN  EFI_HII_HANDLE                          HiiHandle
  )
/*++

Routine Description:

  Get the string based on the TokenID and HII Handle.

Arguments:

  Token       - The Token ID.
  HiiHandle   - Handle of Ifr to be fetched.

Returns:

  The output string.

--*/
{
  CHAR16      *Buffer;
  UINTN       BufferLength;
  EFI_STATUS  Status;

  //
  // Set default string size assumption at no more than 256 bytes
  //
  BufferLength  = 0x100;

  Buffer        = AllocateZeroPool (BufferLength);
  ASSERT (Buffer != NULL);

  Status = Hii->GetString (Hii, HiiHandle, Token, TRUE, NULL, &BufferLength, Buffer);

  if (EFI_ERROR (Status)) {
    if (Status == EFI_BUFFER_TOO_SMALL) {
      //
      // Free the old pool
      //
      FreePool (Buffer);

      //
      // Allocate new pool with correct value
      //
      Buffer = AllocatePool (BufferLength);
      ASSERT (Buffer != NULL);

      Status = Hii->GetString (Hii, HiiHandle, Token, TRUE, NULL, &BufferLength, Buffer);

      if (!EFI_ERROR (Status)) {
        return Buffer;
      }
    }

    ASSERT_EFI_ERROR (Status);
  }

  return Buffer;
}

STATIC
EFI_STATUS
PopulateHomePage (
  IN UINTN                                    NumberOfIfrImages,
  IN EFI_FILE_FORM_TAGS                       *FileFormTagsHead
  )
{
  EFI_STATUS          Status;
  UINTN               Index;
  EFI_IFR_BINARY      *IfrBinary;
  CHAR16              *StringPtr;
  EFI_FILE_FORM_TAGS  *FileFormTags;
  EFI_FORM_TAGS       LocalTags;

  FileFormTags = FileFormTagsHead;

  UiInitMenu ();

  Status = EFI_SUCCESS;

  //
  // If there are no images
  //
  if (NumberOfIfrImages == 0) {
    Status = EFI_NO_MEDIA;
    return Status;
  }
  //
  // IfrBinary points to the beginning of the Binary data linked-list
  //
  IfrBinary = gBinaryDataHead;

  //
  // Print the entries which were in the default language.
  //
  for (Index = 0; Index < NumberOfIfrImages; Index++) {
    LocalTags = FileFormTags->FormTags;

    //
    // Populate the Menu
    //
    StringPtr = GetToken (IfrBinary->TitleToken, IfrBinary->Handle);

    //
    // If the default language doesn't exist, don't add a menu option yet
    //
    if (StringPtr[0] != CHAR_NULL) {
      //
      // We are NOT!! removing this StringPtr buffer via FreePool since it is being used in the menuoptions, we will do
      // it in UiFreeMenu.
      //
      UiAddMenuOption (StringPtr, IfrBinary->Handle, LocalTags.Tags, IfrBinary->FormBinary, Index);
    }
    //
    // Advance to the next HII handle
    //
    IfrBinary     = IfrBinary->Next;
    FileFormTags  = FileFormTags->NextFile;
  }

  return Status;
}

STATIC
UI_MENU_OPTION *
DisplayHomePage (
  IN UINTN                                    NumberOfIfrImages,
  IN EFI_FILE_FORM_TAGS                       *FileFormTagsHead,
  IN UINT8                                    *CallbackData
  )
{
  EFI_STATUS      Status;
  UI_MENU_OPTION  *Selection;

  //
  // This prints the basic home page template which the user sees
  //
  InitPage ();

  Status = PopulateHomePage (NumberOfIfrImages, FileFormTagsHead);

  if (EFI_ERROR (Status)) {
    Selection = NULL;
    return Selection;
  }

  Selection = UiDisplayMenu (FALSE, FileFormTagsHead, (EFI_IFR_DATA_ARRAY *) CallbackData);

  return Selection;
}

STATIC
EFI_STATUS
InitializeBinaryStructures (
  IN  EFI_HII_HANDLE                        *Handle,
  IN  BOOLEAN                               UseDatabase,
  IN  EFI_IFR_PACKET                        *Packet,
  IN  UINT8                                 *NvMapOverride,
  IN  UINTN                                 NumberOfIfrImages,
  OUT EFI_FILE_FORM_TAGS                    **FileFormTagsHead
  )
{
  UINTN                       HandleIndex;
  EFI_STATUS                  Status;
  EFI_IFR_BINARY              *BinaryData;
  EFI_FILE_FORM_TAGS          *FileFormTags;
  UINTN                       SizeOfNvStore;
  EFI_FORM_CALLBACK_PROTOCOL  *FormCallback;
  EFI_VARIABLE_DEFINITION     *VariableDefinition;
  EFI_VARIABLE_DEFINITION     *OverrideDefinition;
  VOID                        *NvMap;
  UINTN                       NvMapSize;
  EFI_HII_VARIABLE_PACK_LIST  *NvMapListHead;
  EFI_HII_VARIABLE_PACK_LIST  *NvMapListNode;

  //
  // Initialize some variables to avoid warnings
  //
  BinaryData        = NULL;
  *FileFormTagsHead = NULL;
  FileFormTags      = NULL;
  gBinaryDataHead   = NULL;
  Status            = EFI_SUCCESS;
  FormCallback      = NULL;
  NvMap             = NULL;
  NvMapSize         = 0;

  if (NumberOfIfrImages > 1) {
    NvMapOverride = NULL;
  }

  for (HandleIndex = 0; HandleIndex < NumberOfIfrImages; HandleIndex += 1) {
    //
    // If the buffers are uninitialized, allocate them, otherwise work on the ->Next members
    //
    if ((BinaryData == NULL) || (FileFormTags == NULL)) {
      //
      // Allocate memory for our Binary Data
      //
      BinaryData = AllocateZeroPool (sizeof (EFI_IFR_BINARY));
      ASSERT (BinaryData);

      //
      // Preserve the Head of what will be a linked-list.
      //
      gBinaryDataHead       = BinaryData;
      gBinaryDataHead->Next = NULL;

      if (UseDatabase) {
        Status = GetIfrBinaryData (Hii, Handle[HandleIndex], NULL, BinaryData);
      } else {
        Status = GetIfrBinaryData (Hii, Handle[HandleIndex], Packet, BinaryData);
      }
      //
      // Allocate memory for our File Form Tags
      //
      FileFormTags = AllocateZeroPool (sizeof (EFI_FILE_FORM_TAGS));
      ASSERT (FileFormTags);

      //
      // Preserve the Head of what will be a linked-list.
      //
      *FileFormTagsHead             = FileFormTags;
      (*FileFormTagsHead)->NextFile = NULL;

    } else {
      //
      // Allocate memory for our Binary Data linked-list
      // Each handle represents a Binary and we will store that data away.
      //
      BinaryData->Next = AllocateZeroPool (sizeof (EFI_IFR_BINARY));
      ASSERT (BinaryData->Next);

      BinaryData        = BinaryData->Next;
      BinaryData->Next  = NULL;

      if (UseDatabase) {
        Status = GetIfrBinaryData (Hii, Handle[HandleIndex], NULL, BinaryData);
      } else {
        Status = GetIfrBinaryData (Hii, Handle[HandleIndex], Packet, BinaryData);
      }

      if (EFI_ERROR (Status)) {
        return EFI_DEVICE_ERROR;
      }
      //
      // Allocate memory for our FileFormTags linked-list
      // Each allocation reserves handle represents a Binary and we will store that data away.
      //
      FileFormTags->NextFile = AllocateZeroPool (sizeof (EFI_FILE_FORM_TAGS));
      ASSERT (FileFormTags->NextFile);

      FileFormTags = FileFormTags->NextFile;
    }
    //
    // endif
    //
    // Tag Structure Initialization
    //
    Status              = InitializeTagStructures (BinaryData, FileFormTags);

    VariableDefinition  = FileFormTags->VariableDefinitions;

    //
    // Allocate memory for our NVRAM Maps for all of our variables
    //
    for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
      //
      // Pad the fake variable size accordingly - this value should reflect the size of information that is not accounted by
      // the mainstream NVRAM variable such as DATE/TIME information that the browser needs to track but is saved to an RTC
      //
      VariableDefinition->VariableFakeSize = (UINT16) (VariableDefinition->VariableSize + VariableDefinition->VariableFakeSize);

      //
      // In the case where a file has no "real" NV data, we should pad the buffer accordingly
      //
      if (VariableDefinition->VariableSize == 0) {
        if (VariableDefinition->VariableFakeSize != 0) {
          VariableDefinition->NvRamMap = AllocateZeroPool (VariableDefinition->VariableFakeSize);
          ASSERT (VariableDefinition->NvRamMap != NULL);
        }
      } else {
        VariableDefinition->NvRamMap = AllocateZeroPool (VariableDefinition->VariableSize);
        ASSERT (VariableDefinition->NvRamMap != NULL);
      }

      if (VariableDefinition->VariableFakeSize != 0) {
        VariableDefinition->FakeNvRamMap = AllocateZeroPool (VariableDefinition->VariableFakeSize);
        ASSERT (VariableDefinition->FakeNvRamMap != NULL);
      }
    }

    Status = gBS->HandleProtocol (
                    (VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
                    &gEfiFormCallbackProtocolGuid,
                    (VOID **) &FormCallback
                    );

    //
    // Since we might have multiple variables, if there is an NvMapOverride we need to use the EFI_VARIABLE_DEFINITION
    // information as the information that we pass back and forth.  NOTE that callbacks that are initiated will only have the
    // NVRAM data refreshed based on the op-code that initiated the callback.  In other words, we will pass to the caller a single
    // NVRAM map for a single variable based on the op-code that the user selected.
    //
    if (NvMapOverride != NULL) {
      VariableDefinition  = FileFormTags->VariableDefinitions;
      OverrideDefinition  = ((EFI_VARIABLE_DEFINITION *) NvMapOverride);

      //
      // Search through the variable definitions.  There should be sufficient passed in settings for the variable op-codes specified
      //
      for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
        if ((!CompareMem (VariableDefinition->VariableName, L"Setup", 10)) && (VariableDefinition->Next == NULL)) {
          if (VariableDefinition->VariableSize != 0) {
            CopyMem (VariableDefinition->NvRamMap, NvMapOverride, VariableDefinition->VariableSize);
          } else {
            CopyMem (VariableDefinition->NvRamMap, NvMapOverride, VariableDefinition->VariableFakeSize);
          }
          break;
        } else {
          VariableDefinition->NvRamMap = OverrideDefinition->NvRamMap;
        }
        //
        // There should NEVER be a ->Next for VariableDefinition and a NULL ->Next for the OverrideDefinition
        //
        ASSERT (OverrideDefinition->Next);
        OverrideDefinition = OverrideDefinition->Next;
      }
    } else {
      VariableDefinition = FileFormTags->VariableDefinitions;

      //
      // Search through the variable definitions.  There should be sufficient passed in settings for the variable op-codes specified
      //
      for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
        SizeOfNvStore = VariableDefinition->VariableSize;

        //
        // Getting the NvStore and placing it into our Global Data
        //
        if ((FormCallback != NULL) && (FormCallback->NvRead != NULL)) {
          Status = FormCallback->NvRead (
                                  FormCallback,
                                  VariableDefinition->VariableName,
                                  &VariableDefinition->Guid,
                                  NULL,
                                  &SizeOfNvStore,
                                  (VOID *) VariableDefinition->NvRamMap
                                  );
        } else {
          Status = gRT->GetVariable (
                          VariableDefinition->VariableName,
                          &VariableDefinition->Guid,
                          NULL,
                          &SizeOfNvStore,
                          (VOID *) VariableDefinition->NvRamMap
                          );
        }

        if (EFI_ERROR (Status)) {
          //
          // If there is a variable that exists already and it is larger than what we calculated the
          // storage needs to be, we must assume the variable size from GetVariable is correct and not
          // allow the truncation of the variable.  It is very possible that the user who created the IFR
          // we are cracking is not referring to a variable that was in a previous map, however we cannot
          // allow it's truncation.
          //
          if (Status == EFI_BUFFER_TOO_SMALL) {
            //
            // If the buffer was too small, we should have the expanded size requirement in SizeOfNvStore now.
            //
            VariableDefinition->VariableSize = (UINT16) SizeOfNvStore;

            //
            // Free the buffer that was allocated that was too small
            //
            FreePool (VariableDefinition->NvRamMap);
            FreePool (VariableDefinition->FakeNvRamMap);

            VariableDefinition->NvRamMap = AllocateZeroPool (SizeOfNvStore);
            VariableDefinition->FakeNvRamMap = AllocateZeroPool (SizeOfNvStore + VariableDefinition->VariableFakeSize);
            ASSERT (VariableDefinition->NvRamMap);
            ASSERT (VariableDefinition->FakeNvRamMap);

            if ((FormCallback != NULL) && (FormCallback->NvRead != NULL)) {
              Status = FormCallback->NvRead (
                                      FormCallback,
                                      VariableDefinition->VariableName,
                                      &VariableDefinition->Guid,
                                      NULL,
                                      &SizeOfNvStore,
                                      (VOID *) VariableDefinition->NvRamMap
                                      );
            } else {
              Status = gRT->GetVariable (
                              VariableDefinition->VariableName,
                              &VariableDefinition->Guid,
                              NULL,
                              &SizeOfNvStore,
                              (VOID *) VariableDefinition->NvRamMap
                              );
            }
          }
          //
          // if the variable was not found, we will retrieve default values
          //
          if (Status == EFI_NOT_FOUND) {

            if (0 == CompareMem (VariableDefinition->VariableName, L"Setup", 10)) {

              NvMapListHead = NULL;

              Status = Hii->GetDefaultImage (Hii, Handle[HandleIndex], EFI_IFR_FLAG_DEFAULT, &NvMapListHead);

              if (!EFI_ERROR (Status)) {
                ASSERT_EFI_ERROR (NULL != NvMapListHead);

                NvMapListNode = NvMapListHead;

                while (NULL != NvMapListNode) {
                  if (VariableDefinition->VariableId == NvMapListNode->VariablePack->VariableId) {
                    NvMap     = (VOID *) ((CHAR8 *) NvMapListNode->VariablePack + sizeof (EFI_HII_VARIABLE_PACK) + NvMapListNode->VariablePack->VariableNameLength);
                    NvMapSize = NvMapListNode->VariablePack->Header.Length  - sizeof (EFI_HII_VARIABLE_PACK) - NvMapListNode->VariablePack->VariableNameLength;
                    break;
                    }
                  NvMapListNode = NvMapListNode->NextVariablePack;
                }

                //
                // Free the buffer that was allocated.
                //
                FreePool (VariableDefinition->NvRamMap);
                FreePool (VariableDefinition->FakeNvRamMap);

                //
                // Allocate, copy the NvRamMap.
                //
                VariableDefinition->VariableFakeSize = (UINT16) (VariableDefinition->VariableFakeSize - VariableDefinition->VariableSize);
                VariableDefinition->VariableSize = (UINT16) NvMapSize;
                VariableDefinition->VariableFakeSize = (UINT16) (VariableDefinition->VariableFakeSize + VariableDefinition->VariableSize);

                VariableDefinition->NvRamMap = AllocateZeroPool (VariableDefinition->VariableSize);
                VariableDefinition->FakeNvRamMap = AllocateZeroPool (NvMapSize + VariableDefinition->VariableFakeSize);

                CopyMem (VariableDefinition->NvRamMap, NvMap, NvMapSize);
                FreePool (NvMapListHead);
              }

            }
            Status = EFI_SUCCESS;
          }
        }
      }
    }

    InitializeTagStructures (BinaryData, FileFormTags);
  }
  //
  // endfor
  //
  return Status;
}

STATIC
EFI_STATUS
GetIfrBinaryData (
  IN      EFI_HII_PROTOCOL *Hii,
  IN      EFI_HII_HANDLE   HiiHandle,
  IN      EFI_IFR_PACKET   *Packet,
  IN OUT  EFI_IFR_BINARY   *BinaryData
  )
/*++

Routine Description:
  Fetch the Ifr binary data.

Arguments:
  Hii         - Point to HII protocol.
  HiiHandle   - Handle of Ifr to be fetched.
  Packet      - Pointer to IFR packet.
  BinaryData  - Buffer to copy the string into

Returns:
  Returns the number of CHAR16 characters that were copied into the OutputString buffer.


--*/
{
  EFI_STATUS        Status;
  EFI_HII_PACKAGES  *PackageList;
  UINTN             BufferSize;
  VOID              *Buffer;
  UINT8             *RawFormBinary;
  EFI_IFR_FORM_SET  *FormOp;
  UINT16            Index;
  UINT16            Index2;
  UINT16            TitleToken;

  //
  // Initialize the TitleToken to 0 just in case not found
  //
  TitleToken = 0;

  //
  // Try for a 32K Buffer
  //
  BufferSize = 0x8000;

  //
  // Allocate memory for our Form binary
  //
  Buffer = AllocateZeroPool (BufferSize);
  ASSERT (Buffer);

  if (Packet == NULL) {
    Status = Hii->GetForms (Hii, HiiHandle, 0, &BufferSize, Buffer);

    if (Status == EFI_BUFFER_TOO_SMALL) {

      FreePool (Buffer);

      //
      // Allocate memory for our Form binary
      //
      Buffer = AllocatePool (BufferSize);
      ASSERT (Buffer);

      Status = Hii->GetForms (Hii, HiiHandle, 0, &BufferSize, Buffer);
    }
  } else {
    //
    // Copies the data to local usable buffer
    //
    CopyMem (Buffer, Packet->IfrData, Packet->IfrData->Header.Length);

    //
    // Register the string data with HII
    //
    PackageList = PreparePackages (2, NULL, Packet->IfrData, Packet->StringData);

    Status      = Hii->NewPack (Hii, PackageList, &HiiHandle);

    FreePool (PackageList);
  }

  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // We now have the IFR binary in our Buffer
  //
  BinaryData->IfrPackage  = Buffer;
  RawFormBinary           = (UINT8 *) ((CHAR8 *) (Buffer) + sizeof (EFI_HII_PACK_HEADER));
  BinaryData->FormBinary  = (UINT8 *) ((CHAR8 *) (Buffer) + sizeof (EFI_HII_PACK_HEADER));
  BinaryData->Handle      = HiiHandle;

  //
  // If a packet was passed in, remove the string data when exiting.
  //
  if (Packet != NULL) {
    BinaryData->UnRegisterOnExit = TRUE;
  } else {
    BinaryData->UnRegisterOnExit = FALSE;
  }
  //
  // Walk through the FormSet Opcodes looking for the FormSet opcode
  // If we hit EFI_IFR_END_SET_OP we know we hit the end of the FormSet.
  //
  for (Index = 0; RawFormBinary[Index] != EFI_IFR_END_FORM_SET_OP;) {
    FormOp  = (EFI_IFR_FORM_SET *) &RawFormBinary[Index];
    Index   = (UINT16) (Index + FormOp->Header.Length);

    if (FormOp->Header.OpCode == EFI_IFR_FORM_SET_OP) {
      TitleToken = FormOp->FormSetTitle;
      //
      // If displaying FrontPage - set the flag signifying it
      //
      switch (FormOp->SubClass) {
      case EFI_FRONT_PAGE_SUBCLASS:
        FrontPageHandle = HiiHandle;

      default:
        gClassOfVfr = FormOp->SubClass;
      }
      //
      // Match GUID to find out the function key setting. If match fail, use the default setting.
      //
      for (Index2 = 0; Index2 < sizeof (gFunctionKeySettingTable) / sizeof (FUNCTIION_KEY_SETTING); Index2++) {
        if (CompareGuid ((EFI_GUID *)(UINTN)&FormOp->Guid, &(gFunctionKeySettingTable[Index2].FormSetGuid))) {
          //
          // Update the function key setting.
          //
          gFunctionKeySetting = gFunctionKeySettingTable[Index2].KeySetting;
          //
          // Function key prompt can not be displayed if the function key has been disabled.
          //
          if ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE) {
            gFunctionOneString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
          }

          if ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO) {
            gFunctionTwoString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
          }

          if ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE) {
            gFunctionNineString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
          }

          if ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN) {
            gFunctionTenString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
          }
        }
      }
    }
  }

  BinaryData->TitleToken = TitleToken;

  return Status;
}

EFI_HANDLE          PrintHandle     = NULL;
EFI_PRINT_PROTOCOL  mPrintProtocol  = { UnicodeVSPrint };

STATIC
EFI_STATUS
InstallPrint (
  VOID
  )
{
  return gBS->InstallProtocolInterface (
                &PrintHandle,
                &gEfiPrintProtocolGuid,
                EFI_NATIVE_INTERFACE,
                &mPrintProtocol
                );
}
