]> git.proxmox.com Git - mirror_edk2.git/blob - ShellPkg/Library/UefiShellDebug1CommandsLib/HexEdit/BufferImage.c
ShellPkg: Clean up source files
[mirror_edk2.git] / ShellPkg / Library / UefiShellDebug1CommandsLib / HexEdit / BufferImage.c
1 /** @file
2 Defines HBufferImage - the view of the file that is visible at any point,
3 as well as the event handlers for editing the file
4
5 Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved. <BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "HexEditor.h"
17
18 extern EFI_HANDLE HImageHandleBackup;
19
20 extern HEFI_EDITOR_FILE_IMAGE HFileImage;
21 extern HEFI_EDITOR_DISK_IMAGE HDiskImage;
22 extern HEFI_EDITOR_MEM_IMAGE HMemImage;
23
24 extern HEFI_EDITOR_FILE_IMAGE HFileImageBackupVar;
25 extern HEFI_EDITOR_DISK_IMAGE HDiskImageBackupVar;
26 extern HEFI_EDITOR_MEM_IMAGE HMemImageBackupVar;
27
28 extern BOOLEAN HEditorMouseAction;
29
30 extern HEFI_EDITOR_GLOBAL_EDITOR HMainEditor;
31 extern HEFI_EDITOR_GLOBAL_EDITOR HMainEditorBackupVar;
32
33 HEFI_EDITOR_BUFFER_IMAGE HBufferImage;
34 HEFI_EDITOR_BUFFER_IMAGE HBufferImageBackupVar;
35
36 //
37 // for basic initialization of HBufferImage
38 //
39 HEFI_EDITOR_BUFFER_IMAGE HBufferImageConst = {
40 NULL,
41 NULL,
42 0,
43 NULL,
44 {
45 0,
46 0
47 },
48 {
49 0,
50 0
51 },
52 {
53 0,
54 0
55 },
56 0,
57 TRUE,
58 FALSE,
59 FileTypeNone,
60 NULL,
61 NULL,
62 NULL
63 };
64
65 //
66 // the whole edit area needs to be refreshed
67 //
68 BOOLEAN HBufferImageNeedRefresh;
69
70 //
71 // only the current line in edit area needs to be refresh
72 //
73 BOOLEAN HBufferImageOnlyLineNeedRefresh;
74
75 BOOLEAN HBufferImageMouseNeedRefresh;
76
77 /**
78 Initialization function for HBufferImage
79
80 @retval EFI_SUCCESS The operation was successful.
81 @retval EFI_LOAD_ERROR A load error occured.
82 **/
83 EFI_STATUS
84 HBufferImageInit (
85 VOID
86 )
87 {
88 EFI_STATUS Status;
89
90 //
91 // basically initialize the HBufferImage
92 //
93 CopyMem (&HBufferImage, &HBufferImageConst, sizeof (HBufferImage));
94
95 //
96 // INIT listhead
97 //
98 HBufferImage.ListHead = AllocateZeroPool (sizeof (LIST_ENTRY));
99 if (HBufferImage.ListHead == NULL) {
100 return EFI_LOAD_ERROR;
101 }
102
103 InitializeListHead (HBufferImage.ListHead);
104
105 HBufferImage.DisplayPosition.Row = 2;
106 HBufferImage.DisplayPosition.Column = 10;
107 HBufferImage.MousePosition.Row = 2;
108 HBufferImage.MousePosition.Column = 10;
109
110 HBufferImage.FileImage = &HFileImage;
111 HBufferImage.DiskImage = &HDiskImage;
112 HBufferImage.MemImage = &HMemImage;
113
114 HBufferImageNeedRefresh = FALSE;
115 HBufferImageOnlyLineNeedRefresh = FALSE;
116 HBufferImageMouseNeedRefresh = FALSE;
117
118 HBufferImageBackupVar.FileImage = &HFileImageBackupVar;
119 HBufferImageBackupVar.DiskImage = &HDiskImageBackupVar;
120 HBufferImageBackupVar.MemImage = &HMemImageBackupVar;
121
122 Status = HFileImageInit ();
123 if (EFI_ERROR (Status)) {
124 return EFI_LOAD_ERROR;
125 }
126
127 Status = HDiskImageInit ();
128 if (EFI_ERROR (Status)) {
129 return EFI_LOAD_ERROR;
130 }
131
132 Status = HMemImageInit ();
133 if (EFI_ERROR (Status)) {
134 return EFI_LOAD_ERROR;
135 }
136
137 return EFI_SUCCESS;
138 }
139
140 /**
141 Backup function for HBufferImage. Only a few fields need to be backup.
142 This is for making the file buffer refresh as few as possible.
143
144 @retval EFI_SUCCESS The operation was successful.
145 **/
146 EFI_STATUS
147 HBufferImageBackup (
148 VOID
149 )
150 {
151 HBufferImageBackupVar.MousePosition = HBufferImage.MousePosition;
152
153 HBufferImageBackupVar.BufferPosition = HBufferImage.BufferPosition;
154
155 HBufferImageBackupVar.Modified = HBufferImage.Modified;
156
157 HBufferImageBackupVar.BufferType = HBufferImage.BufferType;
158 HBufferImageBackupVar.LowVisibleRow = HBufferImage.LowVisibleRow;
159 HBufferImageBackupVar.HighBits = HBufferImage.HighBits;
160
161 //
162 // three kinds of buffer supported
163 // file buffer
164 // disk buffer
165 // memory buffer
166 //
167 switch (HBufferImage.BufferType) {
168 case FileTypeFileBuffer:
169 HFileImageBackup ();
170 break;
171
172 case FileTypeDiskBuffer:
173 HDiskImageBackup ();
174 break;
175
176 case FileTypeMemBuffer:
177 HMemImageBackup ();
178 break;
179
180 default:
181 break;
182 }
183
184 return EFI_SUCCESS;
185 }
186
187 /**
188 Free all the lines in HBufferImage.
189 Fields affected:
190 Lines
191 CurrentLine
192 NumLines
193 ListHead
194
195 @retval EFI_SUCCESS The operation was successful.
196 **/
197 EFI_STATUS
198 HBufferImageFreeLines (
199 VOID
200 )
201 {
202 HFreeLines (HBufferImage.ListHead, HBufferImage.Lines);
203
204 HBufferImage.Lines = NULL;
205 HBufferImage.CurrentLine = NULL;
206 HBufferImage.NumLines = 0;
207
208 return EFI_SUCCESS;
209 }
210
211 /**
212 Cleanup function for HBufferImage
213
214 @retval EFI_SUCCESS The operation was successful.
215 **/
216 EFI_STATUS
217 HBufferImageCleanup (
218 VOID
219 )
220 {
221 EFI_STATUS Status;
222
223 //
224 // free all the lines
225 //
226 Status = HBufferImageFreeLines ();
227
228 SHELL_FREE_NON_NULL (HBufferImage.ListHead);
229 HBufferImage.ListHead = NULL;
230
231 HFileImageCleanup ();
232 HDiskImageCleanup ();
233
234 return Status;
235
236 }
237
238 /**
239 Print Line on Row
240
241 @param[in] Line The lline to print.
242 @param[in] Row The row on screen ( begin from 1 ).
243 @param[in] FRow The FRow.
244 @param[in] Orig The original color.
245 @param[in] New The color to print with.
246
247 @retval EFI_SUCCESS The operation was successful.
248 **/
249 EFI_STATUS
250 HBufferImagePrintLine (
251 IN HEFI_EDITOR_LINE *Line,
252 IN UINTN Row,
253 IN UINTN FRow,
254 IN HEFI_EDITOR_COLOR_UNION Orig,
255 IN HEFI_EDITOR_COLOR_UNION New
256
257 )
258 {
259
260 UINTN Index;
261 UINTN Pos;
262 BOOLEAN Selected;
263 BOOLEAN BeNewColor;
264 UINTN RowStart;
265 UINTN RowEnd;
266 UINTN ColStart;
267 UINTN ColEnd;
268
269 //
270 // variable initialization
271 //
272 ColStart = 0;
273 ColEnd = 0;
274 Selected = FALSE;
275
276 //
277 // print the selected area in opposite color
278 //
279 if (HMainEditor.SelectStart != 0 && HMainEditor.SelectEnd != 0) {
280 RowStart = (HMainEditor.SelectStart - 1) / 0x10 + 1;
281 RowEnd = (HMainEditor.SelectEnd - 1) / 0x10 + 1;
282
283 ColStart = (HMainEditor.SelectStart - 1) % 0x10 + 1;
284 ColEnd = (HMainEditor.SelectEnd - 1) % 0x10 + 1;
285
286 if (FRow >= RowStart && FRow <= RowEnd) {
287 Selected = TRUE;
288 }
289
290 if (FRow > RowStart) {
291 ColStart = 1;
292 }
293
294 if (FRow < RowEnd) {
295 ColEnd = 0x10;
296 }
297
298 }
299
300 if (!HEditorMouseAction) {
301 ShellPrintEx (
302 0,
303 (INT32)Row - 1,
304 L"%8X ",
305 ((INT32)Row - 2 + HBufferImage.LowVisibleRow - 1) * 0x10
306 );
307
308 }
309
310 for (Index = 0; Index < 0x08 && Index < Line->Size; Index++) {
311
312 BeNewColor = FALSE;
313
314 if (Selected) {
315 if (Index + 1 >= ColStart && Index + 1 <= ColEnd) {
316 BeNewColor = TRUE;
317 }
318 }
319
320 if (BeNewColor) {
321 gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F);
322 } else {
323 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data & 0x7F);
324 }
325
326 Pos = 10 + (Index * 3);
327 if (Line->Buffer[Index] < 0x10) {
328 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"0");
329 Pos++;
330 }
331
332 if (Index < 0x07) {
333 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"%x ", Line->Buffer[Index]);
334 } else {
335 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"%x ", Line->Buffer[Index]);
336 }
337
338 }
339
340 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data & 0x7F);
341 while (Index < 0x08) {
342 Pos = 10 + (Index * 3);
343 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L" ");
344 Index++;
345 }
346
347 while (Index < 0x10 && Index < Line->Size) {
348
349 BeNewColor = FALSE;
350
351 if (Selected) {
352 if (Index + 1 >= ColStart && Index + 1 <= ColEnd) {
353 BeNewColor = TRUE;
354 }
355 }
356
357 if (BeNewColor) {
358 gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F);
359 } else {
360 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data & 0x7F);
361 }
362
363 Pos = 10 + (Index * 3) + 1;
364 if (Line->Buffer[Index] < 0x10) {
365 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"0");
366 Pos++;
367 }
368
369 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"%x ", Line->Buffer[Index]);
370 Index++;
371 }
372
373 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data & 0x7F);
374 while (Index < 0x10) {
375 Pos = 10 + (Index * 3) + 1;
376 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L" ");
377 Index++;
378 }
379 //
380 // restore the original color
381 //
382 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data & 0x7F);
383
384 //
385 // PRINT the buffer content
386 //
387 if (!HEditorMouseAction) {
388 for (Index = 0; Index < 0x10 && Index < Line->Size; Index++) {
389 Pos = ASCII_POSITION + Index;
390
391 //
392 // learned from shelle.h -- IsValidChar
393 //
394 if (Line->Buffer[Index] >= L' ') {
395 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"%c", (CHAR16) Line->Buffer[Index]);
396 } else {
397 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L"%c", '.');
398 }
399 }
400
401 while (Index < 0x10) {
402 Pos = ASCII_POSITION + Index;
403 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L" ");
404 Index++;
405 }
406 }
407 //
408 // restore the abundant blank in hex edit area to original color
409 //
410 if (Selected) {
411 if (ColEnd <= 7) {
412 Pos = 10 + (ColEnd - 1) * 3 + 2;
413 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L" ");
414 } else if (ColEnd == 8) {
415 Pos = 10 + (ColEnd - 1) * 3 + 2;
416 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L" ");
417 } else {
418 Pos = 10 + (ColEnd - 1) * 3 + 3;
419 ShellPrintEx ((INT32)Pos - 1, (INT32)Row - 1, L" ");
420 }
421 }
422
423 return EFI_SUCCESS;
424 }
425
426 /**
427 Function to decide if a column number is stored in the high bits.
428
429 @param[in] Column The column to examine.
430 @param[out] FCol The actual column number.
431
432 @retval TRUE The actual column was in high bits and is now in FCol.
433 @retval FALSE There was not a column number in the high bits.
434 **/
435 BOOLEAN
436 HBufferImageIsAtHighBits (
437 IN UINTN Column,
438 OUT UINTN *FCol
439 )
440 {
441 Column -= 10;
442
443 //
444 // NOW AFTER THE SUB, Column start from 0
445 // 23 AND 24 ARE BOTH BLANK
446 //
447 if (Column == 24) {
448 *FCol = 0;
449 return FALSE;
450 }
451
452 if (Column > 24) {
453 Column--;
454 }
455
456 *FCol = (Column / 3) + 1;
457
458 if (Column % 3 == 0) {
459 return TRUE;
460 }
461
462 if ((Column % 3 == 2)) {
463 *FCol = 0;
464 }
465
466 return FALSE;
467 }
468
469 /**
470 Decide if a point is in the already selected area.
471
472 @param[in] MouseRow The row of the point to test.
473 @param[in] MouseCol The col of the point to test.
474
475 @retval TRUE The point is in the selected area.
476 @retval FALSE The point is not in the selected area.
477 **/
478 BOOLEAN
479 HBufferImageIsInSelectedArea (
480 IN UINTN MouseRow,
481 IN UINTN MouseCol
482 )
483 {
484 UINTN FRow;
485 UINTN RowStart;
486 UINTN RowEnd;
487 UINTN ColStart;
488 UINTN ColEnd;
489 UINTN MouseColStart;
490 UINTN MouseColEnd;
491
492 //
493 // judge mouse position whether is in selected area
494 //
495 //
496 // not select
497 //
498 if (HMainEditor.SelectStart == 0 || HMainEditor.SelectEnd == 0) {
499 return FALSE;
500 }
501 //
502 // calculate the select area
503 //
504 RowStart = (HMainEditor.SelectStart - 1) / 0x10 + 1;
505 RowEnd = (HMainEditor.SelectEnd - 1) / 0x10 + 1;
506
507 ColStart = (HMainEditor.SelectStart - 1) % 0x10 + 1;
508 ColEnd = (HMainEditor.SelectEnd - 1) % 0x10 + 1;
509
510 FRow = HBufferImage.LowVisibleRow + MouseRow - 2;
511 if (FRow < RowStart || FRow > RowEnd) {
512 return FALSE;
513 }
514
515 if (FRow > RowStart) {
516 ColStart = 1;
517 }
518
519 if (FRow < RowEnd) {
520 ColEnd = 0x10;
521 }
522
523 MouseColStart = 10 + (ColStart - 1) * 3;
524 if (ColStart > 8) {
525 MouseColStart++;
526 }
527
528 MouseColEnd = 10 + (ColEnd - 1) * 3 + 1;
529 if (ColEnd > 8) {
530 MouseColEnd++;
531 }
532
533 if (MouseCol < MouseColStart || MouseCol > MouseColEnd) {
534 return FALSE;
535 }
536
537 return TRUE;
538 }
539
540 /**
541 Set mouse position according to HBufferImage.MousePosition.
542
543 @retval EFI_SUCCESS The operation was successful.
544 **/
545 EFI_STATUS
546 HBufferImageRestoreMousePosition (
547 VOID
548 )
549 {
550 HEFI_EDITOR_COLOR_UNION Orig;
551 HEFI_EDITOR_COLOR_UNION New;
552 UINTN FRow;
553 UINTN FColumn;
554 BOOLEAN HasCharacter;
555 HEFI_EDITOR_LINE *CurrentLine;
556 HEFI_EDITOR_LINE *Line;
557 UINT8 Value;
558 BOOLEAN HighBits;
559
560 Line = NULL;
561 if (HMainEditor.MouseSupported) {
562
563 if (HBufferImageMouseNeedRefresh) {
564
565 HBufferImageMouseNeedRefresh = FALSE;
566
567 //
568 // if mouse position not moved and only mouse action
569 // so do not need to refresh mouse position
570 //
571 if ((
572 HBufferImage.MousePosition.Row == HBufferImageBackupVar.MousePosition.Row &&
573 HBufferImage.MousePosition.Column == HBufferImageBackupVar.MousePosition.Column
574 ) &&
575 HEditorMouseAction
576 ) {
577 return EFI_SUCCESS;
578 }
579 //
580 // backup the old screen attributes
581 //
582 Orig = HMainEditor.ColorAttributes;
583 New.Data = 0;
584 New.Colors.Foreground = Orig.Colors.Background & 0xF;
585 New.Colors.Background = Orig.Colors.Foreground & 0x7;
586
587 //
588 // if in selected area,
589 // so do not need to refresh mouse
590 //
591 if (!HBufferImageIsInSelectedArea (
592 HBufferImageBackupVar.MousePosition.Row,
593 HBufferImageBackupVar.MousePosition.Column
594 )) {
595 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data);
596 } else {
597 gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F);
598 }
599 //
600 // clear the old mouse position
601 //
602 FRow = HBufferImage.LowVisibleRow + HBufferImageBackupVar.MousePosition.Row - 2;
603
604 HighBits = HBufferImageIsAtHighBits (
605 HBufferImageBackupVar.MousePosition.Column,
606 &FColumn
607 );
608
609 HasCharacter = TRUE;
610 if (FRow > HBufferImage.NumLines || FColumn == 0) {
611 HasCharacter = FALSE;
612 } else {
613 CurrentLine = HBufferImage.CurrentLine;
614 Line = HMoveLine (FRow - HBufferImage.BufferPosition.Row);
615
616 if (Line == NULL || FColumn > Line->Size) {
617 HasCharacter = FALSE;
618 }
619
620 HBufferImage.CurrentLine = CurrentLine;
621 }
622
623 ShellPrintEx (
624 (INT32)HBufferImageBackupVar.MousePosition.Column - 1,
625 (INT32)HBufferImageBackupVar.MousePosition.Row - 1,
626 L" "
627 );
628
629 if (HasCharacter) {
630 if (HighBits) {
631 Value = (UINT8) (Line->Buffer[FColumn - 1] & 0xf0);
632 Value = (UINT8) (Value >> 4);
633 } else {
634 Value = (UINT8) (Line->Buffer[FColumn - 1] & 0xf);
635 }
636
637 ShellPrintEx (
638 (INT32)HBufferImageBackupVar.MousePosition.Column - 1,
639 (INT32)HBufferImageBackupVar.MousePosition.Row - 1,
640 L"%x",
641 Value
642 );
643 }
644
645 if (!HBufferImageIsInSelectedArea (
646 HBufferImage.MousePosition.Row,
647 HBufferImage.MousePosition.Column
648 )) {
649 gST->ConOut->SetAttribute (gST->ConOut, New.Data & 0x7F);
650 } else {
651 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data);
652 }
653 //
654 // clear the old mouse position
655 //
656 FRow = HBufferImage.LowVisibleRow + HBufferImage.MousePosition.Row - 2;
657
658 HighBits = HBufferImageIsAtHighBits (
659 HBufferImage.MousePosition.Column,
660 &FColumn
661 );
662
663 HasCharacter = TRUE;
664 if (FRow > HBufferImage.NumLines || FColumn == 0) {
665 HasCharacter = FALSE;
666 } else {
667 CurrentLine = HBufferImage.CurrentLine;
668 Line = HMoveLine (FRow - HBufferImage.BufferPosition.Row);
669
670 if (Line == NULL || FColumn > Line->Size) {
671 HasCharacter = FALSE;
672 }
673
674 HBufferImage.CurrentLine = CurrentLine;
675 }
676
677 ShellPrintEx (
678 (INT32)HBufferImage.MousePosition.Column - 1,
679 (INT32)HBufferImage.MousePosition.Row - 1,
680 L" "
681 );
682
683 if (HasCharacter) {
684 if (HighBits) {
685 Value = (UINT8) (Line->Buffer[FColumn - 1] & 0xf0);
686 Value = (UINT8) (Value >> 4);
687 } else {
688 Value = (UINT8) (Line->Buffer[FColumn - 1] & 0xf);
689 }
690
691 ShellPrintEx (
692 (INT32)HBufferImage.MousePosition.Column - 1,
693 (INT32)HBufferImage.MousePosition.Row - 1,
694 L"%x",
695 Value
696 );
697 }
698 //
699 // end of HasCharacter
700 //
701 gST->ConOut->SetAttribute (gST->ConOut, Orig.Data);
702 }
703 //
704 // end of MouseNeedRefresh
705 //
706 }
707 //
708 // end of MouseSupported
709 //
710 return EFI_SUCCESS;
711 }
712
713 /**
714 Set cursor position according to HBufferImage.DisplayPosition.
715
716 @retval EFI_SUCCESS The operation was successful.
717 **/
718 EFI_STATUS
719 HBufferImageRestorePosition (
720 VOID
721 )
722 {
723 //
724 // set cursor position
725 //
726 gST->ConOut->SetCursorPosition (
727 gST->ConOut,
728 HBufferImage.DisplayPosition.Column - 1,
729 HBufferImage.DisplayPosition.Row - 1
730 );
731
732 return EFI_SUCCESS;
733 }
734
735 /**
736 Refresh function for HBufferImage.
737
738 @retval EFI_SUCCESS The operation was successful.
739 @retval EFI_LOAD_ERROR A Load error occured.
740
741 **/
742 EFI_STATUS
743 HBufferImageRefresh (
744 VOID
745 )
746 {
747 LIST_ENTRY *Link;
748 HEFI_EDITOR_LINE *Line;
749 UINTN Row;
750 HEFI_EDITOR_COLOR_UNION Orig;
751 HEFI_EDITOR_COLOR_UNION New;
752
753 UINTN StartRow;
754 UINTN EndRow;
755 UINTN FStartRow;
756 UINTN Tmp;
757
758 Orig = HMainEditor.ColorAttributes;
759 New.Data = 0;
760 New.Colors.Foreground = Orig.Colors.Background;
761 New.Colors.Background = Orig.Colors.Foreground;
762
763 //
764 // if it's the first time after editor launch, so should refresh
765 //
766 if (HEditorFirst == FALSE) {
767 //
768 // no definite required refresh
769 // and file position displayed on screen has not been changed
770 //
771 if (!HBufferImageNeedRefresh &&
772 !HBufferImageOnlyLineNeedRefresh &&
773 HBufferImageBackupVar.LowVisibleRow == HBufferImage.LowVisibleRow
774 ) {
775 HBufferImageRestoreMousePosition ();
776 HBufferImageRestorePosition ();
777 return EFI_SUCCESS;
778 }
779 }
780
781 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
782
783 //
784 // only need to refresh current line
785 //
786 if (HBufferImageOnlyLineNeedRefresh && HBufferImageBackupVar.LowVisibleRow == HBufferImage.LowVisibleRow) {
787
788 HBufferImagePrintLine (
789 HBufferImage.CurrentLine,
790 HBufferImage.DisplayPosition.Row,
791 HBufferImage.BufferPosition.Row,
792 Orig,
793 New
794 );
795 } else {
796 //
797 // the whole edit area need refresh
798 //
799 if (HEditorMouseAction && HMainEditor.SelectStart != 0 && HMainEditor.SelectEnd != 0) {
800 if (HMainEditor.SelectStart != HMainEditorBackupVar.SelectStart) {
801 if (HMainEditor.SelectStart >= HMainEditorBackupVar.SelectStart && HMainEditorBackupVar.SelectStart != 0) {
802 StartRow = (HMainEditorBackupVar.SelectStart - 1) / 0x10 + 1;
803 } else {
804 StartRow = (HMainEditor.SelectStart - 1) / 0x10 + 1;
805 }
806 } else {
807 StartRow = (HMainEditor.SelectStart - 1) / 0x10 + 1;
808 }
809
810 if (HMainEditor.SelectEnd <= HMainEditorBackupVar.SelectEnd) {
811 EndRow = (HMainEditorBackupVar.SelectEnd - 1) / 0x10 + 1;
812 } else {
813 EndRow = (HMainEditor.SelectEnd - 1) / 0x10 + 1;
814 }
815 //
816 // swap
817 //
818 if (StartRow > EndRow) {
819 Tmp = StartRow;
820 StartRow = EndRow;
821 EndRow = Tmp;
822 }
823
824 FStartRow = StartRow;
825
826 StartRow = 2 + StartRow - HBufferImage.LowVisibleRow;
827 EndRow = 2 + EndRow - HBufferImage.LowVisibleRow;
828
829 } else {
830 //
831 // not mouse selection actions
832 //
833 FStartRow = HBufferImage.LowVisibleRow;
834 StartRow = 2;
835 EndRow = (HMainEditor.ScreenSize.Row - 1);
836 }
837 //
838 // no line
839 //
840 if (HBufferImage.Lines == NULL) {
841 HBufferImageRestoreMousePosition ();
842 HBufferImageRestorePosition ();
843 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
844 return EFI_SUCCESS;
845 }
846 //
847 // get the first line that will be displayed
848 //
849 Line = HMoveLine (FStartRow - HBufferImage.BufferPosition.Row);
850 if (Line == NULL) {
851 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
852 return EFI_LOAD_ERROR;
853 }
854
855 Link = &(Line->Link);
856 Row = StartRow;
857 do {
858 Line = CR (Link, HEFI_EDITOR_LINE, Link, EFI_EDITOR_LINE_LIST);
859
860 //
861 // print line at row
862 //
863 HBufferImagePrintLine (
864 Line,
865 Row,
866 HBufferImage.LowVisibleRow + Row - 2,
867 Orig,
868 New
869 );
870
871 Link = Link->ForwardLink;
872 Row++;
873 } while (Link != HBufferImage.ListHead && Row <= EndRow);
874
875 while (Row <= EndRow) {
876 EditorClearLine (Row, HMainEditor.ScreenSize.Column, HMainEditor.ScreenSize.Row);
877 Row++;
878 }
879 //
880 // while not file end and not screen full
881 //
882 }
883
884 HBufferImageRestoreMousePosition ();
885 HBufferImageRestorePosition ();
886
887 HBufferImageNeedRefresh = FALSE;
888 HBufferImageOnlyLineNeedRefresh = FALSE;
889 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
890
891 return EFI_SUCCESS;
892 }
893
894 /**
895 Read an image into a buffer friom a source.
896
897 @param[in] FileName Pointer to the file name. OPTIONAL and ignored if not FileTypeFileBuffer.
898 @param[in] DiskName Pointer to the disk name. OPTIONAL and ignored if not FileTypeDiskBuffer.
899 @param[in] DiskOffset Offset into the disk. OPTIONAL and ignored if not FileTypeDiskBuffer.
900 @param[in] DiskSize Size of the disk buffer. OPTIONAL and ignored if not FileTypeDiskBuffer.
901 @param[in] MemOffset Offset into the Memory. OPTIONAL and ignored if not FileTypeMemBuffer.
902 @param[in] MemSize Size of the Memory buffer. OPTIONAL and ignored if not FileTypeMemBuffer.
903 @param[in] BufferType The type of buffer to save. IGNORED.
904 @param[in] Recover TRUE for recovermode, FALSE otherwise.
905
906 @return EFI_SUCCESS The operation was successful.
907 **/
908 EFI_STATUS
909 HBufferImageRead (
910 IN CONST CHAR16 *FileName,
911 IN CONST CHAR16 *DiskName,
912 IN UINTN DiskOffset,
913 IN UINTN DiskSize,
914 IN UINTN MemOffset,
915 IN UINTN MemSize,
916 IN EDIT_FILE_TYPE BufferType,
917 IN BOOLEAN Recover
918 )
919 {
920 EFI_STATUS Status;
921 EDIT_FILE_TYPE BufferTypeBackup;
922
923 //
924 // variable initialization
925 //
926 Status = EFI_SUCCESS;
927 HBufferImage.BufferType = BufferType;
928
929 //
930 // three types of buffer supported
931 // file buffer
932 // disk buffer
933 // memory buffer
934 //
935 BufferTypeBackup = HBufferImage.BufferType;
936
937 switch (BufferType) {
938 case FileTypeFileBuffer:
939 Status = HFileImageRead (FileName, Recover);
940 break;
941
942 case FileTypeDiskBuffer:
943 Status = HDiskImageRead (DiskName, DiskOffset, DiskSize, Recover);
944 break;
945
946 case FileTypeMemBuffer:
947 Status = HMemImageRead (MemOffset, MemSize, Recover);
948 break;
949
950 default:
951 Status = EFI_NOT_FOUND;
952 break;
953 }
954
955 if (EFI_ERROR (Status)) {
956 HBufferImage.BufferType = BufferTypeBackup;
957 }
958
959 return Status;
960 }
961
962 /**
963 Save the current image.
964
965 @param[in] FileName Pointer to the file name. OPTIONAL and ignored if not FileTypeFileBuffer.
966 @param[in] DiskName Pointer to the disk name. OPTIONAL and ignored if not FileTypeDiskBuffer.
967 @param[in] DiskOffset Offset into the disk. OPTIONAL and ignored if not FileTypeDiskBuffer.
968 @param[in] DiskSize Size of the disk buffer. OPTIONAL and ignored if not FileTypeDiskBuffer.
969 @param[in] MemOffset Offset into the Memory. OPTIONAL and ignored if not FileTypeMemBuffer.
970 @param[in] MemSize Size of the Memory buffer. OPTIONAL and ignored if not FileTypeMemBuffer.
971 @param[in] BufferType The type of buffer to save. IGNORED.
972
973 @return EFI_SUCCESS The operation was successful.
974 **/
975 EFI_STATUS
976 HBufferImageSave (
977 IN CHAR16 *FileName,
978 IN CHAR16 *DiskName,
979 IN UINTN DiskOffset,
980 IN UINTN DiskSize,
981 IN UINTN MemOffset,
982 IN UINTN MemSize,
983 IN EDIT_FILE_TYPE BufferType
984 )
985 {
986 EFI_STATUS Status;
987 EDIT_FILE_TYPE BufferTypeBackup;
988
989 //
990 // variable initialization
991 //
992 Status = EFI_SUCCESS;
993 BufferTypeBackup = HBufferImage.BufferType;
994
995 switch (HBufferImage.BufferType) {
996 //
997 // file buffer
998 //
999 case FileTypeFileBuffer:
1000 Status = HFileImageSave (FileName);
1001 break;
1002
1003 //
1004 // disk buffer
1005 //
1006 case FileTypeDiskBuffer:
1007 Status = HDiskImageSave (DiskName, DiskOffset, DiskSize);
1008 break;
1009
1010 //
1011 // memory buffer
1012 //
1013 case FileTypeMemBuffer:
1014 Status = HMemImageSave (MemOffset, MemSize);
1015 break;
1016
1017 default:
1018 Status = EFI_NOT_FOUND;
1019 break;
1020 }
1021
1022 if (EFI_ERROR (Status)) {
1023 HBufferImage.BufferType = BufferTypeBackup;
1024 }
1025
1026 return Status;
1027 }
1028
1029 /**
1030 Create a new line and append it to the line list.
1031 Fields affected:
1032 NumLines
1033 Lines
1034
1035 @retval NULL create line failed.
1036 @return the line created.
1037
1038 **/
1039 HEFI_EDITOR_LINE *
1040 HBufferImageCreateLine (
1041 VOID
1042 )
1043 {
1044 HEFI_EDITOR_LINE *Line;
1045
1046 //
1047 // allocate for line structure
1048 //
1049 Line = AllocateZeroPool (sizeof (HEFI_EDITOR_LINE));
1050 if (Line == NULL) {
1051 return NULL;
1052 }
1053
1054 Line->Signature = EFI_EDITOR_LINE_LIST;
1055 Line->Size = 0;
1056
1057 HBufferImage.NumLines++;
1058
1059 //
1060 // insert to line list
1061 //
1062 InsertTailList (HBufferImage.ListHead, &Line->Link);
1063
1064 if (HBufferImage.Lines == NULL) {
1065 HBufferImage.Lines = CR (
1066 HBufferImage.ListHead->ForwardLink,
1067 HEFI_EDITOR_LINE,
1068 Link,
1069 EFI_EDITOR_LINE_LIST
1070 );
1071 }
1072
1073 return Line;
1074 }
1075
1076 /**
1077 Free the current image.
1078
1079 @retval EFI_SUCCESS The operation was successful.
1080 **/
1081 EFI_STATUS
1082 HBufferImageFree (
1083 VOID
1084 )
1085 {
1086 //
1087 // free all lines
1088 //
1089 HBufferImageFreeLines ();
1090
1091 return EFI_SUCCESS;
1092 }
1093
1094 /**
1095 change char to int value based on Hex.
1096
1097 @param[in] Char The input char.
1098
1099 @return The character's index value.
1100 @retval -1 The operation failed.
1101 **/
1102 INTN
1103 HBufferImageCharToHex (
1104 IN CHAR16 Char
1105 )
1106 {
1107 //
1108 // change the character to hex
1109 //
1110 if (Char >= L'0' && Char <= L'9') {
1111 return (Char - L'0');
1112 }
1113
1114 if (Char >= L'a' && Char <= L'f') {
1115 return (Char - L'a' + 10);
1116 }
1117
1118 if (Char >= L'A' && Char <= L'F') {
1119 return (Char - L'A' + 10);
1120 }
1121
1122 return -1;
1123 }
1124
1125 /**
1126 Add character.
1127
1128 @param[in] Char -- input char.
1129
1130 @retval EFI_SUCCESS The operation was successful.
1131 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1132 **/
1133 EFI_STATUS
1134 HBufferImageAddChar (
1135 IN CHAR16 Char
1136 )
1137 {
1138 HEFI_EDITOR_LINE *Line;
1139 HEFI_EDITOR_LINE *NewLine;
1140 INTN Value;
1141 UINT8 Old;
1142 UINTN FRow;
1143 UINTN FCol;
1144 BOOLEAN High;
1145
1146 Value = HBufferImageCharToHex (Char);
1147
1148 //
1149 // invalid input
1150 //
1151 if (Value == -1) {
1152 return EFI_SUCCESS;
1153 }
1154
1155 Line = HBufferImage.CurrentLine;
1156 FRow = HBufferImage.BufferPosition.Row;
1157 FCol = HBufferImage.BufferPosition.Column;
1158 High = HBufferImage.HighBits;
1159
1160 //
1161 // only needs to refresh current line
1162 //
1163 HBufferImageOnlyLineNeedRefresh = TRUE;
1164
1165 //
1166 // not a full line and beyond the last character
1167 //
1168 if (FCol > Line->Size) {
1169 //
1170 // cursor always at high 4 bits
1171 // and always put input to the low 4 bits
1172 //
1173 Line->Buffer[Line->Size] = (UINT8) Value;
1174 Line->Size++;
1175 High = FALSE;
1176 } else {
1177
1178 Old = Line->Buffer[FCol - 1];
1179
1180 //
1181 // always put the input to the low 4 bits
1182 //
1183 Old = (UINT8) (Old & 0x0f);
1184 Old = (UINT8) (Old << 4);
1185 Old = (UINT8) (Value + Old);
1186 Line->Buffer[FCol - 1] = Old;
1187
1188 //
1189 // at the low 4 bits of the last character of a full line
1190 // so if no next line, need to create a new line
1191 //
1192 if (!High && FCol == 0x10) {
1193
1194 HBufferImageOnlyLineNeedRefresh = FALSE;
1195 HBufferImageNeedRefresh = TRUE;
1196
1197 if (Line->Link.ForwardLink == HBufferImage.ListHead) {
1198 //
1199 // last line
1200 //
1201 // create a new line
1202 //
1203 NewLine = HBufferImageCreateLine ();
1204 if (NewLine == NULL) {
1205 return EFI_OUT_OF_RESOURCES;
1206 }
1207 //
1208 // end of NULL
1209 //
1210 }
1211 //
1212 // end of == ListHead
1213 //
1214 }
1215 //
1216 // end of == 0x10
1217 //
1218 // if already at end of this line, scroll it to the start of next line
1219 //
1220 if (FCol == 0x10 && !High) {
1221 //
1222 // definitely has next line
1223 //
1224 FRow++;
1225 FCol = 1;
1226 High = TRUE;
1227 } else {
1228 //
1229 // if not at end of this line, just move to next column
1230 //
1231 if (!High) {
1232 FCol++;
1233 }
1234
1235 if (High) {
1236 High = FALSE;
1237 } else {
1238 High = TRUE;
1239 }
1240
1241 }
1242 //
1243 // end of ==FALSE
1244 //
1245 }
1246 //
1247 // move cursor to right
1248 //
1249 HBufferImageMovePosition (FRow, FCol, High);
1250
1251 if (!HBufferImage.Modified) {
1252 HBufferImage.Modified = TRUE;
1253 }
1254
1255 return EFI_SUCCESS;
1256 }
1257
1258 /**
1259 Delete the previous character.
1260
1261 @retval EFI_SUCCESS The operationw as successful.
1262 **/
1263 EFI_STATUS
1264 HBufferImageDoBackspace (
1265 VOID
1266 )
1267 {
1268 HEFI_EDITOR_LINE *Line;
1269
1270 UINTN FileColumn;
1271 UINTN FPos;
1272 BOOLEAN LastLine;
1273
1274 //
1275 // variable initialization
1276 //
1277 LastLine = FALSE;
1278
1279 //
1280 // already the first character
1281 //
1282 if (HBufferImage.BufferPosition.Row == 1 && HBufferImage.BufferPosition.Column == 1) {
1283 return EFI_SUCCESS;
1284 }
1285
1286 FPos = (HBufferImage.BufferPosition.Row - 1) * 0x10 + HBufferImage.BufferPosition.Column - 1;
1287
1288 FileColumn = HBufferImage.BufferPosition.Column;
1289
1290 Line = HBufferImage.CurrentLine;
1291 LastLine = FALSE;
1292 if (Line->Link.ForwardLink == HBufferImage.ListHead && FileColumn > 1) {
1293 LastLine = TRUE;
1294 }
1295
1296 HBufferImageDeleteCharacterFromBuffer (FPos - 1, 1, NULL);
1297
1298 //
1299 // if is the last line
1300 // then only this line need to be refreshed
1301 //
1302 if (LastLine) {
1303 HBufferImageNeedRefresh = FALSE;
1304 HBufferImageOnlyLineNeedRefresh = TRUE;
1305 } else {
1306 HBufferImageNeedRefresh = TRUE;
1307 HBufferImageOnlyLineNeedRefresh = FALSE;
1308 }
1309
1310 if (!HBufferImage.Modified) {
1311 HBufferImage.Modified = TRUE;
1312 }
1313
1314 return EFI_SUCCESS;
1315 }
1316
1317 /**
1318 ASCII key + Backspace + return.
1319
1320 @param[in] Char The input char.
1321
1322 @retval EFI_SUCCESS The operation was successful.
1323 @retval EFI_LOAD_ERROR A load error occured.
1324 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1325 **/
1326 EFI_STATUS
1327 HBufferImageDoCharInput (
1328 IN CHAR16 Char
1329 )
1330 {
1331 EFI_STATUS Status;
1332
1333 Status = EFI_SUCCESS;
1334
1335 switch (Char) {
1336 case 0:
1337 break;
1338
1339 case 0x08:
1340 Status = HBufferImageDoBackspace ();
1341 break;
1342
1343 case 0x09:
1344 case 0x0a:
1345 case 0x0d:
1346 //
1347 // Tabs, Returns are thought as nothing
1348 //
1349 break;
1350
1351 default:
1352 //
1353 // DEAL WITH ASCII CHAR, filter out thing like ctrl+f
1354 //
1355 if (Char > 127 || Char < 32) {
1356 Status = StatusBarSetStatusString (L"Unknown Command");
1357 } else {
1358 Status = HBufferImageAddChar (Char);
1359 }
1360
1361 break;
1362 }
1363
1364 return Status;
1365 }
1366
1367 /**
1368 Check user specified FileRow is above current screen.
1369
1370 @param[in] FileRow Row of file position ( start from 1 ).
1371
1372 @retval TRUE It is above the current screen.
1373 @retval FALSE It is not above the current screen.
1374
1375 **/
1376 BOOLEAN
1377 HAboveCurrentScreen (
1378 IN UINTN FileRow
1379 )
1380 {
1381 if (FileRow < HBufferImage.LowVisibleRow) {
1382 return TRUE;
1383 }
1384
1385 return FALSE;
1386 }
1387
1388 /**
1389 Check user specified FileRow is under current screen.
1390
1391 @param[in] FileRow Row of file position ( start from 1 ).
1392
1393 @retval TRUE It is under the current screen.
1394 @retval FALSE It is not under the current screen.
1395
1396 **/
1397 BOOLEAN
1398 HUnderCurrentScreen (
1399 IN UINTN FileRow
1400 )
1401 {
1402 if (FileRow > HBufferImage.LowVisibleRow + (HMainEditor.ScreenSize.Row - 2) - 1) {
1403 return TRUE;
1404 }
1405
1406 return FALSE;
1407 }
1408
1409 /**
1410 According to cursor's file position, adjust screen display.
1411
1412 @param[in] NewFilePosRow Row of file position ( start from 1 ).
1413 @param[in] NewFilePosCol Column of file position ( start from 1 ).
1414 @param[in] HighBits Cursor will on high4 bits or low4 bits.
1415 **/
1416 VOID
1417 HBufferImageMovePosition (
1418 IN UINTN NewFilePosRow,
1419 IN UINTN NewFilePosCol,
1420 IN BOOLEAN HighBits
1421 )
1422 {
1423 INTN RowGap;
1424 UINTN Abs;
1425 BOOLEAN Above;
1426 BOOLEAN Under;
1427 UINTN NewDisplayCol;
1428
1429 //
1430 // CALCULATE gap between current file position and new file position
1431 //
1432 RowGap = NewFilePosRow - HBufferImage.BufferPosition.Row;
1433
1434 Under = HUnderCurrentScreen (NewFilePosRow);
1435 Above = HAboveCurrentScreen (NewFilePosRow);
1436
1437 HBufferImage.HighBits = HighBits;
1438
1439 //
1440 // if is below current screen
1441 //
1442 if (Under) {
1443 //
1444 // display row will be unchanged
1445 //
1446 HBufferImage.BufferPosition.Row = NewFilePosRow;
1447 } else {
1448 if (Above) {
1449 //
1450 // has enough above line, so display row unchanged
1451 // not has enough above lines, so the first line is
1452 // at the first display line
1453 //
1454 if (NewFilePosRow < (HBufferImage.DisplayPosition.Row - 2 + 1)) {
1455 HBufferImage.DisplayPosition.Row = NewFilePosRow + 2 - 1;
1456 }
1457
1458 HBufferImage.BufferPosition.Row = NewFilePosRow;
1459 } else {
1460 //
1461 // in current screen
1462 //
1463 HBufferImage.BufferPosition.Row = NewFilePosRow;
1464 if (RowGap <= 0) {
1465 Abs = (UINTN)ABS(RowGap);
1466 HBufferImage.DisplayPosition.Row -= Abs;
1467 } else {
1468 HBufferImage.DisplayPosition.Row += RowGap;
1469 }
1470
1471 }
1472 }
1473
1474 HBufferImage.LowVisibleRow = HBufferImage.BufferPosition.Row - (HBufferImage.DisplayPosition.Row - 2);
1475
1476 //
1477 // always in current screen
1478 //
1479 HBufferImage.BufferPosition.Column = NewFilePosCol;
1480
1481 NewDisplayCol = 10 + (NewFilePosCol - 1) * 3;
1482 if (NewFilePosCol > 0x8) {
1483 NewDisplayCol++;
1484 }
1485
1486 if (!HighBits) {
1487 NewDisplayCol++;
1488 }
1489
1490 HBufferImage.DisplayPosition.Column = NewDisplayCol;
1491
1492 //
1493 // let CurrentLine point to correct line;
1494 //
1495 HBufferImage.CurrentLine = HMoveCurrentLine (RowGap);
1496
1497 }
1498
1499 /**
1500 Scroll cursor to right.
1501
1502 @retval EFI_SUCCESS The operation was successful.
1503 **/
1504 EFI_STATUS
1505 HBufferImageScrollRight (
1506 VOID
1507 )
1508 {
1509 HEFI_EDITOR_LINE *Line;
1510 UINTN FRow;
1511 UINTN FCol;
1512
1513 //
1514 // scroll right will always move to the high4 bits of the next character
1515 //
1516 HBufferImageNeedRefresh = FALSE;
1517 HBufferImageOnlyLineNeedRefresh = FALSE;
1518
1519 Line = HBufferImage.CurrentLine;
1520
1521 FRow = HBufferImage.BufferPosition.Row;
1522 FCol = HBufferImage.BufferPosition.Column;
1523
1524 //
1525 // this line is not full and no next line
1526 //
1527 if (FCol > Line->Size) {
1528 return EFI_SUCCESS;
1529 }
1530 //
1531 // if already at end of this line, scroll it to the start of next line
1532 //
1533 if (FCol == 0x10) {
1534 //
1535 // has next line
1536 //
1537 if (Line->Link.ForwardLink != HBufferImage.ListHead) {
1538 FRow++;
1539 FCol = 1;
1540
1541 } else {
1542 return EFI_SUCCESS;
1543 }
1544 } else {
1545 //
1546 // if not at end of this line, just move to next column
1547 //
1548 FCol++;
1549
1550 }
1551
1552 HBufferImageMovePosition (FRow, FCol, TRUE);
1553
1554 return EFI_SUCCESS;
1555 }
1556
1557 /**
1558 Scroll cursor to left.
1559
1560 @retval EFI_SUCCESS The operation was successful.
1561 **/
1562 EFI_STATUS
1563 HBufferImageScrollLeft (
1564 VOID
1565 )
1566 {
1567
1568 HEFI_EDITOR_LINE *Line;
1569 UINTN FRow;
1570 UINTN FCol;
1571
1572 HBufferImageNeedRefresh = FALSE;
1573 HBufferImageOnlyLineNeedRefresh = FALSE;
1574
1575 Line = HBufferImage.CurrentLine;
1576
1577 FRow = HBufferImage.BufferPosition.Row;
1578 FCol = HBufferImage.BufferPosition.Column;
1579
1580 //
1581 // if already at start of this line, so move to the end of previous line
1582 //
1583 if (FCol <= 1) {
1584 //
1585 // has previous line
1586 //
1587 if (Line->Link.BackLink != HBufferImage.ListHead) {
1588 FRow--;
1589 Line = CR (Line->Link.BackLink, HEFI_EDITOR_LINE, Link, EFI_EDITOR_LINE_LIST);
1590 FCol = Line->Size;
1591 } else {
1592 return EFI_SUCCESS;
1593 }
1594 } else {
1595 //
1596 // if not at start of this line, just move to previous column
1597 //
1598 FCol--;
1599 }
1600
1601 HBufferImageMovePosition (FRow, FCol, TRUE);
1602
1603 return EFI_SUCCESS;
1604 }
1605
1606 /**
1607 Scroll cursor to the next line
1608
1609 @retval EFI_SUCCESS The operation was successful.
1610 **/
1611 EFI_STATUS
1612 HBufferImageScrollDown (
1613 VOID
1614 )
1615 {
1616 HEFI_EDITOR_LINE *Line;
1617 UINTN FRow;
1618 UINTN FCol;
1619 BOOLEAN HighBits;
1620
1621 Line = HBufferImage.CurrentLine;
1622
1623 FRow = HBufferImage.BufferPosition.Row;
1624 FCol = HBufferImage.BufferPosition.Column;
1625 HighBits = HBufferImage.HighBits;
1626
1627 //
1628 // has next line
1629 //
1630 if (Line->Link.ForwardLink != HBufferImage.ListHead) {
1631 FRow++;
1632 Line = CR (Line->Link.ForwardLink, HEFI_EDITOR_LINE, Link, EFI_EDITOR_LINE_LIST);
1633
1634 //
1635 // if the next line is not that long, so move to end of next line
1636 //
1637 if (FCol > Line->Size) {
1638 FCol = Line->Size + 1;
1639 HighBits = TRUE;
1640 }
1641
1642 } else {
1643 return EFI_SUCCESS;
1644 }
1645
1646 HBufferImageMovePosition (FRow, FCol, HighBits);
1647
1648 return EFI_SUCCESS;
1649 }
1650
1651 /**
1652 Scroll cursor to previous line
1653
1654 @retval EFI_SUCCESS The operation was successful.
1655 **/
1656 EFI_STATUS
1657 HBufferImageScrollUp (
1658 VOID
1659 )
1660 {
1661 HEFI_EDITOR_LINE *Line;
1662 UINTN FRow;
1663 UINTN FCol;
1664
1665 Line = HBufferImage.CurrentLine;
1666
1667 FRow = HBufferImage.BufferPosition.Row;
1668 FCol = HBufferImage.BufferPosition.Column;
1669
1670 //
1671 // has previous line
1672 //
1673 if (Line->Link.BackLink != HBufferImage.ListHead) {
1674 FRow--;
1675
1676 } else {
1677 return EFI_SUCCESS;
1678 }
1679
1680 HBufferImageMovePosition (FRow, FCol, HBufferImage.HighBits);
1681
1682 return EFI_SUCCESS;
1683 }
1684
1685 /**
1686 Scroll cursor to next page
1687
1688 @retval EFI_SUCCESS The operation was successful.
1689 **/
1690 EFI_STATUS
1691 HBufferImagePageDown (
1692 VOID
1693 )
1694 {
1695 HEFI_EDITOR_LINE *Line;
1696 UINTN FRow;
1697 UINTN FCol;
1698 UINTN Gap;
1699 BOOLEAN HighBits;
1700
1701 Line = HBufferImage.CurrentLine;
1702
1703 FRow = HBufferImage.BufferPosition.Row;
1704 FCol = HBufferImage.BufferPosition.Column;
1705 HighBits = HBufferImage.HighBits;
1706
1707 //
1708 // has next page
1709 //
1710 if (HBufferImage.NumLines >= FRow + (HMainEditor.ScreenSize.Row - 2)) {
1711 Gap = (HMainEditor.ScreenSize.Row - 2);
1712 } else {
1713 //
1714 // MOVE CURSOR TO LAST LINE
1715 //
1716 Gap = HBufferImage.NumLines - FRow;
1717 }
1718 //
1719 // get correct line
1720 //
1721 Line = HMoveLine (Gap);
1722
1723 //
1724 // if that line, is not that long, so move to the end of that line
1725 //
1726 if (Line != NULL && FCol > Line->Size) {
1727 FCol = Line->Size + 1;
1728 HighBits = TRUE;
1729 }
1730
1731 FRow += Gap;
1732
1733 HBufferImageMovePosition (FRow, FCol, HighBits);
1734
1735 return EFI_SUCCESS;
1736 }
1737
1738 /**
1739 Scroll cursor to previous page
1740
1741 @retval EFI_SUCCESS The operation was successful.
1742 **/
1743 EFI_STATUS
1744 HBufferImagePageUp (
1745 VOID
1746 )
1747 {
1748 UINTN FRow;
1749 UINTN FCol;
1750 UINTN Gap;
1751 INTN Retreat;
1752
1753 FRow = HBufferImage.BufferPosition.Row;
1754 FCol = HBufferImage.BufferPosition.Column;
1755
1756 //
1757 // has previous page
1758 //
1759 if (FRow > (HMainEditor.ScreenSize.Row - 2)) {
1760 Gap = (HMainEditor.ScreenSize.Row - 2);
1761 } else {
1762 //
1763 // the first line of file will displayed on the first line of screen
1764 //
1765 Gap = FRow - 1;
1766 }
1767
1768 Retreat = Gap;
1769 Retreat = -Retreat;
1770
1771 FRow -= Gap;
1772
1773 HBufferImageMovePosition (FRow, FCol, HBufferImage.HighBits);
1774
1775 return EFI_SUCCESS;
1776 }
1777
1778 /**
1779 Scroll cursor to start of line
1780
1781 @retval EFI_SUCCESS The operation was successful.
1782 **/
1783 EFI_STATUS
1784 HBufferImageHome (
1785 VOID
1786 )
1787 {
1788 UINTN FRow;
1789 UINTN FCol;
1790 BOOLEAN HighBits;
1791
1792 //
1793 // curosr will at the high bit
1794 //
1795 FRow = HBufferImage.BufferPosition.Row;
1796 FCol = 1;
1797 HighBits = TRUE;
1798
1799 //
1800 // move cursor position
1801 //
1802 HBufferImageMovePosition (FRow, FCol, HighBits);
1803
1804 return EFI_SUCCESS;
1805 }
1806
1807 /**
1808 Scroll cursor to end of line.
1809
1810 @retval EFI_SUCCESS Teh operation was successful.
1811 **/
1812 EFI_STATUS
1813 HBufferImageEnd (
1814 VOID
1815 )
1816 {
1817 HEFI_EDITOR_LINE *Line;
1818 UINTN FRow;
1819 UINTN FCol;
1820 BOOLEAN HighBits;
1821
1822 //
1823 // need refresh mouse
1824 //
1825 HBufferImageMouseNeedRefresh = TRUE;
1826
1827 Line = HBufferImage.CurrentLine;
1828
1829 FRow = HBufferImage.BufferPosition.Row;
1830
1831 if (Line->Size == 0x10) {
1832 FCol = Line->Size;
1833 HighBits = FALSE;
1834 } else {
1835 FCol = Line->Size + 1;
1836 HighBits = TRUE;
1837 }
1838 //
1839 // move cursor position
1840 //
1841 HBufferImageMovePosition (FRow, FCol, HighBits);
1842
1843 return EFI_SUCCESS;
1844 }
1845
1846 /**
1847 Get the size of the open buffer.
1848
1849 @retval The size in bytes.
1850 **/
1851 UINTN
1852 HBufferImageGetTotalSize (
1853 VOID
1854 )
1855 {
1856 UINTN Size;
1857
1858 HEFI_EDITOR_LINE *Line;
1859
1860 //
1861 // calculate the total size of whole line list's buffer
1862 //
1863 if (HBufferImage.Lines == NULL) {
1864 return 0;
1865 }
1866
1867 Line = CR (
1868 HBufferImage.ListHead->BackLink,
1869 HEFI_EDITOR_LINE,
1870 Link,
1871 EFI_EDITOR_LINE_LIST
1872 );
1873 //
1874 // one line at most 0x10
1875 //
1876 Size = 0x10 * (HBufferImage.NumLines - 1) + Line->Size;
1877
1878 return Size;
1879 }
1880
1881 /**
1882 Delete character from buffer.
1883
1884 @param[in] Pos Position, Pos starting from 0.
1885 @param[in] Count The Count of characters to delete.
1886 @param[out] DeleteBuffer The DeleteBuffer.
1887
1888 @retval EFI_SUCCESS Success
1889 **/
1890 EFI_STATUS
1891 HBufferImageDeleteCharacterFromBuffer (
1892 IN UINTN Pos,
1893 IN UINTN Count,
1894 OUT UINT8 *DeleteBuffer
1895 )
1896 {
1897 UINTN Index;
1898
1899 VOID *Buffer;
1900 UINT8 *BufferPtr;
1901 UINTN Size;
1902
1903 HEFI_EDITOR_LINE *Line;
1904 LIST_ENTRY *Link;
1905
1906 UINTN OldFCol;
1907 UINTN OldFRow;
1908 UINTN OldPos;
1909
1910 UINTN NewPos;
1911
1912 EFI_STATUS Status;
1913
1914 Size = HBufferImageGetTotalSize ();
1915
1916 if (Size < Count) {
1917 return EFI_LOAD_ERROR;
1918 }
1919
1920 if (Size == 0) {
1921 return EFI_SUCCESS;
1922 }
1923
1924 //
1925 // relocate all the HBufferImage fields
1926 //
1927 OldFRow = HBufferImage.BufferPosition.Row;
1928 OldFCol = HBufferImage.BufferPosition.Column;
1929 OldPos = (OldFRow - 1) * 0x10 + OldFCol - 1;
1930
1931 if (Pos > 0) {
1932 //
1933 // has character before it,
1934 // so locate according to block's previous character
1935 //
1936 NewPos = Pos - 1;
1937
1938 } else {
1939 //
1940 // has no character before it,
1941 // so locate according to block's next character
1942 //
1943 NewPos = 0;
1944 }
1945
1946 HBufferImageMovePosition (NewPos / 0x10 + 1, NewPos % 0x10 + 1, TRUE);
1947
1948 Buffer = AllocateZeroPool (Size);
1949 if (Buffer == NULL) {
1950 return EFI_OUT_OF_RESOURCES;
1951 }
1952
1953 HBufferImageListToBuffer (Buffer, Size);
1954
1955 BufferPtr = (UINT8 *) Buffer;
1956
1957 //
1958 // pass deleted buffer out
1959 //
1960 if (DeleteBuffer != NULL) {
1961 for (Index = 0; Index < Count; Index++) {
1962 DeleteBuffer[Index] = BufferPtr[Pos + Index];
1963 }
1964 }
1965 //
1966 // delete the part from Pos
1967 //
1968 for (Index = Pos; Index < Size - Count; Index++) {
1969 BufferPtr[Index] = BufferPtr[Index + Count];
1970 }
1971
1972 Size -= Count;
1973
1974 HBufferImageFreeLines ();
1975
1976 Status = HBufferImageBufferToList (Buffer, Size);
1977 FreePool (Buffer);
1978
1979 if (EFI_ERROR (Status)) {
1980 return Status;
1981 }
1982
1983 Link = HMainEditor.BufferImage->ListHead->ForwardLink;
1984 for (Index = 0; Index < NewPos / 0x10; Index++) {
1985 Link = Link->ForwardLink;
1986 }
1987
1988 Line = CR (Link, HEFI_EDITOR_LINE, Link, EFI_EDITOR_LINE_LIST);
1989 HBufferImage.CurrentLine = Line;
1990
1991 //
1992 // if current cursor position if inside select area
1993 // then move it to the block's NEXT character
1994 //
1995 if (OldPos >= Pos && OldPos < (Pos + Count)) {
1996 NewPos = Pos;
1997 } else {
1998 if (OldPos < Pos) {
1999 NewPos = OldPos;
2000 } else {
2001 NewPos = OldPos - Count;
2002 }
2003 }
2004
2005 HBufferImageMovePosition (NewPos / 0x10 + 1, NewPos % 0x10 + 1, TRUE);
2006
2007 return EFI_SUCCESS;
2008 }
2009
2010 /**
2011 Add character to buffer, add before pos.
2012
2013 @param[in] Pos Position, Pos starting from 0.
2014 @param[in] Count Count of characters to add.
2015 @param[in] AddBuffer Add buffer.
2016
2017 @retval EFI_SUCCESS Success.
2018 **/
2019 EFI_STATUS
2020 HBufferImageAddCharacterToBuffer (
2021 IN UINTN Pos,
2022 IN UINTN Count,
2023 IN UINT8 *AddBuffer
2024 )
2025 {
2026 INTN Index;
2027
2028 VOID *Buffer;
2029 UINT8 *BufferPtr;
2030 UINTN Size;
2031
2032 HEFI_EDITOR_LINE *Line;
2033
2034 LIST_ENTRY *Link;
2035
2036 UINTN OldFCol;
2037 UINTN OldFRow;
2038 UINTN OldPos;
2039
2040 UINTN NewPos;
2041
2042 Size = HBufferImageGetTotalSize ();
2043
2044 //
2045 // relocate all the HBufferImage fields
2046 //
2047 OldFRow = HBufferImage.BufferPosition.Row;
2048 OldFCol = HBufferImage.BufferPosition.Column;
2049 OldPos = (OldFRow - 1) * 0x10 + OldFCol - 1;
2050
2051 //
2052 // move cursor before Pos
2053 //
2054 if (Pos > 0) {
2055 NewPos = Pos - 1;
2056 } else {
2057 NewPos = 0;
2058 }
2059
2060 HBufferImageMovePosition (NewPos / 0x10 + 1, NewPos % 0x10 + 1, TRUE);
2061
2062 Buffer = AllocateZeroPool (Size + Count);
2063 if (Buffer == NULL) {
2064 return EFI_OUT_OF_RESOURCES;
2065 }
2066
2067 HBufferImageListToBuffer (Buffer, Size);
2068
2069 BufferPtr = (UINT8 *) Buffer;
2070
2071 //
2072 // get a place to add
2073 //
2074 for (Index = (INTN) (Size + Count - 1); Index >= (INTN) Pos; Index--) {
2075 BufferPtr[Index] = BufferPtr[Index - Count];
2076 }
2077 //
2078 // add the buffer
2079 //
2080 for (Index = (INTN) 0; Index < (INTN) Count; Index++) {
2081 BufferPtr[Index + Pos] = AddBuffer[Index];
2082 }
2083
2084 Size += Count;
2085
2086 HBufferImageFreeLines ();
2087
2088 HBufferImageBufferToList (Buffer, Size);
2089
2090 FreePool (Buffer);
2091
2092 Link = HMainEditor.BufferImage->ListHead->ForwardLink;
2093 for (Index = 0; Index < (INTN) NewPos / 0x10; Index++) {
2094 Link = Link->ForwardLink;
2095 }
2096
2097 Line = CR (Link, HEFI_EDITOR_LINE, Link, EFI_EDITOR_LINE_LIST);
2098 HBufferImage.CurrentLine = Line;
2099
2100 if (OldPos >= Pos) {
2101 NewPos = OldPos + Count;
2102 } else {
2103 NewPos = OldPos;
2104 }
2105
2106 HBufferImageMovePosition (NewPos / 0x10 + 1, NewPos % 0x10 + 1, TRUE);
2107
2108 return EFI_SUCCESS;
2109 }
2110
2111 /**
2112 Delete current character from line.
2113
2114 @retval EFI_SUCCESS The operationw as successful.
2115 **/
2116 EFI_STATUS
2117 HBufferImageDoDelete (
2118 VOID
2119 )
2120 {
2121
2122 HEFI_EDITOR_LINE *Line;
2123
2124 BOOLEAN LastLine;
2125 UINTN FileColumn;
2126 UINTN FPos;
2127
2128 FPos = (HBufferImage.BufferPosition.Row - 1) * 0x10 + HBufferImage.BufferPosition.Column - 1;
2129
2130 FileColumn = HBufferImage.BufferPosition.Column;
2131
2132 Line = HBufferImage.CurrentLine;
2133
2134 //
2135 // if beyond the last character
2136 //
2137 if (FileColumn > Line->Size) {
2138 return EFI_SUCCESS;
2139 }
2140
2141 LastLine = FALSE;
2142 if (Line->Link.ForwardLink == HBufferImage.ListHead) {
2143 LastLine = TRUE;
2144 }
2145
2146 HBufferImageDeleteCharacterFromBuffer (FPos, 1, NULL);
2147
2148 //
2149 // if is the last line
2150 // then only this line need to be refreshed
2151 //
2152 if (LastLine) {
2153 HBufferImageNeedRefresh = FALSE;
2154 HBufferImageOnlyLineNeedRefresh = TRUE;
2155 } else {
2156 HBufferImageNeedRefresh = TRUE;
2157 HBufferImageOnlyLineNeedRefresh = FALSE;
2158 }
2159
2160 if (!HBufferImage.Modified) {
2161 HBufferImage.Modified = TRUE;
2162 }
2163
2164 return EFI_SUCCESS;
2165 }
2166
2167 /**
2168 Change the raw buffer to a list of lines for the UI.
2169
2170 @param[in] Buffer The pointer to the buffer to fill.
2171 @param[in] Bytes The size of the buffer in bytes.
2172
2173 @retval EFI_SUCCESS The operation was successful.
2174 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
2175 **/
2176 EFI_STATUS
2177 HBufferImageBufferToList (
2178 IN VOID *Buffer,
2179 IN UINTN Bytes
2180 )
2181 {
2182 UINTN TempI;
2183 UINTN TempJ;
2184 UINTN Left;
2185 HEFI_EDITOR_LINE *Line;
2186 UINT8 *BufferPtr;
2187
2188 TempI = 0;
2189 Left = 0;
2190 BufferPtr = (UINT8 *) Buffer;
2191
2192 //
2193 // parse file content line by line
2194 //
2195 while (TempI < Bytes) {
2196 if (Bytes - TempI >= 0x10) {
2197 Left = 0x10;
2198 } else {
2199 Left = Bytes - TempI;
2200 }
2201
2202 //
2203 // allocate a new line
2204 //
2205 Line = HBufferImageCreateLine ();
2206 if (Line == NULL) {
2207 return EFI_OUT_OF_RESOURCES;
2208 }
2209
2210 Line->Size = Left;
2211
2212 for (TempJ = 0; TempJ < Left; TempJ++) {
2213 Line->Buffer[TempJ] = BufferPtr[TempI];
2214 TempI++;
2215 }
2216
2217 }
2218
2219 //
2220 // last line is a full line, SO create a new line
2221 //
2222 if (Left == 0x10 || Bytes == 0) {
2223 Line = HBufferImageCreateLine ();
2224 if (Line == NULL) {
2225 return EFI_OUT_OF_RESOURCES;
2226 }
2227 }
2228
2229 return EFI_SUCCESS;
2230 }
2231
2232 /**
2233 Change the list of lines from the UI to a raw buffer.
2234
2235 @param[in] Buffer The pointer to the buffer to fill.
2236 @param[in] Bytes The size of the buffer in bytes.
2237
2238 @retval EFI_SUCCESS The operation was successful.
2239 **/
2240 EFI_STATUS
2241 HBufferImageListToBuffer (
2242 IN VOID *Buffer,
2243 IN UINTN Bytes
2244 )
2245 {
2246 UINTN Count;
2247 UINTN Index;
2248 HEFI_EDITOR_LINE *Line;
2249 LIST_ENTRY *Link;
2250 UINT8 *BufferPtr;
2251
2252 //
2253 // change the line list to a large buffer
2254 //
2255 if (HBufferImage.Lines == NULL) {
2256 return EFI_SUCCESS;
2257 }
2258
2259 Link = &HBufferImage.Lines->Link;
2260 Count = 0;
2261 BufferPtr = (UINT8 *) Buffer;
2262
2263 //
2264 // deal line by line
2265 //
2266 while (Link != HBufferImage.ListHead) {
2267
2268 Line = CR (Link, HEFI_EDITOR_LINE, Link, EFI_EDITOR_LINE_LIST);
2269
2270 //@todo shouldn't this be an error???
2271 if (Count + Line->Size > Bytes) {
2272 return EFI_SUCCESS;
2273 }
2274
2275 for (Index = 0; Index < Line->Size; Index++) {
2276 BufferPtr[Index] = Line->Buffer[Index];
2277 }
2278
2279 Count += Line->Size;
2280 BufferPtr += Line->Size;
2281
2282 Link = Link->ForwardLink;
2283 }
2284
2285 return EFI_SUCCESS;
2286 }
2287
2288 /**
2289 Move the mouse in the image buffer.
2290
2291 @param[in] TextX The x-coordinate.
2292 @param[in] TextY The y-coordinate.
2293 **/
2294 VOID
2295 HBufferImageAdjustMousePosition (
2296 IN INT32 TextX,
2297 IN INT32 TextY
2298 )
2299 {
2300 UINTN TempX;
2301 UINTN TempY;
2302 UINTN AbsX;
2303 UINTN AbsY;
2304
2305 //
2306 // TextX and TextY is mouse movement data returned by mouse driver
2307 // This function will change it to MousePosition
2308 //
2309 //
2310 // get absolute TempX value
2311 //
2312 if (TextX >= 0) {
2313 AbsX = TextX;
2314 } else {
2315 AbsX = -TextX;
2316 }
2317 //
2318 // get absolute TempY value
2319 //
2320 if (TextY >= 0) {
2321 AbsY = TextY;
2322 } else {
2323 AbsY = -TextY;
2324 }
2325
2326 TempX = HBufferImage.MousePosition.Column;
2327 TempY = HBufferImage.MousePosition.Row;
2328
2329 if (TextX >= 0) {
2330 TempX += TextX;
2331 } else {
2332 if (TempX >= AbsX) {
2333 TempX -= AbsX;
2334 } else {
2335 TempX = 0;
2336 }
2337 }
2338
2339 if (TextY >= 0) {
2340 TempY += TextY;
2341 } else {
2342 if (TempY >= AbsY) {
2343 TempY -= AbsY;
2344 } else {
2345 TempY = 0;
2346 }
2347 }
2348 //
2349 // check whether new mouse column position is beyond screen
2350 // if not, adjust it
2351 //
2352 if (TempX >= 10 && TempX <= (10 + 0x10 * 3 - 1)) {
2353 HBufferImage.MousePosition.Column = TempX;
2354 } else if (TempX < 10) {
2355 HBufferImage.MousePosition.Column = 10;
2356 } else if (TempX > (10 + 0x10 * 3 - 1)) {
2357 HBufferImage.MousePosition.Column = 10 + 0x10 * 3 - 1;
2358 }
2359 //
2360 // check whether new mouse row position is beyond screen
2361 // if not, adjust it
2362 //
2363 if (TempY >= 2 && TempY <= (HMainEditor.ScreenSize.Row - 1)) {
2364 HBufferImage.MousePosition.Row = TempY;
2365 } else if (TempY < 2) {
2366 HBufferImage.MousePosition.Row = 2;
2367 } else if (TempY > (HMainEditor.ScreenSize.Row - 1)) {
2368 HBufferImage.MousePosition.Row = (HMainEditor.ScreenSize.Row - 1);
2369 }
2370
2371 }
2372
2373 /**
2374 Dispatch input to different handler
2375
2376 @param[in] Key The input key:
2377 the keys can be:
2378 ASCII KEY
2379 Backspace/Delete
2380 Direction key: up/down/left/right/pgup/pgdn
2381 Home/End
2382 INS
2383
2384 @retval EFI_SUCCESS The operation was successful.
2385 @retval EFI_LOAD_ERROR A load error occured.
2386 @retval EFI_OUT_OF_RESOURCES A Memory allocation failed.
2387 **/
2388 EFI_STATUS
2389 HBufferImageHandleInput (
2390 IN EFI_INPUT_KEY *Key
2391 )
2392 {
2393 EFI_STATUS Status;
2394
2395 Status = EFI_SUCCESS;
2396
2397 switch (Key->ScanCode) {
2398 //
2399 // ordinary key
2400 //
2401 case SCAN_NULL:
2402 Status = HBufferImageDoCharInput (Key->UnicodeChar);
2403 break;
2404
2405 //
2406 // up arrow
2407 //
2408 case SCAN_UP:
2409 Status = HBufferImageScrollUp ();
2410 break;
2411
2412 //
2413 // down arrow
2414 //
2415 case SCAN_DOWN:
2416 Status = HBufferImageScrollDown ();
2417 break;
2418
2419 //
2420 // right arrow
2421 //
2422 case SCAN_RIGHT:
2423 Status = HBufferImageScrollRight ();
2424 break;
2425
2426 //
2427 // left arrow
2428 //
2429 case SCAN_LEFT:
2430 Status = HBufferImageScrollLeft ();
2431 break;
2432
2433 //
2434 // page up
2435 //
2436 case SCAN_PAGE_UP:
2437 Status = HBufferImagePageUp ();
2438 break;
2439
2440 //
2441 // page down
2442 //
2443 case SCAN_PAGE_DOWN:
2444 Status = HBufferImagePageDown ();
2445 break;
2446
2447 //
2448 // delete
2449 //
2450 case SCAN_DELETE:
2451 Status = HBufferImageDoDelete ();
2452 break;
2453
2454 //
2455 // home
2456 //
2457 case SCAN_HOME:
2458 Status = HBufferImageHome ();
2459 break;
2460
2461 //
2462 // end
2463 //
2464 case SCAN_END:
2465 Status = HBufferImageEnd ();
2466 break;
2467
2468 default:
2469 Status = StatusBarSetStatusString (L"Unknown Command");
2470 break;
2471 }
2472
2473 return Status;
2474 }
2475