xref: /reactos/base/setup/usetup/partlist.c (revision fb5d5ecd)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002, 2003, 2004, 2005 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /* COPYRIGHT:       See COPYING in the top level directory
20  * PROJECT:         ReactOS text-mode setup
21  * FILE:            base/setup/usetup/partlist.c
22  * PURPOSE:         Partition list functions
23  * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
24  */
25 
26 #include "usetup.h"
27 
28 #define NDEBUG
29 #include <debug.h>
30 
31 /* HELPERS FOR PARTITION TYPES **********************************************/
32 
33 typedef struct _PARTITION_TYPE
34 {
35     UCHAR Type;
36     PCHAR Description;
37 } PARTITION_TYPE, *PPARTITION_TYPE;
38 
39 /*
40  * This partition type list was ripped off the kernelDisk.c module from:
41  *
42  * Visopsys Operating System
43  * Copyright (C) 1998-2015 J. Andrew McLaughlin
44  *
45  * This program is free software; you can redistribute it and/or modify it
46  * under the terms of the GNU General Public License as published by the Free
47  * Software Foundation; either version 2 of the License, or (at your option)
48  * any later version.
49  *
50  * This program is distributed in the hope that it will be useful, but
51  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
52  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
53  * for more details.
54  *
55  * You should have received a copy of the GNU General Public License along
56  * with this program; if not, write to the Free Software Foundation, Inc.,
57  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
58  *
59  *
60  * See also https://en.wikipedia.org/wiki/Partition_type#List_of_partition_IDs
61  * and http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
62  * for a complete list.
63  */
64 
65 /* This is a table for keeping known partition type codes and descriptions */
66 static PARTITION_TYPE PartitionTypes[] =
67 {
68     { 0x00, "(Empty)" },
69     { 0x01, "FAT12" },
70     { 0x02, "XENIX root" },
71     { 0x03, "XENIX /usr" },
72     { 0x04, "FAT16 (small)" },
73     { 0x05, "Extended" },
74     { 0x06, "FAT16" },
75     { 0x07, "NTFS/HPFS/exFAT" },
76     { 0x08, "OS/2 or AIX boot" },
77     { 0x09, "AIX data" },
78     { 0x0A, "OS/2 Boot Manager" },
79     { 0x0B, "FAT32" },
80     { 0x0C, "FAT32 (LBA)" },
81     { 0x0E, "FAT16 (LBA)" },
82     { 0x0F, "Extended (LBA)" },
83     { 0x11, "Hidden FAT12" },
84     { 0x12, "FAT diagnostic" },
85     { 0x14, "Hidden FAT16 (small)" },
86     { 0x16, "Hidden FAT16" },
87     { 0x17, "Hidden HPFS or NTFS" },
88     { 0x1B, "Hidden FAT32" },
89     { 0x1C, "Hidden FAT32 (LBA)" },
90     { 0x1E, "Hidden FAT16 (LBA)" },
91     { 0x35, "JFS" },
92     { 0x39, "Plan 9" },
93     { 0x3C, "PartitionMagic" },
94     { 0x3D, "Hidden Netware" },
95     { 0x41, "PowerPC PReP" },
96     { 0x42, "Win2K dynamic extended" },
97     { 0x43, "Old Linux" },
98     { 0x44, "GoBack" },
99     { 0x4D, "QNX4.x" },
100     { 0x4D, "QNX4.x 2nd" },
101     { 0x4D, "QNX4.x 3rd" },
102     { 0x50, "Ontrack R/O" },
103     { 0x51, "Ontrack R/W or Novell" },
104     { 0x52, "CP/M" },
105     { 0x63, "GNU HURD or UNIX SysV" },
106     { 0x64, "Netware 2" },
107     { 0x65, "Netware 3/4" },
108     { 0x66, "Netware SMS" },
109     { 0x67, "Novell" },
110     { 0x68, "Novell" },
111     { 0x69, "Netware 5+" },
112     { 0x7E, "Veritas VxVM public" },
113     { 0x7F, "Veritas VxVM private" },
114     { 0x80, "Minix" },
115     { 0x81, "Linux or Minix" },
116     { 0x82, "Linux swap or Solaris" },
117     { 0x83, "Linux" },
118     { 0x84, "Hibernation" },
119     { 0x85, "Linux extended" },
120     { 0x86, "HPFS or NTFS mirrored" },
121     { 0x87, "HPFS or NTFS mirrored" },
122     { 0x8E, "Linux LVM" },
123     { 0x93, "Hidden Linux" },
124     { 0x96, "CDFS/ISO-9660" },
125     { 0x9F, "BSD/OS" },
126     { 0xA0, "Laptop hibernation" },
127     { 0xA1, "Laptop hibernation" },
128     { 0xA5, "BSD, NetBSD, FreeBSD" },
129     { 0xA6, "OpenBSD" },
130     { 0xA7, "NeXTSTEP" },
131     { 0xA8, "OS-X UFS" },
132     { 0xA9, "NetBSD" },
133     { 0xAB, "OS-X boot" },
134     { 0xAF, "OS-X HFS" },
135     { 0xB6, "NT corrupt mirror" },
136     { 0xB7, "BSDI" },
137     { 0xB8, "BSDI swap" },
138     { 0xBE, "Solaris 8 boot" },
139     { 0xBF, "Solaris x86" },
140     { 0xC0, "NTFT" },
141     { 0xC1, "DR-DOS FAT12" },
142     { 0xC2, "Hidden Linux" },
143     { 0xC3, "Hidden Linux swap" },
144     { 0xC4, "DR-DOS FAT16 (small)" },
145     { 0xC5, "DR-DOS Extended" },
146     { 0xC6, "DR-DOS FAT16" },
147     { 0xC7, "HPFS mirrored" },
148     { 0xCB, "DR-DOS FAT32" },
149     { 0xCC, "DR-DOS FAT32 (LBA)" },
150     { 0xCE, "DR-DOS FAT16 (LBA)" },
151     { 0xD0, "MDOS" },
152     { 0xD1, "MDOS FAT12" },
153     { 0xD4, "MDOS FAT16 (small)" },
154     { 0xD5, "MDOS Extended" },
155     { 0xD6, "MDOS FAT16" },
156     { 0xD8, "CP/M-86" },
157     { 0xDF, "BootIt EMBRM(FAT16/32)" },
158     { 0xEB, "BeOS BFS" },
159     { 0xEE, "EFI GPT protective" },
160     { 0xEF, "EFI filesystem" },
161     { 0xF0, "Linux/PA-RISC boot" },
162     { 0xF2, "DOS 3.3+ second" },
163     { 0xFA, "Bochs" },
164     { 0xFB, "VmWare" },
165     { 0xFC, "VmWare swap" },
166     { 0xFD, "Linux RAID" },
167     { 0xFE, "NT hidden" },
168 };
169 
170 VOID
171 GetPartTypeStringFromPartitionType(
172     IN UCHAR partitionType,
173     OUT PCHAR strPartType,
174     IN ULONG cchPartType)
175 {
176     /* Determine partition type */
177 
178     if (IsContainerPartition(partitionType))
179     {
180         RtlStringCchCopyA(strPartType, cchPartType, MUIGetString(STRING_EXTENDED_PARTITION));
181     }
182     else if (partitionType == PARTITION_ENTRY_UNUSED)
183     {
184         RtlStringCchCopyA(strPartType, cchPartType, MUIGetString(STRING_FORMATUNUSED));
185     }
186     else
187     {
188         UINT i;
189 
190         /* Do the table lookup */
191         for (i = 0; i < ARRAYSIZE(PartitionTypes); i++)
192         {
193             if (partitionType == PartitionTypes[i].Type)
194             {
195                 RtlStringCchCopyA(strPartType, cchPartType, PartitionTypes[i].Description);
196                 return;
197             }
198         }
199 
200         /* We are here because the partition type is unknown */
201         RtlStringCchCopyA(strPartType, cchPartType, MUIGetString(STRING_FORMATUNKNOWN));
202     }
203 }
204 
205 
206 /* FUNCTIONS ****************************************************************/
207 
208 VOID
209 InitPartitionListUi(
210     IN OUT PPARTLIST_UI ListUi,
211     IN PPARTLIST List,
212     IN SHORT Left,
213     IN SHORT Top,
214     IN SHORT Right,
215     IN SHORT Bottom)
216 {
217     ListUi->List = List;
218     // ListUi->FirstShown = NULL;
219     // ListUi->LastShown = NULL;
220 
221     ListUi->Left = Left;
222     ListUi->Top = Top;
223     ListUi->Right = Right;
224     ListUi->Bottom = Bottom;
225 
226     ListUi->Line = 0;
227     ListUi->Offset = 0;
228 
229     // ListUi->Redraw = TRUE;
230 }
231 
232 static
233 VOID
234 PrintEmptyLine(
235     IN PPARTLIST_UI ListUi)
236 {
237     COORD coPos;
238     ULONG Written;
239     USHORT Width;
240     USHORT Height;
241 
242     Width = ListUi->Right - ListUi->Left - 1;
243     Height = ListUi->Bottom - ListUi->Top - 2;
244 
245     coPos.X = ListUi->Left + 1;
246     coPos.Y = ListUi->Top + 1 + ListUi->Line;
247 
248     if (ListUi->Line >= 0 && ListUi->Line <= Height)
249     {
250         FillConsoleOutputAttribute(StdOutput,
251                                    FOREGROUND_WHITE | BACKGROUND_BLUE,
252                                    Width,
253                                    coPos,
254                                    &Written);
255 
256         FillConsoleOutputCharacterA(StdOutput,
257                                     ' ',
258                                     Width,
259                                     coPos,
260                                     &Written);
261     }
262 
263     ListUi->Line++;
264 }
265 
266 static
267 VOID
268 PrintPartitionData(
269     IN PPARTLIST_UI ListUi,
270     IN PDISKENTRY DiskEntry,
271     IN PPARTENTRY PartEntry)
272 {
273     PPARTLIST List = ListUi->List;
274     CHAR LineBuffer[128];
275     COORD coPos;
276     ULONG Written;
277     USHORT Width;
278     USHORT Height;
279     LARGE_INTEGER PartSize;
280     PCHAR Unit;
281     UCHAR Attribute;
282     CHAR PartTypeString[32];
283     PCHAR PartType = PartTypeString;
284 
285     Width = ListUi->Right - ListUi->Left - 1;
286     Height = ListUi->Bottom - ListUi->Top - 2;
287 
288     coPos.X = ListUi->Left + 1;
289     coPos.Y = ListUi->Top + 1 + ListUi->Line;
290 
291     if (PartEntry->IsPartitioned == FALSE)
292     {
293         PartSize.QuadPart = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
294 #if 0
295         if (PartSize.QuadPart >= 10737418240) /* 10 GB */
296         {
297             PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, 1073741824);
298             Unit = MUIGetString(STRING_GB);
299         }
300         else
301 #endif
302         if (PartSize.QuadPart >= 10485760) /* 10 MB */
303         {
304             PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, 1048576);
305             Unit = MUIGetString(STRING_MB);
306         }
307         else
308         {
309             PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, 1024);
310             Unit = MUIGetString(STRING_KB);
311         }
312 
313         sprintf(LineBuffer,
314                 MUIGetString(STRING_UNPSPACE),
315                 PartEntry->LogicalPartition ? "  " : "",
316                 PartEntry->LogicalPartition ? "" : "  ",
317                 PartSize.u.LowPart,
318                 Unit);
319     }
320     else
321     {
322         /* Determine partition type */
323         PartTypeString[0] = '\0';
324         if (PartEntry->New != FALSE)
325         {
326             PartType = MUIGetString(STRING_UNFORMATTED);
327         }
328         else if (PartEntry->IsPartitioned != FALSE)
329         {
330             GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
331                                                PartTypeString,
332                                                ARRAYSIZE(PartTypeString));
333             PartType = PartTypeString;
334         }
335 
336         PartSize.QuadPart = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
337 #if 0
338         if (PartSize.QuadPart >= 10737418240) /* 10 GB */
339         {
340             PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, 1073741824);
341             Unit = MUIGetString(STRING_GB);
342         }
343         else
344 #endif
345         if (PartSize.QuadPart >= 10485760) /* 10 MB */
346         {
347             PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, 1048576);
348             Unit = MUIGetString(STRING_MB);
349         }
350         else
351         {
352             PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, 1024);
353             Unit = MUIGetString(STRING_KB);
354         }
355 
356         if (strcmp(PartType, MUIGetString(STRING_FORMATUNKNOWN)) == 0)
357         {
358             sprintf(LineBuffer,
359                     MUIGetString(STRING_HDDINFOUNK5),
360                     (PartEntry->DriveLetter == 0) ? '-' : PartEntry->DriveLetter,
361                     (PartEntry->DriveLetter == 0) ? '-' : ':',
362                     PartEntry->BootIndicator ? '*' : ' ',
363                     PartEntry->LogicalPartition ? "  " : "",
364                     PartEntry->PartitionType,
365                     PartEntry->LogicalPartition ? "" : "  ",
366                     PartSize.u.LowPart,
367                     Unit);
368         }
369         else
370         {
371             sprintf(LineBuffer,
372                     "%c%c %c %s%-24s%s     %6lu %s",
373                     (PartEntry->DriveLetter == 0) ? '-' : PartEntry->DriveLetter,
374                     (PartEntry->DriveLetter == 0) ? '-' : ':',
375                     PartEntry->BootIndicator ? '*' : ' ',
376                     PartEntry->LogicalPartition ? "  " : "",
377                     PartType,
378                     PartEntry->LogicalPartition ? "" : "  ",
379                     PartSize.u.LowPart,
380                     Unit);
381         }
382     }
383 
384     Attribute = (List->CurrentDisk == DiskEntry &&
385                  List->CurrentPartition == PartEntry) ?
386                  FOREGROUND_BLUE | BACKGROUND_WHITE :
387                  FOREGROUND_WHITE | BACKGROUND_BLUE;
388 
389     if (ListUi->Line >= 0 && ListUi->Line <= Height)
390     {
391         FillConsoleOutputCharacterA(StdOutput,
392                                     ' ',
393                                     Width,
394                                     coPos,
395                                     &Written);
396     }
397     coPos.X += 4;
398     Width -= 8;
399     if (ListUi->Line >= 0 && ListUi->Line <= Height)
400     {
401         FillConsoleOutputAttribute(StdOutput,
402                                    Attribute,
403                                    Width,
404                                    coPos,
405                                    &Written);
406     }
407     coPos.X++;
408     Width -= 2;
409     if (ListUi->Line >= 0 && ListUi->Line <= Height)
410     {
411         WriteConsoleOutputCharacterA(StdOutput,
412                                      LineBuffer,
413                                      min(strlen(LineBuffer), Width),
414                                      coPos,
415                                      &Written);
416     }
417 
418     ListUi->Line++;
419 }
420 
421 static
422 VOID
423 PrintDiskData(
424     IN PPARTLIST_UI ListUi,
425     IN PDISKENTRY DiskEntry)
426 {
427     // PPARTLIST List = ListUi->List;
428     PPARTENTRY PrimaryPartEntry, LogicalPartEntry;
429     PLIST_ENTRY PrimaryEntry, LogicalEntry;
430     CHAR LineBuffer[128];
431     COORD coPos;
432     ULONG Written;
433     USHORT Width;
434     USHORT Height;
435     ULARGE_INTEGER DiskSize;
436     PCHAR Unit;
437 
438     Width = ListUi->Right - ListUi->Left - 1;
439     Height = ListUi->Bottom - ListUi->Top - 2;
440 
441     coPos.X = ListUi->Left + 1;
442     coPos.Y = ListUi->Top + 1 + ListUi->Line;
443 
444     DiskSize.QuadPart = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
445     if (DiskSize.QuadPart >= 10737418240) /* 10 GB */
446     {
447         DiskSize.QuadPart = RoundingDivide(DiskSize.QuadPart, 1073741824);
448         Unit = MUIGetString(STRING_GB);
449     }
450     else
451     {
452         DiskSize.QuadPart = RoundingDivide(DiskSize.QuadPart, 1048576);
453         if (DiskSize.QuadPart == 0)
454             DiskSize.QuadPart = 1;
455         Unit = MUIGetString(STRING_MB);
456     }
457 
458     if (DiskEntry->DriverName.Length > 0)
459     {
460         sprintf(LineBuffer,
461                 MUIGetString(STRING_HDINFOPARTSELECT),
462                 DiskSize.u.LowPart,
463                 Unit,
464                 DiskEntry->DiskNumber,
465                 DiskEntry->Port,
466                 DiskEntry->Bus,
467                 DiskEntry->Id,
468                 DiskEntry->DriverName.Buffer);
469     }
470     else
471     {
472         sprintf(LineBuffer,
473                 MUIGetString(STRING_HDDINFOUNK6),
474                 DiskSize.u.LowPart,
475                 Unit,
476                 DiskEntry->DiskNumber,
477                 DiskEntry->Port,
478                 DiskEntry->Bus,
479                 DiskEntry->Id);
480     }
481 
482     if (ListUi->Line >= 0 && ListUi->Line <= Height)
483     {
484         FillConsoleOutputAttribute(StdOutput,
485                                    FOREGROUND_WHITE | BACKGROUND_BLUE,
486                                    Width,
487                                    coPos,
488                                    &Written);
489 
490         FillConsoleOutputCharacterA(StdOutput,
491                                     ' ',
492                                     Width,
493                                     coPos,
494                                     &Written);
495     }
496 
497     coPos.X++;
498     if (ListUi->Line >= 0 && ListUi->Line <= Height)
499     {
500         WriteConsoleOutputCharacterA(StdOutput,
501                                      LineBuffer,
502                                      min((USHORT)strlen(LineBuffer), Width - 2),
503                                      coPos,
504                                      &Written);
505     }
506 
507     ListUi->Line++;
508 
509     /* Print separator line */
510     PrintEmptyLine(ListUi);
511 
512     /* Print partition lines */
513     PrimaryEntry = DiskEntry->PrimaryPartListHead.Flink;
514     while (PrimaryEntry != &DiskEntry->PrimaryPartListHead)
515     {
516         PrimaryPartEntry = CONTAINING_RECORD(PrimaryEntry, PARTENTRY, ListEntry);
517 
518         PrintPartitionData(ListUi,
519                            DiskEntry,
520                            PrimaryPartEntry);
521 
522         if (IsContainerPartition(PrimaryPartEntry->PartitionType))
523         {
524             LogicalEntry = DiskEntry->LogicalPartListHead.Flink;
525             while (LogicalEntry != &DiskEntry->LogicalPartListHead)
526             {
527                 LogicalPartEntry = CONTAINING_RECORD(LogicalEntry, PARTENTRY, ListEntry);
528 
529                 PrintPartitionData(ListUi,
530                                    DiskEntry,
531                                    LogicalPartEntry);
532 
533                 LogicalEntry = LogicalEntry->Flink;
534             }
535         }
536 
537         PrimaryEntry = PrimaryEntry->Flink;
538     }
539 
540     /* Print separator line */
541     PrintEmptyLine(ListUi);
542 }
543 
544 VOID
545 DrawPartitionList(
546     IN PPARTLIST_UI ListUi)
547 {
548     PPARTLIST List = ListUi->List;
549     PLIST_ENTRY Entry, Entry2;
550     PDISKENTRY DiskEntry;
551     PPARTENTRY PartEntry = NULL;
552     COORD coPos;
553     ULONG Written;
554     SHORT i;
555     SHORT CurrentDiskLine;
556     SHORT CurrentPartLine;
557     SHORT LastLine;
558     BOOLEAN CurrentPartLineFound = FALSE;
559     BOOLEAN CurrentDiskLineFound = FALSE;
560 
561     /* Calculate the line of the current disk and partition */
562     CurrentDiskLine = 0;
563     CurrentPartLine = 0;
564     LastLine = 0;
565 
566     Entry = List->DiskListHead.Flink;
567     while (Entry != &List->DiskListHead)
568     {
569         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
570 
571         LastLine += 2;
572         if (CurrentPartLineFound == FALSE)
573         {
574             CurrentPartLine += 2;
575         }
576 
577         Entry2 = DiskEntry->PrimaryPartListHead.Flink;
578         while (Entry2 != &DiskEntry->PrimaryPartListHead)
579         {
580             PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
581             if (PartEntry == List->CurrentPartition)
582             {
583                 CurrentPartLineFound = TRUE;
584             }
585 
586             Entry2 = Entry2->Flink;
587             if (CurrentPartLineFound == FALSE)
588             {
589                 CurrentPartLine++;
590             }
591 
592             LastLine++;
593         }
594 
595         if (CurrentPartLineFound == FALSE)
596         {
597             Entry2 = DiskEntry->LogicalPartListHead.Flink;
598             while (Entry2 != &DiskEntry->LogicalPartListHead)
599             {
600                 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
601                 if (PartEntry == List->CurrentPartition)
602                 {
603                     CurrentPartLineFound = TRUE;
604                 }
605 
606                 Entry2 = Entry2->Flink;
607                 if (CurrentPartLineFound == FALSE)
608                 {
609                     CurrentPartLine++;
610                 }
611 
612                 LastLine++;
613             }
614         }
615 
616         if (DiskEntry == List->CurrentDisk)
617         {
618             CurrentDiskLineFound = TRUE;
619         }
620 
621         Entry = Entry->Flink;
622         if (Entry != &List->DiskListHead)
623         {
624             if (CurrentDiskLineFound == FALSE)
625             {
626                 CurrentPartLine ++;
627                 CurrentDiskLine = CurrentPartLine;
628             }
629 
630             LastLine++;
631         }
632         else
633         {
634             LastLine--;
635         }
636     }
637 
638     /* If it possible, make the disk name visible */
639     if (CurrentPartLine < ListUi->Offset)
640     {
641         ListUi->Offset = CurrentPartLine;
642     }
643     else if (CurrentPartLine - ListUi->Offset > ListUi->Bottom - ListUi->Top - 2)
644     {
645         ListUi->Offset = CurrentPartLine - (ListUi->Bottom - ListUi->Top - 2);
646     }
647 
648     if (CurrentDiskLine < ListUi->Offset && CurrentPartLine - CurrentDiskLine < ListUi->Bottom - ListUi->Top - 2)
649     {
650         ListUi->Offset = CurrentDiskLine;
651     }
652 
653     /* Draw upper left corner */
654     coPos.X = ListUi->Left;
655     coPos.Y = ListUi->Top;
656     FillConsoleOutputCharacterA(StdOutput,
657                                 0xDA, // '+',
658                                 1,
659                                 coPos,
660                                 &Written);
661 
662     /* Draw upper edge */
663     coPos.X = ListUi->Left + 1;
664     coPos.Y = ListUi->Top;
665     if (ListUi->Offset == 0)
666     {
667         FillConsoleOutputCharacterA(StdOutput,
668                                     0xC4, // '-',
669                                     ListUi->Right - ListUi->Left - 1,
670                                     coPos,
671                                     &Written);
672     }
673     else
674     {
675         FillConsoleOutputCharacterA(StdOutput,
676                                     0xC4, // '-',
677                                     ListUi->Right - ListUi->Left - 5,
678                                     coPos,
679                                     &Written);
680         coPos.X = ListUi->Right - 5;
681         WriteConsoleOutputCharacterA(StdOutput,
682                                      "(\x18)", // "(up)"
683                                      3,
684                                      coPos,
685                                      &Written);
686         coPos.X = ListUi->Right - 2;
687         FillConsoleOutputCharacterA(StdOutput,
688                                     0xC4, // '-',
689                                     2,
690                                     coPos,
691                                     &Written);
692     }
693 
694     /* Draw upper right corner */
695     coPos.X = ListUi->Right;
696     coPos.Y = ListUi->Top;
697     FillConsoleOutputCharacterA(StdOutput,
698                                 0xBF, // '+',
699                                 1,
700                                 coPos,
701                                 &Written);
702 
703     /* Draw left and right edge */
704     for (i = ListUi->Top + 1; i < ListUi->Bottom; i++)
705     {
706         coPos.X = ListUi->Left;
707         coPos.Y = i;
708         FillConsoleOutputCharacterA(StdOutput,
709                                     0xB3, // '|',
710                                     1,
711                                     coPos,
712                                     &Written);
713 
714         coPos.X = ListUi->Right;
715         FillConsoleOutputCharacterA(StdOutput,
716                                     0xB3, //'|',
717                                     1,
718                                     coPos,
719                                     &Written);
720     }
721 
722     /* Draw lower left corner */
723     coPos.X = ListUi->Left;
724     coPos.Y = ListUi->Bottom;
725     FillConsoleOutputCharacterA(StdOutput,
726                                 0xC0, // '+',
727                                 1,
728                                 coPos,
729                                 &Written);
730 
731     /* Draw lower edge */
732     coPos.X = ListUi->Left + 1;
733     coPos.Y = ListUi->Bottom;
734     if (LastLine - ListUi->Offset <= ListUi->Bottom - ListUi->Top - 2)
735     {
736         FillConsoleOutputCharacterA(StdOutput,
737                                     0xC4, // '-',
738                                     ListUi->Right - ListUi->Left - 1,
739                                     coPos,
740                                     &Written);
741     }
742     else
743     {
744         FillConsoleOutputCharacterA(StdOutput,
745                                     0xC4, // '-',
746                                     ListUi->Right - ListUi->Left - 5,
747                                     coPos,
748                                     &Written);
749         coPos.X = ListUi->Right - 5;
750         WriteConsoleOutputCharacterA(StdOutput,
751                                      "(\x19)", // "(down)"
752                                      3,
753                                      coPos,
754                                      &Written);
755        coPos.X = ListUi->Right - 2;
756        FillConsoleOutputCharacterA(StdOutput,
757                                    0xC4, // '-',
758                                    2,
759                                    coPos,
760                                    &Written);
761     }
762 
763     /* Draw lower right corner */
764     coPos.X = ListUi->Right;
765     coPos.Y = ListUi->Bottom;
766     FillConsoleOutputCharacterA(StdOutput,
767                                 0xD9, // '+',
768                                 1,
769                                 coPos,
770                                 &Written);
771 
772     /* print list entries */
773     ListUi->Line = - ListUi->Offset;
774 
775     Entry = List->DiskListHead.Flink;
776     while (Entry != &List->DiskListHead)
777     {
778         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
779 
780         /* Print disk entry */
781         PrintDiskData(ListUi, DiskEntry);
782 
783         Entry = Entry->Flink;
784     }
785 }
786 
787 VOID
788 ScrollDownPartitionList(
789     IN PPARTLIST_UI ListUi)
790 {
791     if (GetNextPartition(ListUi->List))
792         DrawPartitionList(ListUi);
793 }
794 
795 VOID
796 ScrollUpPartitionList(
797     IN PPARTLIST_UI ListUi)
798 {
799     if (GetPrevPartition(ListUi->List))
800         DrawPartitionList(ListUi);
801 }
802 
803 /* EOF */
804