/*++

Copyright (c) 2006 - 2007, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  Strings.c

Abstract:

  This file contains the string processing code to the HII database.

--*/


#include "HiiDatabase.h"

STATIC
VOID
AsciiToUnicode (
  IN    UINT8     *Lang,
  IN    UINT16    *Language
  )
{
  UINT8 Count;

  //
  // Convert the ASCII Lang variable to a Unicode Language variable
  //
  for (Count = 0; Count < 3; Count++) {
    Language[Count] = (CHAR16) Lang[Count];
  }
}

EFI_STATUS
EFIAPI
HiiTestString (
  IN     EFI_HII_PROTOCOL   *This,
  IN     CHAR16             *StringToTest,
  IN OUT UINT32             *FirstMissing,
  OUT    UINT32             *GlyphBufferSize
  )
/*++

Routine Description:
  Test if all of the characters in a string have corresponding font characters.
  
Arguments:

Returns: 

--*/
{
  EFI_HII_GLOBAL_DATA *GlobalData;
  EFI_HII_DATA        *HiiData;
  BOOLEAN             WideChar;
  INT32               Location;

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

  HiiData     = EFI_HII_DATA_FROM_THIS (This);
  GlobalData  = HiiData->GlobalData;

  
  //
  // Rewind through the string looking for a glyph width identifier
  // If no width identifier exists, we assume string has narrow width identifier
  //
  for (WideChar = FALSE, Location = (INT32) *FirstMissing; Location >= 0; Location--) {
    if ((StringToTest[Location] == NARROW_CHAR) || (StringToTest[Location] == WIDE_CHAR)) {
      //
      // We found something that identifies what glyph database to look in
      //
      WideChar = (BOOLEAN) (StringToTest[Location] == WIDE_CHAR);
      break;
    }
  }

  //
  // Walk through the string until you hit the null terminator
  //
  for (*GlyphBufferSize = 0; StringToTest[*FirstMissing] != CHAR_NULL; (*FirstMissing)++) {
    //
    // We found something that identifies what glyph database to look in
    //
    if ((StringToTest[*FirstMissing] == NARROW_CHAR) || (StringToTest[*FirstMissing] == WIDE_CHAR)) {
      WideChar = (BOOLEAN) (StringToTest[*FirstMissing] == WIDE_CHAR);
      continue;
    }

    if (!WideChar) {
      if (CompareMem (
          GlobalData->NarrowGlyphs[StringToTest[*FirstMissing]].GlyphCol1,
          &mUnknownGlyph,
          NARROW_GLYPH_ARRAY_SIZE
          ) == 0
          ) {
        //
        // Break since this glyph isn't defined
        //
        return EFI_NOT_FOUND;
      }
    } else {
      //
      // Can compare wide glyph against only GlyphCol1 since GlyphCol1 and GlyphCol2 are contiguous - just give correct size
      //
      if (CompareMem (
          GlobalData->WideGlyphs[StringToTest[*FirstMissing]].GlyphCol1,
          &mUnknownGlyph,
          WIDE_GLYPH_ARRAY_SIZE
          ) == 0
          ) {
        //
        // Break since this glyph isn't defined
        //
        return EFI_NOT_FOUND;
      }
    }

    *GlyphBufferSize += (WideChar ? sizeof (EFI_WIDE_GLYPH) : sizeof (EFI_NARROW_GLYPH));
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
HiiNewString2 (
  IN     EFI_HII_PROTOCOL       *This,
  IN     CHAR16                 *Language,
  IN     EFI_HII_HANDLE         Handle,
  IN OUT STRING_REF             *Reference,
  IN     CHAR16                 *NewString,
  IN     BOOLEAN                ResetStrings
  )
/*++

Routine Description:

  This function allows a new String to be added to an already existing String Package.
  We will make a buffer the size of the package + EfiStrSize of the new string.  We will
  copy the string package that first gets changed and the following language packages until
  we encounter the NULL string package.  All this time we will ensure that the offsets have
  been adjusted.  

Arguments:
  
  This         -  Pointer to the HII protocol.
  Language     -  Pointer to buffer which contains the language code of this NewString.
  Handle       -  Handle of the package instance to be processed.
  Reference    -  The token number for the string. If 0, new string token to be returned through this parameter.
  NewString    -  Buffer pointer for the new string. 
  ResetStrings -  Indicate if we are resetting a string.
  
Returns: 

  EFI_SUCCESS            - The string has been added or reset to Hii database.
  EFI_INVALID_PARAMETER  - Some parameter passed in is invalid.

--*/
{
  EFI_HII_PACKAGE_INSTANCE  *PackageInstance;
  EFI_HII_PACKAGE_INSTANCE  *StringPackageInstance;
  EFI_HII_DATA              *HiiData;
  EFI_HII_STRING_PACK       *StringPack;
  EFI_HII_STRING_PACK       *NewStringPack;
  EFI_HII_HANDLE_DATABASE   *HandleDatabase;
  EFI_HII_PACKAGE_INSTANCE  *NewBuffer;
  UINT8                     *Location;
  UINT8                     *StringLocation;
  RELOFST                   *StringPointer;
  UINTN                     Count;
  UINTN                     Size;
  UINTN                     Index;
  UINTN                     SecondIndex;
  BOOLEAN                   AddString;
  EFI_STATUS                Status;
  UINTN                     Increment;
  UINTN                     StringCount;
  UINT32                    TotalStringCount;
  UINT32                    OriginalStringCount;
  RELOFST                   StringSize;
  UINT32                    Length;
  RELOFST                   Offset;

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

  HiiData             = EFI_HII_DATA_FROM_THIS (This);

  HandleDatabase      = HiiData->DatabaseHead;
  PackageInstance     = NULL;
  AddString           = FALSE;
  Increment           = 0;
  StringCount         = 0;
  TotalStringCount    = 0;
  OriginalStringCount = 0;

  //
  // Check numeric value against the head of the database
  //
  for (; HandleDatabase != NULL; HandleDatabase = HandleDatabase->NextHandleDatabase) {
    //
    // Match the numeric value with the database entry - if matched, extract PackageInstance
    //
    if (Handle == HandleDatabase->Handle) {
      PackageInstance = HandleDatabase->Buffer;
      if (ResetStrings) {
        TotalStringCount = HandleDatabase->NumberOfTokens;
      }
      break;
    }
  }
  //
  // No handle was found - error condition
  //
  if (PackageInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status = ValidatePack (This, PackageInstance, &StringPackageInstance, &TotalStringCount);

  //
  // This sets Count to 0 or the size of the IfrData.  We intend to use Count as an offset value
  //
  Count = StringPackageInstance->IfrSize;

  //
  // This is the size of the complete series of string packs
  //
  Size = StringPackageInstance->StringSize;

  //
  // Based on if there is IFR data in this package instance, determine
  // what the location is of the beginning of the string data.
  //
  if (StringPackageInstance->IfrSize > 0) {
    Location = (UINT8 *) (&StringPackageInstance->IfrData) + StringPackageInstance->IfrSize;
  } else {
    Location = (UINT8 *) (&StringPackageInstance->IfrData);
  }
  //
  // We allocate a buffer which is big enough for both adding and resetting string.
  // The size is slightly larger than the real size of the packages when we are resetting a string.
  //
  NewBuffer = AllocateZeroPool (
                sizeof (EFI_HII_PACKAGE_INSTANCE) -
                2 * sizeof (VOID *) +
                StringPackageInstance->IfrSize +
                StringPackageInstance->StringSize +
                sizeof (RELOFST) +
                StrSize (NewString)
                );
  ASSERT (NewBuffer);

  //
  // Copy data to new buffer
  //
  NewBuffer->Handle   = StringPackageInstance->Handle;
  NewBuffer->IfrSize  = StringPackageInstance->IfrSize;

  //
  // The worst case scenario for sizing is that we are adding a new string (not replacing one) and there was not a string
  // package to begin with.
  //
  NewBuffer->StringSize = StringPackageInstance->StringSize + StrSize (NewString) + sizeof (EFI_HII_STRING_PACK);

  if (StringPackageInstance->IfrSize > 0) {
    CopyMem (&NewBuffer->IfrData, &StringPackageInstance->IfrData, StringPackageInstance->IfrSize);
  }

  StringPack = (EFI_HII_STRING_PACK *) Location;

  //
  // There may be multiple instances packed together of strings
  // so we must walk the self describing structures until we encounter
  // what we are looking for.  In the meantime, copy everything we encounter
  // to the new buffer.
  //
  CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  for (; Length != 0;) {
    //
    // If passed in Language ISO value is in this string pack's language string
    // then we are dealing with the strings we want.
    //
    CopyMem (&Offset, &StringPack->LanguageNameString, sizeof (RELOFST));
    Status = HiiCompareLanguage ((CHAR16 *) ((CHAR8 *) (StringPack) + Offset), Language);

    if (!EFI_ERROR (Status)) {
      break;
    }

    CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), StringPack, Length);

    Count       = Count + Length;
    StringPack  = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + Length);
    CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  }
  //
  // Found the language pack to update on a particular handle
  // We need to Copy the Contents of this pack and adjust the offset values associated
  // with adding/changing a string.  This is a particular piece of code that screams for
  // it being prone to programming error.
  //
  //
  // Copy the string package up to the string data
  //
  StringPointer = (RELOFST *) (StringPack + 1);
  CopyMem (
    ((CHAR8 *) (&NewBuffer->IfrData) + Count),
    StringPack,
    (UINTN) ((UINTN) (StringPointer) - (UINTN) (StringPack))
    );

  //
  // Determine the number of StringPointers
  //
  if (!ResetStrings) {
    CopyMem (&TotalStringCount, &StringPack->NumStringPointers, sizeof (RELOFST));
  } else {
    //
    // If we are resetting the strings, use the original value when exported
    //
    CopyMem (&OriginalStringCount, &StringPack->NumStringPointers, sizeof (RELOFST));
    ((EFI_HII_STRING_PACK *) ((CHAR8 *) (&NewBuffer->IfrData) + Count))->LanguageNameString -=
      (
        (RELOFST) (OriginalStringCount - TotalStringCount) *
        sizeof (RELOFST)
      );
    ((EFI_HII_STRING_PACK *) ((CHAR8 *) (&NewBuffer->IfrData) + Count))->PrintableLanguageName -=
      (
        (RELOFST) (OriginalStringCount - TotalStringCount) *
        sizeof (RELOFST)
      );
    ((EFI_HII_STRING_PACK *) ((CHAR8 *) (&NewBuffer->IfrData) + Count))->NumStringPointers  = TotalStringCount;
    *Reference = (STRING_REF) (TotalStringCount);
  }
  //
  // If the token value is not valid, error out
  //
  if ((*Reference >= TotalStringCount) && !ResetStrings) {
    gBS->FreePool (NewBuffer);
    return EFI_INVALID_PARAMETER;
  }
  //
  // If Reference is 0, update it with what the new token reference will be and turn the AddString flag on
  //
  if (*Reference == 0) {
    *Reference  = (STRING_REF) (TotalStringCount);
    AddString   = TRUE;
  }

  if (AddString) {
    ((EFI_HII_STRING_PACK *) ((CHAR8 *) (&NewBuffer->IfrData) + Count))->LanguageNameString += sizeof (RELOFST);
    ((EFI_HII_STRING_PACK *) ((CHAR8 *) (&NewBuffer->IfrData) + Count))->PrintableLanguageName += sizeof (RELOFST);
    ((EFI_HII_STRING_PACK *) ((CHAR8 *) (&NewBuffer->IfrData) + Count))->NumStringPointers++;
  }
  //
  // Increment offset by amount of copied data
  //
  Count = Count + ((UINTN) (StringPointer) - (UINTN) StringPack);

  for (Index = 0; Index < TotalStringCount; Index++) {
    //
    // If we are pointing to the size of the changing string value
    // then cache the old string value so you know what the difference is
    //
    if (Index == *Reference) {
      CopyMem (&Offset, &StringPointer[Index], sizeof (RELOFST));

      StringLocation = ((UINT8 *) (StringPack) + Offset);
      for (SecondIndex = 0;
           (StringLocation[SecondIndex] != 0) || (StringLocation[SecondIndex + 1] != 0);
           SecondIndex = SecondIndex + 2
          )
        ;
      SecondIndex = SecondIndex + 2;

      Size        = SecondIndex;

      //
      // NewString is a passed in local string which is assumed to be aligned
      //
      Size = StrSize (NewString) - Size;
    }
    //
    // If we are about to copy the offset of the string that follows the changed string make
    // sure that the offsets are adjusted accordingly
    //
    if ((Index > *Reference) && !ResetStrings) {
      CopyMem (&Offset, &StringPointer[Index], sizeof (RELOFST));
      Offset = (RELOFST) (Offset + Size);
      CopyMem (&StringPointer[Index], &Offset, sizeof (RELOFST));
    }
    //
    // If we are adding a string that means we will have an extra string pointer that will affect all string offsets
    //
    if (AddString) {
      CopyMem (&Offset, &StringPointer[Index], sizeof (RELOFST));
      Offset = (UINT32) (Offset + sizeof (RELOFST));
      CopyMem (&StringPointer[Index], &Offset, sizeof (RELOFST));
    }
    //
    // If resetting the strings, we need to reduce the offset by the difference in the strings
    //
    if (ResetStrings) {
      CopyMem (&Length, &StringPointer[Index], sizeof (RELOFST));
      Length = Length - ((RELOFST) (OriginalStringCount - TotalStringCount) * sizeof (RELOFST));
      CopyMem (&StringPointer[Index], &Length, sizeof (RELOFST));
    }
    //
    // Notice that if the string was being added as a new token, we don't have to worry about the
    // offsets changing in the other indexes
    //
    CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), &StringPointer[Index], sizeof (RELOFST));
    Count = Count + sizeof (RELOFST);
    StringCount++;
  }
  //
  // If we are adding a new string the above for loop did not copy the offset for us
  //
  if (AddString) {
    //
    // Since the Index is pointing to the beginning of the first string, we need to gather the size of the previous
    // offset's string and create an offset to our new string.
    //
    CopyMem (&Offset, &StringPointer[Index - 1], sizeof (RELOFST));
    StringLocation  = (UINT8 *) StringPack;
    StringLocation  = StringLocation + Offset - sizeof (RELOFST);

    //
    // Since StringPack is a packed structure, we need to size it carefully (byte-wise) to avoid alignment issues
    //
    for (Length = 0;
         (StringLocation[Length] != 0) || (StringLocation[Length + 1] != 0);
         Length = (RELOFST) (Length + 2)
        )
      ;
    Length      = (RELOFST) (Length + 2);

    StringSize  = (RELOFST) (Offset + Length);

    //
    // Copy the new string offset
    //
    CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), &StringSize, sizeof (RELOFST));
    Count = Count + sizeof (RELOFST);

    CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
    Length = Length + sizeof (RELOFST);
    CopyMem (&StringPack->Header.Length, &Length, sizeof (UINT32));
  }
  //
  // Set Location to the First String
  //
  if (ResetStrings) {
    Index = OriginalStringCount;
  }
  //
  // Set Location to the First String
  //
  Location  = (UINT8 *) &StringPointer[Index];
  Index     = 0;

  //
  // Keep copying strings until you run into two CHAR16's in a row that are NULL
  //
  do {
    if ((*Reference == Increment) && !AddString) {
      StringLocation = ((UINT8 *) (&NewBuffer->IfrData) + Count);
      CopyMem (StringLocation, NewString, StrSize (NewString));

      //
      // Advance the destination location by Count number of bytes
      //
      Count = Count + StrSize (NewString);

      //
      // Add the difference between the new string and the old string to the length
      //
      CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));

      //
      // Since StringPack is a packed structure, we need to size it carefully (byte-wise) to avoid alignment issues
      //
      StringLocation = (UINT8 *) &Location[Index];
      for (Offset = 0;
           (StringLocation[Offset] != 0) || (StringLocation[Offset + 1] != 0);
           Offset = (RELOFST) (Offset + 2)
          )
        ;
      Offset  = (RELOFST) (Offset + 2);

      Length  = Length + (UINT32) StrSize (NewString) - Offset;

      CopyMem (&StringPack->Header.Length, &Length, sizeof (UINT32));
    } else {
      StringLocation = (UINT8 *) &Location[Index];
      for (Offset = 0;
           (StringLocation[Offset] != 0) || (StringLocation[Offset + 1] != 0);
           Offset = (RELOFST) (Offset + 2)
          )
        ;
      Offset = (RELOFST) (Offset + 2);

      CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), StringLocation, Offset);

      //
      // Advance the destination location by Count number of bytes
      //
      Count = Count + Offset;
    }
    //
    // Retrieve the number of characters to advance the index - should land at beginning of next string
    //
    Index = Index + Offset;
    Increment++;
    StringCount--;
    Offset = 0;
  } while (StringCount > 0);

  //
  // If we are adding a new string, then the above do/while will not suffice
  //
  if (AddString) {
    Offset = (RELOFST) StrSize (NewString);
    CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), NewString, Offset);

    Count = Count + StrSize (NewString);
    CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
    Length = Length + (UINT32) StrSize (NewString);
    CopyMem (&StringPack->Header.Length, &Length, sizeof (UINT32));
  }

  if (ResetStrings) {
    //
    // Skip the remainder of strings in the string package
    //
    StringCount = OriginalStringCount - TotalStringCount;

    while (StringCount > 0) {
      StringLocation = (UINT8 *) &Location[Index];
      for (Offset = 0;
           (StringLocation[Offset] != 0) || (StringLocation[Offset + 1] != 0);
           Offset = (RELOFST) (Offset + 2)
          )
        ;
      Offset  = (RELOFST) (Offset + 2);
      Index   = Index + Offset;
      StringCount--;

      //
      // Adjust the size of the string pack by the string size we just skipped.
      // Also reduce the length by the size of a RelativeOffset value since we
      // obviously would have skipped that as well.
      //
      CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
      Length = Length - Offset - sizeof (RELOFST);
      CopyMem (&StringPack->Header.Length, &Length, sizeof (UINT32));
    }
  }

  StringPack = (EFI_HII_STRING_PACK *) &Location[Index];

  CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  for (; Length != 0;) {

    CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), StringPack, Length);

    Count       = Count + Length;
    StringPack  = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + Length);
    CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  }
  //
  // Copy the null terminator to the new buffer
  //
  CopyMem (((CHAR8 *) (&NewBuffer->IfrData) + Count), StringPack, sizeof (EFI_HII_STRING_PACK));

  //
  // Based on if there is IFR data in this package instance, determine
  // what the location is of the beginning of the string data.
  //
  if (StringPackageInstance->IfrSize > 0) {
    Location      = (UINT8 *) (&StringPackageInstance->IfrData) + StringPackageInstance->IfrSize;
    StringPack    = (EFI_HII_STRING_PACK *) Location;
    Location      = (UINT8 *) (&NewBuffer->IfrData) + NewBuffer->IfrSize;
    NewStringPack = (EFI_HII_STRING_PACK *) Location;
  } else {
    StringPack    = (EFI_HII_STRING_PACK *) (&StringPackageInstance->IfrData);
    NewStringPack = (EFI_HII_STRING_PACK *) (&NewBuffer->IfrData);
  }

  CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  for (; Length != 0;) {
    //
    // Since we updated the old version of the string data as we moved things over
    // And we had a chicken-egg problem with the data we copied, let's post-fix the new
    // buffer with accurate length data.
    //
    CopyMem (&Count, &NewStringPack->Header.Length, sizeof (UINT32));
    CopyMem (&NewStringPack->Header.Length, &StringPack->Header.Length, sizeof (UINT32));
    CopyMem (&StringPack->Header.Length, &Count, sizeof (UINT32));

    CopyMem (&Count, &NewStringPack->Header.Length, sizeof (UINT32));
    NewStringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (NewStringPack) + Count);
    CopyMem (&Count, &StringPack->Header.Length, sizeof (UINT32));
    StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + Count);
    CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  }

  GetPackSize ((VOID *) ((CHAR8 *) (&NewBuffer->IfrData) + NewBuffer->IfrSize), &NewBuffer->StringSize, NULL);

  //
  // Search through the handles until the requested handle is found.
  //
  for (HandleDatabase = HiiData->DatabaseHead;
       HandleDatabase->Handle != 0;
       HandleDatabase = HandleDatabase->NextHandleDatabase
      ) {
    if (HandleDatabase->Handle == StringPackageInstance->Handle) {
      //
      // Free the previous buffer associated with this handle, and assign the new buffer to the handle
      //
      gBS->FreePool (HandleDatabase->Buffer);
      HandleDatabase->Buffer = NewBuffer;
      break;
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
HiiNewString (
  IN     EFI_HII_PROTOCOL       *This,
  IN     CHAR16                 *Language,
  IN     EFI_HII_HANDLE         Handle,
  IN OUT STRING_REF             *Reference,
  IN     CHAR16                 *NewString
  )
/*++

Routine Description:
  This function allows a new String to be added to an already existing String Package.
  We will make a buffer the size of the package + StrSize of the new string.  We will
  copy the string package that first gets changed and the following language packages until
  we encounter the NULL string package.  All this time we will ensure that the offsets have
  been adjusted.  

Arguments:
  
Returns: 

--*/
{
  UINTN       Index;
  CHAR16      *LangCodes;
  CHAR16      Lang[4];
  STRING_REF  OriginalValue;
  EFI_STATUS  Status;

  //
  // To avoid a warning 4 uninitialized variable warning
  //
  Status = EFI_SUCCESS;

  Status = HiiGetPrimaryLanguages (
            This,
            Handle,
            &LangCodes
            );

  if (!EFI_ERROR (Status)) {
    OriginalValue = *Reference;

    if (Language == NULL) {
      for (Index = 0; LangCodes[Index] != 0; Index += 3) {
        *Reference = OriginalValue;
        CopyMem (Lang, &LangCodes[Index], 6);
        Lang[3] = 0;
        Status = HiiNewString2 (
                  This,
                  Lang,
                  Handle,
                  Reference,
                  NewString,
                  FALSE
                  );

      }
    } else {
      Status = HiiNewString2 (
                This,
                Language,
                Handle,
                Reference,
                NewString,
                FALSE
                );
    }

    gBS->FreePool (LangCodes);
  }

  return Status;
}

EFI_STATUS
EFIAPI
HiiResetStrings (
  IN     EFI_HII_PROTOCOL   *This,
  IN     EFI_HII_HANDLE     Handle
  )
/*++

Routine Description:
  
    This function removes any new strings that were added after the initial string export for this handle.

Arguments:

Returns: 

--*/
{
  UINTN       Index;
  CHAR16      *LangCodes;
  CHAR16      Lang[4];
  STRING_REF  Reference;
  CHAR16      NewString;
  EFI_STATUS  Status;

  Reference = 1;
  NewString = 0;

  HiiGetPrimaryLanguages (
    This,
    Handle,
    &LangCodes
    );

  for (Index = 0; LangCodes[Index] != 0; Index += 3) {
    CopyMem (Lang, &LangCodes[Index], 6);
    Lang[3] = 0;
    Status = HiiNewString2 (
              This,
              Lang,
              Handle,
              &Reference,
              &NewString,
              TRUE
              );
    ASSERT_EFI_ERROR (Status);
  }

  gBS->FreePool (LangCodes);
  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
HiiGetString (
  IN     EFI_HII_PROTOCOL    *This,
  IN     EFI_HII_HANDLE      Handle,
  IN     STRING_REF          Token,
  IN     BOOLEAN             Raw,
  IN     CHAR16              *LanguageString,
  IN OUT UINTN               *BufferLengthTemp,
  OUT    EFI_STRING          StringBuffer
  )
/*++

Routine Description:
  
  This function extracts a string from a package already registered with the EFI HII database.

Arguments:
  This            - A pointer to the EFI_HII_PROTOCOL instance.
  Handle          - The HII handle on which the string resides.
  Token           - The string token assigned to the string.
  Raw             - If TRUE, the string is returned unedited in the internal storage format described
                    above. If false, the string returned is edited by replacing <cr> with <space> 
                    and by removing special characters such as the <wide> prefix.
  LanguageString  - Pointer to a NULL-terminated string containing a single ISO 639-2 language
                    identifier, indicating the language to print. If the LanguageString is empty (starts
                    with a NULL), the default system language will be used to determine the language.
  BufferLength    - Length of the StringBuffer. If the status reports that the buffer width is too
                    small, this parameter is filled with the length of the buffer needed.
  StringBuffer    - The buffer designed to receive the characters in the string. Type EFI_STRING is
                    defined in String.

Returns: 
  EFI_INVALID_PARAMETER - If input parameter is invalid.
  EFI_BUFFER_TOO_SMALL  - If the *BufferLength is too small.
  EFI_SUCCESS           - Operation is successful.
  
--*/
{
  EFI_HII_PACKAGE_INSTANCE  *PackageInstance;
  EFI_HII_PACKAGE_INSTANCE  *StringPackageInstance;
  EFI_HII_DATA              *HiiData;
  EFI_HII_HANDLE_DATABASE   *HandleDatabase;
  EFI_HII_STRING_PACK       *StringPack;
  RELOFST                   *StringPointer;
  EFI_STATUS                Status;
  UINTN                     DataSize;
  CHAR8                     Lang[3];
  CHAR16                    Language[3];
  UINT32                    Length;
  UINTN                     Count;
  RELOFST                   Offset;
  UINT16                    *Local;
  UINT16                    Zero;
  UINT16                    Narrow;
  UINT16                    Wide;
  UINT16                    NoBreak;
  BOOLEAN                   LangFound;
  UINT16                    *BufferLength = (UINT16 *) BufferLengthTemp;

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

  LangFound       = TRUE;

  DataSize        = sizeof (Lang);

  HiiData         = EFI_HII_DATA_FROM_THIS (This);

  PackageInstance = NULL;
  Zero            = 0;
  Narrow          = NARROW_CHAR;
  Wide            = WIDE_CHAR;
  NoBreak         = NON_BREAKING_CHAR;

  //
  // Check numeric value against the head of the database
  //
  for (HandleDatabase = HiiData->DatabaseHead;
       HandleDatabase != NULL;
       HandleDatabase = HandleDatabase->NextHandleDatabase
      ) {
    //
    // Match the numeric value with the database entry - if matched, extract PackageInstance
    //
    if (Handle == HandleDatabase->Handle) {
      PackageInstance = HandleDatabase->Buffer;
      break;
    }
  }
  //
  // No handle was found - error condition
  //
  if (PackageInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status = ValidatePack (This, PackageInstance, &StringPackageInstance, NULL);

  //
  // If there is no specified language, assume the system default language
  //
  if (LanguageString == NULL) {
    //
    // Get system default language
    //
    Status = gRT->GetVariable (
                    (CHAR16 *) L"Lang",
                    &gEfiGlobalVariableGuid,
                    NULL,
                    &DataSize,
                    Lang
                    );

    if (EFI_ERROR (Status)) {
      //
      // If Lang doesn't exist, just use the first language you find
      //
      LangFound = FALSE;
      goto LangNotFound;
    }
    //
    // Convert the ASCII Lang variable to a Unicode Language variable
    //
    AsciiToUnicode ((UINT8 *)Lang, Language);
  } else {
    //
    // Copy input ISO value to Language variable
    //
    CopyMem (Language, LanguageString, 6);
  }
  //
  // Based on if there is IFR data in this package instance, determine
  // what the location is of the beginning of the string data.
  //
LangNotFound:
  if (StringPackageInstance->IfrSize > 0) {
    StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (&StringPackageInstance->IfrData) + StringPackageInstance->IfrSize);
  } else {
    StringPack = (EFI_HII_STRING_PACK *) (&StringPackageInstance->IfrData);
  }
  //
  // If Token is 0, extract entire string package
  //
  if (Token == 0) {
    //
    // Compute the entire string pack length, including all languages' and the terminating pack's.
    //
    Length = 0;
    while (0 != StringPack->Header.Length) {
      Length += StringPack->Header.Length;
      StringPack = (VOID*)(((UINT8*)StringPack) + StringPack->Header.Length);
    }
    //
    // Back to the start of package.
    //
    StringPack = (VOID*)(((UINT8*)StringPack) - Length); 
    //
    // Terminating zero sub-pack.
    //
    Length += sizeof (EFI_HII_STRING_PACK); 

    //
    // If trying to get the entire string package and have insufficient space.  Return error.
    //
    if (Length > *BufferLength || StringBuffer == NULL) {
      *BufferLength = (UINT16)Length;
      return EFI_BUFFER_TOO_SMALL;
    }
    //
    // Copy the Pack to the caller's buffer.
    //
    *BufferLength = (UINT16)Length;
    CopyMem (StringBuffer, StringPack, Length);

    return EFI_SUCCESS;
  }
  //
  // There may be multiple instances packed together of strings
  // so we must walk the self describing structures until we encounter
  // what we are looking for, and then extract the string we are looking for
  //
  CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
  for (; Length != 0;) {
    //
    // If passed in Language ISO value is in this string pack's language string
    // then we are dealing with the strings we want.
    //
    CopyMem (&Offset, &StringPack->LanguageNameString, sizeof (RELOFST));
    Status = HiiCompareLanguage ((CHAR16 *) ((CHAR8 *) (StringPack) + Offset), Language);

    //
    // If we cannot find the lang variable, we skip this check and use the first language available
    //
    if (LangFound) {
      if (EFI_ERROR (Status)) {
        StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + Length);
        CopyMem (&Length, &StringPack->Header.Length, sizeof (UINT32));
        continue;
      }
    }

    StringPointer = (RELOFST *) (StringPack + 1);

    //
    // We have the right string package - size it, and copy it to the StringBuffer
    //
    if (Token >= StringPack->NumStringPointers) {
      return EFI_INVALID_PARAMETER;
    } else {
      CopyMem (&Offset, &StringPointer[Token], sizeof (RELOFST));
    }
    //
    // Since StringPack is a packed structure, we need to determine the string's
    // size safely, thus byte-wise.  Post-increment the size to include the null-terminator
    //
    Local = (UINT16 *) ((CHAR8 *) (StringPack) + Offset);
    for (Count = 0; CompareMem (&Local[Count], &Zero, 2); Count++)
      ;
    Count++;

    Count = Count * sizeof (CHAR16);;

    if (*BufferLength >= Count && StringBuffer != NULL) {
      //
      // Copy the string to the user's buffer
      //
      if (Raw) {
        CopyMem (StringBuffer, Local, Count);
      } else {
        for (Count = 0; CompareMem (Local, &Zero, 2); Local++) {
          //
          // Skip "Narraw, Wide, NoBreak"
          //
          if (CompareMem (Local, &Narrow,  2) &&
              CompareMem (Local, &Wide,    2) && 
              CompareMem (Local, &NoBreak, 2)) {          
            CopyMem (&StringBuffer[Count++], Local, 2);          
          }        
        } 
        //
        // Add "NULL" at the end.
        //
        CopyMem (&StringBuffer[Count], &Zero, 2);
        Count++;
        Count *= sizeof (CHAR16);
      }

      *BufferLength = (UINT16) Count;
      return EFI_SUCCESS;
    } else {
      *BufferLength = (UINT16) Count;
      return EFI_BUFFER_TOO_SMALL;
    }

  }

  LangFound = FALSE;
  goto LangNotFound;
}

EFI_STATUS
EFIAPI
HiiGetLine (
  IN     EFI_HII_PROTOCOL   *This,
  IN     EFI_HII_HANDLE     Handle,
  IN     STRING_REF         Token,
  IN OUT UINT16             *Index,
  IN     UINT16             LineWidth,
  IN     CHAR16             *LanguageString,
  IN OUT UINT16             *BufferLength,
  OUT    EFI_STRING         StringBuffer
  )
/*++

Routine Description:

  This function allows a program to extract a part of a string of not more than a given width.  
  With repeated calls, this allows a calling program to extract "lines" of text that fit inside 
  columns.  The effort of measuring the fit of strings inside columns is localized to this call.

Arguments:

Returns: 

--*/
{
  UINTN                     Count;
  EFI_HII_PACKAGE_INSTANCE  *PackageInstance;
  EFI_HII_PACKAGE_INSTANCE  *StringPackageInstance;
  EFI_HII_DATA              *HiiData;
  EFI_HII_HANDLE_DATABASE   *HandleDatabase;
  EFI_HII_STRING_PACK       *StringPack;
  RELOFST                   *StringPointer;
  CHAR16                    *Location;
  EFI_STATUS                Status;
  UINTN                     DataSize;
  CHAR8                     Lang[3];
  CHAR16                    Language[3];

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

  HiiData         = EFI_HII_DATA_FROM_THIS (This);

  HandleDatabase  = HiiData->DatabaseHead;

  PackageInstance = NULL;
  DataSize        = 4;

  //
  // Check numeric value against the head of the database
  //
  for (; HandleDatabase != NULL; HandleDatabase = HandleDatabase->NextHandleDatabase) {
    //
    // Match the numeric value with the database entry - if matched, extract PackageInstance
    //
    if (Handle == HandleDatabase->Handle) {
      PackageInstance = HandleDatabase->Buffer;
    }
  }
  //
  // No handle was found - error condition
  //
  if (PackageInstance == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Status = ValidatePack (This, PackageInstance, &StringPackageInstance, NULL);

  //
  // If there is no specified language, assume the system default language
  //
  if (LanguageString == NULL) {
    //
    // Get system default language
    //
    Status = gRT->GetVariable (
                    (CHAR16 *) L"Lang",
                    &gEfiGlobalVariableGuid,
                    NULL,
                    &DataSize,
                    Lang
                    );

    if (EFI_ERROR (Status)) {
      return Status;
    }
    //
    // Convert the ASCII Lang variable to a Unicode Language variable
    //
    AsciiToUnicode ((UINT8 *)Lang, Language);
  } else {
    //
    // Copy input ISO value to Language variable
    //
    CopyMem (Language, LanguageString, 6);
  }
  //
  // Based on if there is IFR data in this package instance, determine
  // what the location is of the beginning of the string data.
  //
  if (StringPackageInstance->IfrSize > 0) {
    StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (&StringPackageInstance->IfrData) + StringPackageInstance->IfrSize);
  } else {
    StringPack = (EFI_HII_STRING_PACK *) (&StringPackageInstance->IfrData);
  }

  StringPointer = (RELOFST *) (StringPack + 1);

  //
  // There may be multiple instances packed together of strings
  // so we must walk the self describing structures until we encounter
  // what we are looking for, and then extract the string we are looking for
  //
  for (; StringPack->Header.Length != 0;) {
    //
    // If passed in Language ISO value is in this string pack's language string
    // then we are dealing with the strings we want.
    //
    Status = HiiCompareLanguage ((CHAR16 *) ((CHAR8 *) (StringPack) + StringPack->LanguageNameString), Language);

    if (EFI_ERROR (Status)) {
      StringPack = (EFI_HII_STRING_PACK *) ((CHAR8 *) (StringPack) + StringPack->Header.Length);
      continue;
    }

    Location = (CHAR16 *) ((CHAR8 *) (StringPack) + StringPointer[Token] +*Index * 2);

    //
    // If the size of the remaining string is less than the LineWidth
    // then copy the entire thing
    //
    if (StrSize (Location) <= LineWidth) {
      if (*BufferLength >= StrSize (Location)) {
        StrCpy (StringBuffer, Location);
        return EFI_SUCCESS;
      } else {
        *BufferLength = (UINT16) StrSize (Location);
        return EFI_BUFFER_TOO_SMALL;
      }
    } else {
      //
      // Rewind the string from the maximum size until we see a space the break the line
      //
      for (Count = LineWidth; Location[Count] != 0x0020; Count--)
        ;

      //
      // Put the index at the next character
      //
      *Index = (UINT16) (Count + 1);

      if (*BufferLength >= Count) {
        StrnCpy (StringBuffer, Location, Count);
        return EFI_SUCCESS;
      } else {
        *BufferLength = (UINT16) Count;
        return EFI_BUFFER_TOO_SMALL;
      }
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
HiiCompareLanguage (
  IN  CHAR16                *LanguageStringLocation,
  IN  CHAR16                *Language
  )
{
  UINT8   *Local;
  UINTN   Index;
  CHAR16  *InputString;
  CHAR16  *OriginalInputString;

  //
  // Allocate a temporary buffer for InputString
  //
  InputString = AllocateZeroPool (0x100);

  ASSERT (InputString);

  OriginalInputString = InputString;

  Local               = (UINT8 *) LanguageStringLocation;

  //
  // Determine the size of this packed string safely (e.g. access by byte), post-increment
  // to include the null-terminator
  //
  for (Index = 0; Local[Index] != 0; Index = Index + 2)
    ;
  //
  // MARMAR  Index = Index + 2;
  //
  // This is a packed structure that this location comes from, so let's make sure
  // the value is aligned by copying it to a local variable and working on it.
  //
  CopyMem (InputString, LanguageStringLocation, Index);

  for (Index = 0; Index < 3; Index++) {
    InputString[Index]  = (CHAR16) (InputString[Index] | 0x20);
    Language[Index]     = (CHAR16) (Language[Index] | 0x20);
  }
  //
  // If the Language is the same return success
  //
  if (CompareMem (LanguageStringLocation, Language, 6) == 0) {
    gBS->FreePool (InputString);
    return EFI_SUCCESS;
  }
  //
  // Skip the first three letters that comprised the primary language,
  // see if what is being compared against is a secondary language
  //
  InputString = InputString + 3;

  //
  // If the Language is not the same as the Primary language, see if there are any
  // secondary languages, and if there are see if we have a match.  If not, return an error.
  //
  for (Index = 0; InputString[Index] != 0; Index = Index + 3) {
    //
    // Getting in here means we have a secondary language
    //
    if (CompareMem (&InputString[Index], Language, 6) == 0) {
      gBS->FreePool (InputString);
      return EFI_SUCCESS;
    }
  }
  //
  // If nothing was found, return the error
  //
  gBS->FreePool (OriginalInputString);
  return EFI_NOT_FOUND;

}
