xref: /reactos/base/setup/usetup/partlist.c (revision 40462c92)
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 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            base/setup/usetup/partlist.c
23  * PURPOSE:         Partition list functions
24  * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
25  */
26 
27 #include "usetup.h"
28 
29 #define NDEBUG
30 #include <debug.h>
31 
32 /* HELPERS FOR PARTITION TYPES **********************************************/
33 
34 VOID
35 GetPartTypeStringFromPartitionType(
36     IN UCHAR partitionType,
37     OUT PCHAR strPartType,
38     IN ULONG cchPartType)
39 {
40     /* Determine partition type */
41 
42     if (IsContainerPartition(partitionType))
43     {
44         RtlStringCchCopyA(strPartType, cchPartType, MUIGetString(STRING_EXTENDED_PARTITION));
45     }
46     else if (partitionType == PARTITION_ENTRY_UNUSED)
47     {
48         RtlStringCchCopyA(strPartType, cchPartType, MUIGetString(STRING_FORMATUNUSED));
49     }
50     else
51     {
52         UINT i;
53 
54         /* Do the table lookup */
55         for (i = 0; i < ARRAYSIZE(PartitionTypes); i++)
56         {
57             if (partitionType == PartitionTypes[i].Type)
58             {
59                 RtlStringCchCopyA(strPartType, cchPartType, PartitionTypes[i].Description);
60                 return;
61             }
62         }
63 
64         /* We are here because the partition type is unknown */
65         RtlStringCchCopyA(strPartType, cchPartType, MUIGetString(STRING_FORMATUNKNOWN));
66     }
67 }
68 
69 
70 /* FUNCTIONS ****************************************************************/
71 
72 VOID
73 InitPartitionListUi(
74     IN OUT PPARTLIST_UI ListUi,
75     IN PPARTLIST List,
76     IN PPARTENTRY CurrentEntry OPTIONAL,
77     IN SHORT Left,
78     IN SHORT Top,
79     IN SHORT Right,
80     IN SHORT Bottom)
81 {
82     ListUi->List = List;
83     // ListUi->FirstShown = NULL;
84     // ListUi->LastShown = NULL;
85 
86     ListUi->Left = Left;
87     ListUi->Top = Top;
88     ListUi->Right = Right;
89     ListUi->Bottom = Bottom;
90 
91     ListUi->Line = 0;
92     ListUi->Offset = 0;
93 
94     // ListUi->Redraw = TRUE;
95 
96     /* Search for first usable disk and partition */
97     if (!CurrentEntry)
98     {
99         ListUi->CurrentDisk = NULL;
100         ListUi->CurrentPartition = NULL;
101 
102         if (!IsListEmpty(&List->DiskListHead))
103         {
104             ListUi->CurrentDisk = CONTAINING_RECORD(List->DiskListHead.Flink,
105                                                     DISKENTRY, ListEntry);
106 
107             if (!IsListEmpty(&ListUi->CurrentDisk->PrimaryPartListHead))
108             {
109                 ListUi->CurrentPartition = CONTAINING_RECORD(ListUi->CurrentDisk->PrimaryPartListHead.Flink,
110                                                              PARTENTRY, ListEntry);
111             }
112         }
113     }
114     else
115     {
116         /*
117          * The CurrentEntry must belong to the associated partition list,
118          * and the latter must therefore not be empty.
119          */
120         ASSERT(!IsListEmpty(&List->DiskListHead));
121         ASSERT(CurrentEntry->DiskEntry->PartList == List);
122 
123         ListUi->CurrentPartition = CurrentEntry;
124         ListUi->CurrentDisk = CurrentEntry->DiskEntry;
125     }
126 }
127 
128 static
129 VOID
130 PrintEmptyLine(
131     IN PPARTLIST_UI ListUi)
132 {
133     COORD coPos;
134     ULONG Written;
135     USHORT Width;
136     USHORT Height;
137 
138     Width = ListUi->Right - ListUi->Left - 1;
139     Height = ListUi->Bottom - ListUi->Top - 2;
140 
141     coPos.X = ListUi->Left + 1;
142     coPos.Y = ListUi->Top + 1 + ListUi->Line;
143 
144     if (ListUi->Line >= 0 && ListUi->Line <= Height)
145     {
146         FillConsoleOutputAttribute(StdOutput,
147                                    FOREGROUND_WHITE | BACKGROUND_BLUE,
148                                    Width,
149                                    coPos,
150                                    &Written);
151 
152         FillConsoleOutputCharacterA(StdOutput,
153                                     ' ',
154                                     Width,
155                                     coPos,
156                                     &Written);
157     }
158 
159     ListUi->Line++;
160 }
161 
162 static
163 VOID
164 PrintPartitionData(
165     IN PPARTLIST_UI ListUi,
166     IN PDISKENTRY DiskEntry,
167     IN PPARTENTRY PartEntry)
168 {
169     CHAR LineBuffer[128];
170     COORD coPos;
171     ULONG Written;
172     USHORT Width;
173     USHORT Height;
174     LARGE_INTEGER PartSize;
175     PCHAR Unit;
176     UCHAR Attribute;
177     CHAR PartTypeString[32];
178     PCHAR PartType = PartTypeString;
179 
180     Width = ListUi->Right - ListUi->Left - 1;
181     Height = ListUi->Bottom - ListUi->Top - 2;
182 
183     coPos.X = ListUi->Left + 1;
184     coPos.Y = ListUi->Top + 1 + ListUi->Line;
185 
186     /* Get the partition size */
187     PartSize.QuadPart = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
188 #if 0
189     if (PartSize.QuadPart >= 10 * GB) /* 10 GB */
190     {
191         PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, GB);
192         Unit = MUIGetString(STRING_GB);
193     }
194     else
195 #endif
196     if (PartSize.QuadPart >= 10 * MB) /* 10 MB */
197     {
198         PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, MB);
199         Unit = MUIGetString(STRING_MB);
200     }
201     else
202     {
203         PartSize.QuadPart = RoundingDivide(PartSize.QuadPart, KB);
204         Unit = MUIGetString(STRING_KB);
205     }
206 
207     if (PartEntry->IsPartitioned == FALSE)
208     {
209         sprintf(LineBuffer,
210                 MUIGetString(STRING_UNPSPACE),
211                 PartEntry->LogicalPartition ? "  " : "",
212                 PartEntry->LogicalPartition ? "" : "  ",
213                 PartSize.u.LowPart,
214                 Unit);
215     }
216     else
217     {
218         /* Determine partition type */
219         PartTypeString[0] = '\0';
220         if (PartEntry->New != FALSE)
221         {
222             PartType = MUIGetString(STRING_UNFORMATTED);
223         }
224         else if (PartEntry->IsPartitioned != FALSE)
225         {
226             GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
227                                                PartTypeString,
228                                                ARRAYSIZE(PartTypeString));
229             PartType = PartTypeString;
230         }
231 
232         if (strcmp(PartType, MUIGetString(STRING_FORMATUNKNOWN)) == 0)
233         {
234             sprintf(LineBuffer,
235                     MUIGetString(STRING_HDDINFOUNK5),
236                     (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
237                     (PartEntry->DriveLetter == 0) ? '-' : ':',
238                     PartEntry->BootIndicator ? '*' : ' ',
239                     PartEntry->LogicalPartition ? "  " : "",
240                     PartEntry->PartitionType,
241                     PartEntry->LogicalPartition ? "" : "  ",
242                     PartSize.u.LowPart,
243                     Unit);
244         }
245         else
246         {
247             sprintf(LineBuffer,
248                     "%c%c %c %s%-24s%s     %6lu %s",
249                     (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
250                     (PartEntry->DriveLetter == 0) ? '-' : ':',
251                     PartEntry->BootIndicator ? '*' : ' ',
252                     PartEntry->LogicalPartition ? "  " : "",
253                     PartType,
254                     PartEntry->LogicalPartition ? "" : "  ",
255                     PartSize.u.LowPart,
256                     Unit);
257         }
258     }
259 
260     Attribute = (ListUi->CurrentDisk == DiskEntry &&
261                  ListUi->CurrentPartition == PartEntry) ?
262                  FOREGROUND_BLUE | BACKGROUND_WHITE :
263                  FOREGROUND_WHITE | BACKGROUND_BLUE;
264 
265     if (ListUi->Line >= 0 && ListUi->Line <= Height)
266     {
267         FillConsoleOutputCharacterA(StdOutput,
268                                     ' ',
269                                     Width,
270                                     coPos,
271                                     &Written);
272     }
273     coPos.X += 4;
274     Width -= 8;
275     if (ListUi->Line >= 0 && ListUi->Line <= Height)
276     {
277         FillConsoleOutputAttribute(StdOutput,
278                                    Attribute,
279                                    Width,
280                                    coPos,
281                                    &Written);
282     }
283     coPos.X++;
284     Width -= 2;
285     if (ListUi->Line >= 0 && ListUi->Line <= Height)
286     {
287         WriteConsoleOutputCharacterA(StdOutput,
288                                      LineBuffer,
289                                      min(strlen(LineBuffer), Width),
290                                      coPos,
291                                      &Written);
292     }
293 
294     ListUi->Line++;
295 }
296 
297 static
298 VOID
299 PrintDiskData(
300     IN PPARTLIST_UI ListUi,
301     IN PDISKENTRY DiskEntry)
302 {
303     PPARTENTRY PrimaryPartEntry, LogicalPartEntry;
304     PLIST_ENTRY PrimaryEntry, LogicalEntry;
305     CHAR LineBuffer[128];
306     COORD coPos;
307     ULONG Written;
308     USHORT Width;
309     USHORT Height;
310     ULARGE_INTEGER DiskSize;
311     PCHAR Unit;
312 
313     Width = ListUi->Right - ListUi->Left - 1;
314     Height = ListUi->Bottom - ListUi->Top - 2;
315 
316     coPos.X = ListUi->Left + 1;
317     coPos.Y = ListUi->Top + 1 + ListUi->Line;
318 
319     DiskSize.QuadPart = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
320     if (DiskSize.QuadPart >= 10 * GB) /* 10 GB */
321     {
322         DiskSize.QuadPart = RoundingDivide(DiskSize.QuadPart, GB);
323         Unit = MUIGetString(STRING_GB);
324     }
325     else
326     {
327         DiskSize.QuadPart = RoundingDivide(DiskSize.QuadPart, MB);
328         if (DiskSize.QuadPart == 0)
329             DiskSize.QuadPart = 1;
330         Unit = MUIGetString(STRING_MB);
331     }
332 
333     //
334     // FIXME: We *MUST* use TXTSETUP.SIF strings from section "DiskDriverMap" !!
335     //
336     if (DiskEntry->DriverName.Length > 0)
337     {
338         sprintf(LineBuffer,
339                 MUIGetString(STRING_HDINFOPARTSELECT_1),
340                 DiskSize.u.LowPart,
341                 Unit,
342                 DiskEntry->DiskNumber,
343                 DiskEntry->Port,
344                 DiskEntry->Bus,
345                 DiskEntry->Id,
346                 &DiskEntry->DriverName,
347                 DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
348                 DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
349                                                               "RAW");
350     }
351     else
352     {
353         sprintf(LineBuffer,
354                 MUIGetString(STRING_HDINFOPARTSELECT_2),
355                 DiskSize.u.LowPart,
356                 Unit,
357                 DiskEntry->DiskNumber,
358                 DiskEntry->Port,
359                 DiskEntry->Bus,
360                 DiskEntry->Id,
361                 DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
362                 DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
363                                                               "RAW");
364     }
365 
366     if (ListUi->Line >= 0 && ListUi->Line <= Height)
367     {
368         FillConsoleOutputAttribute(StdOutput,
369                                    FOREGROUND_WHITE | BACKGROUND_BLUE,
370                                    Width,
371                                    coPos,
372                                    &Written);
373 
374         FillConsoleOutputCharacterA(StdOutput,
375                                     ' ',
376                                     Width,
377                                     coPos,
378                                     &Written);
379     }
380 
381     coPos.X++;
382     if (ListUi->Line >= 0 && ListUi->Line <= Height)
383     {
384         WriteConsoleOutputCharacterA(StdOutput,
385                                      LineBuffer,
386                                      min((USHORT)strlen(LineBuffer), Width - 2),
387                                      coPos,
388                                      &Written);
389     }
390 
391     ListUi->Line++;
392 
393     /* Print separator line */
394     PrintEmptyLine(ListUi);
395 
396     /* Print partition lines */
397     for (PrimaryEntry = DiskEntry->PrimaryPartListHead.Flink;
398          PrimaryEntry != &DiskEntry->PrimaryPartListHead;
399          PrimaryEntry = PrimaryEntry->Flink)
400     {
401         PrimaryPartEntry = CONTAINING_RECORD(PrimaryEntry, PARTENTRY, ListEntry);
402 
403         PrintPartitionData(ListUi,
404                            DiskEntry,
405                            PrimaryPartEntry);
406 
407         if (IsContainerPartition(PrimaryPartEntry->PartitionType))
408         {
409             for (LogicalEntry = DiskEntry->LogicalPartListHead.Flink;
410                  LogicalEntry != &DiskEntry->LogicalPartListHead;
411                  LogicalEntry = LogicalEntry->Flink)
412             {
413                 LogicalPartEntry = CONTAINING_RECORD(LogicalEntry, PARTENTRY, ListEntry);
414 
415                 PrintPartitionData(ListUi,
416                                    DiskEntry,
417                                    LogicalPartEntry);
418             }
419         }
420     }
421 
422     /* Print separator line */
423     PrintEmptyLine(ListUi);
424 }
425 
426 VOID
427 DrawPartitionList(
428     IN PPARTLIST_UI ListUi)
429 {
430     PPARTLIST List = ListUi->List;
431     PLIST_ENTRY Entry, Entry2;
432     PDISKENTRY DiskEntry;
433     PPARTENTRY PartEntry = NULL;
434     COORD coPos;
435     ULONG Written;
436     USHORT Width;
437     USHORT Height;
438     SHORT i;
439     SHORT CurrentDiskLine;
440     SHORT CurrentPartLine;
441     SHORT LastLine;
442     BOOLEAN CurrentPartLineFound = FALSE;
443     BOOLEAN CurrentDiskLineFound = FALSE;
444 
445     Width = ListUi->Right - ListUi->Left - 1;
446     Height = ListUi->Bottom - ListUi->Top - 2;
447 
448     /* Calculate the line of the current disk and partition */
449     CurrentDiskLine = 0;
450     CurrentPartLine = 0;
451     LastLine = 0;
452 
453     for (Entry = List->DiskListHead.Flink;
454          Entry != &List->DiskListHead;
455          Entry = Entry->Flink)
456     {
457         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
458 
459         LastLine += 2;
460         if (CurrentPartLineFound == FALSE)
461         {
462             CurrentPartLine += 2;
463         }
464 
465         for (Entry2 = DiskEntry->PrimaryPartListHead.Flink;
466              Entry2 != &DiskEntry->PrimaryPartListHead;
467              Entry2 = Entry2->Flink)
468         {
469             PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
470             if (PartEntry == ListUi->CurrentPartition)
471             {
472                 CurrentPartLineFound = TRUE;
473             }
474 
475             if (CurrentPartLineFound == FALSE)
476             {
477                 CurrentPartLine++;
478             }
479 
480             LastLine++;
481         }
482 
483         if (CurrentPartLineFound == FALSE)
484         {
485             for (Entry2 = DiskEntry->LogicalPartListHead.Flink;
486                  Entry2 != &DiskEntry->LogicalPartListHead;
487                  Entry2 = Entry2->Flink)
488             {
489                 PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry);
490                 if (PartEntry == ListUi->CurrentPartition)
491                 {
492                     CurrentPartLineFound = TRUE;
493                 }
494 
495                 if (CurrentPartLineFound == FALSE)
496                 {
497                     CurrentPartLine++;
498                 }
499 
500                 LastLine++;
501             }
502         }
503 
504         if (DiskEntry == ListUi->CurrentDisk)
505         {
506             CurrentDiskLineFound = TRUE;
507         }
508 
509         if (Entry->Flink != &List->DiskListHead)
510         {
511             if (CurrentDiskLineFound == FALSE)
512             {
513                 CurrentPartLine++;
514                 CurrentDiskLine = CurrentPartLine;
515             }
516 
517             LastLine++;
518         }
519         else
520         {
521             LastLine--;
522         }
523     }
524 
525     /* If it possible, make the disk name visible */
526     if (CurrentPartLine < ListUi->Offset)
527     {
528         ListUi->Offset = CurrentPartLine;
529     }
530     else if (CurrentPartLine - ListUi->Offset > Height)
531     {
532         ListUi->Offset = CurrentPartLine - Height;
533     }
534 
535     if (CurrentDiskLine < ListUi->Offset && CurrentPartLine - CurrentDiskLine < Height)
536     {
537         ListUi->Offset = CurrentDiskLine;
538     }
539 
540     /* Draw upper left corner */
541     coPos.X = ListUi->Left;
542     coPos.Y = ListUi->Top;
543     FillConsoleOutputCharacterA(StdOutput,
544                                 0xDA, // '+',
545                                 1,
546                                 coPos,
547                                 &Written);
548 
549     /* Draw upper edge */
550     coPos.X = ListUi->Left + 1;
551     coPos.Y = ListUi->Top;
552     if (ListUi->Offset == 0)
553     {
554         FillConsoleOutputCharacterA(StdOutput,
555                                     0xC4, // '-',
556                                     Width,
557                                     coPos,
558                                     &Written);
559     }
560     else
561     {
562         FillConsoleOutputCharacterA(StdOutput,
563                                     0xC4, // '-',
564                                     Width - 4,
565                                     coPos,
566                                     &Written);
567         coPos.X = ListUi->Right - 5;
568         WriteConsoleOutputCharacterA(StdOutput,
569                                      "(\x18)", // "(up)"
570                                      3,
571                                      coPos,
572                                      &Written);
573         coPos.X = ListUi->Right - 2;
574         FillConsoleOutputCharacterA(StdOutput,
575                                     0xC4, // '-',
576                                     2,
577                                     coPos,
578                                     &Written);
579     }
580 
581     /* Draw upper right corner */
582     coPos.X = ListUi->Right;
583     coPos.Y = ListUi->Top;
584     FillConsoleOutputCharacterA(StdOutput,
585                                 0xBF, // '+',
586                                 1,
587                                 coPos,
588                                 &Written);
589 
590     /* Draw left and right edge */
591     for (i = ListUi->Top + 1; i < ListUi->Bottom; i++)
592     {
593         coPos.X = ListUi->Left;
594         coPos.Y = i;
595         FillConsoleOutputCharacterA(StdOutput,
596                                     0xB3, // '|',
597                                     1,
598                                     coPos,
599                                     &Written);
600 
601         coPos.X = ListUi->Right;
602         FillConsoleOutputCharacterA(StdOutput,
603                                     0xB3, //'|',
604                                     1,
605                                     coPos,
606                                     &Written);
607     }
608 
609     /* Draw lower left corner */
610     coPos.X = ListUi->Left;
611     coPos.Y = ListUi->Bottom;
612     FillConsoleOutputCharacterA(StdOutput,
613                                 0xC0, // '+',
614                                 1,
615                                 coPos,
616                                 &Written);
617 
618     /* Draw lower edge */
619     coPos.X = ListUi->Left + 1;
620     coPos.Y = ListUi->Bottom;
621     if (LastLine - ListUi->Offset <= Height)
622     {
623         FillConsoleOutputCharacterA(StdOutput,
624                                     0xC4, // '-',
625                                     Width,
626                                     coPos,
627                                     &Written);
628     }
629     else
630     {
631         FillConsoleOutputCharacterA(StdOutput,
632                                     0xC4, // '-',
633                                     Width - 4,
634                                     coPos,
635                                     &Written);
636         coPos.X = ListUi->Right - 5;
637         WriteConsoleOutputCharacterA(StdOutput,
638                                      "(\x19)", // "(down)"
639                                      3,
640                                      coPos,
641                                      &Written);
642        coPos.X = ListUi->Right - 2;
643        FillConsoleOutputCharacterA(StdOutput,
644                                    0xC4, // '-',
645                                    2,
646                                    coPos,
647                                    &Written);
648     }
649 
650     /* Draw lower right corner */
651     coPos.X = ListUi->Right;
652     coPos.Y = ListUi->Bottom;
653     FillConsoleOutputCharacterA(StdOutput,
654                                 0xD9, // '+',
655                                 1,
656                                 coPos,
657                                 &Written);
658 
659     /* Print list entries */
660     ListUi->Line = -ListUi->Offset;
661 
662     for (Entry = List->DiskListHead.Flink;
663          Entry != &List->DiskListHead;
664          Entry = Entry->Flink)
665     {
666         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
667 
668         /* Print disk entry */
669         PrintDiskData(ListUi, DiskEntry);
670     }
671 }
672 
673 VOID
674 ScrollDownPartitionList(
675     IN PPARTLIST_UI ListUi)
676 {
677     PPARTENTRY NextPart = GetNextPartition(ListUi->List, ListUi->CurrentPartition);
678     if (NextPart)
679     {
680         ListUi->CurrentPartition = NextPart;
681         ListUi->CurrentDisk = NextPart->DiskEntry;
682         DrawPartitionList(ListUi);
683     }
684 }
685 
686 VOID
687 ScrollUpPartitionList(
688     IN PPARTLIST_UI ListUi)
689 {
690     PPARTENTRY PrevPart = GetPrevPartition(ListUi->List, ListUi->CurrentPartition);
691     if (PrevPart)
692     {
693         ListUi->CurrentPartition = PrevPart;
694         ListUi->CurrentDisk = PrevPart->DiskEntry;
695         DrawPartitionList(ListUi);
696     }
697 }
698 
699 /* EOF */
700