]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Universal/SetupBrowserDxe/ProcessOptions.c
Tracker merger. "There is no "NV" display when change the certain option in NT32"
[mirror_edk2.git] / IntelFrameworkModulePkg / Universal / SetupBrowserDxe / ProcessOptions.c
1 /**@file
2 Implementation for handling the User Interface option processing.
3
4 Copyright (c) 2006 - 2007 Intel Corporation. <BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Setup.h"
16 #include "Ui.h"
17
18 EFI_STATUS
19 ExtractRequestedNvMap (
20 IN EFI_FILE_FORM_TAGS *FileFormTags,
21 IN UINT16 VariableId,
22 OUT EFI_VARIABLE_DEFINITION **VariableDefinition
23 )
24 {
25 *VariableDefinition = FileFormTags->VariableDefinitions;
26
27 //
28 // Extract the data from the NV variable - consumer will free the buffer.
29 //
30 for (; *VariableDefinition != NULL; *VariableDefinition = (*VariableDefinition)->Next) {
31 //
32 // If there is a variable with this ID return with EFI_SUCCESS
33 //
34 if (!CompareMem (&(*VariableDefinition)->VariableId, &VariableId, sizeof (UINT16))) {
35 return EFI_SUCCESS;
36 }
37 }
38
39 return EFI_NOT_FOUND;
40 }
41
42 EFI_STATUS
43 ExtractNvValue (
44 IN EFI_FILE_FORM_TAGS *FileFormTags,
45 IN UINT16 VariableId,
46 IN UINT16 VariableSize,
47 IN UINT16 OffsetValue,
48 OUT VOID **Buffer
49 )
50 {
51 EFI_STATUS Status;
52 EFI_VARIABLE_DEFINITION *VariableDefinition;
53
54 Status = ExtractRequestedNvMap (FileFormTags, VariableId, &VariableDefinition);
55
56 if (!EFI_ERROR (Status)) {
57 //
58 // Allocate sufficient space for the data and copy it into the outgoing buffer
59 //
60 if (VariableSize != 0) {
61 *Buffer = AllocateZeroPool (VariableSize);
62 ASSERT (*Buffer != NULL);
63 CopyMem (*Buffer, &VariableDefinition->NvRamMap[OffsetValue], VariableSize);
64 }
65 return EFI_SUCCESS;
66 }
67
68 return Status;
69 }
70
71 STATIC
72 VOID
73 AdjustNvMap (
74 IN EFI_FILE_FORM_TAGS *FileFormTags,
75 IN UI_MENU_OPTION *MenuOption
76 )
77 {
78 CHAR8 *NvRamMap;
79 UINTN SizeRequired;
80 UINTN Index;
81 UINTN CachedStart;
82 EFI_VARIABLE_DEFINITION *VariableDefinition;
83
84 CachedStart = 0;
85
86 SizeRequired = MenuOption->ThisTag->StorageStart + MenuOption->ThisTag->StorageWidth;
87
88 ExtractRequestedNvMap (FileFormTags, MenuOption->Tags->VariableNumber, &VariableDefinition);
89
90 //
91 // We arrived here because the current NvRamMap is too small for the new op-code to store things and
92 // we need to adjust the buffer to support this.
93 //
94 NvRamMap = AllocateZeroPool (SizeRequired + 1);
95 ASSERT (NvRamMap != NULL);
96
97 //
98 // Copy current NvRamMap to the new NvRamMap
99 //
100 CopyMem (NvRamMap, VariableDefinition->NvRamMap, VariableDefinition->VariableFakeSize);
101
102 //
103 // Remember, the only time we come here is because we are in the NVPlus section of the NvRamMap
104 //
105 for (Index = MenuOption->TagIndex;
106 (MenuOption->Tags[Index].Operand != EFI_IFR_END_FORM_OP) && (MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP);
107 Index++
108 ) {
109
110 switch (MenuOption->Tags[Index].Operand) {
111 case EFI_IFR_ORDERED_LIST_OP:
112 case EFI_IFR_ONE_OF_OP:
113 CachedStart = MenuOption->Tags[Index].StorageStart;
114 break;
115
116 case EFI_IFR_ONE_OF_OPTION_OP:
117 if (MenuOption->Tags[Index].Flags & EFI_IFR_FLAG_DEFAULT) {
118 CopyMem (&NvRamMap[CachedStart], &MenuOption->Tags[Index].Value, 2);
119 }
120 break;
121
122 case EFI_IFR_CHECKBOX_OP:
123 CopyMem (&NvRamMap[MenuOption->Tags[Index].StorageStart], &MenuOption->Tags[Index].Flags, 1);
124 break;
125
126 case EFI_IFR_NUMERIC_OP:
127 case EFI_IFR_DATE_OP:
128 case EFI_IFR_TIME_OP:
129 case EFI_IFR_STRING_OP:
130 case EFI_IFR_PASSWORD_OP:
131 CopyMem (
132 &NvRamMap[MenuOption->Tags[Index].StorageStart],
133 &MenuOption->Tags[Index].Value,
134 MenuOption->Tags[Index].StorageWidth
135 );
136 break;
137
138 }
139 }
140
141 FreePool (VariableDefinition->NvRamMap);
142 VariableDefinition->NvRamMap = NvRamMap;
143 VariableDefinition->VariableFakeSize = (UINT16) SizeRequired;
144 }
145
146 EFI_STATUS
147 ProcessOptions (
148 IN UI_MENU_OPTION *MenuOption,
149 IN BOOLEAN Selected,
150 IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
151 IN EFI_IFR_DATA_ARRAY *PageData,
152 OUT CHAR16 **OptionString
153 )
154 {
155 EFI_STATUS Status;
156 CHAR16 *StringPtr;
157 UINTN Index;
158 UINTN CachedIndex;
159 EFI_FILE_FORM_TAGS *FileFormTags;
160 EFI_TAG *Tag;
161 CHAR16 FormattedNumber[6];
162 UINT16 Number;
163 UINT16 Value;
164 UINT16 *ValueArray;
165 UINT16 *NvRamMap;
166 CHAR8 *TmpNvRamMap;
167 UINTN Default;
168 UINTN StringCount;
169 CHAR16 Character[2];
170 UINTN Count;
171 EFI_TIME Time;
172 EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
173 STRING_REF PopUp;
174 CHAR16 NullCharacter;
175 EFI_INPUT_KEY Key;
176 EFI_VARIABLE_DEFINITION *VariableDefinition;
177 BOOLEAN OrderedList;
178 BOOLEAN Initialized;
179 UINT16 KeyValue;
180 BOOLEAN Skip;
181
182 FileFormTags = FileFormTagsHead;
183
184 for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
185 FileFormTags = FileFormTags->NextFile;
186 }
187
188 OrderedList = FALSE;
189 Initialized = FALSE;
190 ValueArray = NULL;
191 VariableDefinition = NULL;
192 Skip = FALSE;
193
194 ZeroMem (&Time, sizeof (EFI_TIME));
195
196 StringPtr = (CHAR16 *) L"\0";
197 Tag = MenuOption->ThisTag;
198 ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
199
200 if (Tag->StorageStart > VariableDefinition->VariableSize) {
201 NvRamMap = (UINT16 *) &VariableDefinition->FakeNvRamMap[Tag->StorageStart];
202 } else {
203 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
204 }
205
206 StringCount = 0;
207 Character[1] = 0;
208 Count = 0;
209 Default = 0;
210 NullCharacter = CHAR_NULL;
211 FormCallback = NULL;
212
213 if (MenuOption->ThisTag->Operand == EFI_IFR_ORDERED_LIST_OP) {
214 OrderedList = TRUE;
215 if (((UINT8 *) NvRamMap)[0] != 0x00) {
216 Initialized = TRUE;
217 }
218 }
219
220 ZeroMem (FormattedNumber, 12);
221
222 Status = gBS->HandleProtocol (
223 (VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
224 &gEfiFormCallbackProtocolGuid,
225 (VOID **) &FormCallback
226 );
227
228 if (*OptionString != NULL) {
229 FreePool (*OptionString);
230 *OptionString = NULL;
231 }
232
233 switch (Tag->Operand) {
234
235 case EFI_IFR_ORDERED_LIST_OP:
236 case EFI_IFR_ONE_OF_OP:
237 //
238 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
239 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
240 // the NvMap so that we can properly display the information
241 //
242 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
243 AdjustNvMap (FileFormTags, MenuOption);
244 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
245 }
246
247 CachedIndex = MenuOption->TagIndex;
248
249 //
250 // search for EFI_IFR_ONE_OF_OPTION_OP until you hit the EFI_IFR_END_ONE_OF_OP,
251 // each of the .Text in the options are going to be what gets displayed. Break each into 26 char chunks
252 // when hit right/left arrow allows for selection - then repopulate Tag[TagIndex] with the choice
253 //
254 for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP; Index++) {
255 //
256 // We found an option - which assumedly has a string. We will eventually have to support
257 // wrapping of strings. For now, let's pretend they don't wrap and code that up.
258 //
259 // Count how many strings there are
260 //
261 if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
262 //
263 // If one of the options for the one-of has an interactive flag, back-define the oneof to have one too
264 //
265 if (MenuOption->Tags[Index].Flags & EFI_IFR_FLAG_INTERACTIVE) {
266 MenuOption->Tags[CachedIndex].Flags = (UINT8) (MenuOption->Tags[CachedIndex].Flags | EFI_IFR_FLAG_INTERACTIVE);
267 }
268
269 StringCount++;
270 }
271 }
272 //
273 // We now know how many strings we will have, so we can allocate the
274 // space required for the array or strings.
275 //
276 *OptionString = AllocateZeroPool (StringCount * (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
277 ASSERT (*OptionString);
278
279 //
280 // Add left delimeter to string
281 //
282 *OptionString[0] = LEFT_ONEOF_DELIMITER;
283
284 //
285 // Retrieve the current OneOf value
286 //
287 if (Selected) {
288 //
289 // Auto selection from list
290 //
291 Value = 0;
292 //
293 // Copy current setting to the seed Value
294 //
295 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
296 ValueArray = AllocateZeroPool (MenuOption->ThisTag->StorageWidth);
297 ASSERT (ValueArray != NULL);
298 CopyMem (ValueArray, NvRamMap, MenuOption->ThisTag->StorageWidth);
299 } else {
300 CopyMem (&Value, NvRamMap, MenuOption->ThisTag->StorageWidth);
301 CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
302 }
303
304 Number = Value;
305 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
306 Status = GetSelectionInputPopUp (MenuOption, Tag, MenuOption->ThisTag->StorageWidth, ValueArray, &KeyValue);
307 } else {
308 Status = GetSelectionInputPopUp (MenuOption, Tag, 1, &Value, &KeyValue);
309 }
310
311 if (!EFI_ERROR (Status)) {
312 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
313 CopyMem (NvRamMap, ValueArray, MenuOption->ThisTag->StorageWidth);
314 FreePool (ValueArray);
315 } else {
316 //
317 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
318 //
319 CopyMem (NvRamMap, &Value, Tag->StorageWidth);
320 MenuOption->ThisTag->Key = KeyValue;
321 }
322 //
323 // If a late check is required save off the information. This is used when consistency checks
324 // are required, but certain values might be bound by an impossible consistency check such as
325 // if two questions are bound by consistency checks and each only has two possible choices, there
326 // would be no way for a user to switch the values. Thus we require late checking.
327 //
328 if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
329 CopyMem (&Tag->OldValue, &Value, Tag->StorageWidth);
330 } else {
331 //
332 // In theory, passing the value and the Id are sufficient to determine what needs
333 // to be done. The Id is the key to look for the entry needed in the Inconsistency
334 // database. That will yields operand and ID data - and since the ID's correspond
335 // to the NV storage, we can determine the values for other IDs there.
336 //
337 if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
338 if (PopUp == 0x0000) {
339 //
340 // Restore Old Value
341 //
342 if (!Tag->Suppress && !Tag->GrayOut) {
343 CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
344 break;
345 }
346 } else {
347
348 StringPtr = GetToken (PopUp, MenuOption->Handle);
349
350 CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);
351
352 do {
353 Status = WaitForKeyStroke (&Key);
354
355 switch (Key.UnicodeChar) {
356
357 case CHAR_CARRIAGE_RETURN:
358 //
359 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
360 //
361 CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
362 FreePool (StringPtr);
363 break;
364
365 default:
366 break;
367 }
368 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
369 }
370 }
371 }
372
373 UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
374 } else {
375 if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
376 FreePool (ValueArray);
377 }
378
379 return EFI_SUCCESS;
380 }
381 } else {
382 for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP; Index++) {
383 //
384 // We found an option - which assumedly has a string. We will eventually have to support
385 // wrapping of strings. For now, let's pretend they don't wrap and code that up.
386 //
387 if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
388 if (OrderedList) {
389 if (!Initialized) {
390 //
391 // If the first entry is invalid, then the "default" settings are based on what is reflected
392 // in the order of the op-codes
393 //
394 ((UINT8 *) NvRamMap)[Index - MenuOption->TagIndex - 1] = (UINT8) MenuOption->Tags[Index].Value;
395 }
396 //
397 // Only display 3 lines of stuff at most
398 //
399 if ((Index - MenuOption->TagIndex) > ORDERED_LIST_SIZE) {
400 break;
401 }
402
403 if (((Index - MenuOption->TagIndex) != 1) && !Skip) {
404 Character[0] = LEFT_ONEOF_DELIMITER;
405 NewStrCat (OptionString[0], Character);
406 }
407
408 MenuOption->ThisTag->NumberOfLines = (UINT16) (Index - MenuOption->TagIndex);
409 if (!Initialized) {
410 StringPtr = GetToken (MenuOption->Tags[Index].Text, MenuOption->Handle);
411 } else {
412 for (Value = (UINT16) (MenuOption->TagIndex + 1);
413 MenuOption->Tags[Value].Operand != EFI_IFR_END_ONE_OF_OP;
414 Value++
415 ) {
416 if (MenuOption->Tags[Value].Value == ((UINT8 *) NvRamMap)[Index - MenuOption->TagIndex - 1]) {
417 StringPtr = GetToken (MenuOption->Tags[Value].Text, MenuOption->Handle);
418 break;
419 }
420 }
421
422 if (MenuOption->Tags[Value].Operand == EFI_IFR_END_ONE_OF_OP) {
423 Skip = TRUE;
424 continue;
425 }
426 }
427
428 Skip = FALSE;
429 NewStrCat (OptionString[0], StringPtr);
430 Character[0] = RIGHT_ONEOF_DELIMITER;
431 NewStrCat (OptionString[0], Character);
432 Character[0] = CHAR_CARRIAGE_RETURN;
433 NewStrCat (OptionString[0], Character);
434
435 //
436 // Remove Buffer allocated for StringPtr after it has been used.
437 //
438 FreePool (StringPtr);
439 } else {
440 //
441 // The option value is the same as what is stored in NV store. Print this.
442 //
443 if (!CompareMem (&(MenuOption->Tags[Index].Value), NvRamMap, MenuOption->ThisTag->StorageWidth)) {
444 StringPtr = GetToken (MenuOption->Tags[Index].Text, MenuOption->Handle);
445 NewStrCat (OptionString[0], StringPtr);
446 Character[0] = RIGHT_ONEOF_DELIMITER;
447 NewStrCat (OptionString[0], Character);
448 //
449 // Remove Buffer allocated for StringPtr after it has been used.
450 //
451 FreePool (StringPtr);
452 Default = 0;
453 break;
454 }
455
456 if ((MenuOption->Tags[Index].Flags & EFI_IFR_FLAG_DEFAULT) == 1) {
457 Default = MenuOption->Tags[Index].Text;
458 Value = MenuOption->Tags[Index].Value;
459 };
460 }
461 }
462 }
463 //
464 // We didn't find a value that matched a setting in the NVRAM Map - display default - set default
465 //
466 if (Default != 0) {
467 //
468 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
469 //
470 CopyMem (NvRamMap, &Value, MenuOption->ThisTag->StorageWidth);
471
472 StringPtr = GetToken ((UINT16) Default, MenuOption->Handle);
473 NewStrCat (OptionString[0], StringPtr);
474 Character[0] = RIGHT_ONEOF_DELIMITER;
475 NewStrCat (OptionString[0], Character);
476 //
477 // Remove Buffer allocated for StringPtr after it has been used.
478 //
479 FreePool (StringPtr);
480 }
481 }
482 break;
483
484 case EFI_IFR_CHECKBOX_OP:
485 //
486 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
487 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
488 // the NvMap so that we can properly display the information
489 //
490 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
491 AdjustNvMap (FileFormTags, MenuOption);
492 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
493 }
494
495 Default = Tag->Flags & 1;
496 //
497 // If hit spacebar, set or unset Tag[TagIndex].Flags based on it's previous value - BOOLEAN
498 //
499 *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
500 ASSERT (*OptionString);
501
502 //
503 // Since Checkboxes are BOOLEAN values, bit 0 of the Flags bit defines the default option, therefore, if
504 // the default option (only one option for checkboxes) is on, then the default value is on. Tag.Default is not
505 // an active field for Checkboxes.
506 //
507 StrnCpy (OptionString[0], (CHAR16 *) LEFT_CHECKBOX_DELIMITER, 1);
508
509 //
510 // Since this is a BOOLEAN operation, flip bit 0 upon selection
511 //
512 if (Selected) {
513 Tag->Value = (UINT16) (Tag->Value ^ 1);
514 *(UINT8 *) NvRamMap = (UINT8) (Tag->Value & 1);
515 UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
516 }
517
518 if ((*(UINT8 *) NvRamMap & 1) == 0x01) {
519 NewStrCat (OptionString[0], (CHAR16 *) CHECK_ON);
520 //
521 // If someone reset default variables - we may need to reload from our NvMapping....
522 //
523 Tag->Value = *(UINT8 *) NvRamMap;
524 } else {
525 //
526 // If someone reset default variables - we may need to reload from our NvMapping....
527 //
528 NewStrCat (OptionString[0], (CHAR16 *) CHECK_OFF);
529 Tag->Value = *(UINT8 *) NvRamMap;
530 }
531
532 NewStrCat (OptionString[0], (CHAR16 *) RIGHT_CHECKBOX_DELIMITER);
533 NewStrCat (OptionString[0], StringPtr);
534 break;
535
536 case EFI_IFR_NUMERIC_OP:
537 //
538 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
539 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
540 // the NvMap so that we can properly display the information
541 //
542 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
543 AdjustNvMap (FileFormTags, MenuOption);
544 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
545 }
546
547 *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
548 ASSERT (*OptionString);
549
550 //
551 // Add left delimeter to string
552 //
553 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
554
555 //
556 // Retrieve the current numeric value
557 //
558 if (Selected) {
559 //
560 // Go ask for input
561 //
562 if (Tag->Step == 0) {
563 //
564 // Manual Input
565 //
566 Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, REGULAR_NUMERIC, &Number);
567 if (!EFI_ERROR (Status)) {
568 CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
569 UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
570
571 //
572 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
573 //
574 CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
575 } else {
576 return EFI_SUCCESS;
577 }
578 } else {
579 //
580 // Auto selection from list
581 //
582 if ((((Tag->StorageWidth == 1) && (UINT8) (*NvRamMap) > Tag->Maximum) || ((UINT8) (*NvRamMap) < Tag->Minimum)) ||
583 (((Tag->StorageWidth == 2) && *NvRamMap > Tag->Maximum) || (*NvRamMap < Tag->Minimum))
584 ) {
585 //
586 // Seed Number with valid value if currently invalid
587 //
588 Number = Tag->Default;
589 } else {
590 if (Tag->StorageWidth == 1) {
591 Number = (UINT8) (*NvRamMap);
592 } else {
593 Number = *NvRamMap;
594 }
595 }
596
597 Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, REGULAR_NUMERIC, &Number);
598 if (!EFI_ERROR (Status)) {
599 CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
600 UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
601
602 //
603 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
604 //
605 CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
606 } else {
607 return EFI_SUCCESS;
608 }
609 }
610 } else {
611 if (((Tag->StorageWidth == 1) && (UINT8) (*NvRamMap) <= Tag->Maximum && (UINT8) (*NvRamMap) >= Tag->Minimum) ||
612 ((Tag->StorageWidth == 2) && *NvRamMap <= Tag->Maximum && *NvRamMap >= Tag->Minimum)
613 ) {
614 if (Tag->StorageWidth == 1) {
615 Number = (UINT8) (*NvRamMap);
616 } else {
617 Number = *NvRamMap;
618 }
619 UnicodeValueToString (
620 FormattedNumber,
621 FALSE,
622 (UINTN) Number,
623 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
624 );
625 Number = (UINT16) GetStringWidth (FormattedNumber);
626 StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
627 } else {
628 //
629 // If *NvRamMap isn't within parameters, set it to within parameters
630 //
631 //
632 // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
633 //
634 CopyMem (NvRamMap, &Tag->Default, MenuOption->ThisTag->StorageWidth);
635 Number = Tag->Default;
636
637 UnicodeValueToString (
638 FormattedNumber,
639 FALSE,
640 (UINTN) Number,
641 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
642 );
643 Number = (UINT16) GetStringWidth (FormattedNumber);
644 StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
645 }
646
647 *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
648 NewStrCat (OptionString[0] + (Number / 2) + 1, StringPtr);
649 }
650 break;
651
652 case EFI_IFR_DATE_OP:
653 //
654 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
655 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
656 // the NvMap so that we can properly display the information
657 //
658 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
659 AdjustNvMap (FileFormTags, MenuOption);
660 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
661 }
662
663 Status = gRT->GetTime (&Time, NULL);
664 if (EFI_ERROR (Status)) {
665 return EFI_SUCCESS;
666 }
667 //
668 // This for loop advances Index till it points immediately after a date entry. We can then
669 // subtract MenuOption->TagIndex from Index and find out relative to the start of the Date
670 // structure which field we were in. For instance, if TagIndex was 52, and we advanced Index
671 // to 53 and found it to no longer point to a date operand, we were pointing to the last of 3
672 // date operands.
673 //
674 //
675 // This has BUGBUG potential....fix this - if someone wants to ask two DATE questions in a row.....code
676 // against such silliness.
677 //
678 // Also, we want to internationalize the order of the date information. We need to code for it as well.
679 //
680 for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand == EFI_IFR_DATE_OP; Index++)
681 ;
682
683 //
684 // Count 0 = We entered on the first Date operand
685 // Count 1 = We entered on the second Date operand
686 // Count 2 = We entered on the third Date operand
687 //
688 Count = 3 - (Index - MenuOption->TagIndex);
689 if (Count > 2) {
690 return EFI_SUCCESS;
691 }
692 //
693 // This is similar to numerics, except for the following:
694 // We will under normal circumstances get 3 consecutive calls
695 // to process this opcodes data.
696 //
697 *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
698 ASSERT (*OptionString);
699
700 switch (Count) {
701 case 0:
702 if (Selected) {
703 Number = (UINT16) Time.Month;
704
705 if (Tag->Step == 0) {
706 MenuOption->OptCol++;
707 Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, DATE_NUMERIC, &Number);
708 } else {
709 //
710 // Seed value with current setting
711 //
712 Tag->Value = (UINT16) Time.Month;
713 Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, DATE_NUMERIC, &Number);
714 }
715
716 if (!EFI_ERROR (Status)) {
717 Time.Month = (UINT8) Number;
718 gRT->SetTime (&Time);
719 }
720 }
721
722 VariableDefinition->FakeNvRamMap[Tag->Id] = Time.Month;
723 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
724
725 UnicodeValueToString (
726 FormattedNumber,
727 FALSE,
728 (UINTN) Time.Month,
729 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
730 );
731 Number = (UINT16) GetStringWidth (FormattedNumber);
732
733 if (Number == 4) {
734 FormattedNumber[2] = FormattedNumber[1];
735 FormattedNumber[1] = FormattedNumber[0];
736 FormattedNumber[0] = L'0';
737 Number = 6;
738 }
739
740 StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
741 *(OptionString[0] + Number / 2) = DATE_SEPARATOR;
742 StrCat (OptionString[0] + (Number / 2) + 1, StringPtr);
743 break;
744
745 case 1:
746 if (Selected) {
747 Number = (UINT16) Time.Day;
748
749 if (Tag->Step == 0) {
750 Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, DATE_NUMERIC, &Number);
751 } else {
752 //
753 // Seed value with current setting
754 //
755 Tag->Value = (UINT16) Time.Day;
756 Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, DATE_NUMERIC, &Number);
757 }
758
759 if (!EFI_ERROR (Status)) {
760 Time.Day = (UINT8) Number;
761 gRT->SetTime (&Time);
762 }
763 }
764
765 VariableDefinition->FakeNvRamMap[Tag->Id] = Time.Day;
766 SetUnicodeMem (OptionString[0], 4, L' ');
767
768 UnicodeValueToString (
769 FormattedNumber,
770 FALSE,
771 (UINTN) Time.Day,
772 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
773 );
774 Number = (UINT16) GetStringWidth (FormattedNumber);
775 if (Number == 4) {
776 FormattedNumber[2] = FormattedNumber[1];
777 FormattedNumber[1] = FormattedNumber[0];
778 FormattedNumber[0] = L'0';
779 Number = 6;
780 }
781
782 StrnCpy (OptionString[0] + 4, FormattedNumber, Number);
783 *(OptionString[0] + Number / 2 + 3) = DATE_SEPARATOR;
784 StrCat (OptionString[0] + (Number / 2) + 4, StringPtr);
785 break;
786
787 case 2:
788 if (Selected) {
789 Number = (UINT16) Time.Year;
790
791 if (Tag->Step == 0) {
792 Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, DATE_NUMERIC, &Number);
793 } else {
794 //
795 // Seed value with current setting
796 //
797 Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, DATE_NUMERIC, &Number);
798 }
799
800 if (!EFI_ERROR (Status)) {
801 Time.Year = (UINT16) Number;
802 gRT->SetTime (&Time);
803 }
804 }
805
806 Tag->Value = (UINT16) Time.Year;
807 VariableDefinition->FakeNvRamMap[Tag->Id] = (UINT8) Tag->Value;
808 VariableDefinition->FakeNvRamMap[Tag->Id + 1] = (UINT8) (Tag->Value >> 8);
809 SetUnicodeMem (OptionString[0], 7, L' ');
810 UnicodeValueToString (
811 FormattedNumber,
812 FALSE,
813 (UINTN) Time.Year,
814 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
815 );
816 Number = (UINT16) GetStringWidth (FormattedNumber);
817 StrnCpy (OptionString[0] + 7, FormattedNumber, Number);
818 *(OptionString[0] + Number / 2 + 6) = RIGHT_NUMERIC_DELIMITER;
819 StrCat (OptionString[0] + (Number / 2) + 7, StringPtr);
820 break;
821 }
822
823 break;
824
825 //
826 // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
827 // We need to add code to support the NVRam storage version of Date - this is the 1% case where someone
828 // might want to set an alarm and actually preserve the data in NVRam so a driver can pick up the instruction
829 // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
830 //
831 case EFI_IFR_TIME_OP:
832 //
833 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
834 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
835 // the NvMap so that we can properly display the information
836 //
837 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
838 AdjustNvMap (FileFormTags, MenuOption);
839 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
840 }
841
842 Status = gRT->GetTime (&Time, NULL);
843 if (EFI_ERROR (Status)) {
844 return EFI_SUCCESS;
845 }
846 //
847 // This is similar to numerics, except for the following:
848 // We will under normal circumstances get 3 consecutive calls
849 // to process this opcodes data.
850 //
851 *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
852 ASSERT (*OptionString);
853
854 //
855 // This for loop advances Index till it points immediately after a date entry. We can then
856 // subtract MenuOption->TagIndex from Index and find out relative to the start of the Date
857 // structure which field we were in. For instance, if TagIndex was 52, and we advanced Index
858 // to 53 and found it to no longer point to a date operand, we were pointing to the last of 3
859 // date operands.
860 //
861 for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand == EFI_IFR_TIME_OP; Index++)
862 ;
863 //
864 // Count 0 = We entered on the first Date operand
865 // Count 1 = We entered on the second Date operand
866 // Count 2 = We entered on the third Date operand
867 //
868 Count = 3 - (Index - MenuOption->TagIndex);
869 if (Count > 2) {
870 return EFI_SUCCESS;
871 }
872
873 switch (Count) {
874 case 0:
875 Number = Time.Hour;
876 break;
877
878 case 1:
879 Number = Time.Minute;
880 break;
881
882 case 2:
883 Number = Time.Second;
884 }
885 //
886 // Retrieve the current numeric value
887 //
888 if (Selected) {
889 //
890 // Go ask for input
891 //
892 if (Tag->Step == 0) {
893 //
894 // Manual Input
895 //
896 Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, TIME_NUMERIC, &Number);
897 if (!EFI_ERROR (Status)) {
898 *NvRamMap = Number;
899 Time.Nanosecond = 0;
900 gRT->SetTime (&Time);
901 } else {
902 return EFI_SUCCESS;
903 }
904 } else {
905 //
906 // Auto selection from list
907 //
908 Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, TIME_NUMERIC, &Number);
909 if (!EFI_ERROR (Status)) {
910 *NvRamMap = Number;
911 } else {
912 return EFI_SUCCESS;
913 }
914 }
915
916 switch (Count) {
917 case 0:
918 Time.Hour = (UINT8) Number;
919 break;
920
921 case 1:
922 Time.Minute = (UINT8) Number;
923 break;
924
925 case 2:
926 Time.Second = (UINT8) Number;
927 }
928
929 Time.Nanosecond = 0;
930 gRT->SetTime (&Time);
931 } else {
932 switch (Count) {
933 case 0:
934 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
935 UnicodeValueToString (
936 FormattedNumber,
937 FALSE,
938 (UINTN) Time.Hour,
939 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
940 );
941 Number = (UINT16) GetStringWidth (FormattedNumber);
942 if (Number == 4) {
943 FormattedNumber[2] = FormattedNumber[1];
944 FormattedNumber[1] = FormattedNumber[0];
945 FormattedNumber[0] = L'0';
946 Number = 6;
947 }
948
949 StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
950 *(OptionString[0] + Number / 2) = TIME_SEPARATOR;
951 StrCat (OptionString[0] + (Number / 2) + 1, StringPtr);
952 break;
953
954 case 1:
955 SetUnicodeMem (OptionString[0], 4, L' ');
956 UnicodeValueToString (
957 FormattedNumber,
958 FALSE,
959 (UINTN) Time.Minute,
960 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
961 );
962 Number = (UINT16) GetStringWidth (FormattedNumber);
963 if (Number == 4) {
964 FormattedNumber[2] = FormattedNumber[1];
965 FormattedNumber[1] = FormattedNumber[0];
966 FormattedNumber[0] = L'0';
967 Number = 6;
968 }
969
970 StrnCpy (OptionString[0] + 4, FormattedNumber, Number);
971 *(OptionString[0] + Number / 2 + 3) = TIME_SEPARATOR;
972 StrCat (OptionString[0] + (Number / 2) + 4, StringPtr);
973 break;
974
975 case 2:
976 SetUnicodeMem (OptionString[0], 7, L' ');
977 UnicodeValueToString (
978 FormattedNumber,
979 FALSE,
980 (UINTN) Time.Second,
981 (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
982 );
983 Number = (UINT16) GetStringWidth (FormattedNumber);
984 if (Number == 4) {
985 FormattedNumber[2] = FormattedNumber[1];
986 FormattedNumber[1] = FormattedNumber[0];
987 FormattedNumber[0] = L'0';
988 Number = 6;
989 }
990
991 StrnCpy (OptionString[0] + 7, FormattedNumber, Number);
992 *(OptionString[0] + Number / 2 + 6) = RIGHT_NUMERIC_DELIMITER;
993 StrCat (OptionString[0] + (Number / 2) + 7, StringPtr);
994 break;
995 }
996 //
997 // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
998 // We need to add code to support the NVRam storage version of Date - this is the 1% case where someone
999 // might want to set an alarm and actually preserve the data in NVRam so a driver can pick up the instruction
1000 // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
1001 //
1002 }
1003 break;
1004
1005 case EFI_IFR_STRING_OP:
1006 //
1007 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
1008 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
1009 // the NvMap so that we can properly display the information
1010 //
1011 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
1012 AdjustNvMap (FileFormTags, MenuOption);
1013 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
1014 }
1015
1016 *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
1017 ASSERT (*OptionString);
1018
1019 if (Selected) {
1020 StringPtr = AllocateZeroPool (Tag->Maximum);
1021 ASSERT (StringPtr);
1022
1023 Status = ReadString (MenuOption, StringPtr);
1024
1025 if (!EFI_ERROR (Status)) {
1026 CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
1027 CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], StringPtr, Tag->StorageWidth);
1028
1029 UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
1030 }
1031
1032 FreePool (StringPtr);
1033 return Status;
1034 } else {
1035 for (Index = 0; Index < gOptionBlockWidth; Index++) {
1036 if (VariableDefinition->NvRamMap[Tag->StorageStart + (Index * 2)] != 0x0000) {
1037 CopyMem (OptionString[0] + Index, &VariableDefinition->NvRamMap[Tag->StorageStart + (Index * 2)], 2);
1038 } else {
1039 if (Index == 0) {
1040 *(OptionString[0] + Index) = '_';
1041 *(OptionString[0] + 1 + Index) = 0;
1042 }
1043 break;
1044 }
1045 }
1046
1047 return Status;
1048 }
1049
1050 case EFI_IFR_PASSWORD_OP:
1051 //
1052 // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
1053 // created entry which has an expanded NvMap requirement. We won't save this information - but we need to adjust
1054 // the NvMap so that we can properly display the information
1055 //
1056 if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
1057 AdjustNvMap (FileFormTags, MenuOption);
1058 NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
1059 }
1060
1061 if (Selected) {
1062 StringPtr = AllocateZeroPool (Tag->Maximum);
1063 ASSERT (StringPtr);
1064
1065 //
1066 // If interactive, read the password and do the appropriate callbacks in that routine.
1067 // Since interactive passwords assume to handle the password data in a separate variable
1068 // storage, we don't need to do more than what is below for password callbacks
1069 //
1070 if (Tag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
1071 MenuOption->Tags[0].CallbackHandle = FileFormTags->FormTags.Tags[0].CallbackHandle;
1072 Status = ReadPassword (MenuOption, TRUE, Tag, PageData, FALSE, FileFormTags, StringPtr);
1073 ZeroMem (StringPtr, Tag->Maximum);
1074
1075 if (EFI_ERROR (Status)) {
1076 if (Status == EFI_NOT_READY) {
1077 FreePool (StringPtr);
1078 return EFI_SUCCESS;
1079 }
1080 }
1081
1082 Status = ReadPassword (MenuOption, TRUE, Tag, PageData, TRUE, FileFormTags, StringPtr);
1083 FreePool (StringPtr);
1084 return EFI_SUCCESS;
1085 }
1086
1087 for (Index = 0; Index < Tag->Maximum; Index++) {
1088 if (VariableDefinition->NvRamMap[Tag->StorageStart + Index] != 0x00) {
1089 //
1090 // There is something there! Prompt for password
1091 //
1092 Status = ReadPassword (MenuOption, TRUE, Tag, PageData, FALSE, FileFormTags, StringPtr);
1093 if (EFI_ERROR (Status)) {
1094 FreePool (StringPtr);
1095 return EFI_SUCCESS;
1096 }
1097
1098 if (Tag->Encoding == 1) {
1099 EncodePassword (StringPtr, (UINT8) Tag->Maximum);
1100 Status = CompareMem (StringPtr, &VariableDefinition->NvRamMap[Tag->StorageStart], Tag->Maximum);
1101 } else {
1102 Status = CompareMem (StringPtr, &VariableDefinition->NvRamMap[Tag->StorageStart], Tag->Maximum);
1103 }
1104
1105 if (Status != 0) {
1106 FreePool (StringPtr);
1107 return EFI_SUCCESS;
1108 } else {
1109 break;
1110 }
1111 }
1112 }
1113 //
1114 // Clean the string
1115 //
1116 ZeroMem (StringPtr, Tag->Maximum);
1117
1118 //
1119 // No password set! Go ahead and prompt the user for a password.
1120 //
1121 Status = ReadPassword (MenuOption, FALSE, Tag, PageData, FALSE, FileFormTags, StringPtr);
1122
1123 if (EFI_ERROR (Status)) {
1124 //
1125 // User couldn't figure out how to type two identical passwords
1126 //
1127 FreePool (StringPtr);
1128 return EFI_SUCCESS;
1129 }
1130 //
1131 // Very simple example of how one MIGHT do password encoding
1132 //
1133 if (Tag->Encoding == 1) {
1134 EncodePassword (StringPtr, (UINT8) Tag->Maximum);
1135 }
1136
1137 TmpNvRamMap = AllocatePool (VariableDefinition->VariableSize);
1138 ASSERT (TmpNvRamMap != NULL);
1139
1140 Count = VariableDefinition->VariableSize;
1141
1142 if ((FormCallback != NULL) && (FormCallback->NvRead != NULL)) {
1143 Status = FormCallback->NvRead (
1144 FormCallback,
1145 VariableDefinition->VariableName,
1146 &VariableDefinition->Guid,
1147 NULL,
1148 &Count,
1149 (VOID *) TmpNvRamMap
1150 );
1151 } else {
1152 Status = gRT->GetVariable (
1153 VariableDefinition->VariableName,
1154 &VariableDefinition->Guid,
1155 NULL,
1156 &Count,
1157 (VOID *) TmpNvRamMap
1158 );
1159 }
1160
1161 CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], StringPtr, Tag->StorageWidth);
1162 CopyMem (&TmpNvRamMap[Tag->StorageStart], StringPtr, Tag->StorageWidth);
1163
1164 if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
1165 Status = FormCallback->NvWrite (
1166 FormCallback,
1167 VariableDefinition->VariableName,
1168 &VariableDefinition->Guid,
1169 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1170 VariableDefinition->VariableSize,
1171 (VOID *) TmpNvRamMap,
1172 &gResetRequired
1173 );
1174 } else {
1175 Status = gRT->SetVariable (
1176 VariableDefinition->VariableName,
1177 &VariableDefinition->Guid,
1178 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
1179 VariableDefinition->VariableSize,
1180 (VOID *) TmpNvRamMap
1181 );
1182 }
1183
1184 FreePool (TmpNvRamMap);
1185 FreePool (StringPtr);
1186 break;
1187 }
1188
1189 default:
1190 break;
1191 }
1192
1193 return EFI_SUCCESS;
1194 }
1195
1196 /**
1197 Split StringPtr to several lines of strings stored in FormattedString and the glyph width of
1198 each line cannot exceed gHelpBlockWidth.
1199
1200 @param StringPtr The pointer of string
1201 @param FormattedString The pointer of format string
1202 @param RowCount The count of row
1203
1204 **/
1205 VOID
1206 ProcessHelpString (
1207 IN CHAR16 *StringPtr,
1208 OUT CHAR16 **FormattedString,
1209 IN UINTN RowCount
1210 )
1211 {
1212 CONST UINTN BlockWidth = (UINTN) gHelpBlockWidth - 1;
1213 UINTN AllocateSize;
1214 //
1215 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
1216 //
1217 UINTN CurrIndex;
1218 UINTN PrevCurrIndex;
1219 UINTN LineCount;
1220 UINTN VirtualLineCount;
1221 //
1222 // GlyphOffset stores glyph width of current screen-line
1223 //
1224 UINTN GlyphOffset;
1225 //
1226 // GlyphWidth equals to 2 if we meet width directive
1227 //
1228 UINTN GlyphWidth;
1229 //
1230 // during scanning, we remember the position of last space character
1231 // in case that if next word cannot put in current line, we could restore back to the position
1232 // of last space character
1233 // while we should also remmeber the glyph width of the last space character for restoring
1234 //
1235 UINTN LastSpaceIndex;
1236 UINTN LastSpaceGlyphWidth;
1237 //
1238 // every time we begin to form a new screen-line, we should remember glyph width of single character
1239 // of last line
1240 //
1241 UINTN LineStartGlyphWidth;
1242 UINTN *IndexArray;
1243 UINTN *OldIndexArray;
1244
1245 //
1246 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
1247 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
1248 // to bring the width directive of the last line to current screen-line.
1249 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
1250 // different from that of "\wideabcde", we should remember the width directive.
1251 //
1252 AllocateSize = 0x20;
1253 IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
1254
1255 if (*FormattedString != NULL) {
1256 FreePool (*FormattedString);
1257 *FormattedString = NULL;
1258 }
1259
1260 for (PrevCurrIndex = 0, CurrIndex = 0, LineCount = 0, LastSpaceIndex = 0,
1261 IndexArray[0] = 0, GlyphWidth = 1, GlyphOffset = 0, LastSpaceGlyphWidth = 1, LineStartGlyphWidth = 1;
1262 (StringPtr[CurrIndex] != CHAR_NULL);
1263 CurrIndex ++) {
1264
1265 if (LineCount == AllocateSize) {
1266 AllocateSize += 0x10;
1267 OldIndexArray = IndexArray;
1268 IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
1269 CopyMem (IndexArray, OldIndexArray, LineCount * sizeof (UINTN) * 3);
1270 if (OldIndexArray != NULL) {
1271 FreePool (OldIndexArray);
1272 }
1273 }
1274
1275 switch (StringPtr[CurrIndex]) {
1276
1277 case NARROW_CHAR:
1278 case WIDE_CHAR:
1279 GlyphWidth = ((StringPtr[CurrIndex] == WIDE_CHAR) ? 2 : 1);
1280 if (CurrIndex == 0) {
1281 LineStartGlyphWidth = GlyphWidth;
1282 }
1283 break;
1284
1285 //
1286 // char is '\n'
1287 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
1288 //
1289 case CHAR_LINEFEED:
1290 //
1291 // Store a range of string as a line
1292 //
1293 IndexArray[LineCount*3] = PrevCurrIndex;
1294 IndexArray[LineCount*3+1] = CurrIndex;
1295 IndexArray[LineCount*3+2] = LineStartGlyphWidth;
1296 LineCount ++;
1297 //
1298 // Reset offset and save begin position of line
1299 //
1300 GlyphOffset = 0;
1301 LineStartGlyphWidth = GlyphWidth;
1302 PrevCurrIndex = CurrIndex + 1;
1303 break;
1304
1305 //
1306 // char is '\r'
1307 // "\r\n" and "\r" both are handled here
1308 //
1309 case CHAR_CARRIAGE_RETURN:
1310 if (StringPtr[CurrIndex + 1] == CHAR_LINEFEED) {
1311 //
1312 // next char is '\n'
1313 //
1314 IndexArray[LineCount*3] = PrevCurrIndex;
1315 IndexArray[LineCount*3+1] = CurrIndex;
1316 IndexArray[LineCount*3+2] = LineStartGlyphWidth;
1317 LineCount ++;
1318 CurrIndex ++;
1319 }
1320 GlyphOffset = 0;
1321 LineStartGlyphWidth = GlyphWidth;
1322 PrevCurrIndex = CurrIndex + 1;
1323 break;
1324
1325 //
1326 // char is space or other char
1327 //
1328 default:
1329 GlyphOffset += GlyphWidth;
1330 if (GlyphOffset >= BlockWidth) {
1331 if (LastSpaceIndex > PrevCurrIndex) {
1332 //
1333 // LastSpaceIndex points to space inside current screen-line,
1334 // restore to LastSpaceIndex
1335 // (Otherwise the word is too long to fit one screen-line, just cut it)
1336 //
1337 CurrIndex = LastSpaceIndex;
1338 GlyphWidth = LastSpaceGlyphWidth;
1339 } else if (GlyphOffset > BlockWidth) {
1340 //
1341 // the word is too long to fit one screen-line and we don't get the chance
1342 // of GlyphOffset == BlockWidth because GlyphWidth = 2
1343 //
1344 CurrIndex --;
1345 }
1346
1347 IndexArray[LineCount*3] = PrevCurrIndex;
1348 IndexArray[LineCount*3+1] = CurrIndex + 1;
1349 IndexArray[LineCount*3+2] = LineStartGlyphWidth;
1350 LineStartGlyphWidth = GlyphWidth;
1351 LineCount ++;
1352 //
1353 // Reset offset and save begin position of line
1354 //
1355 GlyphOffset = 0;
1356 PrevCurrIndex = CurrIndex + 1;
1357 }
1358
1359 //
1360 // LastSpaceIndex: remember position of last space
1361 //
1362 if (StringPtr[CurrIndex] == CHAR_SPACE) {
1363 LastSpaceIndex = CurrIndex;
1364 LastSpaceGlyphWidth = GlyphWidth;
1365 }
1366 break;
1367 }
1368 }
1369
1370 if (GlyphOffset > 0) {
1371 IndexArray[LineCount*3] = PrevCurrIndex;
1372 IndexArray[LineCount*3+1] = CurrIndex;
1373 IndexArray[LineCount*3+2] = GlyphWidth;
1374 LineCount ++;
1375 }
1376
1377 if (LineCount == 0) {
1378 //
1379 // in case we meet null string
1380 //
1381 IndexArray[0] = 0;
1382 IndexArray[1] = 1;
1383 //
1384 // we assume null string's glyph width is 1
1385 //
1386 IndexArray[1] = 1;
1387 LineCount ++;
1388 }
1389
1390 VirtualLineCount = RowCount * (LineCount / RowCount + (LineCount % RowCount > 0));
1391 *FormattedString = AllocateZeroPool (VirtualLineCount * (BlockWidth + 1) * sizeof (CHAR16) * 2);
1392
1393 for (CurrIndex = 0; CurrIndex < LineCount; CurrIndex ++) {
1394 *(*FormattedString + CurrIndex * 2 * (BlockWidth + 1)) = (CHAR16)((IndexArray[CurrIndex*3+2] == 2) ? WIDE_CHAR : NARROW_CHAR);
1395 StrnCpy (
1396 *FormattedString + CurrIndex * 2 * (BlockWidth + 1) + 1,
1397 StringPtr + IndexArray[CurrIndex*3],
1398 IndexArray[CurrIndex*3+1]-IndexArray[CurrIndex*3]
1399 );
1400 }
1401
1402 if (IndexArray != NULL) {
1403 FreePool (IndexArray);
1404 }
1405 }
1406
1407 VOID
1408 IfrToFormTag (
1409 IN UINT8 OpCode,
1410 IN EFI_TAG *TargetTag,
1411 IN VOID *FormData,
1412 EFI_VARIABLE_DEFINITION *VariableDefinitionsHead
1413 )
1414 {
1415 UINT16 TempValue;
1416 CHAR16 *VariableName;
1417 CHAR8 *AsciiString;
1418 EFI_VARIABLE_DEFINITION *VariableDefinitions;
1419 EFI_VARIABLE_DEFINITION *PreviousVariableDefinitions;
1420 STATIC UINT16 VariableSize;
1421 EFI_GUID Guid;
1422 STATIC UINT16 CurrentVariable;
1423 STATIC UINT16 CurrentVariable2;
1424 UINTN Index;
1425
1426 switch (OpCode) {
1427 case EFI_IFR_FORM_OP:
1428 CopyMem (&TargetTag->Id, &((EFI_IFR_FORM *) FormData)->FormId, sizeof (UINT16));
1429 CopyMem (&TargetTag->Text, &((EFI_IFR_FORM *) FormData)->FormTitle, sizeof (UINT16));
1430 TargetTag->VariableNumber = CurrentVariable;
1431 if (VariableDefinitionsHead != NULL) {
1432 VariableName = AllocateZeroPool (12);
1433 ASSERT (VariableName != NULL);
1434 CopyMem (VariableName, L"Setup", 12);
1435 VariableDefinitionsHead->VariableName = VariableName;
1436 VariableDefinitionsHead->VariableSize = VariableSize;
1437 CopyMem (&VariableDefinitionsHead->Guid, &Guid, sizeof (EFI_GUID));
1438 }
1439 break;
1440
1441 case EFI_IFR_SUBTITLE_OP:
1442 TargetTag->NumberOfLines = 1;
1443 CopyMem (&TargetTag->Text, &((EFI_IFR_SUBTITLE *) FormData)->SubTitle, sizeof (UINT16));
1444 TargetTag->VariableNumber = CurrentVariable;
1445 break;
1446
1447 case EFI_IFR_TEXT_OP:
1448 TargetTag->NumberOfLines = 1;
1449 CopyMem (&TargetTag->Text, &((EFI_IFR_TEXT *) FormData)->Text, sizeof (UINT16));
1450 CopyMem (&TargetTag->Help, &((EFI_IFR_TEXT *) FormData)->Help, sizeof (UINT16));
1451 TargetTag->VariableNumber = CurrentVariable;
1452
1453 //
1454 // To optimize the encoding size, certain opcodes have optional fields such as those
1455 // inside the if() statement. If the encoded length is the complete size, then we
1456 // know we have valid data encoded that we want to integrate
1457 //
1458 if (((EFI_IFR_TEXT *) FormData)->Header.Length == sizeof (EFI_IFR_TEXT)) {
1459 //
1460 // Text has no help associated with it, but in case there is a second entry due to
1461 // dynamic/interactive flags being active, bring this data over.
1462 //
1463 CopyMem (&TargetTag->TextTwo, &((EFI_IFR_TEXT *) FormData)->TextTwo, sizeof (UINT16));
1464 TargetTag->Flags = ((EFI_IFR_TEXT *) FormData)->Flags;
1465 CopyMem (&TargetTag->Key, &((EFI_IFR_TEXT *) FormData)->Key, sizeof (UINT16));
1466 }
1467 break;
1468
1469 case EFI_IFR_ONE_OF_OPTION_OP:
1470 CopyMem (&TargetTag->Text, &((EFI_IFR_ONE_OF_OPTION *) FormData)->Option, sizeof (UINT16));
1471 CopyMem (&TargetTag->Value, &((EFI_IFR_ONE_OF_OPTION *) FormData)->Value, sizeof (UINT16));
1472 TargetTag->Flags = ((EFI_IFR_ONE_OF_OPTION *) FormData)->Flags;
1473 CopyMem (&TargetTag->Key, &((EFI_IFR_ONE_OF_OPTION *) FormData)->Key, sizeof (UINT16));
1474 TargetTag->VariableNumber = CurrentVariable;
1475 break;
1476
1477 case EFI_IFR_CHECKBOX_OP:
1478 TargetTag->Flags = ((EFI_IFR_CHECKBOX *) FormData)->Flags;
1479 TargetTag->ResetRequired = (BOOLEAN) (TargetTag->Flags & EFI_IFR_FLAG_RESET_REQUIRED);
1480 CopyMem (&TargetTag->Key, &((EFI_IFR_CHECKBOX *) FormData)->Key, sizeof (UINT16));
1481 TargetTag->VariableNumber = CurrentVariable;
1482 break;
1483
1484 case EFI_IFR_NUMERIC_OP:
1485 TargetTag->Flags = ((EFI_IFR_NUMERIC *) FormData)->Flags;
1486 CopyMem (&TargetTag->Key, &((EFI_IFR_NUMERIC *) FormData)->Key, sizeof (UINT16));
1487 TargetTag->VariableNumber = CurrentVariable;
1488 break;
1489
1490 case EFI_IFR_STRING_OP:
1491 //
1492 // Convert EFI_IFR_STRING.MinSize and EFI_IFR_STRING.MaxSize to actual minimum and maximum bytes
1493 // and store to EFI_TAG.Minimum and EFI_TAG.Maximum
1494 //
1495 TempValue = 0;
1496 CopyMem (&TempValue, &((EFI_IFR_STRING *) FormData)->MinSize, sizeof (UINT8));
1497 TempValue = (UINT16) (TempValue * 2);
1498 CopyMem (&TargetTag->Minimum, &TempValue, sizeof (UINT16));
1499
1500 TempValue = 0;
1501 CopyMem (&TempValue, &((EFI_IFR_STRING *) FormData)->MaxSize, sizeof (UINT8));
1502 TempValue = (UINT16) (TempValue * 2);
1503 CopyMem (&TargetTag->Maximum, &TempValue, sizeof (UINT16));
1504 CopyMem (&TargetTag->StorageWidth, &TempValue, sizeof (UINT16));
1505 TargetTag->Flags = (UINT8) (((EFI_IFR_STRING *) FormData)->Flags);
1506 TargetTag->ResetRequired = (BOOLEAN) (TargetTag->Flags & EFI_IFR_FLAG_RESET_REQUIRED);
1507 CopyMem (&TargetTag->Key, &((EFI_IFR_STRING *) FormData)->Key, sizeof (UINT16));
1508 TargetTag->VariableNumber = CurrentVariable;
1509 break;
1510
1511 case EFI_IFR_PASSWORD_OP:
1512 TempValue = 0;
1513 CopyMem (&TempValue, &((EFI_IFR_PASSWORD *) FormData)->MinSize, sizeof (UINT8));
1514 TempValue = (UINT16) (TempValue * 2);
1515 CopyMem (&TargetTag->Minimum, &TempValue, sizeof (UINT16));
1516
1517 TempValue = 0;
1518 CopyMem (&TempValue, &((EFI_IFR_PASSWORD *) FormData)->MaxSize, sizeof (UINT8));
1519 TempValue = (UINT16) (TempValue * 2);
1520 CopyMem (&TargetTag->Maximum, &TempValue, sizeof (UINT16));
1521 CopyMem (&TargetTag->StorageWidth, &TempValue, sizeof (UINT16));
1522 TargetTag->Flags = ((EFI_IFR_PASSWORD *) FormData)->Flags;
1523 TargetTag->ResetRequired = (BOOLEAN) (TargetTag->Flags & EFI_IFR_FLAG_RESET_REQUIRED);
1524 CopyMem (&TargetTag->Key, &((EFI_IFR_PASSWORD *) FormData)->Key, sizeof (UINT16));
1525 CopyMem (&TargetTag->Encoding, &((EFI_IFR_PASSWORD *) FormData)->Encoding, sizeof (UINT16));
1526 TargetTag->VariableNumber = CurrentVariable;
1527 break;
1528
1529 case EFI_IFR_VARSTORE_OP:
1530 //
1531 // It should NEVER be NULL
1532 //
1533 if (VariableDefinitionsHead == NULL) {
1534 break;
1535 }
1536
1537 VariableDefinitions = VariableDefinitionsHead;
1538
1539 //
1540 // Advance VariableDefinitions to the last entry
1541 //
1542 for (; VariableDefinitions != NULL; VariableDefinitions = VariableDefinitions->Next) {
1543 PreviousVariableDefinitions = VariableDefinitions;
1544 //
1545 // If there is a variable with this GUID and ID already, we need to bail out
1546 //
1547 if (!CompareMem (&VariableDefinitions->Guid, &((EFI_IFR_VARSTORE *) FormData)->Guid, sizeof (EFI_GUID)) &&
1548 !CompareMem (&VariableDefinitions->VariableId, &((EFI_IFR_VARSTORE *) FormData)->VarId, sizeof (UINT16))
1549 ) {
1550 return ;
1551 }
1552
1553 if (VariableDefinitions->Next == NULL) {
1554 break;
1555 }
1556 }
1557 //
1558 // If the last entry has a variable in it already, allocate a new entry and use it
1559 //
1560 if (VariableDefinitions->VariableName != NULL) {
1561 VariableDefinitions->Next = AllocateZeroPool (sizeof (EFI_VARIABLE_DEFINITION));
1562 ASSERT (VariableDefinitions->Next != NULL);
1563 PreviousVariableDefinitions = VariableDefinitions;
1564 VariableDefinitions = VariableDefinitions->Next;
1565 VariableDefinitions->Previous = PreviousVariableDefinitions;
1566 }
1567 //
1568 // Copy the Variable data to our linked list
1569 //
1570 CopyMem (&VariableDefinitions->VariableId, &((EFI_IFR_VARSTORE *) FormData)->VarId, sizeof (UINT16));
1571 CopyMem (&VariableDefinitions->VariableSize, &((EFI_IFR_VARSTORE *) FormData)->Size, sizeof (UINT16));
1572 CopyMem (&VariableDefinitions->Guid, &((EFI_IFR_VARSTORE *) FormData)->Guid, sizeof (EFI_GUID));
1573
1574 //
1575 // The ASCII String which is immediately past the EFI_IFR_VARSTORE is inferred by the structure definition
1576 // due to it being variable sized. There are rules preventing it from being > 40 characters long and should
1577 // be enforced by the compiler.
1578 //
1579 AsciiString = (CHAR8 *) (&((EFI_IFR_VARSTORE *) FormData)->Size);
1580 AsciiString = AsciiString + 2;
1581 VariableDefinitions->VariableName = AllocateZeroPool ((AsciiStrLen (AsciiString) + 1) * 2);
1582 ASSERT (VariableDefinitions->VariableName != NULL);
1583 for (Index = 0; AsciiString[Index] != 0; Index++) {
1584 VariableDefinitions->VariableName[Index] = (CHAR16) AsciiString[Index];
1585 }
1586
1587 VariableDefinitions->VariableName[Index] = 0;
1588
1589 //
1590 // Propogate the tag information for this op-code
1591 //
1592 CopyMem (&TargetTag->VariableNumber, &((EFI_IFR_VARSTORE *) FormData)->VarId, sizeof (UINT16));
1593 CopyMem (&TargetTag->GuidValue, &((EFI_IFR_VARSTORE *) FormData)->Guid, sizeof (EFI_GUID));
1594 CopyMem (&TargetTag->StorageWidth, &((EFI_IFR_VARSTORE *) FormData)->Size, sizeof (UINT16));
1595 CopyMem (&TargetTag->Maximum, &((EFI_IFR_VARSTORE *) FormData)->Size, sizeof (UINT16));
1596 break;
1597
1598 case EFI_IFR_VARSTORE_SELECT_OP:
1599 CopyMem (&TargetTag->VariableNumber, &((EFI_IFR_VARSTORE_SELECT *) FormData)->VarId, sizeof (UINT16));
1600 CopyMem (&CurrentVariable, &((EFI_IFR_VARSTORE_SELECT *) FormData)->VarId, sizeof (UINT16));
1601 CurrentVariable2 = CurrentVariable;
1602 break;
1603
1604 case EFI_IFR_VARSTORE_SELECT_PAIR_OP:
1605 CopyMem (&TargetTag->VariableNumber, &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->VarId, sizeof (UINT16));
1606 CopyMem (
1607 &TargetTag->VariableNumber2,
1608 &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->SecondaryVarId,
1609 sizeof (UINT16)
1610 );
1611 CopyMem (&CurrentVariable, &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->VarId, sizeof (UINT16));
1612 CopyMem (&CurrentVariable2, &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->SecondaryVarId, sizeof (UINT16));
1613 break;
1614
1615 case EFI_IFR_REF_OP:
1616 TargetTag->NumberOfLines = 1;
1617 CopyMem (&TargetTag->Id, &((EFI_IFR_REF *) FormData)->FormId, sizeof (UINT16));
1618 CopyMem (&TargetTag->Key, &((EFI_IFR_REF *) FormData)->Key, sizeof (UINT16));
1619 CopyMem (&TargetTag->Text, &((EFI_IFR_REF *) FormData)->Prompt, sizeof (UINT16));
1620 CopyMem (&TargetTag->Help, &((EFI_IFR_REF *) FormData)->Help, sizeof (UINT16));
1621 TargetTag->Flags = ((EFI_IFR_REF *) FormData)->Flags;
1622 TargetTag->VariableNumber = CurrentVariable;
1623 break;
1624
1625 case EFI_IFR_EQ_ID_VAL_OP:
1626 CopyMem (&TargetTag->Value, &((EFI_IFR_EQ_ID_VAL *) FormData)->Value, sizeof (UINT16));
1627 CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_ID_VAL *) FormData)->QuestionId, sizeof (UINT16));
1628 TargetTag->StorageWidth = ((EFI_IFR_EQ_ID_VAL *) FormData)->Width;
1629 TargetTag->VariableNumber = CurrentVariable;
1630 break;
1631
1632 case EFI_IFR_EQ_VAR_VAL_OP:
1633 CopyMem (&TargetTag->Value, &((EFI_IFR_EQ_VAR_VAL *) FormData)->Value, sizeof (UINT16));
1634 CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_VAR_VAL *) FormData)->VariableId, sizeof (UINT16));
1635 TargetTag->VariableNumber = CurrentVariable;
1636 break;
1637
1638 case EFI_IFR_EQ_ID_ID_OP:
1639 CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_ID_ID *) FormData)->QuestionId1, sizeof (UINT16));
1640 CopyMem (&TargetTag->Id2, &((EFI_IFR_EQ_ID_ID *) FormData)->QuestionId2, sizeof (UINT16));
1641 TargetTag->StorageWidth = ((EFI_IFR_EQ_ID_ID *) FormData)->Width;
1642 TargetTag->VariableNumber = CurrentVariable;
1643 TargetTag->VariableNumber = CurrentVariable2;
1644 break;
1645
1646 case EFI_IFR_EQ_ID_LIST_OP:
1647 CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_ID_LIST *) FormData)->QuestionId, sizeof (UINT16));
1648 CopyMem (&TargetTag->Id2, &((EFI_IFR_EQ_ID_LIST *) FormData)->ListLength, sizeof (UINT16));
1649 TargetTag->StorageWidth = ((EFI_IFR_EQ_ID_LIST *) FormData)->Width;
1650
1651 TargetTag->IntList = AllocateZeroPool (TargetTag->Id2 * sizeof (UINT16));
1652 ASSERT (TargetTag->IntList);
1653
1654 for (TempValue = 0; TempValue < TargetTag->Id2; TempValue++) {
1655 CopyMem (
1656 &TargetTag->IntList[TempValue],
1657 &((EFI_IFR_EQ_ID_LIST *) FormData)->ValueList[TempValue],
1658 sizeof (UINT16)
1659 );
1660 }
1661
1662 TargetTag->VariableNumber = CurrentVariable;
1663 break;
1664
1665 case EFI_IFR_FORM_SET_OP:
1666 CopyMem (&VariableSize, &((EFI_IFR_FORM_SET *) FormData)->NvDataSize, sizeof (UINT16));
1667 CopyMem (&Guid, &((EFI_IFR_FORM_SET *) FormData)->Guid, sizeof (EFI_GUID));
1668 //
1669 // If there is a size specified in the formste, we will establish a "default" variable
1670 //
1671 if (VariableDefinitionsHead != NULL) {
1672 VariableName = AllocateZeroPool (12);
1673 ASSERT (VariableName != NULL);
1674 CopyMem (VariableName, L"Setup", 12);
1675 VariableDefinitionsHead->VariableName = VariableName;
1676 VariableDefinitionsHead->VariableSize = VariableSize;
1677 CopyMem (&VariableDefinitionsHead->Guid, &Guid, sizeof (EFI_GUID));
1678 }
1679 break;
1680
1681 case EFI_IFR_END_FORM_SET_OP:
1682 CurrentVariable = 0;
1683 CurrentVariable2 = 0;
1684 break;
1685 }
1686
1687 return ;
1688 }