blob: 0d4cfa4cf06f58a21cfd7d9d2a66b61e8a0cf4cf [file] [log] [blame]
/** @file
Implementation for handling the User Interface option processing.
Copyright (c) 2004 - 2020, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "FormDisplay.h"
#define MAX_TIME_OUT_LEN 0x10
/**
Concatenate a narrow string to another string.
@param Destination The destination string.
@param DestMax The Max length of destination string.
@param Source The source string. The string to be concatenated.
to the end of Destination.
**/
VOID
NewStrCat (
IN OUT CHAR16 *Destination,
IN UINTN DestMax,
IN CHAR16 *Source
)
{
UINTN Length;
for (Length = 0; Destination[Length] != 0; Length++) {
}
//
// We now have the length of the original string
// We can safely assume for now that we are concatenating a narrow value to this string.
// For instance, the string is "XYZ" and cat'ing ">"
// If this assumption changes, we need to make this routine a bit more complex
//
Destination[Length] = NARROW_CHAR;
Length++;
StrCpyS (Destination + Length, DestMax - Length, Source);
}
/**
Get UINT64 type value.
@param Value Input Hii value.
@retval UINT64 Return the UINT64 type value.
**/
UINT64
HiiValueToUINT64 (
IN EFI_HII_VALUE *Value
)
{
UINT64 RetVal;
RetVal = 0;
switch (Value->Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
RetVal = Value->Value.u8;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
RetVal = Value->Value.u16;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
RetVal = Value->Value.u32;
break;
case EFI_IFR_TYPE_BOOLEAN:
RetVal = Value->Value.b;
break;
case EFI_IFR_TYPE_DATE:
RetVal = *(UINT64 *)&Value->Value.date;
break;
case EFI_IFR_TYPE_TIME:
RetVal = (*(UINT64 *)&Value->Value.time) & 0xffffff;
break;
default:
RetVal = Value->Value.u64;
break;
}
return RetVal;
}
/**
Check whether this value type can be transfer to EFI_IFR_TYPE_BUFFER type.
EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
EFI_IFR_TYPE_BUFFER when do the value compare.
@param Value Expression value to compare on.
@retval TRUE This value type can be transter to EFI_IFR_TYPE_BUFFER type.
@retval FALSE This value type can't be transter to EFI_IFR_TYPE_BUFFER type.
**/
BOOLEAN
IsTypeInBuffer (
IN EFI_HII_VALUE *Value
)
{
switch (Value->Type) {
case EFI_IFR_TYPE_BUFFER:
case EFI_IFR_TYPE_DATE:
case EFI_IFR_TYPE_TIME:
case EFI_IFR_TYPE_REF:
return TRUE;
default:
return FALSE;
}
}
/**
Check whether this value type can be transfer to EFI_IFR_TYPE_UINT64
@param Value Expression value to compare on.
@retval TRUE This value type can be transter to EFI_IFR_TYPE_BUFFER type.
@retval FALSE This value type can't be transter to EFI_IFR_TYPE_BUFFER type.
**/
BOOLEAN
IsTypeInUINT64 (
IN EFI_HII_VALUE *Value
)
{
switch (Value->Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
case EFI_IFR_TYPE_NUM_SIZE_16:
case EFI_IFR_TYPE_NUM_SIZE_32:
case EFI_IFR_TYPE_NUM_SIZE_64:
case EFI_IFR_TYPE_BOOLEAN:
return TRUE;
default:
return FALSE;
}
}
/**
Return the buffer length and buffer pointer for this value.
EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
EFI_IFR_TYPE_BUFFER when do the value compare.
@param Value Expression value to compare on.
@param Buf Return the buffer pointer.
@param BufLen Return the buffer length.
**/
VOID
GetBufAndLenForValue (
IN EFI_HII_VALUE *Value,
OUT UINT8 **Buf,
OUT UINT16 *BufLen
)
{
switch (Value->Type) {
case EFI_IFR_TYPE_BUFFER:
*Buf = Value->Buffer;
*BufLen = Value->BufferLen;
break;
case EFI_IFR_TYPE_DATE:
*Buf = (UINT8 *)(&Value->Value.date);
*BufLen = (UINT16)sizeof (EFI_HII_DATE);
break;
case EFI_IFR_TYPE_TIME:
*Buf = (UINT8 *)(&Value->Value.time);
*BufLen = (UINT16)sizeof (EFI_HII_TIME);
break;
case EFI_IFR_TYPE_REF:
*Buf = (UINT8 *)(&Value->Value.ref);
*BufLen = (UINT16)sizeof (EFI_HII_REF);
break;
default:
*Buf = NULL;
*BufLen = 0;
}
}
/**
Compare two Hii value.
@param Value1 Expression value to compare on left-hand.
@param Value2 Expression value to compare on right-hand.
@param Result Return value after compare.
retval 0 Two operators equal.
return Positive value if Value1 is greater than Value2.
retval Negative value if Value1 is less than Value2.
@param HiiHandle Only required for string compare.
@retval other Could not perform compare on two values.
@retval EFI_SUCCESS Compare the value success.
**/
EFI_STATUS
CompareHiiValue (
IN EFI_HII_VALUE *Value1,
IN EFI_HII_VALUE *Value2,
OUT INTN *Result,
IN EFI_HII_HANDLE HiiHandle OPTIONAL
)
{
INT64 Temp64;
CHAR16 *Str1;
CHAR16 *Str2;
UINTN Len;
UINT8 *Buf1;
UINT16 Buf1Len;
UINT8 *Buf2;
UINT16 Buf2Len;
if ((Value1->Type == EFI_IFR_TYPE_STRING) && (Value2->Type == EFI_IFR_TYPE_STRING)) {
if ((Value1->Value.string == 0) || (Value2->Value.string == 0)) {
//
// StringId 0 is reserved
//
return EFI_INVALID_PARAMETER;
}
if (Value1->Value.string == Value2->Value.string) {
*Result = 0;
return EFI_SUCCESS;
}
Str1 = GetToken (Value1->Value.string, HiiHandle);
if (Str1 == NULL) {
//
// String not found
//
return EFI_NOT_FOUND;
}
Str2 = GetToken (Value2->Value.string, HiiHandle);
if (Str2 == NULL) {
FreePool (Str1);
return EFI_NOT_FOUND;
}
*Result = StrCmp (Str1, Str2);
FreePool (Str1);
FreePool (Str2);
return EFI_SUCCESS;
}
//
// Take types(date, time, ref, buffer) as buffer
//
if (IsTypeInBuffer (Value1) && IsTypeInBuffer (Value2)) {
GetBufAndLenForValue (Value1, &Buf1, &Buf1Len);
GetBufAndLenForValue (Value2, &Buf2, &Buf2Len);
Len = Buf1Len > Buf2Len ? Buf2Len : Buf1Len;
*Result = CompareMem (Buf1, Buf2, Len);
if ((*Result == 0) && (Buf1Len != Buf2Len)) {
//
// In this case, means base on samll number buffer, the data is same
// So which value has more data, which value is bigger.
//
*Result = Buf1Len > Buf2Len ? 1 : -1;
}
return EFI_SUCCESS;
}
//
// Take remain types(integer, boolean, date/time) as integer
//
if (IsTypeInUINT64 (Value1) && IsTypeInUINT64 (Value2)) {
Temp64 = HiiValueToUINT64 (Value1) - HiiValueToUINT64 (Value2);
if (Temp64 > 0) {
*Result = 1;
} else if (Temp64 < 0) {
*Result = -1;
} else {
*Result = 0;
}
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
/**
Search an Option of a Question by its value.
@param Question The Question
@param OptionValue Value for Option to be searched.
@retval Pointer Pointer to the found Option.
@retval NULL Option not found.
**/
DISPLAY_QUESTION_OPTION *
ValueToOption (
IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
IN EFI_HII_VALUE *OptionValue
)
{
LIST_ENTRY *Link;
DISPLAY_QUESTION_OPTION *Option;
INTN Result;
EFI_HII_VALUE Value;
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ZeroMem (&Value, sizeof (EFI_HII_VALUE));
Value.Type = Option->OptionOpCode->Type;
CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value));
if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
return Option;
}
Link = GetNextNode (&Question->OptionListHead, Link);
}
return NULL;
}
/**
Return data element in an Array by its Index.
@param Array The data array.
@param Type Type of the data in this array.
@param Index Zero based index for data in this array.
@retval Value The data to be returned
**/
UINT64
GetArrayData (
IN VOID *Array,
IN UINT8 Type,
IN UINTN Index
)
{
UINT64 Data;
ASSERT (Array != NULL);
Data = 0;
switch (Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
Data = (UINT64)*(((UINT8 *)Array) + Index);
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
Data = (UINT64)*(((UINT16 *)Array) + Index);
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
Data = (UINT64)*(((UINT32 *)Array) + Index);
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
Data = (UINT64)*(((UINT64 *)Array) + Index);
break;
default:
break;
}
return Data;
}
/**
Set value of a data element in an Array by its Index.
@param Array The data array.
@param Type Type of the data in this array.
@param Index Zero based index for data in this array.
@param Value The value to be set.
**/
VOID
SetArrayData (
IN VOID *Array,
IN UINT8 Type,
IN UINTN Index,
IN UINT64 Value
)
{
ASSERT (Array != NULL);
switch (Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
*(((UINT8 *)Array) + Index) = (UINT8)Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
*(((UINT16 *)Array) + Index) = (UINT16)Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
*(((UINT32 *)Array) + Index) = (UINT32)Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
*(((UINT64 *)Array) + Index) = (UINT64)Value;
break;
default:
break;
}
}
/**
Check whether this value already in the array, if yes, return the index.
@param Array The data array.
@param Type Type of the data in this array.
@param Value The value to be find.
@param Index The index in the array which has same value with Value.
@retval TRUE Found the value in the array.
@retval FALSE Not found the value.
**/
BOOLEAN
FindArrayData (
IN VOID *Array,
IN UINT8 Type,
IN UINT64 Value,
OUT UINTN *Index OPTIONAL
)
{
UINTN Count;
UINT64 TmpValue;
UINT64 ValueComp;
ASSERT (Array != NULL);
Count = 0;
TmpValue = 0;
switch (Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
ValueComp = (UINT8)Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
ValueComp = (UINT16)Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
ValueComp = (UINT32)Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
ValueComp = (UINT64)Value;
break;
default:
ValueComp = 0;
break;
}
while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) {
if (ValueComp == TmpValue) {
if (Index != NULL) {
*Index = Count;
}
return TRUE;
}
Count++;
}
return FALSE;
}
/**
Print Question Value according to it's storage width and display attributes.
@param Question The Question to be printed.
@param FormattedNumber Buffer for output string.
@param BufferSize The FormattedNumber buffer size in bytes.
@retval EFI_SUCCESS Print success.
@retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.
**/
EFI_STATUS
PrintFormattedNumber (
IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
IN OUT CHAR16 *FormattedNumber,
IN UINTN BufferSize
)
{
INT64 Value;
CHAR16 *Format;
EFI_HII_VALUE *QuestionValue;
EFI_IFR_NUMERIC *NumericOp;
if (BufferSize < (21 * sizeof (CHAR16))) {
return EFI_BUFFER_TOO_SMALL;
}
QuestionValue = &Question->CurrentValue;
NumericOp = (EFI_IFR_NUMERIC *)Question->OpCode;
Value = (INT64)QuestionValue->Value.u64;
switch (NumericOp->Flags & EFI_IFR_DISPLAY) {
case EFI_IFR_DISPLAY_INT_DEC:
switch (QuestionValue->Type) {
case EFI_IFR_NUMERIC_SIZE_1:
Value = (INT64)((INT8)QuestionValue->Value.u8);
break;
case EFI_IFR_NUMERIC_SIZE_2:
Value = (INT64)((INT16)QuestionValue->Value.u16);
break;
case EFI_IFR_NUMERIC_SIZE_4:
Value = (INT64)((INT32)QuestionValue->Value.u32);
break;
case EFI_IFR_NUMERIC_SIZE_8:
default:
break;
}
if (Value < 0) {
Value = -Value;
Format = L"-%ld";
} else {
Format = L"%ld";
}
break;
case EFI_IFR_DISPLAY_UINT_DEC:
Format = L"%ld";
break;
case EFI_IFR_DISPLAY_UINT_HEX:
Format = L"%lx";
break;
default:
return EFI_UNSUPPORTED;
}
UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);
return EFI_SUCCESS;
}
/**
Draw a pop up windows based on the dimension, number of lines and
strings specified.
@param RequestedWidth The width of the pop-up.
@param NumberOfLines The number of lines.
@param Marker The variable argument list for the list of string to be printed.
**/
VOID
CreateSharedPopUp (
IN UINTN RequestedWidth,
IN UINTN NumberOfLines,
IN VA_LIST Marker
)
{
UINTN Index;
UINTN Count;
CHAR16 Character;
UINTN Start;
UINTN End;
UINTN Top;
UINTN Bottom;
CHAR16 *String;
UINTN DimensionsWidth;
UINTN DimensionsHeight;
DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
if ((RequestedWidth + 2) > DimensionsWidth) {
RequestedWidth = DimensionsWidth - 2;
}
//
// Subtract the PopUp width from total Columns, allow for one space extra on
// each end plus a border.
//
Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gStatementDimensions.LeftColumn + 1;
End = Start + RequestedWidth + 1;
Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1;
Bottom = Top + NumberOfLines + 2;
Character = BOXDRAW_DOWN_RIGHT;
PrintCharAt (Start, Top, Character);
Character = BOXDRAW_HORIZONTAL;
for (Index = Start; Index + 2 < End; Index++) {
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
Character = BOXDRAW_DOWN_LEFT;
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
Character = BOXDRAW_VERTICAL;
Count = 0;
for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
String = VA_ARG (Marker, CHAR16 *);
//
// This will clear the background of the line - we never know who might have been
// here before us. This differs from the next clear in that it used the non-reverse
// video for normal printing.
//
if (GetStringWidth (String) / 2 > 1) {
ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
}
//
// Passing in a space results in the assumption that this is where typing will occur
//
if (String[0] == L' ') {
ClearLines (Start + 1, End - 1, Index + 1, Index + 1, GetPopupInverseColor ());
}
//
// Passing in a NULL results in a blank space
//
if (String[0] == CHAR_NULL) {
ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
}
PrintStringAt (
((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1,
Index + 1,
String
);
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
PrintCharAt (Start, Index + 1, Character);
PrintCharAt (End - 1, Index + 1, Character);
}
Character = BOXDRAW_UP_RIGHT;
PrintCharAt (Start, Bottom - 1, Character);
Character = BOXDRAW_HORIZONTAL;
for (Index = Start; Index + 2 < End; Index++) {
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
Character = BOXDRAW_UP_LEFT;
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
/**
Draw a pop up windows based on the dimension, number of lines and
strings specified.
@param RequestedWidth The width of the pop-up.
@param NumberOfLines The number of lines.
@param ... A series of text strings that displayed in the pop-up.
**/
VOID
EFIAPI
CreateMultiStringPopUp (
IN UINTN RequestedWidth,
IN UINTN NumberOfLines,
...
)
{
VA_LIST Marker;
VA_START (Marker, NumberOfLines);
CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
VA_END (Marker);
}
/**
Process nothing.
@param Event The Event need to be process
@param Context The context of the event.
**/
VOID
EFIAPI
EmptyEventProcess (
IN EFI_EVENT Event,
IN VOID *Context
)
{
}
/**
Process for the refresh interval statement.
@param Event The Event need to be process
@param Context The context of the event.
**/
VOID
EFIAPI
RefreshTimeOutProcess (
IN EFI_EVENT Event,
IN VOID *Context
)
{
WARNING_IF_CONTEXT *EventInfo;
CHAR16 TimeOutString[MAX_TIME_OUT_LEN];
EventInfo = (WARNING_IF_CONTEXT *)Context;
if (*(EventInfo->TimeOut) == 0) {
gBS->CloseEvent (Event);
gBS->SignalEvent (EventInfo->SyncEvent);
return;
}
UnicodeSPrint (TimeOutString, MAX_TIME_OUT_LEN, L"%d", *(EventInfo->TimeOut));
CreateDialog (NULL, gEmptyString, EventInfo->ErrorInfo, gPressEnter, gEmptyString, TimeOutString, NULL);
*(EventInfo->TimeOut) -= 1;
}
/**
Display error message for invalid password.
**/
VOID
PasswordInvalid (
VOID
)
{
EFI_INPUT_KEY Key;
//
// Invalid password, prompt error message
//
do {
CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}
/**
Process password op code.
@param MenuOption The menu for current password op code.
@retval EFI_SUCCESS Question Option process success.
@retval Other Question Option process fail.
**/
EFI_STATUS
PasswordProcess (
IN UI_MENU_OPTION *MenuOption
)
{
CHAR16 *StringPtr;
CHAR16 *TempString;
UINTN Maximum;
EFI_STATUS Status;
EFI_IFR_PASSWORD *PasswordInfo;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
EFI_INPUT_KEY Key;
Question = MenuOption->ThisTag;
PasswordInfo = (EFI_IFR_PASSWORD *)Question->OpCode;
Maximum = PasswordInfo->MaxSize;
Status = EFI_SUCCESS;
StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
ASSERT (StringPtr);
//
// Use a NULL password to test whether old password is required
//
*StringPtr = 0;
Status = Question->PasswordCheck (gFormData, Question, StringPtr);
if ((Status == EFI_NOT_AVAILABLE_YET) || (Status == EFI_UNSUPPORTED)) {
//
// Password can't be set now.
//
if (Status == EFI_UNSUPPORTED) {
do {
CreateDialog (&Key, gEmptyString, gPasswordUnsupported, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}
FreePool (StringPtr);
return EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
//
// Old password exist, ask user for the old password
//
Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
if (EFI_ERROR (Status)) {
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
FreePool (StringPtr);
return Status;
}
//
// Check user input old password
//
Status = Question->PasswordCheck (gFormData, Question, StringPtr);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_READY) {
//
// Typed in old password incorrect
//
PasswordInvalid ();
} else {
Status = EFI_SUCCESS;
}
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
FreePool (StringPtr);
return Status;
}
}
//
// Ask for new password
//
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
if (EFI_ERROR (Status)) {
//
// Reset state machine for password
//
Question->PasswordCheck (gFormData, Question, NULL);
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
FreePool (StringPtr);
return Status;
}
//
// Confirm new password
//
TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
ASSERT (TempString);
Status = ReadString (MenuOption, gConfirmPassword, TempString);
if (EFI_ERROR (Status)) {
//
// Reset state machine for password
//
Question->PasswordCheck (gFormData, Question, NULL);
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
FreePool (StringPtr);
FreePool (TempString);
return Status;
}
//
// Compare two typed-in new passwords
//
if (StrCmp (StringPtr, TempString) == 0) {
gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
gUserInput->InputValue.Value.string = HiiSetString (gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
Status = EFI_SUCCESS;
} else {
//
// Reset state machine for password
//
Question->PasswordCheck (gFormData, Question, NULL);
//
// Two password mismatch, prompt error message
//
do {
CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
Status = EFI_INVALID_PARAMETER;
}
ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
FreePool (TempString);
FreePool (StringPtr);
return Status;
}
/**
Print some debug message about mismatched menu info.
@param MenuOption The MenuOption for this Question.
**/
VOID
PrintMismatchMenuInfo (
IN UI_MENU_OPTION *MenuOption
)
{
CHAR16 *FormTitleStr;
CHAR16 *FormSetTitleStr;
CHAR16 *OneOfOptionStr;
CHAR16 *QuestionName;
LIST_ENTRY *Link;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
EFI_IFR_ORDERED_LIST *OrderList;
UINT8 Index;
EFI_HII_VALUE HiiValue;
EFI_HII_VALUE *QuestionValue;
DISPLAY_QUESTION_OPTION *Option;
UINT8 *ValueArray;
UINT8 ValueType;
EFI_IFR_FORM_SET *FormsetBuffer;
UINTN FormsetBufferSize;
Question = MenuOption->ThisTag;
if (!EFI_ERROR (HiiGetFormSetFromHiiHandle (gFormData->HiiHandle, &FormsetBuffer, &FormsetBufferSize))) {
FormSetTitleStr = GetToken (FormsetBuffer->FormSetTitle, gFormData->HiiHandle);
FormTitleStr = GetToken (gFormData->FormTitle, gFormData->HiiHandle);
DEBUG ((DEBUG_ERROR, "\n[%a]: Mismatch Formset : Formset Guid = %g, FormSet title = %s\n", gEfiCallerBaseName, &gFormData->FormSetGuid, FormSetTitleStr));
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Form : FormId = %d, Form title = %s.\n", gEfiCallerBaseName, gFormData->FormId, FormTitleStr));
}
if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
QuestionName = GetToken (((EFI_IFR_ORDERED_LIST *)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle);
Link = GetFirstNode (&Question->OptionListHead);
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ValueType = Option->OptionOpCode->Type;
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error : OrderedList value in the array doesn't match with option value.\n", gEfiCallerBaseName));
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: Name = %s.\n", gEfiCallerBaseName, QuestionName));
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: OrderedList array value :\n", gEfiCallerBaseName));
OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
for (Index = 0; Index < OrderList->MaxContainers; Index++) {
ValueArray = Question->CurrentValue.Buffer;
HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
DEBUG ((DEBUG_ERROR, " Value[%d] =%ld.\n", Index, HiiValue.Value.u64));
}
} else if (Question->OpCode->OpCode == EFI_IFR_ONE_OF_OP) {
QuestionName = GetToken (((EFI_IFR_ONE_OF *)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle);
QuestionValue = &Question->CurrentValue;
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error : OneOf value doesn't match with option value.\n", gEfiCallerBaseName));
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : Name = %s.\n", gEfiCallerBaseName, QuestionName));
switch (QuestionValue->Type) {
case EFI_IFR_TYPE_NUM_SIZE_64:
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %ld.\n", gEfiCallerBaseName, QuestionValue->Value.u64));
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u32));
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u16));
break;
case EFI_IFR_TYPE_NUM_SIZE_8:
DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u8));
break;
default:
ASSERT (FALSE);
break;
}
}
Index = 0;
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
OneOfOptionStr = GetToken (Option->OptionOpCode->Option, gFormData->HiiHandle);
switch (Option->OptionOpCode->Type) {
case EFI_IFR_TYPE_NUM_SIZE_64:
DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %ld, Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u64, OneOfOptionStr));
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %d, Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u32, OneOfOptionStr));
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %d, Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u16, OneOfOptionStr));
break;
case EFI_IFR_TYPE_NUM_SIZE_8:
DEBUG ((DEBUG_ERROR, "[%a]: Option %d : Option Value = %d, Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u8, OneOfOptionStr));
break;
default:
ASSERT (FALSE);
break;
}
Link = GetNextNode (&Question->OptionListHead, Link);
Index++;
}
}
/**
Process a Question's Option (whether selected or un-selected).
@param MenuOption The MenuOption for this Question.
@param Selected TRUE: if Question is selected.
@param OptionString Pointer of the Option String to be displayed.
@param SkipErrorValue Whether need to return when value without option for it.
@retval EFI_SUCCESS Question Option process success.
@retval Other Question Option process fail.
**/
EFI_STATUS
ProcessOptions (
IN UI_MENU_OPTION *MenuOption,
IN BOOLEAN Selected,
OUT CHAR16 **OptionString,
IN BOOLEAN SkipErrorValue
)
{
EFI_STATUS Status;
CHAR16 *StringPtr;
UINTN Index;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
CHAR16 FormattedNumber[21];
UINT16 Number;
CHAR16 Character[2];
EFI_INPUT_KEY Key;
UINTN BufferSize;
DISPLAY_QUESTION_OPTION *OneOfOption;
LIST_ENTRY *Link;
EFI_HII_VALUE HiiValue;
EFI_HII_VALUE *QuestionValue;
DISPLAY_QUESTION_OPTION *Option;
UINTN Index2;
UINT8 *ValueArray;
UINT8 ValueType;
EFI_IFR_ORDERED_LIST *OrderList;
BOOLEAN ValueInvalid;
UINTN MaxLen;
Status = EFI_SUCCESS;
StringPtr = NULL;
Character[1] = L'\0';
*OptionString = NULL;
ValueInvalid = FALSE;
ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow;
Question = MenuOption->ThisTag;
QuestionValue = &Question->CurrentValue;
switch (Question->OpCode->OpCode) {
case EFI_IFR_ORDERED_LIST_OP:
//
// Check whether there are Options of this OrderedList
//
if (IsListEmpty (&Question->OptionListHead)) {
break;
}
OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
Link = GetFirstNode (&Question->OptionListHead);
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ValueType = OneOfOption->OptionOpCode->Type;
ValueArray = Question->CurrentValue.Buffer;
if (Selected) {
//
// Go ask for input
//
Status = GetSelectionInputPopUp (MenuOption);
} else {
//
// We now know how many strings we will have, so we can allocate the
// space required for the array or strings.
//
MaxLen = OrderList->MaxContainers * BufferSize / sizeof (CHAR16);
*OptionString = AllocateZeroPool (MaxLen * sizeof (CHAR16));
ASSERT (*OptionString);
HiiValue.Type = ValueType;
HiiValue.Value.u64 = 0;
for (Index = 0; Index < OrderList->MaxContainers; Index++) {
HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
if (HiiValue.Value.u64 == 0) {
//
// Values for the options in ordered lists should never be a 0
//
break;
}
OneOfOption = ValueToOption (Question, &HiiValue);
if (OneOfOption == NULL) {
//
// Print debug msg for the mistach menu.
//
PrintMismatchMenuInfo (MenuOption);
if (SkipErrorValue) {
//
// Just try to get the option string, skip the value which not has option.
//
continue;
}
//
// Show error message
//
do {
CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
//
// The initial value of the orderedlist is invalid, force to be valid value
// Exit current DisplayForm with new value.
//
gUserInput->SelectedStatement = Question;
gMisMatch = TRUE;
ValueArray = AllocateZeroPool (Question->CurrentValue.BufferLen);
ASSERT (ValueArray != NULL);
gUserInput->InputValue.Buffer = ValueArray;
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
Link = GetFirstNode (&Question->OptionListHead);
Index2 = 0;
while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) {
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
Link = GetNextNode (&Question->OptionListHead, Link);
SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64);
Index2++;
}
SetArrayData (ValueArray, ValueType, Index2, 0);
FreePool (*OptionString);
*OptionString = NULL;
return EFI_NOT_FOUND;
}
Character[0] = LEFT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], MaxLen, Character);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
ASSERT (StringPtr != NULL);
NewStrCat (OptionString[0], MaxLen, StringPtr);
Character[0] = RIGHT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], MaxLen, Character);
Character[0] = CHAR_CARRIAGE_RETURN;
NewStrCat (OptionString[0], MaxLen, Character);
FreePool (StringPtr);
}
//
// If valid option more than the max container, skip these options.
//
if (Index >= OrderList->MaxContainers) {
break;
}
//
// Search the other options, try to find the one not in the container.
//
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
Link = GetNextNode (&Question->OptionListHead, Link);
if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) {
continue;
}
//
// Print debug msg for the mistach menu.
//
PrintMismatchMenuInfo (MenuOption);
if (SkipErrorValue) {
//
// Not report error, just get the correct option string info.
//
Character[0] = LEFT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], MaxLen, Character);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
ASSERT (StringPtr != NULL);
NewStrCat (OptionString[0], MaxLen, StringPtr);
Character[0] = RIGHT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], MaxLen, Character);
Character[0] = CHAR_CARRIAGE_RETURN;
NewStrCat (OptionString[0], MaxLen, Character);
FreePool (StringPtr);
continue;
}
if (!ValueInvalid) {
ValueInvalid = TRUE;
//
// Show error message
//
do {
CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
//
// The initial value of the orderedlist is invalid, force to be valid value
// Exit current DisplayForm with new value.
//
gUserInput->SelectedStatement = Question;
gMisMatch = TRUE;
ValueArray = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer);
ASSERT (ValueArray != NULL);
gUserInput->InputValue.Buffer = ValueArray;
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
}
SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64);
}
if (ValueInvalid) {
FreePool (*OptionString);
*OptionString = NULL;
return EFI_NOT_FOUND;
}
}
break;
case EFI_IFR_ONE_OF_OP:
//
// Check whether there are Options of this OneOf
//
if (IsListEmpty (&Question->OptionListHead)) {
break;
}
if (Selected) {
//
// Go ask for input
//
Status = GetSelectionInputPopUp (MenuOption);
} else {
MaxLen = BufferSize / sizeof (CHAR16);
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
OneOfOption = ValueToOption (Question, QuestionValue);
if (OneOfOption == NULL) {
//
// Print debug msg for the mistach menu.
//
PrintMismatchMenuInfo (MenuOption);
if (SkipErrorValue) {
//
// Not report error, just get the correct option string info.
//
Link = GetFirstNode (&Question->OptionListHead);
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
} else {
//
// Show error message
//
do {
CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
//
// Force the Question value to be valid
// Exit current DisplayForm with new value.
//
Link = GetFirstNode (&Question->OptionListHead);
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
gUserInput->InputValue.Type = Option->OptionOpCode->Type;
switch (gUserInput->InputValue.Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16));
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32));
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64));
break;
default:
ASSERT (FALSE);
break;
}
gUserInput->SelectedStatement = Question;
gMisMatch = TRUE;
FreePool (*OptionString);
*OptionString = NULL;
return EFI_NOT_FOUND;
}
}
Character[0] = LEFT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], MaxLen, Character);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
ASSERT (StringPtr != NULL);
NewStrCat (OptionString[0], MaxLen, StringPtr);
Character[0] = RIGHT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], MaxLen, Character);
FreePool (StringPtr);
}
break;
case EFI_IFR_CHECKBOX_OP:
if (Selected) {
//
// Since this is a BOOLEAN operation, flip it upon selection
//
gUserInput->InputValue.Type = QuestionValue->Type;
gUserInput->InputValue.Value.b = (BOOLEAN)(QuestionValue->Value.b ? FALSE : TRUE);
//
// Perform inconsistent check
//
return EFI_SUCCESS;
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
*OptionString[0] = LEFT_CHECKBOX_DELIMITER;
if (QuestionValue->Value.b) {
*(OptionString[0] + 1) = CHECK_ON;
} else {
*(OptionString[0] + 1) = CHECK_OFF;
}
*(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
}
break;
case EFI_IFR_NUMERIC_OP:
if (Selected) {
//
// Go ask for input
//
Status = GetNumericInput (MenuOption);
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
*OptionString[0] = LEFT_NUMERIC_DELIMITER;
//
// Formatted print
//
PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
Number = (UINT16)GetStringWidth (FormattedNumber);
CopyMem (OptionString[0] + 1, FormattedNumber, Number);
*(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
}
break;
case EFI_IFR_DATE_OP:
if (Selected) {
//
// This is similar to numerics
//
Status = GetNumericInput (MenuOption);
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
switch (MenuOption->Sequence) {
case 0:
*OptionString[0] = LEFT_NUMERIC_DELIMITER;
if (QuestionValue->Value.date.Month == 0xff) {
UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??");
} else {
UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
}
*(OptionString[0] + 3) = DATE_SEPARATOR;
break;
case 1:
SetUnicodeMem (OptionString[0], 4, L' ');
if (QuestionValue->Value.date.Day == 0xff) {
UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??");
} else {
UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
}
*(OptionString[0] + 6) = DATE_SEPARATOR;
break;
case 2:
SetUnicodeMem (OptionString[0], 7, L' ');
if (QuestionValue->Value.date.Year == 0xff) {
UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"????");
} else {
UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year);
}
*(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
break;
}
}
break;
case EFI_IFR_TIME_OP:
if (Selected) {
//
// This is similar to numerics
//
Status = GetNumericInput (MenuOption);
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
switch (MenuOption->Sequence) {
case 0:
*OptionString[0] = LEFT_NUMERIC_DELIMITER;
if (QuestionValue->Value.time.Hour == 0xff) {
UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??");
} else {
UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
}
*(OptionString[0] + 3) = TIME_SEPARATOR;
break;
case 1:
SetUnicodeMem (OptionString[0], 4, L' ');
if (QuestionValue->Value.time.Minute == 0xff) {
UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??");
} else {
UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
}
*(OptionString[0] + 6) = TIME_SEPARATOR;
break;
case 2:
SetUnicodeMem (OptionString[0], 7, L' ');
if (QuestionValue->Value.time.Second == 0xff) {
UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"??");
} else {
UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
}
*(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
break;
}
}
break;
case EFI_IFR_STRING_OP:
if (Selected) {
StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16));
ASSERT (StringPtr);
CopyMem (StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen);
Status = ReadString (MenuOption, gPromptForData, StringPtr);
if (EFI_ERROR (Status)) {
FreePool (StringPtr);
return Status;
}
gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
gUserInput->InputValue.Value.string = HiiSetString (gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
FreePool (StringPtr);
return EFI_SUCCESS;
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
if (((CHAR16 *)Question->CurrentValue.Buffer)[0] == 0x0000) {
*(OptionString[0]) = '_';
} else {
if (Question->CurrentValue.BufferLen < BufferSize) {
BufferSize = Question->CurrentValue.BufferLen;
}
CopyMem (OptionString[0], (CHAR16 *)Question->CurrentValue.Buffer, BufferSize);
}
}
break;
case EFI_IFR_PASSWORD_OP:
if (Selected) {
Status = PasswordProcess (MenuOption);
}
break;
default:
break;
}
return Status;
}
/**
Process the help string: Split StringPtr to several lines of strings stored in
FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
@param StringPtr The entire help string.
@param FormattedString The oupput formatted string.
@param EachLineWidth The max string length of each line in the formatted string.
@param RowCount TRUE: if Question is selected.
**/
UINTN
ProcessHelpString (
IN CHAR16 *StringPtr,
OUT CHAR16 **FormattedString,
OUT UINT16 *EachLineWidth,
IN UINTN RowCount
)
{
UINTN Index;
CHAR16 *OutputString;
UINTN TotalRowNum;
UINTN CheckedNum;
UINT16 GlyphWidth;
UINT16 LineWidth;
UINT16 MaxStringLen;
UINT16 StringLen;
TotalRowNum = 0;
CheckedNum = 0;
GlyphWidth = 1;
Index = 0;
MaxStringLen = 0;
StringLen = 0;
//
// Set default help string width.
//
LineWidth = (UINT16)(gHelpBlockWidth - 1);
//
// Get row number of the String.
//
while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
if (StringLen > MaxStringLen) {
MaxStringLen = StringLen;
}
TotalRowNum++;
FreePool (OutputString);
}
*EachLineWidth = MaxStringLen;
*FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16));
ASSERT (*FormattedString != NULL);
//
// Generate formatted help string array.
//
GlyphWidth = 1;
Index = 0;
while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16));
CheckedNum++;
FreePool (OutputString);
}
return TotalRowNum;
}