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