xref: /reactos/base/setup/usetup/usetup.c (revision 62919904)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002, 2003, 2004 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/usetup.c
23  * PURPOSE:         Text-mode setup
24  * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
25  *                  Hervé Poussineau (hpoussin@reactos.org)
26  */
27 
28 #include <usetup.h>
29 #include <math.h>
30 #include <ntstrsafe.h>
31 
32 #include "bootsup.h"
33 #include "chkdsk.h"
34 #include "cmdcons.h"
35 #include "devinst.h"
36 #include "format.h"
37 
38 #define NDEBUG
39 #include <debug.h>
40 
41 
42 /* GLOBALS & LOCALS *********************************************************/
43 
44 HANDLE ProcessHeap;
45 BOOLEAN IsUnattendedSetup = FALSE;
46 
47 static USETUP_DATA USetupData;
48 
49 /* The partition where to perform the installation */
50 static PPARTENTRY InstallPartition = NULL;
51 /*
52  * The system partition we will actually use. It can be different from
53  * PartitionList->SystemPartition in case we don't support it, or we install
54  * on a removable disk.
55  * We may indeed not support the original system partition in case we do not
56  * have write support on it. Please note that this situation is partly a HACK
57  * and MUST NEVER happen on architectures where real system partitions are
58  * mandatory (because then they are formatted in FAT FS and we support write
59  * operation on them).
60  */
61 static PPARTENTRY SystemPartition = NULL;
62 
63 
64 /* OTHER Stuff *****/
65 
66 PCWSTR SelectedLanguageId;
67 static WCHAR DefaultLanguage[20];   // Copy of string inside LanguageList
68 static WCHAR DefaultKBLayout[20];   // Copy of string inside KeyboardList
69 
70 static BOOLEAN RepairUpdateFlag = FALSE;
71 
72 /* Global partition list on the system */
73 static PPARTLIST PartitionList = NULL;
74 
75 /* Currently selected partition entry in the list */
76 static PPARTENTRY CurrentPartition = NULL;
77 
78 /* List of supported file systems for the partition to be formatted */
79 static PFILE_SYSTEM_LIST FileSystemList = NULL;
80 
81 /* Machine state for the formatter */
82 static PPARTENTRY TempPartition = NULL;
83 static FORMATMACHINESTATE FormatState = Start;
84 
85 /*****************************************************/
86 
87 static PNTOS_INSTALLATION CurrentInstallation = NULL;
88 static PGENERIC_LIST NtOsInstallsList = NULL;
89 
90 
91 /* FUNCTIONS ****************************************************************/
92 
93 static VOID
94 PrintString(char* fmt,...)
95 {
96     char buffer[512];
97     va_list ap;
98     UNICODE_STRING UnicodeString;
99     ANSI_STRING AnsiString;
100 
101     va_start(ap, fmt);
102     vsprintf(buffer, fmt, ap);
103     va_end(ap);
104 
105     RtlInitAnsiString(&AnsiString, buffer);
106     RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
107     NtDisplayString(&UnicodeString);
108     RtlFreeUnicodeString(&UnicodeString);
109 }
110 
111 
112 static VOID
113 DrawBox(IN SHORT xLeft,
114         IN SHORT yTop,
115         IN SHORT Width,
116         IN SHORT Height)
117 {
118     COORD coPos;
119     DWORD Written;
120 
121     /* Draw upper left corner */
122     coPos.X = xLeft;
123     coPos.Y = yTop;
124     FillConsoleOutputCharacterA(StdOutput,
125                                 0xDA, // '+',
126                                 1,
127                                 coPos,
128                                 &Written);
129 
130     /* Draw upper edge */
131     coPos.X = xLeft + 1;
132     coPos.Y = yTop;
133     FillConsoleOutputCharacterA(StdOutput,
134                                 0xC4, // '-',
135                                 Width - 2,
136                                 coPos,
137                                 &Written);
138 
139     /* Draw upper right corner */
140     coPos.X = xLeft + Width - 1;
141     coPos.Y = yTop;
142     FillConsoleOutputCharacterA(StdOutput,
143                                 0xBF, // '+',
144                                 1,
145                                 coPos,
146                                 &Written);
147 
148     /* Draw right edge, inner space and left edge */
149     for (coPos.Y = yTop + 1; coPos.Y < yTop + Height - 1; coPos.Y++)
150     {
151         coPos.X = xLeft;
152         FillConsoleOutputCharacterA(StdOutput,
153                                     0xB3, // '|',
154                                     1,
155                                     coPos,
156                                     &Written);
157 
158         coPos.X = xLeft + 1;
159         FillConsoleOutputCharacterA(StdOutput,
160                                     ' ',
161                                     Width - 2,
162                                     coPos,
163                                     &Written);
164 
165         coPos.X = xLeft + Width - 1;
166         FillConsoleOutputCharacterA(StdOutput,
167                                     0xB3, // '|',
168                                     1,
169                                     coPos,
170                                     &Written);
171     }
172 
173     /* Draw lower left corner */
174     coPos.X = xLeft;
175     coPos.Y = yTop + Height - 1;
176     FillConsoleOutputCharacterA(StdOutput,
177                                 0xC0, // '+',
178                                 1,
179                                 coPos,
180                                 &Written);
181 
182     /* Draw lower edge */
183     coPos.X = xLeft + 1;
184     coPos.Y = yTop + Height - 1;
185     FillConsoleOutputCharacterA(StdOutput,
186                                 0xC4, // '-',
187                                 Width - 2,
188                                 coPos,
189                                 &Written);
190 
191     /* Draw lower right corner */
192     coPos.X = xLeft + Width - 1;
193     coPos.Y = yTop + Height - 1;
194     FillConsoleOutputCharacterA(StdOutput,
195                                 0xD9, // '+',
196                                 1,
197                                 coPos,
198                                 &Written);
199 }
200 
201 
202 VOID
203 PopupError(PCCH Text,
204            PCCH Status,
205            PINPUT_RECORD Ir,
206            ULONG WaitEvent)
207 {
208     SHORT yTop;
209     SHORT xLeft;
210     COORD coPos;
211     DWORD Written;
212     ULONG Length;
213     ULONG MaxLength;
214     ULONG Lines;
215     PCHAR p;
216     PCCH pnext;
217     BOOLEAN LastLine;
218     SHORT Width;
219     SHORT Height;
220 
221     /* Count text lines and longest line */
222     MaxLength = 0;
223     Lines = 0;
224     pnext = Text;
225 
226     while (TRUE)
227     {
228         p = strchr(pnext, '\n');
229 
230         if (p == NULL)
231         {
232             Length = strlen(pnext);
233             LastLine = TRUE;
234         }
235         else
236         {
237             Length = (ULONG)(p - pnext);
238             LastLine = FALSE;
239         }
240 
241         Lines++;
242 
243         if (Length > MaxLength)
244             MaxLength = Length;
245 
246         if (LastLine)
247             break;
248 
249         pnext = p + 1;
250     }
251 
252     /* Check length of status line */
253     if (Status != NULL)
254     {
255         Length = strlen(Status);
256 
257         if (Length > MaxLength)
258             MaxLength = Length;
259     }
260 
261     Width = MaxLength + 4;
262     Height = Lines + 2;
263 
264     if (Status != NULL)
265         Height += 2;
266 
267     yTop = (yScreen - Height) / 2;
268     xLeft = (xScreen - Width) / 2;
269 
270 
271     /* Set screen attributes */
272     coPos.X = xLeft;
273     for (coPos.Y = yTop; coPos.Y < yTop + Height; coPos.Y++)
274     {
275         FillConsoleOutputAttribute(StdOutput,
276                                    FOREGROUND_RED | BACKGROUND_WHITE,
277                                    Width,
278                                    coPos,
279                                    &Written);
280     }
281 
282     DrawBox(xLeft, yTop, Width, Height);
283 
284     /* Print message text */
285     coPos.Y = yTop + 1;
286     pnext = Text;
287     while (TRUE)
288     {
289         p = strchr(pnext, '\n');
290 
291         if (p == NULL)
292         {
293             Length = strlen(pnext);
294             LastLine = TRUE;
295         }
296         else
297         {
298             Length = (ULONG)(p - pnext);
299             LastLine = FALSE;
300         }
301 
302         if (Length != 0)
303         {
304             coPos.X = xLeft + 2;
305             WriteConsoleOutputCharacterA(StdOutput,
306                                          pnext,
307                                          Length,
308                                          coPos,
309                                          &Written);
310         }
311 
312         if (LastLine)
313             break;
314 
315         coPos.Y++;
316         pnext = p + 1;
317     }
318 
319     /* Print separator line and status text */
320     if (Status != NULL)
321     {
322         coPos.Y = yTop + Height - 3;
323         coPos.X = xLeft;
324         FillConsoleOutputCharacterA(StdOutput,
325                                     0xC3, // '+',
326                                     1,
327                                     coPos,
328                                     &Written);
329 
330         coPos.X = xLeft + 1;
331         FillConsoleOutputCharacterA(StdOutput,
332                                     0xC4, // '-',
333                                     Width - 2,
334                                     coPos,
335                                     &Written);
336 
337         coPos.X = xLeft + Width - 1;
338         FillConsoleOutputCharacterA(StdOutput,
339                                     0xB4, // '+',
340                                     1,
341                                     coPos,
342                                     &Written);
343 
344         coPos.Y++;
345         coPos.X = xLeft + 2;
346         WriteConsoleOutputCharacterA(StdOutput,
347                                      Status,
348                                      min(strlen(Status), (SIZE_T)Width - 4),
349                                      coPos,
350                                      &Written);
351     }
352 
353     if (WaitEvent == POPUP_WAIT_NONE)
354         return;
355 
356     while (TRUE)
357     {
358         CONSOLE_ConInKey(Ir);
359 
360         if (WaitEvent == POPUP_WAIT_ANY_KEY ||
361             Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)
362         {
363             return;
364         }
365     }
366 }
367 
368 
369 /*
370  * Confirm quit setup
371  * RETURNS
372  *   TRUE: Quit setup.
373  *   FALSE: Don't quit setup.
374  */
375 static BOOL
376 ConfirmQuit(PINPUT_RECORD Ir)
377 {
378     BOOL Result = FALSE;
379     MUIDisplayError(ERROR_NOT_INSTALLED, NULL, POPUP_WAIT_NONE);
380 
381     while (TRUE)
382     {
383         CONSOLE_ConInKey(Ir);
384 
385         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
386             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
387         {
388             Result = TRUE;
389             break;
390         }
391         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
392         {
393             Result = FALSE;
394             break;
395         }
396     }
397 
398     return Result;
399 }
400 
401 
402 static VOID
403 UpdateKBLayout(VOID)
404 {
405     PGENERIC_LIST_ENTRY ListEntry;
406     PCWSTR pszNewLayout;
407 
408     pszNewLayout = MUIDefaultKeyboardLayout(SelectedLanguageId);
409 
410     if (USetupData.LayoutList == NULL)
411     {
412         USetupData.LayoutList = CreateKeyboardLayoutList(USetupData.SetupInf, SelectedLanguageId, DefaultKBLayout);
413         if (USetupData.LayoutList == NULL)
414         {
415             /* FIXME: Handle error! */
416             return;
417         }
418     }
419 
420     /* Search for default layout (if provided) */
421     if (pszNewLayout != NULL)
422     {
423         for (ListEntry = GetFirstListEntry(USetupData.LayoutList); ListEntry;
424              ListEntry = GetNextListEntry(ListEntry))
425         {
426             if (!wcscmp(pszNewLayout, ((PGENENTRY)GetListEntryData(ListEntry))->Id))
427             {
428                 SetCurrentListEntry(USetupData.LayoutList, ListEntry);
429                 break;
430             }
431         }
432     }
433 }
434 
435 
436 static NTSTATUS
437 NTAPI
438 GetSettingDescription(
439     IN PGENERIC_LIST_ENTRY Entry,
440     OUT PSTR Buffer,
441     IN SIZE_T cchBufferSize)
442 {
443     return RtlStringCchPrintfA(Buffer, cchBufferSize, "%S",
444                                ((PGENENTRY)GetListEntryData(Entry))->Value);
445 }
446 
447 static NTSTATUS
448 NTAPI
449 GetNTOSInstallationName(
450     IN PGENERIC_LIST_ENTRY Entry,
451     OUT PSTR Buffer,
452     IN SIZE_T cchBufferSize)
453 {
454     PNTOS_INSTALLATION NtOsInstall = (PNTOS_INSTALLATION)GetListEntryData(Entry);
455     PPARTENTRY PartEntry = NtOsInstall->PartEntry;
456 
457     if (PartEntry && PartEntry->DriveLetter)
458     {
459         /* We have retrieved a partition that is mounted */
460         return RtlStringCchPrintfA(Buffer, cchBufferSize,
461                                    "%C:%S  \"%S\"",
462                                    PartEntry->DriveLetter,
463                                    NtOsInstall->PathComponent,
464                                    NtOsInstall->InstallationName);
465     }
466     else
467     {
468         /* We failed somewhere, just show the NT path */
469         return RtlStringCchPrintfA(Buffer, cchBufferSize,
470                                    "%wZ  \"%S\"",
471                                    &NtOsInstall->SystemNtPath,
472                                    NtOsInstall->InstallationName);
473     }
474 }
475 
476 
477 /*
478  * Displays the LanguagePage.
479  *
480  * Next pages: WelcomePage, QuitPage
481  *
482  * SIDEEFFECTS
483  *  Init SelectedLanguageId
484  *  Init USetupData.LanguageId
485  *
486  * RETURNS
487  *   Number of the next page.
488  */
489 static PAGE_NUMBER
490 LanguagePage(PINPUT_RECORD Ir)
491 {
492     GENERIC_LIST_UI ListUi;
493     PCWSTR NewLanguageId;
494     BOOL RefreshPage = FALSE;
495 
496     /* Initialize the computer settings list */
497     if (USetupData.LanguageList == NULL)
498     {
499         USetupData.LanguageList = CreateLanguageList(USetupData.SetupInf, DefaultLanguage);
500         if (USetupData.LanguageList == NULL)
501         {
502            PopupError("Setup failed to initialize available translations", NULL, NULL, POPUP_WAIT_NONE);
503            return WELCOME_PAGE;
504         }
505     }
506 
507     SelectedLanguageId = DefaultLanguage;
508     USetupData.LanguageId = 0;
509 
510     /* Load the font */
511     SetConsoleCodePage();
512     UpdateKBLayout();
513 
514     /*
515      * If there is no language or just a single one in the list,
516      * skip the language selection process altogether.
517      */
518     if (GetNumberOfListEntries(USetupData.LanguageList) <= 1)
519     {
520         USetupData.LanguageId = (LANGID)(wcstol(SelectedLanguageId, NULL, 16) & 0xFFFF);
521         return WELCOME_PAGE;
522     }
523 
524     InitGenericListUi(&ListUi, USetupData.LanguageList, GetSettingDescription);
525     DrawGenericList(&ListUi,
526                     2, 18,
527                     xScreen - 3,
528                     yScreen - 3);
529 
530     ScrollToPositionGenericList(&ListUi, GetDefaultLanguageIndex());
531 
532     MUIDisplayPage(LANGUAGE_PAGE);
533 
534     while (TRUE)
535     {
536         CONSOLE_ConInKey(Ir);
537 
538         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
539             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
540         {
541             ScrollDownGenericList(&ListUi);
542             RefreshPage = TRUE;
543         }
544         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
545                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
546         {
547             ScrollUpGenericList(&ListUi);
548             RefreshPage = TRUE;
549         }
550         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
551             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_NEXT))  /* PAGE DOWN */
552         {
553             ScrollPageDownGenericList(&ListUi);
554             RefreshPage = TRUE;
555         }
556         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
557                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_PRIOR))  /* PAGE UP */
558         {
559             ScrollPageUpGenericList(&ListUi);
560             RefreshPage = TRUE;
561         }
562         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
563                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
564         {
565             if (ConfirmQuit(Ir))
566                 return QUIT_PAGE;
567             else
568                 RedrawGenericList(&ListUi);
569         }
570         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
571         {
572             ASSERT(GetNumberOfListEntries(USetupData.LanguageList) >= 1);
573 
574             SelectedLanguageId =
575                 ((PGENENTRY)GetListEntryData(GetCurrentListEntry(USetupData.LanguageList)))->Id;
576 
577             USetupData.LanguageId = (LANGID)(wcstol(SelectedLanguageId, NULL, 16) & 0xFFFF);
578 
579             if (wcscmp(SelectedLanguageId, DefaultLanguage))
580             {
581                 UpdateKBLayout();
582             }
583 
584             /* Load the font */
585             SetConsoleCodePage();
586 
587             return WELCOME_PAGE;
588         }
589         else if ((Ir->Event.KeyEvent.uChar.AsciiChar > 0x60) && (Ir->Event.KeyEvent.uChar.AsciiChar < 0x7b))
590         {
591             /* a-z */
592             GenericListKeyPress(&ListUi, Ir->Event.KeyEvent.uChar.AsciiChar);
593             RefreshPage = TRUE;
594         }
595 
596         if (RefreshPage)
597         {
598             ASSERT(GetNumberOfListEntries(USetupData.LanguageList) >= 1);
599 
600             NewLanguageId =
601                 ((PGENENTRY)GetListEntryData(GetCurrentListEntry(USetupData.LanguageList)))->Id;
602 
603             if (wcscmp(SelectedLanguageId, NewLanguageId))
604             {
605                 /* Clear the language page */
606                 MUIClearPage(LANGUAGE_PAGE);
607 
608                 SelectedLanguageId = NewLanguageId;
609 
610                 /* Load the font */
611                 SetConsoleCodePage();
612 
613                 /* Redraw language selection page in native language */
614                 MUIDisplayPage(LANGUAGE_PAGE);
615             }
616 
617             RefreshPage = FALSE;
618         }
619     }
620 
621     return WELCOME_PAGE;
622 }
623 
624 
625 /*
626  * Start page
627  *
628  * Next pages:
629  *  LanguagePage (at once, default)
630  *  InstallIntroPage (at once, if unattended)
631  *  QuitPage
632  *
633  * SIDEEFFECTS
634  *  Init Sdi
635  *  Init USetupData.SourcePath
636  *  Init USetupData.SourceRootPath
637  *  Init USetupData.SourceRootDir
638  *  Init USetupData.SetupInf
639  *  Init USetupData.RequiredPartitionDiskSpace
640  *  Init IsUnattendedSetup
641  *  If unattended, init *List and sets the Codepage
642  *  If unattended, init SelectedLanguageId
643  *  If unattended, init USetupData.LanguageId
644  *
645  * RETURNS
646  *   Number of the next page.
647  */
648 static PAGE_NUMBER
649 SetupStartPage(PINPUT_RECORD Ir)
650 {
651     ULONG Error;
652     PGENERIC_LIST_ENTRY ListEntry;
653     PCWSTR LocaleId;
654 
655     MUIDisplayPage(SETUP_INIT_PAGE);
656 
657     /* Initialize Setup, phase 1 */
658     Error = InitializeSetup(&USetupData, 1);
659     if (Error != ERROR_SUCCESS)
660     {
661         MUIDisplayError(Error, Ir, POPUP_WAIT_ENTER);
662         return QUIT_PAGE;
663     }
664 
665     /* Initialize the user-mode PnP manager */
666     if (!EnableUserModePnpManager())
667         DPRINT1("The user-mode PnP manager could not initialize, expect unavailable devices!\n");
668 
669     /* Wait for any immediate pending installations to finish */
670     if (WaitNoPendingInstallEvents(NULL) != STATUS_WAIT_0)
671         DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
672 
673     CheckUnattendedSetup(&USetupData);
674 
675     if (IsUnattendedSetup)
676     {
677         // TODO: Read options from inf
678         /* Load the hardware, language and keyboard layout lists */
679 
680         USetupData.ComputerList = CreateComputerTypeList(USetupData.SetupInf);
681         USetupData.DisplayList = CreateDisplayDriverList(USetupData.SetupInf);
682         USetupData.KeyboardList = CreateKeyboardDriverList(USetupData.SetupInf);
683 
684         USetupData.LanguageList = CreateLanguageList(USetupData.SetupInf, DefaultLanguage);
685 
686         /* new part */
687         SelectedLanguageId = DefaultLanguage;
688         wcscpy(DefaultLanguage, USetupData.LocaleID);
689         USetupData.LanguageId = (LANGID)(wcstol(SelectedLanguageId, NULL, 16) & 0xFFFF);
690 
691         USetupData.LayoutList = CreateKeyboardLayoutList(USetupData.SetupInf, SelectedLanguageId, DefaultKBLayout);
692 
693         /* first we hack LanguageList */
694         for (ListEntry = GetFirstListEntry(USetupData.LanguageList); ListEntry;
695              ListEntry = GetNextListEntry(ListEntry))
696         {
697             LocaleId = ((PGENENTRY)GetListEntryData(ListEntry))->Id;
698             if (!wcsicmp(USetupData.LocaleID, LocaleId))
699             {
700                 DPRINT("found %S in LanguageList\n", LocaleId);
701                 SetCurrentListEntry(USetupData.LanguageList, ListEntry);
702                 break;
703             }
704         }
705 
706         /* now LayoutList */
707         for (ListEntry = GetFirstListEntry(USetupData.LayoutList); ListEntry;
708              ListEntry = GetNextListEntry(ListEntry))
709         {
710             LocaleId = ((PGENENTRY)GetListEntryData(ListEntry))->Id;
711             if (!wcsicmp(USetupData.LocaleID, LocaleId))
712             {
713                 DPRINT("found %S in LayoutList\n", LocaleId);
714                 SetCurrentListEntry(USetupData.LayoutList, ListEntry);
715                 break;
716             }
717         }
718 
719         SetConsoleCodePage();
720 
721         return INSTALL_INTRO_PAGE;
722     }
723 
724     return LANGUAGE_PAGE;
725 }
726 
727 
728 /*
729  * Displays the WelcomePage.
730  *
731  * Next pages:
732  *  InstallIntroPage (default)
733  *  RepairIntroPage
734  *  RecoveryPage
735  *  LicensePage
736  *  QuitPage
737  *
738  * RETURNS
739  *   Number of the next page.
740  */
741 static PAGE_NUMBER
742 WelcomePage(PINPUT_RECORD Ir)
743 {
744     MUIDisplayPage(WELCOME_PAGE);
745 
746     while (TRUE)
747     {
748         CONSOLE_ConInKey(Ir);
749 
750         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
751             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
752         {
753             if (ConfirmQuit(Ir))
754                 return QUIT_PAGE;
755 
756             break;
757         }
758         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
759         {
760             return INSTALL_INTRO_PAGE;
761         }
762         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'R') /* R */
763         {
764             return RECOVERY_PAGE; // REPAIR_INTRO_PAGE;
765         }
766         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'L') /* L */
767         {
768             return LICENSE_PAGE;
769         }
770     }
771 
772     return WELCOME_PAGE;
773 }
774 
775 
776 /*
777  * Displays the License page.
778  *
779  * Next page:
780  *  WelcomePage (default)
781  *
782  * RETURNS
783  *   Number of the next page.
784  */
785 static PAGE_NUMBER
786 LicensePage(PINPUT_RECORD Ir)
787 {
788     MUIDisplayPage(LICENSE_PAGE);
789 
790     while (TRUE)
791     {
792         CONSOLE_ConInKey(Ir);
793 
794         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
795         {
796             return WELCOME_PAGE;
797         }
798     }
799 
800     return LICENSE_PAGE;
801 }
802 
803 
804 /*
805  * Displays the RepairIntroPage.
806  *
807  * Next pages:
808  *  RebootPage (default)
809  *  InstallIntroPage
810  *  RecoveryPage
811  *  IntroPage
812  *
813  * RETURNS
814  *   Number of the next page.
815  */
816 static PAGE_NUMBER
817 RepairIntroPage(PINPUT_RECORD Ir)
818 {
819     MUIDisplayPage(REPAIR_INTRO_PAGE);
820 
821     while (TRUE)
822     {
823         CONSOLE_ConInKey(Ir);
824 
825         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
826         {
827             return REBOOT_PAGE;
828         }
829         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'U')  /* U */
830         {
831             RepairUpdateFlag = TRUE;
832             return INSTALL_INTRO_PAGE;
833         }
834         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'R')  /* R */
835         {
836             return RECOVERY_PAGE;
837         }
838         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
839                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE))  /* ESC */
840         {
841             return WELCOME_PAGE;
842         }
843     }
844 
845     return REPAIR_INTRO_PAGE;
846 }
847 
848 /*
849  * Displays the UpgradeRepairPage.
850  *
851  * Next pages:
852  *  RebootPage (default)
853  *  InstallIntroPage
854  *  RecoveryPage
855  *  WelcomePage
856  *
857  * RETURNS
858  *   Number of the next page.
859  */
860 static PAGE_NUMBER
861 UpgradeRepairPage(PINPUT_RECORD Ir)
862 {
863     GENERIC_LIST_UI ListUi;
864 
865 /*** HACK!! ***/
866     if (PartitionList == NULL)
867     {
868         PartitionList = CreatePartitionList();
869         if (PartitionList == NULL)
870         {
871             /* FIXME: show an error dialog */
872             MUIDisplayError(ERROR_DRIVE_INFORMATION, Ir, POPUP_WAIT_ENTER);
873             return QUIT_PAGE;
874         }
875         else if (IsListEmpty(&PartitionList->DiskListHead))
876         {
877             MUIDisplayError(ERROR_NO_HDD, Ir, POPUP_WAIT_ENTER);
878             return QUIT_PAGE;
879         }
880 
881         /* Reset the formatter machine state */
882         TempPartition = NULL;
883         FormatState = Start;
884     }
885 /**************/
886 
887     NtOsInstallsList = CreateNTOSInstallationsList(PartitionList);
888     if (!NtOsInstallsList)
889         DPRINT1("Failed to get a list of NTOS installations; continue installation...\n");
890 
891     /*
892      * If there is no available installation (or just a single one??) that can
893      * be updated in the list, just continue with the regular installation.
894      */
895     if (!NtOsInstallsList || GetNumberOfListEntries(NtOsInstallsList) == 0)
896     {
897         RepairUpdateFlag = FALSE;
898 
899         // return INSTALL_INTRO_PAGE;
900         return DEVICE_SETTINGS_PAGE;
901         // return SCSI_CONTROLLER_PAGE;
902     }
903 
904     MUIDisplayPage(UPGRADE_REPAIR_PAGE);
905 
906     InitGenericListUi(&ListUi, NtOsInstallsList, GetNTOSInstallationName);
907     DrawGenericList(&ListUi,
908                     2, 23,
909                     xScreen - 3,
910                     yScreen - 3);
911 
912     // return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
913     while (TRUE)
914     {
915         CONSOLE_ConInKey(Ir);
916 
917         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00)
918         {
919             switch (Ir->Event.KeyEvent.wVirtualKeyCode)
920             {
921             case VK_DOWN:   /* DOWN */
922                 ScrollDownGenericList(&ListUi);
923                 break;
924             case VK_UP:     /* UP */
925                 ScrollUpGenericList(&ListUi);
926                 break;
927             case VK_NEXT:   /* PAGE DOWN */
928                 ScrollPageDownGenericList(&ListUi);
929                 break;
930             case VK_PRIOR:  /* PAGE UP */
931                 ScrollPageUpGenericList(&ListUi);
932                 break;
933             case VK_F3:     /* F3 */
934             {
935                 if (ConfirmQuit(Ir))
936                     return QUIT_PAGE;
937                 else
938                     RedrawGenericList(&ListUi);
939                 break;
940             }
941             case VK_ESCAPE: /* ESC */
942             {
943                 RestoreGenericListUiState(&ListUi);
944                 // return nextPage;    // prevPage;
945 
946                 // return INSTALL_INTRO_PAGE;
947                 return DEVICE_SETTINGS_PAGE;
948                 // return SCSI_CONTROLLER_PAGE;
949             }
950             }
951         }
952         else
953         {
954             // switch (toupper(Ir->Event.KeyEvent.uChar.AsciiChar))
955             // if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
956             if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'U')  /* U */
957             {
958                 /* Retrieve the current installation */
959                 ASSERT(GetNumberOfListEntries(NtOsInstallsList) >= 1);
960 
961                 CurrentInstallation =
962                     (PNTOS_INSTALLATION)GetListEntryData(GetCurrentListEntry(NtOsInstallsList));
963 
964                 DPRINT1("Selected installation for repair: \"%S\" ; DiskNumber = %d , PartitionNumber = %d\n",
965                         CurrentInstallation->InstallationName, CurrentInstallation->DiskNumber, CurrentInstallation->PartitionNumber);
966 
967                 RepairUpdateFlag = TRUE;
968 
969                 // return nextPage;
970                 /***/return INSTALL_INTRO_PAGE;/***/
971             }
972             else if ((Ir->Event.KeyEvent.uChar.AsciiChar > 0x60) &&
973                      (Ir->Event.KeyEvent.uChar.AsciiChar < 0x7b))   /* a-z */
974             {
975                 GenericListKeyPress(&ListUi, Ir->Event.KeyEvent.uChar.AsciiChar);
976             }
977         }
978     }
979 
980     return UPGRADE_REPAIR_PAGE;
981 }
982 
983 
984 /*
985  * Displays the InstallIntroPage.
986  *
987  * Next pages:
988  *  DeviceSettingsPage  (At once if repair or update is selected)
989  *  SelectPartitionPage (At once if unattended setup)
990  *  DeviceSettingsPage  (default)
991  *  QuitPage
992  *
993  * RETURNS
994  *   Number of the next page.
995  */
996 static PAGE_NUMBER
997 InstallIntroPage(PINPUT_RECORD Ir)
998 {
999     if (RepairUpdateFlag)
1000     {
1001 #if 1 /* Old code that looks good */
1002 
1003         // return SELECT_PARTITION_PAGE;
1004         return DEVICE_SETTINGS_PAGE;
1005 
1006 #else /* Possible new code? */
1007 
1008         return DEVICE_SETTINGS_PAGE;
1009         // return SCSI_CONTROLLER_PAGE;
1010 
1011 #endif
1012     }
1013 
1014     if (IsUnattendedSetup)
1015         return SELECT_PARTITION_PAGE;
1016 
1017     MUIDisplayPage(INSTALL_INTRO_PAGE);
1018 
1019     while (TRUE)
1020     {
1021         CONSOLE_ConInKey(Ir);
1022 
1023         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1024             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
1025         {
1026             if (ConfirmQuit(Ir))
1027                 return QUIT_PAGE;
1028 
1029             break;
1030         }
1031         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1032         {
1033             return UPGRADE_REPAIR_PAGE;
1034         }
1035     }
1036 
1037     return INSTALL_INTRO_PAGE;
1038 }
1039 
1040 
1041 #if 0
1042 static PAGE_NUMBER
1043 ScsiControllerPage(PINPUT_RECORD Ir)
1044 {
1045     // MUIDisplayPage(SCSI_CONTROLLER_PAGE);
1046 
1047     CONSOLE_SetTextXY(6, 8, "Setup detected the following mass storage devices:");
1048 
1049     /* FIXME: print loaded mass storage driver descriptions */
1050 #if 0
1051     CONSOLE_SetTextXY(8, 10, "TEST device");
1052 #endif
1053 
1054     CONSOLE_SetStatusText("   ENTER = Continue   F3 = Quit");
1055 
1056     while (TRUE)
1057     {
1058         CONSOLE_ConInKey(Ir);
1059 
1060         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1061             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
1062         {
1063             if (ConfirmQuit(Ir))
1064                 return QUIT_PAGE;
1065 
1066             break;
1067         }
1068         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1069         {
1070             return DEVICE_SETTINGS_PAGE;
1071         }
1072     }
1073 
1074     return SCSI_CONTROLLER_PAGE;
1075 }
1076 
1077 static PAGE_NUMBER
1078 OemDriverPage(PINPUT_RECORD Ir)
1079 {
1080     // MUIDisplayPage(OEM_DRIVER_PAGE);
1081 
1082     CONSOLE_SetTextXY(6, 8, "This is the OEM driver page!");
1083 
1084     /* FIXME: Implement!! */
1085 
1086     CONSOLE_SetStatusText("   ENTER = Continue   F3 = Quit");
1087 
1088     while (TRUE)
1089     {
1090         CONSOLE_ConInKey(Ir);
1091 
1092         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1093             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
1094         {
1095             if (ConfirmQuit(Ir))
1096                 return QUIT_PAGE;
1097 
1098             break;
1099         }
1100         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1101         {
1102             return DEVICE_SETTINGS_PAGE;
1103         }
1104     }
1105 
1106     return OEM_DRIVER_PAGE;
1107 }
1108 #endif
1109 
1110 
1111 /*
1112  * Displays the DeviceSettingsPage.
1113  *
1114  * Next pages:
1115  *  SelectPartitionPage (At once if repair or update is selected)
1116  *  ComputerSettingsPage
1117  *  DisplaySettingsPage
1118  *  KeyboardSettingsPage
1119  *  LayoutsettingsPage
1120  *  SelectPartitionPage
1121  *  QuitPage
1122  *
1123  * SIDEEFFECTS
1124  *  Init USetupData.ComputerList
1125  *  Init USetupData.DisplayList
1126  *  Init USetupData.KeyboardList
1127  *  Init USetupData.LayoutList
1128  *
1129  * RETURNS
1130  *   Number of the next page.
1131  */
1132 static PAGE_NUMBER
1133 DeviceSettingsPage(PINPUT_RECORD Ir)
1134 {
1135     static ULONG Line = 16;
1136 
1137     /* Initialize the computer settings list */
1138     if (USetupData.ComputerList == NULL)
1139     {
1140         USetupData.ComputerList = CreateComputerTypeList(USetupData.SetupInf);
1141         if (USetupData.ComputerList == NULL)
1142         {
1143             MUIDisplayError(ERROR_LOAD_COMPUTER, Ir, POPUP_WAIT_ENTER);
1144             return QUIT_PAGE;
1145         }
1146     }
1147 
1148     /* Initialize the display settings list */
1149     if (USetupData.DisplayList == NULL)
1150     {
1151         USetupData.DisplayList = CreateDisplayDriverList(USetupData.SetupInf);
1152         if (USetupData.DisplayList == NULL)
1153         {
1154             MUIDisplayError(ERROR_LOAD_DISPLAY, Ir, POPUP_WAIT_ENTER);
1155             return QUIT_PAGE;
1156         }
1157     }
1158 
1159     /* Initialize the keyboard settings list */
1160     if (USetupData.KeyboardList == NULL)
1161     {
1162         USetupData.KeyboardList = CreateKeyboardDriverList(USetupData.SetupInf);
1163         if (USetupData.KeyboardList == NULL)
1164         {
1165             MUIDisplayError(ERROR_LOAD_KEYBOARD, Ir, POPUP_WAIT_ENTER);
1166             return QUIT_PAGE;
1167         }
1168     }
1169 
1170     /* Initialize the keyboard layout list */
1171     if (USetupData.LayoutList == NULL)
1172     {
1173         USetupData.LayoutList = CreateKeyboardLayoutList(USetupData.SetupInf, SelectedLanguageId, DefaultKBLayout);
1174         if (USetupData.LayoutList == NULL)
1175         {
1176             /* FIXME: report error */
1177             MUIDisplayError(ERROR_LOAD_KBLAYOUT, Ir, POPUP_WAIT_ENTER);
1178             return QUIT_PAGE;
1179         }
1180     }
1181 
1182     if (RepairUpdateFlag)
1183         return SELECT_PARTITION_PAGE;
1184 
1185     // if (IsUnattendedSetup)
1186         // return SELECT_PARTITION_PAGE;
1187 
1188     MUIDisplayPage(DEVICE_SETTINGS_PAGE);
1189 
1190     DrawGenericListCurrentItem(USetupData.ComputerList, GetSettingDescription, 25, 11);
1191     DrawGenericListCurrentItem(USetupData.DisplayList , GetSettingDescription, 25, 12);
1192     DrawGenericListCurrentItem(USetupData.KeyboardList, GetSettingDescription, 25, 13);
1193     DrawGenericListCurrentItem(USetupData.LayoutList  , GetSettingDescription, 25, 14);
1194 
1195     CONSOLE_InvertTextXY(24, Line, 48, 1);
1196 
1197     while (TRUE)
1198     {
1199         CONSOLE_ConInKey(Ir);
1200 
1201         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1202             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
1203         {
1204             CONSOLE_NormalTextXY(24, Line, 48, 1);
1205 
1206             if (Line == 14)
1207                 Line = 16;
1208             else if (Line == 16)
1209                 Line = 11;
1210             else
1211                 Line++;
1212 
1213             CONSOLE_InvertTextXY(24, Line, 48, 1);
1214         }
1215         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1216                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
1217         {
1218             CONSOLE_NormalTextXY(24, Line, 48, 1);
1219 
1220             if (Line == 11)
1221                 Line = 16;
1222             else if (Line == 16)
1223                 Line = 14;
1224             else
1225                 Line--;
1226 
1227             CONSOLE_InvertTextXY(24, Line, 48, 1);
1228         }
1229         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1230                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1231         {
1232             if (ConfirmQuit(Ir))
1233                 return QUIT_PAGE;
1234 
1235             break;
1236         }
1237         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1238         {
1239             if (Line == 11)
1240                 return COMPUTER_SETTINGS_PAGE;
1241             else if (Line == 12)
1242                 return DISPLAY_SETTINGS_PAGE;
1243             else if (Line == 13)
1244                 return KEYBOARD_SETTINGS_PAGE;
1245             else if (Line == 14)
1246                 return LAYOUT_SETTINGS_PAGE;
1247             else if (Line == 16)
1248                 return SELECT_PARTITION_PAGE;
1249         }
1250     }
1251 
1252     return DEVICE_SETTINGS_PAGE;
1253 }
1254 
1255 
1256 /*
1257  * Handles generic selection lists.
1258  *
1259  * PARAMS
1260  * GenericList: The list to handle.
1261  * nextPage: The page it needs to jump to after this page.
1262  * Ir: The PINPUT_RECORD
1263  */
1264 static PAGE_NUMBER
1265 HandleGenericList(PGENERIC_LIST_UI ListUi,
1266                   PAGE_NUMBER nextPage,
1267                   PINPUT_RECORD Ir)
1268 {
1269     while (TRUE)
1270     {
1271         CONSOLE_ConInKey(Ir);
1272 
1273         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1274             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
1275         {
1276             ScrollDownGenericList(ListUi);
1277         }
1278         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1279                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
1280         {
1281             ScrollUpGenericList(ListUi);
1282         }
1283         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1284                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_NEXT))  /* PAGE DOWN */
1285         {
1286             ScrollPageDownGenericList(ListUi);
1287         }
1288         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1289                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_PRIOR))  /* PAGE UP */
1290         {
1291             ScrollPageUpGenericList(ListUi);
1292         }
1293         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1294                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1295         {
1296             if (ConfirmQuit(Ir))
1297                 return QUIT_PAGE;
1298             else
1299                 RedrawGenericList(ListUi);
1300         }
1301         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1302                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE))  /* ESC */
1303         {
1304             RestoreGenericListUiState(ListUi);
1305             return nextPage;    // Use some "prevPage;" instead?
1306         }
1307         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1308         {
1309             return nextPage;
1310         }
1311         else if ((Ir->Event.KeyEvent.uChar.AsciiChar > 0x60) && (Ir->Event.KeyEvent.uChar.AsciiChar < 0x7b))
1312         {
1313             /* a-z */
1314             GenericListKeyPress(ListUi, Ir->Event.KeyEvent.uChar.AsciiChar);
1315         }
1316     }
1317 }
1318 
1319 
1320 /*
1321  * Displays the ComputerSettingsPage.
1322  *
1323  * Next pages:
1324  *  DeviceSettingsPage
1325  *  QuitPage
1326  *
1327  * RETURNS
1328  *   Number of the next page.
1329  */
1330 static PAGE_NUMBER
1331 ComputerSettingsPage(PINPUT_RECORD Ir)
1332 {
1333     GENERIC_LIST_UI ListUi;
1334     MUIDisplayPage(COMPUTER_SETTINGS_PAGE);
1335 
1336     InitGenericListUi(&ListUi, USetupData.ComputerList, GetSettingDescription);
1337     DrawGenericList(&ListUi,
1338                     2, 18,
1339                     xScreen - 3,
1340                     yScreen - 3);
1341 
1342     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1343 }
1344 
1345 
1346 /*
1347  * Displays the DisplaySettingsPage.
1348  *
1349  * Next pages:
1350  *  DeviceSettingsPage
1351  *  QuitPage
1352  *
1353  * RETURNS
1354  *   Number of the next page.
1355  */
1356 static PAGE_NUMBER
1357 DisplaySettingsPage(PINPUT_RECORD Ir)
1358 {
1359     GENERIC_LIST_UI ListUi;
1360     MUIDisplayPage(DISPLAY_SETTINGS_PAGE);
1361 
1362     InitGenericListUi(&ListUi, USetupData.DisplayList, GetSettingDescription);
1363     DrawGenericList(&ListUi,
1364                     2, 18,
1365                     xScreen - 3,
1366                     yScreen - 3);
1367 
1368     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1369 }
1370 
1371 
1372 /*
1373  * Displays the KeyboardSettingsPage.
1374  *
1375  * Next pages:
1376  *  DeviceSettingsPage
1377  *  QuitPage
1378  *
1379  * RETURNS
1380  *   Number of the next page.
1381  */
1382 static PAGE_NUMBER
1383 KeyboardSettingsPage(PINPUT_RECORD Ir)
1384 {
1385     GENERIC_LIST_UI ListUi;
1386     MUIDisplayPage(KEYBOARD_SETTINGS_PAGE);
1387 
1388     InitGenericListUi(&ListUi, USetupData.KeyboardList, GetSettingDescription);
1389     DrawGenericList(&ListUi,
1390                     2, 18,
1391                     xScreen - 3,
1392                     yScreen - 3);
1393 
1394     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1395 }
1396 
1397 
1398 /*
1399  * Displays the LayoutSettingsPage.
1400  *
1401  * Next pages:
1402  *  DeviceSettingsPage
1403  *  QuitPage
1404  *
1405  * RETURNS
1406  *   Number of the next page.
1407  */
1408 static PAGE_NUMBER
1409 LayoutSettingsPage(PINPUT_RECORD Ir)
1410 {
1411     GENERIC_LIST_UI ListUi;
1412     MUIDisplayPage(LAYOUT_SETTINGS_PAGE);
1413 
1414     InitGenericListUi(&ListUi, USetupData.LayoutList, GetSettingDescription);
1415     DrawGenericList(&ListUi,
1416                     2, 18,
1417                     xScreen - 3,
1418                     yScreen - 3);
1419 
1420     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1421 }
1422 
1423 
1424 static BOOL
1425 IsDiskSizeValid(PPARTENTRY PartEntry)
1426 {
1427     ULONGLONG size;
1428 
1429     size = PartEntry->SectorCount.QuadPart * PartEntry->DiskEntry->BytesPerSector;
1430     size = (size + (512 * KB)) / MB;  /* in MBytes */
1431 
1432     if (size < USetupData.RequiredPartitionDiskSpace)
1433     {
1434         /* Partition is too small so ask for another one */
1435         DPRINT1("Partition is too small (size: %I64u MB), required disk space is %lu MB\n", size, USetupData.RequiredPartitionDiskSpace);
1436         return FALSE;
1437     }
1438     else
1439     {
1440         return TRUE;
1441     }
1442 }
1443 
1444 
1445 /*
1446  * Displays the SelectPartitionPage.
1447  *
1448  * Next pages:
1449  *  SelectFileSystemPage (At once if unattended)
1450  *  SelectFileSystemPage (Default if free space is selected)
1451  *  CreatePrimaryPartitionPage
1452  *  CreateExtendedPartitionPage
1453  *  CreateLogicalPartitionPage
1454  *  ConfirmDeleteSystemPartitionPage (if the selected partition is the system partition, aka with the boot flag set)
1455  *  DeletePartitionPage
1456  *  QuitPage
1457  *
1458  * SIDEEFFECTS
1459  *  Set InstallShortcut (only if not unattended + free space is selected)
1460  *
1461  * RETURNS
1462  *   Number of the next page.
1463  */
1464 static PAGE_NUMBER
1465 SelectPartitionPage(PINPUT_RECORD Ir)
1466 {
1467     PARTLIST_UI ListUi;
1468     ULONG Error;
1469 
1470     if (PartitionList == NULL)
1471     {
1472         PartitionList = CreatePartitionList();
1473         if (PartitionList == NULL)
1474         {
1475             MUIDisplayError(ERROR_DRIVE_INFORMATION, Ir, POPUP_WAIT_ENTER);
1476             return QUIT_PAGE;
1477         }
1478         else if (IsListEmpty(&PartitionList->DiskListHead))
1479         {
1480             MUIDisplayError(ERROR_NO_HDD, Ir, POPUP_WAIT_ENTER);
1481             return QUIT_PAGE;
1482         }
1483 
1484         /* Reset the formatter machine state */
1485         TempPartition = NULL;
1486         FormatState = Start;
1487     }
1488 
1489     if (RepairUpdateFlag)
1490     {
1491         ASSERT(CurrentInstallation);
1492 
1493         /* Determine the selected installation disk & partition */
1494         InstallPartition = SelectPartition(PartitionList,
1495                                            CurrentInstallation->DiskNumber,
1496                                            CurrentInstallation->PartitionNumber);
1497         if (!InstallPartition)
1498         {
1499             DPRINT1("RepairUpdateFlag == TRUE, SelectPartition() returned FALSE, assert!\n");
1500             ASSERT(FALSE);
1501         }
1502         ASSERT(!IsContainerPartition(InstallPartition->PartitionType));
1503 
1504         return SELECT_FILE_SYSTEM_PAGE;
1505     }
1506 
1507     MUIDisplayPage(SELECT_PARTITION_PAGE);
1508 
1509     InitPartitionListUi(&ListUi, PartitionList,
1510                         CurrentPartition,
1511                         2, 23,
1512                         xScreen - 3,
1513                         yScreen - 3);
1514     DrawPartitionList(&ListUi);
1515 
1516     if (IsUnattendedSetup)
1517     {
1518         /* Determine the selected installation disk & partition */
1519         InstallPartition = SelectPartition(PartitionList,
1520                                            USetupData.DestinationDiskNumber,
1521                                            USetupData.DestinationPartitionNumber);
1522         if (!InstallPartition)
1523         {
1524             CurrentPartition = ListUi.CurrentPartition;
1525 
1526             if (USetupData.AutoPartition)
1527             {
1528                 ASSERT(CurrentPartition != NULL);
1529                 ASSERT(!IsContainerPartition(CurrentPartition->PartitionType));
1530 
1531                 if (CurrentPartition->LogicalPartition)
1532                 {
1533                     CreateLogicalPartition(PartitionList,
1534                                            CurrentPartition,
1535                                            CurrentPartition->SectorCount.QuadPart,
1536                                            TRUE);
1537                 }
1538                 else
1539                 {
1540                     CreatePrimaryPartition(PartitionList,
1541                                            CurrentPartition,
1542                                            CurrentPartition->SectorCount.QuadPart,
1543                                            TRUE);
1544                 }
1545 
1546 // FIXME?? Aren't we going to enter an infinite loop, if this test fails??
1547                 if (!IsDiskSizeValid(CurrentPartition))
1548                 {
1549                     MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
1550                                     USetupData.RequiredPartitionDiskSpace);
1551                     return SELECT_PARTITION_PAGE; /* let the user select another partition */
1552                 }
1553 
1554                 InstallPartition = CurrentPartition;
1555                 return SELECT_FILE_SYSTEM_PAGE;
1556             }
1557         }
1558         else
1559         {
1560             ASSERT(!IsContainerPartition(InstallPartition->PartitionType));
1561 
1562             DrawPartitionList(&ListUi); // FIXME: Doesn't make much sense...
1563 
1564 // FIXME?? Aren't we going to enter an infinite loop, if this test fails??
1565             if (!IsDiskSizeValid(InstallPartition))
1566             {
1567                 MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
1568                                 USetupData.RequiredPartitionDiskSpace);
1569                 return SELECT_PARTITION_PAGE; /* let the user select another partition */
1570             }
1571 
1572             return SELECT_FILE_SYSTEM_PAGE;
1573         }
1574     }
1575 
1576     while (TRUE)
1577     {
1578         CurrentPartition = ListUi.CurrentPartition;
1579 
1580         /* Update status text */
1581         if (CurrentPartition == NULL)
1582         {
1583             CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLCREATEPARTITION));
1584         }
1585         else if (CurrentPartition->LogicalPartition)
1586         {
1587             if (CurrentPartition->IsPartitioned)
1588             {
1589                 CONSOLE_SetStatusText(MUIGetString(STRING_DELETEPARTITION));
1590             }
1591             else
1592             {
1593                 CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLCREATELOGICAL));
1594             }
1595         }
1596         else
1597         {
1598             if (CurrentPartition->IsPartitioned)
1599             {
1600                 if (IsContainerPartition(CurrentPartition->PartitionType))
1601                 {
1602                     CONSOLE_SetStatusText(MUIGetString(STRING_DELETEPARTITION));
1603                 }
1604                 else
1605                 {
1606                     CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLDELETEPARTITION));
1607                 }
1608             }
1609             else
1610             {
1611                 CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLCREATEPARTITION));
1612             }
1613         }
1614 
1615         CONSOLE_ConInKey(Ir);
1616 
1617         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1618             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1619         {
1620             if (ConfirmQuit(Ir))
1621             {
1622                 DestroyPartitionList(PartitionList);
1623                 PartitionList = NULL;
1624                 return QUIT_PAGE;
1625             }
1626 
1627             break;
1628         }
1629         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1630                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
1631         {
1632             ScrollDownPartitionList(&ListUi);
1633         }
1634         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1635                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
1636         {
1637             ScrollUpPartitionList(&ListUi);
1638         }
1639         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN)  /* ENTER */
1640         {
1641             ASSERT(CurrentPartition != NULL);
1642 
1643             if (IsContainerPartition(CurrentPartition->PartitionType))
1644                 continue; // return SELECT_PARTITION_PAGE;
1645 
1646             /*
1647              * Check whether the user wants to install ReactOS on a disk that
1648              * is not recognized by the computer's firmware and if so, display
1649              * a warning since such disks may not be bootable.
1650              */
1651             if (CurrentPartition->DiskEntry->MediaType == FixedMedia &&
1652                 !CurrentPartition->DiskEntry->BiosFound)
1653             {
1654                 PopupError("The disk you have selected for installing ReactOS\n"
1655                            "is not visible by the firmware of your computer,\n"
1656                            "and so may not be bootable.\n"
1657                            "Press ENTER to continue nonetheless.",
1658                            MUIGetString(STRING_CONTINUE),
1659                            Ir, POPUP_WAIT_ENTER);
1660                 // return SELECT_PARTITION_PAGE;
1661             }
1662 
1663             if (CurrentPartition->IsPartitioned == FALSE)
1664             {
1665                 if (CurrentPartition->LogicalPartition)
1666                 {
1667                     Error = LogicalPartitionCreationChecks(CurrentPartition);
1668                     if (Error != NOT_AN_ERROR)
1669                     {
1670                         MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1671                         return SELECT_PARTITION_PAGE;
1672                     }
1673 
1674                     CreateLogicalPartition(PartitionList,
1675                                            CurrentPartition,
1676                                            0ULL,
1677                                            TRUE);
1678                 }
1679                 else
1680                 {
1681                     Error = PrimaryPartitionCreationChecks(CurrentPartition);
1682                     if (Error != NOT_AN_ERROR)
1683                     {
1684                         MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1685                         return SELECT_PARTITION_PAGE;
1686                     }
1687 
1688                     CreatePrimaryPartition(PartitionList,
1689                                            CurrentPartition,
1690                                            0ULL,
1691                                            TRUE);
1692                 }
1693             }
1694 
1695             if (!IsDiskSizeValid(CurrentPartition))
1696             {
1697                 MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
1698                                 USetupData.RequiredPartitionDiskSpace);
1699                 return SELECT_PARTITION_PAGE; /* let the user select another partition */
1700             }
1701 
1702             InstallPartition = CurrentPartition;
1703             return SELECT_FILE_SYSTEM_PAGE;
1704         }
1705         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'P')  /* P */
1706         {
1707             ASSERT(CurrentPartition != NULL);
1708 
1709             if (CurrentPartition->LogicalPartition == FALSE)
1710             {
1711                 Error = PrimaryPartitionCreationChecks(CurrentPartition);
1712                 if (Error != NOT_AN_ERROR)
1713                 {
1714                     MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1715                     return SELECT_PARTITION_PAGE;
1716                 }
1717 
1718                 return CREATE_PRIMARY_PARTITION_PAGE;
1719             }
1720         }
1721         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'E')  /* E */
1722         {
1723             ASSERT(CurrentPartition != NULL);
1724 
1725             if (CurrentPartition->LogicalPartition == FALSE)
1726             {
1727                 Error = ExtendedPartitionCreationChecks(CurrentPartition);
1728                 if (Error != NOT_AN_ERROR)
1729                 {
1730                     MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1731                     return SELECT_PARTITION_PAGE;
1732                 }
1733 
1734                 return CREATE_EXTENDED_PARTITION_PAGE;
1735             }
1736         }
1737         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'L')  /* L */
1738         {
1739             ASSERT(CurrentPartition != NULL);
1740 
1741             if (CurrentPartition->LogicalPartition)
1742             {
1743                 Error = LogicalPartitionCreationChecks(CurrentPartition);
1744                 if (Error != NOT_AN_ERROR)
1745                 {
1746                     MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1747                     return SELECT_PARTITION_PAGE;
1748                 }
1749 
1750                 return CREATE_LOGICAL_PARTITION_PAGE;
1751             }
1752         }
1753         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'D')  /* D */
1754         {
1755             UNICODE_STRING CurrentPartitionU;
1756             WCHAR PathBuffer[MAX_PATH];
1757 
1758             ASSERT(CurrentPartition != NULL);
1759 
1760             if (CurrentPartition->IsPartitioned == FALSE)
1761             {
1762                 MUIDisplayError(ERROR_DELETE_SPACE, Ir, POPUP_WAIT_ANY_KEY);
1763                 return SELECT_PARTITION_PAGE;
1764             }
1765 
1766 // TODO: Do something similar before trying to format the partition?
1767             if (!CurrentPartition->New &&
1768                 !IsContainerPartition(CurrentPartition->PartitionType) &&
1769                 CurrentPartition->FormatState != Unformatted)
1770             {
1771                 ASSERT(CurrentPartition->PartitionNumber != 0);
1772 
1773                 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
1774                         L"\\Device\\Harddisk%lu\\Partition%lu\\",
1775                         CurrentPartition->DiskEntry->DiskNumber,
1776                         CurrentPartition->PartitionNumber);
1777                 RtlInitUnicodeString(&CurrentPartitionU, PathBuffer);
1778 
1779                 /*
1780                  * Check whether the user attempts to delete the partition on which
1781                  * the installation source is present. If so, fail with an error.
1782                  */
1783                 // &USetupData.SourceRootPath
1784                 if (RtlPrefixUnicodeString(&CurrentPartitionU, &USetupData.SourcePath, TRUE))
1785                 {
1786                     MUIDisplayError(ERROR_SOURCE_PATH, Ir, POPUP_WAIT_ENTER);
1787                     return SELECT_PARTITION_PAGE;
1788                 }
1789             }
1790 
1791             if (CurrentPartition == PartitionList->SystemPartition ||
1792                 IsPartitionActive(CurrentPartition))
1793             {
1794                 return CONFIRM_DELETE_SYSTEM_PARTITION_PAGE;
1795             }
1796 
1797             return DELETE_PARTITION_PAGE;
1798         }
1799     }
1800 
1801     return SELECT_PARTITION_PAGE;
1802 }
1803 
1804 
1805 #define PARTITION_SIZE_INPUT_FIELD_LENGTH 9
1806 /* Restriction for MaxSize */
1807 #define PARTITION_MAXSIZE (pow(10, (PARTITION_SIZE_INPUT_FIELD_LENGTH - 1)) - 1)
1808 
1809 static VOID
1810 ShowPartitionSizeInputBox(SHORT Left,
1811                           SHORT Top,
1812                           SHORT Right,
1813                           SHORT Bottom,
1814                           ULONG MaxSize,
1815                           PWSTR InputBuffer,
1816                           PBOOLEAN Quit,
1817                           PBOOLEAN Cancel)
1818 {
1819     INPUT_RECORD Ir;
1820     COORD coPos;
1821     DWORD Written;
1822     CHAR Buffer[128];
1823     INT Length, Pos;
1824     WCHAR ch;
1825     SHORT iLeft;
1826     SHORT iTop;
1827 
1828     if (Quit != NULL)
1829         *Quit = FALSE;
1830 
1831     if (Cancel != NULL)
1832         *Cancel = FALSE;
1833 
1834     DrawBox(Left, Top, Right - Left + 1, Bottom - Top + 1);
1835 
1836     /* Print message */
1837     coPos.X = Left + 2;
1838     coPos.Y = Top + 2;
1839     strcpy(Buffer, MUIGetString(STRING_PARTITIONSIZE));
1840     iLeft = coPos.X + strlen(Buffer) + 1;
1841     iTop = coPos.Y;
1842 
1843     WriteConsoleOutputCharacterA(StdOutput,
1844                                  Buffer,
1845                                  strlen(Buffer),
1846                                  coPos,
1847                                  &Written);
1848 
1849     sprintf(Buffer, MUIGetString(STRING_MAXSIZE), MaxSize);
1850     coPos.X = iLeft + PARTITION_SIZE_INPUT_FIELD_LENGTH + 1;
1851     coPos.Y = iTop;
1852     WriteConsoleOutputCharacterA(StdOutput,
1853                                  Buffer,
1854                                  strlen(Buffer),
1855                                  coPos,
1856                                  &Written);
1857 
1858     swprintf(InputBuffer, L"%lu", MaxSize);
1859     Length = wcslen(InputBuffer);
1860     Pos = Length;
1861     CONSOLE_SetInputTextXY(iLeft,
1862                            iTop,
1863                            PARTITION_SIZE_INPUT_FIELD_LENGTH,
1864                            InputBuffer);
1865     CONSOLE_SetCursorXY(iLeft + Length, iTop);
1866     CONSOLE_SetCursorType(TRUE, TRUE);
1867 
1868     while (TRUE)
1869     {
1870         CONSOLE_ConInKey(&Ir);
1871 
1872         if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1873             (Ir.Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1874         {
1875             if (Quit != NULL)
1876                 *Quit = TRUE;
1877 
1878             InputBuffer[0] = UNICODE_NULL;
1879             CONSOLE_SetCursorType(TRUE, FALSE);
1880             break;
1881         }
1882         else if (Ir.Event.KeyEvent.wVirtualKeyCode == VK_RETURN)    /* ENTER */
1883         {
1884             CONSOLE_SetCursorType(TRUE, FALSE);
1885             break;
1886         }
1887         else if (Ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)    /* ESC */
1888         {
1889             if (Cancel != NULL)
1890                 *Cancel = TRUE;
1891 
1892             InputBuffer[0] = UNICODE_NULL;
1893             CONSOLE_SetCursorType(TRUE, FALSE);
1894             break;
1895         }
1896         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1897                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
1898         {
1899             Pos = 0;
1900             CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1901         }
1902         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1903                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
1904         {
1905             Pos = Length;
1906             CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1907         }
1908         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1909                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_LEFT))  /* LEFT */
1910         {
1911             if (Pos > 0)
1912             {
1913                 Pos--;
1914                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1915             }
1916         }
1917         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1918                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT))  /* RIGHT */
1919         {
1920             if (Pos < Length)
1921             {
1922                 Pos++;
1923                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1924             }
1925         }
1926         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1927                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_DELETE))  /* DEL */
1928         {
1929             if (Pos < Length)
1930             {
1931                 memmove(&InputBuffer[Pos],
1932                         &InputBuffer[Pos + 1],
1933                         (Length - Pos - 1) * sizeof(WCHAR));
1934                 InputBuffer[Length - 1] = UNICODE_NULL;
1935 
1936                 Length--;
1937                 CONSOLE_SetInputTextXY(iLeft,
1938                                        iTop,
1939                                        PARTITION_SIZE_INPUT_FIELD_LENGTH,
1940                                        InputBuffer);
1941                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1942             }
1943         }
1944         else if (Ir.Event.KeyEvent.wVirtualKeyCode == VK_BACK)  /* BACKSPACE */
1945         {
1946             if (Pos > 0)
1947             {
1948                 if (Pos < Length)
1949                     memmove(&InputBuffer[Pos - 1],
1950                             &InputBuffer[Pos],
1951                             (Length - Pos) * sizeof(WCHAR));
1952                 InputBuffer[Length - 1] = UNICODE_NULL;
1953 
1954                 Pos--;
1955                 Length--;
1956                 CONSOLE_SetInputTextXY(iLeft,
1957                                        iTop,
1958                                        PARTITION_SIZE_INPUT_FIELD_LENGTH,
1959                                        InputBuffer);
1960                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1961             }
1962         }
1963         else if (Ir.Event.KeyEvent.uChar.AsciiChar != 0x00)
1964         {
1965             if (Length < PARTITION_SIZE_INPUT_FIELD_LENGTH - 1)
1966             {
1967                 ch = (WCHAR)Ir.Event.KeyEvent.uChar.AsciiChar;
1968 
1969                 if ((ch >= L'0') && (ch <= L'9'))
1970                 {
1971                     if (Pos < Length)
1972                         memmove(&InputBuffer[Pos + 1],
1973                                 &InputBuffer[Pos],
1974                                 (Length - Pos) * sizeof(WCHAR));
1975                     InputBuffer[Length + 1] = UNICODE_NULL;
1976                     InputBuffer[Pos] = ch;
1977 
1978                     Pos++;
1979                     Length++;
1980                     CONSOLE_SetInputTextXY(iLeft,
1981                                            iTop,
1982                                            PARTITION_SIZE_INPUT_FIELD_LENGTH,
1983                                            InputBuffer);
1984                     CONSOLE_SetCursorXY(iLeft + Pos, iTop);
1985                 }
1986             }
1987         }
1988     }
1989 }
1990 
1991 
1992 /*
1993  * Displays the CreatePrimaryPartitionPage.
1994  *
1995  * Next pages:
1996  *  SelectPartitionPage
1997  *  SelectFileSystemPage (default)
1998  *  QuitPage
1999  *
2000  * RETURNS
2001  *   Number of the next page.
2002  */
2003 static PAGE_NUMBER
2004 CreatePrimaryPartitionPage(PINPUT_RECORD Ir)
2005 {
2006     PPARTENTRY PartEntry;
2007     PDISKENTRY DiskEntry;
2008     BOOLEAN Quit;
2009     BOOLEAN Cancel;
2010     WCHAR InputBuffer[50];
2011     ULONG MaxSize;
2012     ULONGLONG PartSize;
2013     ULONGLONG DiskSize;
2014     ULONGLONG SectorCount;
2015     PCHAR Unit;
2016 
2017     if (PartitionList == NULL || CurrentPartition == NULL)
2018     {
2019         /* FIXME: show an error dialog */
2020         return QUIT_PAGE;
2021     }
2022 
2023     PartEntry = CurrentPartition;
2024     DiskEntry = CurrentPartition->DiskEntry;
2025 
2026     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
2027 
2028     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHOOSENEWPARTITION));
2029 
2030     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2031 #if 0
2032     if (DiskSize >= 10 * GB) /* 10 GB */
2033     {
2034         DiskSize = DiskSize / GB;
2035         Unit = MUIGetString(STRING_GB);
2036     }
2037     else
2038 #endif
2039     {
2040         DiskSize = DiskSize / MB;
2041         if (DiskSize == 0)
2042             DiskSize = 1;
2043 
2044         Unit = MUIGetString(STRING_MB);
2045     }
2046 
2047     if (DiskEntry->DriverName.Length > 0)
2048     {
2049         CONSOLE_PrintTextXY(6, 10,
2050                             MUIGetString(STRING_HDINFOPARTCREATE_1),
2051                             DiskSize,
2052                             Unit,
2053                             DiskEntry->DiskNumber,
2054                             DiskEntry->Port,
2055                             DiskEntry->Bus,
2056                             DiskEntry->Id,
2057                             &DiskEntry->DriverName,
2058                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2059                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2060                                                                           "RAW");
2061     }
2062     else
2063     {
2064         CONSOLE_PrintTextXY(6, 10,
2065                             MUIGetString(STRING_HDINFOPARTCREATE_2),
2066                             DiskSize,
2067                             Unit,
2068                             DiskEntry->DiskNumber,
2069                             DiskEntry->Port,
2070                             DiskEntry->Bus,
2071                             DiskEntry->Id,
2072                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2073                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2074                                                                           "RAW");
2075     }
2076 
2077     CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_HDDSIZE));
2078 
2079 #if 0
2080     CONSOLE_PrintTextXY(8, 10, "Maximum size of the new partition is %I64u MB",
2081                         CurrentPartition->SectorCount * DiskEntry->BytesPerSector / MB);
2082 #endif
2083 
2084     CONSOLE_SetStatusText(MUIGetString(STRING_CREATEPARTITION));
2085 
2086     PartEntry = CurrentPartition;
2087     while (TRUE)
2088     {
2089         MaxSize = (PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector) / MB;  /* in MBytes (rounded) */
2090 
2091         if (MaxSize > PARTITION_MAXSIZE)
2092             MaxSize = PARTITION_MAXSIZE;
2093 
2094         ShowPartitionSizeInputBox(12, 14, xScreen - 12, 17, /* left, top, right, bottom */
2095                                   MaxSize, InputBuffer, &Quit, &Cancel);
2096 
2097         if (Quit)
2098         {
2099             if (ConfirmQuit(Ir))
2100                 return QUIT_PAGE;
2101 
2102             break;
2103         }
2104         else if (Cancel)
2105         {
2106             return SELECT_PARTITION_PAGE;
2107         }
2108         else
2109         {
2110             PartSize = _wcstoui64(InputBuffer, NULL, 10);
2111 
2112             if (PartSize < 1)
2113             {
2114                 /* Too small */
2115                 continue;
2116             }
2117 
2118             if (PartSize > MaxSize)
2119             {
2120                 /* Too large */
2121                 continue;
2122             }
2123 
2124             /* Convert to bytes */
2125             if (PartSize == MaxSize)
2126             {
2127                 /* Use all of the unpartitioned disk space */
2128                 SectorCount = PartEntry->SectorCount.QuadPart;
2129             }
2130             else
2131             {
2132                 /* Calculate the sector count from the size in MB */
2133                 SectorCount = PartSize * MB / DiskEntry->BytesPerSector;
2134 
2135                 /* But never get larger than the unpartitioned disk space */
2136                 if (SectorCount > PartEntry->SectorCount.QuadPart)
2137                     SectorCount = PartEntry->SectorCount.QuadPart;
2138             }
2139 
2140             DPRINT ("Partition size: %I64u bytes\n", PartSize);
2141 
2142             CreatePrimaryPartition(PartitionList,
2143                                    CurrentPartition,
2144                                    SectorCount,
2145                                    FALSE);
2146 
2147             return SELECT_PARTITION_PAGE;
2148         }
2149     }
2150 
2151     return CREATE_PRIMARY_PARTITION_PAGE;
2152 }
2153 
2154 
2155 /*
2156  * Displays the CreateExtendedPartitionPage.
2157  *
2158  * Next pages:
2159  *  SelectPartitionPage (default)
2160  *  QuitPage
2161  *
2162  * RETURNS
2163  *   Number of the next page.
2164  */
2165 static PAGE_NUMBER
2166 CreateExtendedPartitionPage(PINPUT_RECORD Ir)
2167 {
2168     PPARTENTRY PartEntry;
2169     PDISKENTRY DiskEntry;
2170     BOOLEAN Quit;
2171     BOOLEAN Cancel;
2172     WCHAR InputBuffer[50];
2173     ULONG MaxSize;
2174     ULONGLONG PartSize;
2175     ULONGLONG DiskSize;
2176     ULONGLONG SectorCount;
2177     PCHAR Unit;
2178 
2179     if (PartitionList == NULL || CurrentPartition == NULL)
2180     {
2181         /* FIXME: show an error dialog */
2182         return QUIT_PAGE;
2183     }
2184 
2185     PartEntry = CurrentPartition;
2186     DiskEntry = CurrentPartition->DiskEntry;
2187 
2188     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
2189 
2190     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHOOSE_NEW_EXTENDED_PARTITION));
2191 
2192     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2193 #if 0
2194     if (DiskSize >= 10 * GB) /* 10 GB */
2195     {
2196         DiskSize = DiskSize / GB;
2197         Unit = MUIGetString(STRING_GB);
2198     }
2199     else
2200 #endif
2201     {
2202         DiskSize = DiskSize / MB;
2203         if (DiskSize == 0)
2204             DiskSize = 1;
2205 
2206         Unit = MUIGetString(STRING_MB);
2207     }
2208 
2209     if (DiskEntry->DriverName.Length > 0)
2210     {
2211         CONSOLE_PrintTextXY(6, 10,
2212                             MUIGetString(STRING_HDINFOPARTCREATE_1),
2213                             DiskSize,
2214                             Unit,
2215                             DiskEntry->DiskNumber,
2216                             DiskEntry->Port,
2217                             DiskEntry->Bus,
2218                             DiskEntry->Id,
2219                             &DiskEntry->DriverName,
2220                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2221                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2222                                                                           "RAW");
2223     }
2224     else
2225     {
2226         CONSOLE_PrintTextXY(6, 10,
2227                             MUIGetString(STRING_HDINFOPARTCREATE_2),
2228                             DiskSize,
2229                             Unit,
2230                             DiskEntry->DiskNumber,
2231                             DiskEntry->Port,
2232                             DiskEntry->Bus,
2233                             DiskEntry->Id,
2234                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2235                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2236                                                                           "RAW");
2237     }
2238 
2239     CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_HDDSIZE));
2240 
2241 #if 0
2242     CONSOLE_PrintTextXY(8, 10, "Maximum size of the new partition is %I64u MB",
2243                         CurrentPartition->SectorCount * DiskEntry->BytesPerSector / MB);
2244 #endif
2245 
2246     CONSOLE_SetStatusText(MUIGetString(STRING_CREATEPARTITION));
2247 
2248     PartEntry = CurrentPartition;
2249     while (TRUE)
2250     {
2251         MaxSize = (PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector) / MB;  /* in MBytes (rounded) */
2252 
2253         if (MaxSize > PARTITION_MAXSIZE)
2254             MaxSize = PARTITION_MAXSIZE;
2255 
2256         ShowPartitionSizeInputBox(12, 14, xScreen - 12, 17, /* left, top, right, bottom */
2257                                   MaxSize, InputBuffer, &Quit, &Cancel);
2258 
2259         if (Quit)
2260         {
2261             if (ConfirmQuit(Ir))
2262                 return QUIT_PAGE;
2263 
2264             break;
2265         }
2266         else if (Cancel)
2267         {
2268             return SELECT_PARTITION_PAGE;
2269         }
2270         else
2271         {
2272             PartSize = _wcstoui64(InputBuffer, NULL, 10);
2273 
2274             if (PartSize < 1)
2275             {
2276                 /* Too small */
2277                 continue;
2278             }
2279 
2280             if (PartSize > MaxSize)
2281             {
2282                 /* Too large */
2283                 continue;
2284             }
2285 
2286             /* Convert to bytes */
2287             if (PartSize == MaxSize)
2288             {
2289                 /* Use all of the unpartitioned disk space */
2290                 SectorCount = PartEntry->SectorCount.QuadPart;
2291             }
2292             else
2293             {
2294                 /* Calculate the sector count from the size in MB */
2295                 SectorCount = PartSize * MB / DiskEntry->BytesPerSector;
2296 
2297                 /* But never get larger than the unpartitioned disk space */
2298                 if (SectorCount > PartEntry->SectorCount.QuadPart)
2299                     SectorCount = PartEntry->SectorCount.QuadPart;
2300             }
2301 
2302             DPRINT ("Partition size: %I64u bytes\n", PartSize);
2303 
2304             CreateExtendedPartition(PartitionList,
2305                                     CurrentPartition,
2306                                     SectorCount);
2307 
2308             return SELECT_PARTITION_PAGE;
2309         }
2310     }
2311 
2312     return CREATE_EXTENDED_PARTITION_PAGE;
2313 }
2314 
2315 
2316 /*
2317  * Displays the CreateLogicalPartitionPage.
2318  *
2319  * Next pages:
2320  *  SelectFileSystemPage (default)
2321  *  QuitPage
2322  *
2323  * RETURNS
2324  *   Number of the next page.
2325  */
2326 static PAGE_NUMBER
2327 CreateLogicalPartitionPage(PINPUT_RECORD Ir)
2328 {
2329     PPARTENTRY PartEntry;
2330     PDISKENTRY DiskEntry;
2331     BOOLEAN Quit;
2332     BOOLEAN Cancel;
2333     WCHAR InputBuffer[50];
2334     ULONG MaxSize;
2335     ULONGLONG PartSize;
2336     ULONGLONG DiskSize;
2337     ULONGLONG SectorCount;
2338     PCHAR Unit;
2339 
2340     if (PartitionList == NULL || CurrentPartition == NULL)
2341     {
2342         /* FIXME: show an error dialog */
2343         return QUIT_PAGE;
2344     }
2345 
2346     PartEntry = CurrentPartition;
2347     DiskEntry = CurrentPartition->DiskEntry;
2348 
2349     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
2350 
2351     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHOOSE_NEW_LOGICAL_PARTITION));
2352 
2353     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2354 #if 0
2355     if (DiskSize >= 10 * GB) /* 10 GB */
2356     {
2357         DiskSize = DiskSize / GB;
2358         Unit = MUIGetString(STRING_GB);
2359     }
2360     else
2361 #endif
2362     {
2363         DiskSize = DiskSize / MB;
2364         if (DiskSize == 0)
2365             DiskSize = 1;
2366 
2367         Unit = MUIGetString(STRING_MB);
2368     }
2369 
2370     if (DiskEntry->DriverName.Length > 0)
2371     {
2372         CONSOLE_PrintTextXY(6, 10,
2373                             MUIGetString(STRING_HDINFOPARTCREATE_1),
2374                             DiskSize,
2375                             Unit,
2376                             DiskEntry->DiskNumber,
2377                             DiskEntry->Port,
2378                             DiskEntry->Bus,
2379                             DiskEntry->Id,
2380                             &DiskEntry->DriverName,
2381                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2382                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2383                                                                           "RAW");
2384     }
2385     else
2386     {
2387         CONSOLE_PrintTextXY(6, 10,
2388                             MUIGetString(STRING_HDINFOPARTCREATE_2),
2389                             DiskSize,
2390                             Unit,
2391                             DiskEntry->DiskNumber,
2392                             DiskEntry->Port,
2393                             DiskEntry->Bus,
2394                             DiskEntry->Id,
2395                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2396                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2397                                                                           "RAW");
2398     }
2399 
2400     CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_HDDSIZE));
2401 
2402 #if 0
2403     CONSOLE_PrintTextXY(8, 10, "Maximum size of the new partition is %I64u MB",
2404                         CurrentPartition->SectorCount * DiskEntry->BytesPerSector / MB);
2405 #endif
2406 
2407     CONSOLE_SetStatusText(MUIGetString(STRING_CREATEPARTITION));
2408 
2409     PartEntry = CurrentPartition;
2410     while (TRUE)
2411     {
2412         MaxSize = (PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector) / MB;  /* in MBytes (rounded) */
2413 
2414         if (MaxSize > PARTITION_MAXSIZE)
2415             MaxSize = PARTITION_MAXSIZE;
2416 
2417         ShowPartitionSizeInputBox(12, 14, xScreen - 12, 17, /* left, top, right, bottom */
2418                                   MaxSize, InputBuffer, &Quit, &Cancel);
2419 
2420         if (Quit)
2421         {
2422             if (ConfirmQuit(Ir))
2423                 return QUIT_PAGE;
2424 
2425             break;
2426         }
2427         else if (Cancel)
2428         {
2429             return SELECT_PARTITION_PAGE;
2430         }
2431         else
2432         {
2433             PartSize = _wcstoui64(InputBuffer, NULL, 10);
2434 
2435             if (PartSize < 1)
2436             {
2437                 /* Too small */
2438                 continue;
2439             }
2440 
2441             if (PartSize > MaxSize)
2442             {
2443                 /* Too large */
2444                 continue;
2445             }
2446 
2447             /* Convert to bytes */
2448             if (PartSize == MaxSize)
2449             {
2450                 /* Use all of the unpartitioned disk space */
2451                 SectorCount = PartEntry->SectorCount.QuadPart;
2452             }
2453             else
2454             {
2455                 /* Calculate the sector count from the size in MB */
2456                 SectorCount = PartSize * MB / DiskEntry->BytesPerSector;
2457 
2458                 /* But never get larger than the unpartitioned disk space */
2459                 if (SectorCount > PartEntry->SectorCount.QuadPart)
2460                     SectorCount = PartEntry->SectorCount.QuadPart;
2461             }
2462 
2463             DPRINT("Partition size: %I64u bytes\n", PartSize);
2464 
2465             CreateLogicalPartition(PartitionList,
2466                                    CurrentPartition,
2467                                    SectorCount,
2468                                    FALSE);
2469 
2470             return SELECT_PARTITION_PAGE;
2471         }
2472     }
2473 
2474     return CREATE_LOGICAL_PARTITION_PAGE;
2475 }
2476 
2477 
2478 /*
2479  * Displays the ConfirmDeleteSystemPartitionPage.
2480  *
2481  * Next pages:
2482  *  DeletePartitionPage (default)
2483  *  SelectPartitionPage
2484  *
2485  * RETURNS
2486  *   Number of the next page.
2487  */
2488 static PAGE_NUMBER
2489 ConfirmDeleteSystemPartitionPage(PINPUT_RECORD Ir)
2490 {
2491     MUIDisplayPage(CONFIRM_DELETE_SYSTEM_PARTITION_PAGE);
2492 
2493     while (TRUE)
2494     {
2495         CONSOLE_ConInKey(Ir);
2496 
2497         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2498             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
2499         {
2500             if (ConfirmQuit(Ir))
2501                 return QUIT_PAGE;
2502 
2503             break;
2504         }
2505         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN) /* ENTER */
2506         {
2507             return DELETE_PARTITION_PAGE;
2508         }
2509         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)  /* ESC */
2510         {
2511             return SELECT_PARTITION_PAGE;
2512         }
2513     }
2514 
2515     return CONFIRM_DELETE_SYSTEM_PARTITION_PAGE;
2516 }
2517 
2518 
2519 /*
2520  * Displays the DeletePartitionPage.
2521  *
2522  * Next pages:
2523  *  SelectPartitionPage (default)
2524  *  QuitPage
2525  *
2526  * RETURNS
2527  *   Number of the next page.
2528  */
2529 static PAGE_NUMBER
2530 DeletePartitionPage(PINPUT_RECORD Ir)
2531 {
2532     PPARTENTRY PartEntry;
2533     PDISKENTRY DiskEntry;
2534     ULONGLONG DiskSize;
2535     ULONGLONG PartSize;
2536     PCHAR Unit;
2537     CHAR PartTypeString[32];
2538 
2539     if (PartitionList == NULL || CurrentPartition == NULL)
2540     {
2541         /* FIXME: show an error dialog */
2542         return QUIT_PAGE;
2543     }
2544 
2545     PartEntry = CurrentPartition;
2546     DiskEntry = CurrentPartition->DiskEntry;
2547 
2548     MUIDisplayPage(DELETE_PARTITION_PAGE);
2549 
2550     /* Adjust partition type */
2551     GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
2552                                        PartTypeString,
2553                                        ARRAYSIZE(PartTypeString));
2554 
2555     PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2556 #if 0
2557     if (PartSize >= 10 * GB) /* 10 GB */
2558     {
2559         PartSize = PartSize / GB;
2560         Unit = MUIGetString(STRING_GB);
2561     }
2562     else
2563 #endif
2564     if (PartSize >= 10 * MB) /* 10 MB */
2565     {
2566         PartSize = PartSize / MB;
2567         Unit = MUIGetString(STRING_MB);
2568     }
2569     else
2570     {
2571         PartSize = PartSize / KB;
2572         Unit = MUIGetString(STRING_KB);
2573     }
2574 
2575     if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
2576     {
2577         CONSOLE_PrintTextXY(6, 10,
2578                             MUIGetString(STRING_HDDINFOUNK2),
2579                             (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2580                             (PartEntry->DriveLetter == 0) ? '-' : ':',
2581                             PartEntry->PartitionType,
2582                             PartSize,
2583                             Unit);
2584     }
2585     else
2586     {
2587         CONSOLE_PrintTextXY(6, 10,
2588                             "   %c%c  %s    %I64u %s",
2589                             (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2590                             (PartEntry->DriveLetter == 0) ? '-' : ':',
2591                             PartTypeString,
2592                             PartSize,
2593                             Unit);
2594     }
2595 
2596     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2597 #if 0
2598     if (DiskSize >= 10 * GB) /* 10 GB */
2599     {
2600         DiskSize = DiskSize / GB;
2601         Unit = MUIGetString(STRING_GB);
2602     }
2603     else
2604 #endif
2605     {
2606         DiskSize = DiskSize / MB;
2607         if (DiskSize == 0)
2608             DiskSize = 1;
2609 
2610         Unit = MUIGetString(STRING_MB);
2611     }
2612 
2613     if (DiskEntry->DriverName.Length > 0)
2614     {
2615         CONSOLE_PrintTextXY(6, 12,
2616                             MUIGetString(STRING_HDINFOPARTDELETE_1),
2617                             DiskSize,
2618                             Unit,
2619                             DiskEntry->DiskNumber,
2620                             DiskEntry->Port,
2621                             DiskEntry->Bus,
2622                             DiskEntry->Id,
2623                             &DiskEntry->DriverName,
2624                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2625                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2626                                                                           "RAW");
2627     }
2628     else
2629     {
2630         CONSOLE_PrintTextXY(6, 12,
2631                             MUIGetString(STRING_HDINFOPARTDELETE_2),
2632                             DiskSize,
2633                             Unit,
2634                             DiskEntry->DiskNumber,
2635                             DiskEntry->Port,
2636                             DiskEntry->Bus,
2637                             DiskEntry->Id,
2638                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2639                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2640                                                                           "RAW");
2641     }
2642 
2643     while (TRUE)
2644     {
2645         CONSOLE_ConInKey(Ir);
2646 
2647         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2648             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
2649         {
2650             if (ConfirmQuit(Ir))
2651                 return QUIT_PAGE;
2652 
2653             break;
2654         }
2655         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)  /* ESC */
2656         {
2657             return SELECT_PARTITION_PAGE;
2658         }
2659         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'L') /* L */
2660         {
2661             DeletePartition(PartitionList,
2662                             CurrentPartition,
2663                             &CurrentPartition);
2664             return SELECT_PARTITION_PAGE;
2665         }
2666     }
2667 
2668     return DELETE_PARTITION_PAGE;
2669 }
2670 
2671 
2672 static VOID
2673 ResetFileSystemList(VOID)
2674 {
2675     if (!FileSystemList)
2676         return;
2677 
2678     DestroyFileSystemList(FileSystemList);
2679     FileSystemList = NULL;
2680 }
2681 
2682 /*
2683  * Displays the SelectFileSystemPage.
2684  *
2685  * Next pages:
2686  *  CheckFileSystemPage (At once if RepairUpdate is selected)
2687  *  CheckFileSystemPage (At once if Unattended and not USetupData.FormatPartition)
2688  *  FormatPartitionPage (At once if Unattended and USetupData.FormatPartition)
2689  *  SelectPartitionPage (If the user aborts)
2690  *  FormatPartitionPage (Default)
2691  *  QuitPage
2692  *
2693  * SIDEEFFECTS
2694  *  Calls UpdatePartitionType()
2695  *  Calls FindSupportedSystemPartition()
2696  *
2697  * RETURNS
2698  *   Number of the next page.
2699  */
2700 static PAGE_NUMBER
2701 SelectFileSystemPage(PINPUT_RECORD Ir)
2702 {
2703     PPARTENTRY PartEntry;
2704     PDISKENTRY DiskEntry;
2705     ULONGLONG DiskSize;
2706     ULONGLONG PartSize;
2707     PCHAR DiskUnit;
2708     PCHAR PartUnit;
2709     CHAR PartTypeString[32];
2710     FORMATMACHINESTATE PreviousFormatState;
2711     PCWSTR DefaultFs;
2712 
2713     DPRINT("SelectFileSystemPage()\n");
2714 
2715     if (PartitionList == NULL || InstallPartition == NULL)
2716     {
2717         /* FIXME: show an error dialog */
2718         return QUIT_PAGE;
2719     }
2720 
2721     /* Find or set the active system partition when starting formatting */
2722     if (FormatState == Start)
2723     {
2724         /*
2725          * If we install on a fixed disk, try to find a supported system
2726          * partition on the system. Otherwise if we install on a removable disk
2727          * use the install partition as the system partition.
2728          */
2729         // TODO: Include that logic inside the FindSupportedSystemPartition() function?
2730         if (InstallPartition->DiskEntry->MediaType == FixedMedia)
2731         {
2732             SystemPartition = FindSupportedSystemPartition(PartitionList,
2733                                                            FALSE,
2734                                                            InstallPartition->DiskEntry,
2735                                                            InstallPartition);
2736             /* Use the original system partition as the old active partition hint */
2737             PartEntry = PartitionList->SystemPartition;
2738 
2739             if ( SystemPartition && PartitionList->SystemPartition &&
2740                 (SystemPartition != PartitionList->SystemPartition) )
2741             {
2742                 DPRINT1("We are using a different system partition!!!!\n");
2743 
2744                 MUIDisplayPage(CHANGE_SYSTEM_PARTITION);
2745 
2746                 {
2747                 PPARTENTRY PartEntry; // Shadow variable
2748 
2749                 PartEntry = PartitionList->SystemPartition;
2750                 DiskEntry = PartEntry->DiskEntry;
2751 
2752                 /* Adjust partition size */
2753                 PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2754                 if (PartSize >= 10 * GB) /* 10 GB */
2755                 {
2756                     PartSize = PartSize / GB;
2757                     PartUnit = MUIGetString(STRING_GB);
2758                 }
2759                 else
2760                 {
2761                     PartSize = PartSize / MB;
2762                     PartUnit = MUIGetString(STRING_MB);
2763                 }
2764 
2765                 /* Adjust partition type */
2766                 GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
2767                                                    PartTypeString,
2768                                                    ARRAYSIZE(PartTypeString));
2769 
2770                 if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
2771                 {
2772                     CONSOLE_PrintTextXY(8, 10,
2773                                         MUIGetString(STRING_HDDINFOUNK4),
2774                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2775                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2776                                         PartEntry->PartitionType,
2777                                         PartSize,
2778                                         PartUnit);
2779                 }
2780                 else
2781                 {
2782                     CONSOLE_PrintTextXY(8, 10,
2783                                         "%c%c  %s    %I64u %s",
2784                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2785                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2786                                         PartTypeString,
2787                                         PartSize,
2788                                         PartUnit);
2789                 }
2790 
2791 
2792                 /* Adjust disk size */
2793                 DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2794                 if (DiskSize >= 10 * GB) /* 10 GB */
2795                 {
2796                     DiskSize = DiskSize / GB;
2797                     DiskUnit = MUIGetString(STRING_GB);
2798                 }
2799                 else
2800                 {
2801                     DiskSize = DiskSize / MB;
2802                     DiskUnit = MUIGetString(STRING_MB);
2803                 }
2804 
2805                 CONSOLE_PrintTextXY(8, 14, MUIGetString(STRING_HDINFOPARTZEROED_1),
2806                                     DiskEntry->DiskNumber,
2807                                     DiskSize,
2808                                     DiskUnit,
2809                                     DiskEntry->Port,
2810                                     DiskEntry->Bus,
2811                                     DiskEntry->Id,
2812                                     &DiskEntry->DriverName,
2813                                     DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2814                                     DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2815                                                                                   "RAW");
2816 
2817 
2818                 PartEntry = SystemPartition;
2819                 DiskEntry = PartEntry->DiskEntry;
2820 
2821                 /* Adjust partition size */
2822                 PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2823                 if (PartSize >= 10 * GB) /* 10 GB */
2824                 {
2825                     PartSize = PartSize / GB;
2826                     PartUnit = MUIGetString(STRING_GB);
2827                 }
2828                 else
2829                 {
2830                     PartSize = PartSize / MB;
2831                     PartUnit = MUIGetString(STRING_MB);
2832                 }
2833 
2834                 /* Adjust partition type */
2835                 GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
2836                                                    PartTypeString,
2837                                                    ARRAYSIZE(PartTypeString));
2838 
2839                 if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
2840                 {
2841                     CONSOLE_PrintTextXY(8, 23,
2842                                         MUIGetString(STRING_HDDINFOUNK4),
2843                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2844                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2845                                         PartEntry->PartitionType,
2846                                         PartSize,
2847                                         PartUnit);
2848                 }
2849                 else
2850                 {
2851                     CONSOLE_PrintTextXY(8, 23,
2852                                         "%c%c  %s    %I64u %s",
2853                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2854                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2855                                         PartTypeString,
2856                                         PartSize,
2857                                         PartUnit);
2858                 }
2859 
2860                 }
2861 
2862                 while (TRUE)
2863                 {
2864                     CONSOLE_ConInKey(Ir);
2865 
2866                     if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN) /* ENTER */
2867                     {
2868                         break;
2869                     }
2870                     else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)  /* ESC */
2871                     {
2872                         return SELECT_PARTITION_PAGE;
2873                     }
2874                 }
2875 
2876                 CONSOLE_ClearScreen();
2877                 CONSOLE_Flush();
2878             }
2879         }
2880         else // if (InstallPartition->DiskEntry->MediaType == RemovableMedia)
2881         {
2882             SystemPartition = InstallPartition;
2883             /* Don't specify any old active partition hint */
2884             PartEntry = NULL;
2885         }
2886 
2887         if (!SystemPartition)
2888         {
2889             /* FIXME: improve the error dialog */
2890             //
2891             // Error dialog should say that we cannot find a suitable
2892             // system partition and create one on the system. At this point,
2893             // it may be nice to ask the user whether he wants to continue,
2894             // or use an external drive as the system drive/partition
2895             // (e.g. floppy, USB drive, etc...)
2896             //
2897             PopupError("The ReactOS Setup could not find a supported system partition\n"
2898                        "on your system or could not create a new one. Without such partition\n"
2899                        "the Setup program cannot install ReactOS.\n"
2900                        "Press ENTER to return to the partition selection list.",
2901                        MUIGetString(STRING_CONTINUE),
2902                        Ir, POPUP_WAIT_ENTER);
2903             return SELECT_PARTITION_PAGE;
2904         }
2905 
2906         /*
2907          * If the system partition can be created in some
2908          * non-partitioned space, create it now.
2909          */
2910         if (!SystemPartition->IsPartitioned)
2911         {
2912             // if (IsUnattendedSetup)
2913             {
2914                 CreatePrimaryPartition(PartitionList,
2915                                        SystemPartition,
2916                                        0LL, // SystemPartition->SectorCount.QuadPart,
2917                                        TRUE);
2918                 ASSERT(SystemPartition->IsPartitioned);
2919             }
2920             // else
2921             {
2922             }
2923         }
2924 
2925         /* Set it as such */
2926         if (!SetActivePartition(PartitionList, SystemPartition, PartEntry))
2927         {
2928             DPRINT1("SetActivePartition(0x%p) failed?!\n", SystemPartition);
2929             ASSERT(FALSE);
2930         }
2931 
2932         /* Commit all partition changes to all the disks */
2933         if (!WritePartitionsToDisk(PartitionList))
2934         {
2935             DPRINT("WritePartitionsToDisk() failed\n");
2936             MUIDisplayError(ERROR_WRITE_PTABLE, Ir, POPUP_WAIT_ENTER);
2937             return QUIT_PAGE;
2938         }
2939 
2940         /*
2941          * In all cases, whether or not we are going to perform a formatting,
2942          * we must perform a filesystem check of both the system and the
2943          * installation partitions.
2944          */
2945         InstallPartition->NeedsCheck = TRUE;
2946         if (SystemPartition != InstallPartition)
2947             SystemPartition->NeedsCheck = TRUE;
2948 
2949         /*
2950          * In case we just repair an existing installation, or make
2951          * an unattended setup without formatting, just go to the
2952          * filesystem check step.
2953          */
2954         if (RepairUpdateFlag)
2955             return CHECK_FILE_SYSTEM_PAGE;
2956 
2957         if (IsUnattendedSetup && !USetupData.FormatPartition)
2958             return CHECK_FILE_SYSTEM_PAGE;
2959     }
2960 
2961     // ASSERT(SystemPartition->IsPartitioned);
2962 
2963     /* Reset the filesystem list for each partition that is to be formatted */
2964     ResetFileSystemList();
2965 
2966     PreviousFormatState = FormatState;
2967     switch (FormatState)
2968     {
2969         case Start:
2970         {
2971             /*
2972              * We start by formatting the system partition in case it is new
2973              * (it didn't exist before) and is not the same as the installation
2974              * partition. Otherwise we just require a filesystem check on it,
2975              * and start by formatting the installation partition instead.
2976              */
2977 
2978             ASSERT(SystemPartition->IsPartitioned);
2979 
2980             if ((SystemPartition != InstallPartition) &&
2981                 (SystemPartition->FormatState == Unformatted))
2982             {
2983                 TempPartition = SystemPartition;
2984                 TempPartition->NeedsCheck = TRUE;
2985 
2986                 // TODO: Should we let the user using a custom file-system,
2987                 // or should we always use FAT(32) for it?
2988                 // For "compatibility", FAT(32) would be best indeed.
2989 
2990                 FormatState = FormatSystemPartition;
2991                 DPRINT1("FormatState: Start --> FormatSystemPartition\n");
2992             }
2993             else
2994             {
2995                 TempPartition = InstallPartition;
2996                 TempPartition->NeedsCheck = TRUE;
2997 
2998                 if (SystemPartition != InstallPartition)
2999                 {
3000                     /* The system partition is separate, so it had better be formatted! */
3001                     ASSERT((SystemPartition->FormatState == Preformatted) ||
3002                            (SystemPartition->FormatState == Formatted));
3003 
3004                     /* Require a filesystem check on the system partition too */
3005                     SystemPartition->NeedsCheck = TRUE;
3006                 }
3007 
3008                 FormatState = FormatInstallPartition;
3009                 DPRINT1("FormatState: Start --> FormatInstallPartition\n");
3010             }
3011             break;
3012         }
3013 
3014         case FormatSystemPartition:
3015         {
3016             TempPartition = InstallPartition;
3017             TempPartition->NeedsCheck = TRUE;
3018 
3019             FormatState = FormatInstallPartition;
3020             DPRINT1("FormatState: FormatSystemPartition --> FormatInstallPartition\n");
3021             break;
3022         }
3023 
3024         case FormatInstallPartition:
3025         case FormatOtherPartition:
3026         {
3027             if (GetNextUnformattedPartition(PartitionList,
3028                                             NULL,
3029                                             &TempPartition))
3030             {
3031                 FormatState = FormatOtherPartition;
3032                 TempPartition->NeedsCheck = TRUE;
3033 
3034                 if (FormatState == FormatInstallPartition)
3035                     DPRINT1("FormatState: FormatInstallPartition --> FormatOtherPartition\n");
3036                 else
3037                     DPRINT1("FormatState: FormatOtherPartition --> FormatOtherPartition\n");
3038             }
3039             else
3040             {
3041                 FormatState = FormatDone;
3042 
3043                 if (FormatState == FormatInstallPartition)
3044                     DPRINT1("FormatState: FormatInstallPartition --> FormatDone\n");
3045                 else
3046                     DPRINT1("FormatState: FormatOtherPartition --> FormatDone\n");
3047 
3048                 return CHECK_FILE_SYSTEM_PAGE;
3049             }
3050             break;
3051         }
3052 
3053         case FormatDone:
3054         {
3055             DPRINT1("FormatState: FormatDone\n");
3056             return CHECK_FILE_SYSTEM_PAGE;
3057         }
3058 
3059         default:
3060         {
3061             DPRINT1("FormatState: Invalid value %ld\n", FormatState);
3062             ASSERT(FALSE);
3063             return QUIT_PAGE;
3064         }
3065     }
3066 
3067     PartEntry = TempPartition;
3068     DiskEntry = TempPartition->DiskEntry;
3069 
3070     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3071 
3072     /* Adjust disk size */
3073     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
3074     if (DiskSize >= 10 * GB) /* 10 GB */
3075     {
3076         DiskSize = DiskSize / GB;
3077         DiskUnit = MUIGetString(STRING_GB);
3078     }
3079     else
3080     {
3081         DiskSize = DiskSize / MB;
3082         DiskUnit = MUIGetString(STRING_MB);
3083     }
3084 
3085     /* Adjust partition size */
3086     PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
3087     if (PartSize >= 10 * GB) /* 10 GB */
3088     {
3089         PartSize = PartSize / GB;
3090         PartUnit = MUIGetString(STRING_GB);
3091     }
3092     else
3093     {
3094         PartSize = PartSize / MB;
3095         PartUnit = MUIGetString(STRING_MB);
3096     }
3097 
3098     /* Adjust partition type */
3099     GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
3100                                        PartTypeString,
3101                                        ARRAYSIZE(PartTypeString));
3102 
3103     MUIDisplayPage(SELECT_FILE_SYSTEM_PAGE);
3104 
3105     if (PartEntry->AutoCreate)
3106     {
3107         CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NEWPARTITION));
3108 
3109 #if 0
3110         CONSOLE_PrintTextXY(8, 10, "Partition %lu (%I64u %s) %s of",
3111                             PartEntry->PartitionNumber,
3112                             PartSize,
3113                             PartUnit,
3114                             PartTypeString);
3115 #endif
3116 
3117         CONSOLE_PrintTextXY(8, 10, MUIGetString(STRING_HDINFOPARTZEROED_1),
3118                             DiskEntry->DiskNumber,
3119                             DiskSize,
3120                             DiskUnit,
3121                             DiskEntry->Port,
3122                             DiskEntry->Bus,
3123                             DiskEntry->Id,
3124                             &DiskEntry->DriverName,
3125                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
3126                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
3127                                                                           "RAW");
3128 
3129         CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_PARTFORMAT));
3130 
3131         PartEntry->AutoCreate = FALSE;
3132     }
3133     else if (PartEntry->New)
3134     {
3135         switch (FormatState)
3136         {
3137             case FormatSystemPartition:
3138                 CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NONFORMATTEDSYSTEMPART));
3139                 break;
3140 
3141             case FormatInstallPartition:
3142                 CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NONFORMATTEDPART));
3143                 break;
3144 
3145             case FormatOtherPartition:
3146                 CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NONFORMATTEDOTHERPART));
3147                 break;
3148 
3149             default:
3150                 ASSERT(FALSE);
3151                 break;
3152         }
3153 
3154         CONSOLE_PrintTextXY(8, 10, MUIGetString(STRING_HDINFOPARTZEROED_1),
3155                             DiskEntry->DiskNumber,
3156                             DiskSize,
3157                             DiskUnit,
3158                             DiskEntry->Port,
3159                             DiskEntry->Bus,
3160                             DiskEntry->Id,
3161                             &DiskEntry->DriverName,
3162                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
3163                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
3164                                                                           "RAW");
3165 
3166         CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_PARTFORMAT));
3167     }
3168     else
3169     {
3170         CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_INSTALLONPART));
3171 
3172         if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
3173         {
3174             CONSOLE_PrintTextXY(8, 10,
3175                                 MUIGetString(STRING_HDDINFOUNK4),
3176                                 (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
3177                                 (PartEntry->DriveLetter == 0) ? '-' : ':',
3178                                 PartEntry->PartitionType,
3179                                 PartSize,
3180                                 PartUnit);
3181         }
3182         else
3183         {
3184             CONSOLE_PrintTextXY(8, 10,
3185                                 "%c%c  %s    %I64u %s",
3186                                 (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
3187                                 (PartEntry->DriveLetter == 0) ? '-' : ':',
3188                                 PartTypeString,
3189                                 PartSize,
3190                                 PartUnit);
3191         }
3192 
3193         CONSOLE_PrintTextXY(6, 12, MUIGetString(STRING_HDINFOPARTEXISTS_1),
3194                             DiskEntry->DiskNumber,
3195                             DiskSize,
3196                             DiskUnit,
3197                             DiskEntry->Port,
3198                             DiskEntry->Bus,
3199                             DiskEntry->Id,
3200                             &DiskEntry->DriverName,
3201                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
3202                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
3203                                                                           "RAW");
3204     }
3205 
3206     ASSERT(FileSystemList == NULL);
3207 
3208     if (IsUnattendedSetup)
3209     {
3210         ASSERT(USetupData.FormatPartition);
3211 
3212         switch (USetupData.FsType)
3213         {
3214             /* 1 is for BtrFS */
3215             case 1:
3216                 DefaultFs = L"BTRFS";
3217                 break;
3218 
3219             /* If we don't understand input, default to FAT */
3220             default:
3221                 DefaultFs = L"FAT";
3222                 break;
3223         }
3224     }
3225     else
3226     {
3227         /* By default select the "FAT" file system */
3228         DefaultFs = L"FAT";
3229     }
3230 
3231     /* Create the file system list */
3232     // TODO: Display only the FSes compatible with the selected partition!
3233     FileSystemList = CreateFileSystemList(6, 26,
3234                                           PartEntry->New ||
3235                                           PartEntry->FormatState == Unformatted,
3236                                           DefaultFs);
3237     if (FileSystemList == NULL)
3238     {
3239         /* FIXME: show an error dialog */
3240         return QUIT_PAGE;
3241     }
3242 
3243     if (IsUnattendedSetup)
3244     {
3245         ASSERT(USetupData.FormatPartition);
3246         return FORMAT_PARTITION_PAGE;
3247     }
3248 
3249     DrawFileSystemList(FileSystemList);
3250 
3251     while (TRUE)
3252     {
3253         CONSOLE_ConInKey(Ir);
3254 
3255         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3256             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3257         {
3258             if (ConfirmQuit(Ir))
3259             {
3260                 /* Reset the filesystem list */
3261                 ResetFileSystemList();
3262                 return QUIT_PAGE;
3263             }
3264 
3265             break;
3266         }
3267         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3268                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE))  /* ESC */
3269         {
3270             /* Reset the formatter machine state */
3271             TempPartition = NULL;
3272             FormatState = Start;
3273 
3274             /* Reset the filesystem list */
3275             ResetFileSystemList();
3276 
3277             return SELECT_PARTITION_PAGE;
3278         }
3279         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3280                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
3281         {
3282             ScrollDownFileSystemList(FileSystemList);
3283         }
3284         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3285                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
3286         {
3287             ScrollUpFileSystemList(FileSystemList);
3288         }
3289         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN) /* ENTER */
3290         {
3291             if (!FileSystemList->Selected->FileSystem)
3292             {
3293                 ASSERT(!TempPartition->New && TempPartition->FormatState != Unformatted);
3294 
3295                 /*
3296                  * Skip formatting this partition. We will also ignore
3297                  * filesystem checks on it, unless it is either the system
3298                  * or the installation partition.
3299                  */
3300                 if (TempPartition != SystemPartition &&
3301                     TempPartition != InstallPartition)
3302                 {
3303                     PartEntry->NeedsCheck = FALSE;
3304                 }
3305 
3306                 return SELECT_FILE_SYSTEM_PAGE;
3307             }
3308             else
3309             {
3310                 /* Format this partition */
3311                 return FORMAT_PARTITION_PAGE;
3312             }
3313         }
3314     }
3315 
3316     FormatState = PreviousFormatState;
3317 
3318     return SELECT_FILE_SYSTEM_PAGE;
3319 }
3320 
3321 
3322 /*
3323  * Displays the FormatPartitionPage.
3324  *
3325  * Next pages:
3326  *  InstallDirectoryPage (At once if IsUnattendedSetup or InstallShortcut)
3327  *  SelectPartitionPage  (At once)
3328  *  QuitPage
3329  *
3330  * SIDEEFFECTS
3331  *  Sets InstallPartition->FormatState
3332  *  Sets USetupData.DestinationRootPath
3333  *
3334  * RETURNS
3335  *   Number of the next page.
3336  */
3337 static PAGE_NUMBER
3338 FormatPartitionPage(PINPUT_RECORD Ir)
3339 {
3340     NTSTATUS Status;
3341     PPARTENTRY PartEntry;
3342     PDISKENTRY DiskEntry;
3343     PFILE_SYSTEM_ITEM SelectedFileSystem;
3344     UNICODE_STRING PartitionRootPath;
3345     WCHAR PathBuffer[MAX_PATH];
3346     CHAR Buffer[MAX_PATH];
3347 
3348 #ifndef NDEBUG
3349     ULONG Line;
3350     ULONG i;
3351     PPARTITION_INFORMATION PartitionInfo;
3352 #endif
3353 
3354     DPRINT("FormatPartitionPage()\n");
3355 
3356     MUIDisplayPage(FORMAT_PARTITION_PAGE);
3357 
3358     if (PartitionList == NULL || TempPartition == NULL)
3359     {
3360         /* FIXME: show an error dialog */
3361         return QUIT_PAGE;
3362     }
3363 
3364     PartEntry = TempPartition;
3365     DiskEntry = TempPartition->DiskEntry;
3366 
3367     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3368 
3369     SelectedFileSystem = FileSystemList->Selected;
3370     ASSERT(SelectedFileSystem && SelectedFileSystem->FileSystem);
3371 
3372     while (TRUE)
3373     {
3374         if (!IsUnattendedSetup)
3375             CONSOLE_ConInKey(Ir);
3376 
3377         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3378             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3379         {
3380             if (ConfirmQuit(Ir))
3381             {
3382                 /* Reset the filesystem list */
3383                 ResetFileSystemList();
3384                 return QUIT_PAGE;
3385             }
3386 
3387             break;
3388         }
3389         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN || IsUnattendedSetup) /* ENTER */
3390         {
3391             CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
3392 
3393             if (!PreparePartitionForFormatting(PartEntry, SelectedFileSystem->FileSystem))
3394             {
3395                 /* FIXME: show an error dialog */
3396 
3397                 /* Reset the filesystem list */
3398                 ResetFileSystemList();
3399 
3400                 return QUIT_PAGE;
3401             }
3402 
3403 #ifndef NDEBUG
3404             CONSOLE_PrintTextXY(6, 12,
3405                                 "Cylinders: %I64u  Tracks/Cyl: %lu  Sectors/Trk: %lu  Bytes/Sec: %lu  %c",
3406                                 DiskEntry->Cylinders,
3407                                 DiskEntry->TracksPerCylinder,
3408                                 DiskEntry->SectorsPerTrack,
3409                                 DiskEntry->BytesPerSector,
3410                                 DiskEntry->Dirty ? '*' : ' ');
3411 
3412             Line = 13;
3413 
3414             for (i = 0; i < DiskEntry->LayoutBuffer->PartitionCount; i++)
3415             {
3416                 PartitionInfo = &DiskEntry->LayoutBuffer->PartitionEntry[i];
3417 
3418                 CONSOLE_PrintTextXY(6, Line,
3419                                     "%2u:  %2lu  %c  %12I64u  %12I64u  %02x",
3420                                     i,
3421                                     PartitionInfo->PartitionNumber,
3422                                     PartitionInfo->BootIndicator ? 'A' : '-',
3423                                     PartitionInfo->StartingOffset.QuadPart / DiskEntry->BytesPerSector,
3424                                     PartitionInfo->PartitionLength.QuadPart / DiskEntry->BytesPerSector,
3425                                     PartitionInfo->PartitionType);
3426                 Line++;
3427             }
3428 #endif
3429 
3430             /* Commit the partition changes to the disk */
3431             Status = WritePartitions(DiskEntry);
3432             if (!NT_SUCCESS(Status))
3433             {
3434                 DPRINT1("WritePartitions(disk %lu) failed, Status 0x%08lx\n",
3435                         DiskEntry->DiskNumber, Status);
3436 
3437                 MUIDisplayError(ERROR_WRITE_PTABLE, Ir, POPUP_WAIT_ENTER);
3438 
3439                 /* Reset the filesystem list */
3440                 ResetFileSystemList();
3441 
3442                 return QUIT_PAGE;
3443             }
3444 
3445             /* Set PartitionRootPath */
3446             RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
3447                     L"\\Device\\Harddisk%lu\\Partition%lu",
3448                     DiskEntry->DiskNumber,
3449                     PartEntry->PartitionNumber);
3450             RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
3451             DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath);
3452 
3453             /* Format the partition */
3454             Status = FormatPartition(&PartitionRootPath,
3455                                      SelectedFileSystem->FileSystem,
3456                                      SelectedFileSystem->QuickFormat);
3457             if (Status == STATUS_NOT_SUPPORTED)
3458             {
3459                 sprintf(Buffer,
3460                         "Setup is currently unable to format a partition in %S.\n"
3461                         "\n"
3462                         "  \x07  Press ENTER to continue Setup.\n"
3463                         "  \x07  Press F3 to quit Setup.",
3464                         SelectedFileSystem->FileSystem);
3465 
3466                 PopupError(Buffer,
3467                            MUIGetString(STRING_QUITCONTINUE),
3468                            NULL, POPUP_WAIT_NONE);
3469 
3470                 while (TRUE)
3471                 {
3472                     CONSOLE_ConInKey(Ir);
3473 
3474                     if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00 &&
3475                         Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)  /* F3 */
3476                     {
3477                         if (ConfirmQuit(Ir))
3478                         {
3479                             /* Reset the filesystem list */
3480                             ResetFileSystemList();
3481                             return QUIT_PAGE;
3482                         }
3483                         else
3484                         {
3485                             return SELECT_FILE_SYSTEM_PAGE;
3486                         }
3487                     }
3488                     else if (Ir->Event.KeyEvent.uChar.AsciiChar == VK_RETURN) /* ENTER */
3489                     {
3490                         return SELECT_FILE_SYSTEM_PAGE;
3491                     }
3492                 }
3493             }
3494             else if (!NT_SUCCESS(Status))
3495             {
3496                 DPRINT1("FormatPartition() failed with status 0x%08lx\n", Status);
3497                 MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, PathBuffer);
3498 
3499                 /* Reset the filesystem list */
3500                 ResetFileSystemList();
3501 
3502                 return QUIT_PAGE;
3503             }
3504 
3505 //
3506 // TODO: Here, call a partlist.c function that update the actual FS name
3507 // and the label fields of the volume.
3508 //
3509             PartEntry->FormatState = Formatted;
3510             // PartEntry->FileSystem  = FileSystem;
3511             PartEntry->New = FALSE;
3512 
3513 #ifndef NDEBUG
3514             CONSOLE_SetStatusText("   Done.  Press any key ...");
3515             CONSOLE_ConInKey(Ir);
3516 #endif
3517 
3518             return SELECT_FILE_SYSTEM_PAGE;
3519         }
3520     }
3521 
3522     return FORMAT_PARTITION_PAGE;
3523 }
3524 
3525 
3526 /*
3527  * Displays the CheckFileSystemPage.
3528  *
3529  * Next pages:
3530  *  InstallDirectoryPage (At once)
3531  *  QuitPage
3532  *
3533  * SIDEEFFECTS
3534  *  Inits or reloads FileSystemList
3535  *
3536  * RETURNS
3537  *   Number of the next page.
3538  */
3539 static PAGE_NUMBER
3540 CheckFileSystemPage(PINPUT_RECORD Ir)
3541 {
3542     NTSTATUS Status;
3543     PDISKENTRY DiskEntry;
3544     PPARTENTRY PartEntry;
3545     UNICODE_STRING PartitionRootPath;
3546     WCHAR PathBuffer[MAX_PATH];
3547     CHAR Buffer[MAX_PATH];
3548 
3549     if (PartitionList == NULL)
3550     {
3551         /* FIXME: show an error dialog */
3552         return QUIT_PAGE;
3553     }
3554 
3555     if (!GetNextUncheckedPartition(PartitionList, &DiskEntry, &PartEntry))
3556     {
3557         return INSTALL_DIRECTORY_PAGE;
3558     }
3559 
3560     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3561 
3562     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHECKINGPART));
3563 
3564     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
3565 
3566     DPRINT1("CheckFileSystemPage -- PartitionType: 0x%02X ; FileSystem: %S\n",
3567             PartEntry->PartitionType, (*PartEntry->FileSystem ? PartEntry->FileSystem : L"n/a"));
3568 
3569     /* HACK: Do not try to check a partition with an unknown filesystem */
3570     if (!*PartEntry->FileSystem)
3571     {
3572         PartEntry->NeedsCheck = FALSE;
3573         return CHECK_FILE_SYSTEM_PAGE;
3574     }
3575 
3576     /* Set PartitionRootPath */
3577     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
3578             L"\\Device\\Harddisk%lu\\Partition%lu",
3579             DiskEntry->DiskNumber,
3580             PartEntry->PartitionNumber);
3581     RtlInitUnicodeString(&PartitionRootPath, PathBuffer);
3582     DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath);
3583 
3584     /* Check the partition */
3585     Status = ChkdskPartition(&PartitionRootPath, PartEntry->FileSystem);
3586     if (Status == STATUS_NOT_SUPPORTED)
3587     {
3588         /*
3589          * Partition checking is not supported with the current filesystem,
3590          * so disable FS checks on it.
3591          */
3592         PartEntry->NeedsCheck = FALSE;
3593 
3594         RtlStringCbPrintfA(Buffer,
3595                            sizeof(Buffer),
3596                            "Setup is currently unable to check a partition formatted in %S.\n"
3597                            "\n"
3598                            "  \x07  Press ENTER to continue Setup.\n"
3599                            "  \x07  Press F3 to quit Setup.",
3600                            PartEntry->FileSystem);
3601 
3602         PopupError(Buffer,
3603                    MUIGetString(STRING_QUITCONTINUE),
3604                    NULL, POPUP_WAIT_NONE);
3605 
3606         while (TRUE)
3607         {
3608             CONSOLE_ConInKey(Ir);
3609 
3610             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00 &&
3611                 Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)  /* F3 */
3612             {
3613                 if (ConfirmQuit(Ir))
3614                     return QUIT_PAGE;
3615                 else
3616                     return CHECK_FILE_SYSTEM_PAGE;
3617             }
3618             else if (Ir->Event.KeyEvent.uChar.AsciiChar == VK_RETURN) /* ENTER */
3619             {
3620                 return CHECK_FILE_SYSTEM_PAGE;
3621             }
3622         }
3623     }
3624     else if (!NT_SUCCESS(Status))
3625     {
3626         DPRINT("ChkdskPartition() failed with status 0x%08lx\n", Status);
3627         sprintf(Buffer, "ChkDsk detected some disk errors.\n(Status 0x%08lx).\n", Status);
3628         PopupError(Buffer,
3629                    MUIGetString(STRING_CONTINUE),
3630                    Ir, POPUP_WAIT_ENTER);
3631     }
3632 
3633     PartEntry->NeedsCheck = FALSE;
3634     return CHECK_FILE_SYSTEM_PAGE;
3635 }
3636 
3637 
3638 static BOOLEAN
3639 IsValidPath(
3640     IN PCWSTR InstallDir)
3641 {
3642     UINT i, Length;
3643 
3644     Length = wcslen(InstallDir);
3645 
3646     // TODO: Add check for 8.3 too.
3647 
3648     /* Path must be at least 2 characters long */
3649 //    if (Length < 2)
3650 //        return FALSE;
3651 
3652     /* Path must start with a backslash */
3653 //    if (InstallDir[0] != L'\\')
3654 //        return FALSE;
3655 
3656     /* Path must not end with a backslash */
3657     if (InstallDir[Length - 1] == L'\\')
3658         return FALSE;
3659 
3660     /* Path must not contain whitespace characters */
3661     for (i = 0; i < Length; i++)
3662     {
3663         if (iswspace(InstallDir[i]))
3664             return FALSE;
3665     }
3666 
3667     /* Path component must not end with a dot */
3668     for (i = 0; i < Length; i++)
3669     {
3670         if (InstallDir[i] == L'\\' && i > 0)
3671         {
3672             if (InstallDir[i - 1] == L'.')
3673                 return FALSE;
3674         }
3675     }
3676 
3677     if (InstallDir[Length - 1] == L'.')
3678         return FALSE;
3679 
3680     return TRUE;
3681 }
3682 
3683 
3684 /*
3685  * Displays the InstallDirectoryPage.
3686  *
3687  * Next pages:
3688  *  PrepareCopyPage
3689  *  QuitPage
3690  *
3691  * RETURNS
3692  *   Number of the next page.
3693  */
3694 static PAGE_NUMBER
3695 InstallDirectoryPage(PINPUT_RECORD Ir)
3696 {
3697     NTSTATUS Status;
3698     ULONG Length, Pos;
3699     WCHAR c;
3700     WCHAR InstallDir[MAX_PATH];
3701 
3702     /* We do not need the filesystem list anymore */
3703     ResetFileSystemList();
3704 
3705     if (PartitionList == NULL || InstallPartition == NULL)
3706     {
3707         /* FIXME: show an error dialog */
3708         return QUIT_PAGE;
3709     }
3710 
3711     // if (IsUnattendedSetup)
3712     if (RepairUpdateFlag)
3713         wcscpy(InstallDir, CurrentInstallation->PathComponent); // SystemNtPath
3714     else if (USetupData.InstallationDirectory[0])
3715         wcscpy(InstallDir, USetupData.InstallationDirectory);
3716     else
3717         wcscpy(InstallDir, L"\\ReactOS");
3718 
3719     /*
3720      * Check the validity of the predefined 'InstallDir'. If we are either
3721      * in unattended setup or in update/repair mode, and the installation path
3722      * is valid, just perform the installation. Otherwise (either in the case
3723      * of an invalid path, or we are in regular setup), display the UI and allow
3724      * the user to specify a new installation path.
3725      */
3726     if ((RepairUpdateFlag || IsUnattendedSetup) && IsValidPath(InstallDir))
3727     {
3728         Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition);
3729         if (!NT_SUCCESS(Status))
3730         {
3731             DPRINT1("InitDestinationPaths() failed. Status code: 0x%lx", Status);
3732             MUIDisplayError(ERROR_NO_BUILD_PATH, Ir, POPUP_WAIT_ENTER);
3733             return QUIT_PAGE;
3734         }
3735 
3736         /*
3737          * Check whether the user attempts to install ReactOS within the
3738          * installation source directory, or in a subdirectory thereof.
3739          * If so, fail with an error.
3740          */
3741         if (RtlPrefixUnicodeString(&USetupData.SourcePath, &USetupData.DestinationPath, TRUE))
3742         {
3743             MUIDisplayError(ERROR_SOURCE_DIR, Ir, POPUP_WAIT_ENTER);
3744             return INSTALL_DIRECTORY_PAGE;
3745         }
3746 
3747         return PREPARE_COPY_PAGE;
3748     }
3749 
3750     Length = wcslen(InstallDir);
3751     Pos = Length;
3752 
3753     MUIDisplayPage(INSTALL_DIRECTORY_PAGE);
3754     CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3755     CONSOLE_SetCursorXY(8 + Pos, 11);
3756     CONSOLE_SetCursorType(TRUE, TRUE);
3757 
3758     while (TRUE)
3759     {
3760         CONSOLE_ConInKey(Ir);
3761 
3762         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3763             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3764         {
3765             CONSOLE_SetCursorType(TRUE, FALSE);
3766 
3767             if (ConfirmQuit(Ir))
3768                 return QUIT_PAGE;
3769 
3770             CONSOLE_SetCursorType(TRUE, TRUE);
3771             break;
3772         }
3773         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3774                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DELETE))  /* DEL */
3775         {
3776             if (Pos < Length)
3777             {
3778                 memmove(&InstallDir[Pos],
3779                         &InstallDir[Pos + 1],
3780                         (Length - Pos - 1) * sizeof(WCHAR));
3781                 InstallDir[Length - 1] = UNICODE_NULL;
3782 
3783                 Length--;
3784                 CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3785                 CONSOLE_SetCursorXY(8 + Pos, 11);
3786             }
3787         }
3788         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3789                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
3790         {
3791             Pos = 0;
3792             CONSOLE_SetCursorXY(8 + Pos, 11);
3793         }
3794         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3795                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
3796         {
3797             Pos = Length;
3798             CONSOLE_SetCursorXY(8 + Pos, 11);
3799         }
3800         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3801                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_LEFT))  /* LEFT */
3802         {
3803             if (Pos > 0)
3804             {
3805                 Pos--;
3806                 CONSOLE_SetCursorXY(8 + Pos, 11);
3807             }
3808         }
3809         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3810                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT))  /* RIGHT */
3811         {
3812             if (Pos < Length)
3813             {
3814                 Pos++;
3815                 CONSOLE_SetCursorXY(8 + Pos, 11);
3816             }
3817         }
3818         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
3819         {
3820             CONSOLE_SetCursorType(TRUE, FALSE);
3821 
3822             /*
3823              * Check for the validity of the installation directory and pop up
3824              * an error if it is not the case. Then the user can fix its input.
3825              */
3826             if (!IsValidPath(InstallDir))
3827             {
3828                 MUIDisplayError(ERROR_DIRECTORY_NAME, Ir, POPUP_WAIT_ENTER);
3829                 return INSTALL_DIRECTORY_PAGE;
3830             }
3831 
3832             Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition);
3833             if (!NT_SUCCESS(Status))
3834             {
3835                 DPRINT1("InitDestinationPaths() failed. Status code: 0x%lx", Status);
3836                 MUIDisplayError(ERROR_NO_BUILD_PATH, Ir, POPUP_WAIT_ENTER);
3837                 return QUIT_PAGE;
3838             }
3839 
3840             /*
3841              * Check whether the user attempts to install ReactOS within the
3842              * installation source directory, or in a subdirectory thereof.
3843              * If so, fail with an error.
3844              */
3845             if (RtlPrefixUnicodeString(&USetupData.SourcePath, &USetupData.DestinationPath, TRUE))
3846             {
3847                 MUIDisplayError(ERROR_SOURCE_DIR, Ir, POPUP_WAIT_ENTER);
3848                 return INSTALL_DIRECTORY_PAGE;
3849             }
3850 
3851             return PREPARE_COPY_PAGE;
3852         }
3853         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x08) /* BACKSPACE */
3854         {
3855             if (Pos > 0)
3856             {
3857                 if (Pos < Length)
3858                     memmove(&InstallDir[Pos - 1],
3859                             &InstallDir[Pos],
3860                             (Length - Pos) * sizeof(WCHAR));
3861                 InstallDir[Length - 1] = UNICODE_NULL;
3862 
3863                 Pos--;
3864                 Length--;
3865                 CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3866                 CONSOLE_SetCursorXY(8 + Pos, 11);
3867             }
3868         }
3869         else if (isprint(Ir->Event.KeyEvent.uChar.AsciiChar))
3870         {
3871             if (Length < 50)
3872             {
3873                 c = (WCHAR)Ir->Event.KeyEvent.uChar.AsciiChar;
3874                 if (iswalpha(c) || iswdigit(c) || c == '.' || c == '\\' || c == '-' || c == '_')
3875                 {
3876                     if (Pos < Length)
3877                         memmove(&InstallDir[Pos + 1],
3878                                 &InstallDir[Pos],
3879                                 (Length - Pos) * sizeof(WCHAR));
3880                     InstallDir[Length + 1] = UNICODE_NULL;
3881                     InstallDir[Pos] = c;
3882 
3883                     Pos++;
3884                     Length++;
3885                     CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3886                     CONSOLE_SetCursorXY(8 + Pos, 11);
3887                 }
3888             }
3889         }
3890     }
3891 
3892     return INSTALL_DIRECTORY_PAGE;
3893 }
3894 
3895 
3896 // PSETUP_ERROR_ROUTINE
3897 static VOID
3898 __cdecl
3899 USetupErrorRoutine(
3900     IN PUSETUP_DATA pSetupData,
3901     ...)
3902 {
3903     INPUT_RECORD Ir;
3904     va_list arg_ptr;
3905 
3906     va_start(arg_ptr, pSetupData);
3907 
3908     if (pSetupData->LastErrorNumber >= ERROR_SUCCESS &&
3909         pSetupData->LastErrorNumber <  ERROR_LAST_ERROR_CODE)
3910     {
3911         // Note: the "POPUP_WAIT_ENTER" actually depends on the LastErrorNumber...
3912         MUIDisplayErrorV(pSetupData->LastErrorNumber, &Ir, POPUP_WAIT_ENTER, arg_ptr);
3913     }
3914 
3915     va_end(arg_ptr);
3916 }
3917 
3918 /*
3919  * Displays the PrepareCopyPage.
3920  *
3921  * Next pages:
3922  *  FileCopyPage(At once)
3923  *  QuitPage
3924  *
3925  * SIDEEFFECTS
3926  * Calls PrepareFileCopy
3927  *
3928  * RETURNS
3929  *   Number of the next page.
3930  */
3931 static PAGE_NUMBER
3932 PrepareCopyPage(PINPUT_RECORD Ir)
3933 {
3934     // ERROR_NUMBER ErrorNumber;
3935     BOOLEAN Success;
3936 
3937     MUIDisplayPage(PREPARE_COPY_PAGE);
3938 
3939     /* ErrorNumber = */ Success = PrepareFileCopy(&USetupData, NULL);
3940     if (/*ErrorNumber != ERROR_SUCCESS*/ !Success)
3941     {
3942         // MUIDisplayError(ErrorNumber, Ir, POPUP_WAIT_ENTER);
3943         return QUIT_PAGE;
3944     }
3945 
3946     return FILE_COPY_PAGE;
3947 }
3948 
3949 typedef struct _COPYCONTEXT
3950 {
3951     ULONG TotalOperations;
3952     ULONG CompletedOperations;
3953     PPROGRESSBAR ProgressBar;
3954     PPROGRESSBAR MemoryBars[4];
3955 } COPYCONTEXT, *PCOPYCONTEXT;
3956 
3957 static VOID
3958 SetupUpdateMemoryInfo(IN PCOPYCONTEXT CopyContext,
3959                       IN BOOLEAN First)
3960 {
3961     SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
3962 
3963     /* Get the memory information from the system */
3964     NtQuerySystemInformation(SystemPerformanceInformation,
3965                              &PerfInfo,
3966                              sizeof(PerfInfo),
3967                              NULL);
3968 
3969     /* Check if this is initial setup */
3970     if (First)
3971     {
3972         /* Set maximum limits to be total RAM pages */
3973         ProgressSetStepCount(CopyContext->MemoryBars[0], PerfInfo.CommitLimit);
3974         ProgressSetStepCount(CopyContext->MemoryBars[1], PerfInfo.CommitLimit);
3975         ProgressSetStepCount(CopyContext->MemoryBars[2], PerfInfo.CommitLimit);
3976     }
3977 
3978     /* Set current values */
3979     ProgressSetStep(CopyContext->MemoryBars[0], PerfInfo.PagedPoolPages + PerfInfo.NonPagedPoolPages);
3980     ProgressSetStep(CopyContext->MemoryBars[1], PerfInfo.ResidentSystemCachePage);
3981     ProgressSetStep(CopyContext->MemoryBars[2], PerfInfo.AvailablePages);
3982 }
3983 
3984 static UINT
3985 CALLBACK
3986 FileCopyCallback(PVOID Context,
3987                  UINT Notification,
3988                  UINT_PTR Param1,
3989                  UINT_PTR Param2)
3990 {
3991     PCOPYCONTEXT CopyContext = (PCOPYCONTEXT)Context;
3992     PFILEPATHS_W FilePathInfo;
3993     PCWSTR SrcFileName, DstFileName;
3994 
3995     switch (Notification)
3996     {
3997         case SPFILENOTIFY_STARTSUBQUEUE:
3998         {
3999             CopyContext->TotalOperations = (ULONG)Param2;
4000             CopyContext->CompletedOperations = 0;
4001             ProgressSetStepCount(CopyContext->ProgressBar,
4002                                  CopyContext->TotalOperations);
4003             SetupUpdateMemoryInfo(CopyContext, TRUE);
4004             break;
4005         }
4006 
4007         case SPFILENOTIFY_STARTDELETE:
4008         case SPFILENOTIFY_STARTRENAME:
4009         case SPFILENOTIFY_STARTCOPY:
4010         {
4011             FilePathInfo = (PFILEPATHS_W)Param1;
4012 
4013             if (Notification == SPFILENOTIFY_STARTDELETE)
4014             {
4015                 /* Display delete message */
4016                 ASSERT(Param2 == FILEOP_DELETE);
4017 
4018                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
4019                 if (DstFileName) ++DstFileName;
4020                 else DstFileName = FilePathInfo->Target;
4021 
4022                 CONSOLE_SetStatusText(MUIGetString(STRING_DELETING),
4023                                       DstFileName);
4024             }
4025             else if (Notification == SPFILENOTIFY_STARTRENAME)
4026             {
4027                 /* Display move/rename message */
4028                 ASSERT(Param2 == FILEOP_RENAME);
4029 
4030                 SrcFileName = wcsrchr(FilePathInfo->Source, L'\\');
4031                 if (SrcFileName) ++SrcFileName;
4032                 else SrcFileName = FilePathInfo->Source;
4033 
4034                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
4035                 if (DstFileName) ++DstFileName;
4036                 else DstFileName = FilePathInfo->Target;
4037 
4038                 if (!wcsicmp(SrcFileName, DstFileName))
4039                     Param2 = STRING_MOVING;
4040                 else
4041                     Param2 = STRING_RENAMING;
4042 
4043                 CONSOLE_SetStatusText(MUIGetString(Param2),
4044                                       SrcFileName, DstFileName);
4045             }
4046             else if (Notification == SPFILENOTIFY_STARTCOPY)
4047             {
4048                 /* Display copy message */
4049                 ASSERT(Param2 == FILEOP_COPY);
4050 
4051                 /* NOTE: When extracting from CABs the Source is the CAB name */
4052                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
4053                 if (DstFileName) ++DstFileName;
4054                 else DstFileName = FilePathInfo->Target;
4055 
4056                 CONSOLE_SetStatusText(MUIGetString(STRING_COPYING),
4057                                       DstFileName);
4058             }
4059 
4060             SetupUpdateMemoryInfo(CopyContext, FALSE);
4061             break;
4062         }
4063 
4064         case SPFILENOTIFY_COPYERROR:
4065         {
4066             FilePathInfo = (PFILEPATHS_W)Param1;
4067 
4068             DPRINT1("An error happened while trying to copy file '%S' (error 0x%08lx), skipping it...\n",
4069                     FilePathInfo->Target, FilePathInfo->Win32Error);
4070             return FILEOP_SKIP;
4071         }
4072 
4073         case SPFILENOTIFY_ENDDELETE:
4074         case SPFILENOTIFY_ENDRENAME:
4075         case SPFILENOTIFY_ENDCOPY:
4076         {
4077             CopyContext->CompletedOperations++;
4078 
4079             /* SYSREG checkpoint */
4080             if (CopyContext->TotalOperations >> 1 == CopyContext->CompletedOperations)
4081                 DPRINT1("CHECKPOINT:HALF_COPIED\n");
4082 
4083             ProgressNextStep(CopyContext->ProgressBar);
4084             SetupUpdateMemoryInfo(CopyContext, FALSE);
4085             break;
4086         }
4087     }
4088 
4089     return FILEOP_DOIT;
4090 }
4091 
4092 
4093 /*
4094  * Displays the FileCopyPage.
4095  *
4096  * Next pages:
4097  *  RegistryPage(At once)
4098  *
4099  * SIDEEFFECTS
4100  *  Calls DoFileCopy
4101  *
4102  * RETURNS
4103  *   Number of the next page.
4104  */
4105 static PAGE_NUMBER
4106 FileCopyPage(PINPUT_RECORD Ir)
4107 {
4108     COPYCONTEXT CopyContext;
4109     UINT MemBarWidth;
4110 
4111     MUIDisplayPage(FILE_COPY_PAGE);
4112 
4113     /* Create context for the copy process */
4114     CopyContext.TotalOperations = 0;
4115     CopyContext.CompletedOperations = 0;
4116 
4117     /* Create the progress bar as well */
4118     CopyContext.ProgressBar = CreateProgressBar(13,
4119                                                 26,
4120                                                 xScreen - 13,
4121                                                 yScreen - 20,
4122                                                 10,
4123                                                 24,
4124                                                 TRUE,
4125                                                 MUIGetString(STRING_SETUPCOPYINGFILES));
4126 
4127     // fit memory bars to screen width, distribute them uniform
4128     MemBarWidth = (xScreen - 26) / 5;
4129     MemBarWidth -= MemBarWidth % 2;  // make even
4130     /* ATTENTION: The following progress bars are debug stuff, which should not be translated!! */
4131     /* Create the paged pool progress bar */
4132     CopyContext.MemoryBars[0] = CreateProgressBar(13,
4133                                                   40,
4134                                                   13 + MemBarWidth,
4135                                                   43,
4136                                                   13,
4137                                                   44,
4138                                                   FALSE,
4139                                                   "Kernel Pool");
4140 
4141     /* Create the non paged pool progress bar */
4142     CopyContext.MemoryBars[1] = CreateProgressBar((xScreen / 2)- (MemBarWidth / 2),
4143                                                   40,
4144                                                   (xScreen / 2) + (MemBarWidth / 2),
4145                                                   43,
4146                                                   (xScreen / 2)- (MemBarWidth / 2),
4147                                                   44,
4148                                                   FALSE,
4149                                                   "Kernel Cache");
4150 
4151     /* Create the global memory progress bar */
4152     CopyContext.MemoryBars[2] = CreateProgressBar(xScreen - 13 - MemBarWidth,
4153                                                   40,
4154                                                   xScreen - 13,
4155                                                   43,
4156                                                   xScreen - 13 - MemBarWidth,
4157                                                   44,
4158                                                   FALSE,
4159                                                   "Free Memory");
4160 
4161     /* Do the file copying */
4162     DoFileCopy(&USetupData, FileCopyCallback, &CopyContext);
4163 
4164     /* If we get here, we're done, so cleanup the progress bar */
4165     DestroyProgressBar(CopyContext.ProgressBar);
4166     DestroyProgressBar(CopyContext.MemoryBars[0]);
4167     DestroyProgressBar(CopyContext.MemoryBars[1]);
4168     DestroyProgressBar(CopyContext.MemoryBars[2]);
4169 
4170     /* Create the $winnt$.inf file */
4171     InstallSetupInfFile(&USetupData);
4172 
4173     /* Go display the next page */
4174     return REGISTRY_PAGE;
4175 }
4176 
4177 
4178 static VOID
4179 __cdecl
4180 RegistryStatus(IN REGISTRY_STATUS RegStatus, ...)
4181 {
4182     /* WARNING: Please keep this lookup table in sync with the resources! */
4183     static const UINT StringIDs[] =
4184     {
4185         STRING_DONE,                    /* Success */
4186         STRING_REGHIVEUPDATE,           /* RegHiveUpdate */
4187         STRING_IMPORTFILE,              /* ImportRegHive */
4188         STRING_DISPLAYSETTINGSUPDATE,   /* DisplaySettingsUpdate */
4189         STRING_LOCALESETTINGSUPDATE,    /* LocaleSettingsUpdate */
4190         STRING_ADDKBLAYOUTS,            /* KeybLayouts */
4191         STRING_KEYBOARDSETTINGSUPDATE,  /* KeybSettingsUpdate */
4192         STRING_CODEPAGEINFOUPDATE,      /* CodePageInfoUpdate */
4193     };
4194 
4195     va_list args;
4196 
4197     if (RegStatus < ARRAYSIZE(StringIDs))
4198     {
4199         va_start(args, RegStatus);
4200         CONSOLE_SetStatusTextV(MUIGetString(StringIDs[RegStatus]), args);
4201         va_end(args);
4202     }
4203     else
4204     {
4205         CONSOLE_SetStatusText("Unknown status %d", RegStatus);
4206     }
4207 }
4208 
4209 /*
4210  * Displays the RegistryPage.
4211  *
4212  * Next pages:
4213  *  SuccessPage (if RepairUpdate)
4214  *  BootLoaderPage (default)
4215  *  QuitPage
4216  *
4217  * SIDEEFFECTS
4218  *  Calls UpdateRegistry
4219  *
4220  * RETURNS
4221  *   Number of the next page.
4222  */
4223 static PAGE_NUMBER
4224 RegistryPage(PINPUT_RECORD Ir)
4225 {
4226     ULONG Error;
4227 
4228     MUIDisplayPage(REGISTRY_PAGE);
4229 
4230     Error = UpdateRegistry(&USetupData,
4231                            RepairUpdateFlag,
4232                            PartitionList,
4233                            InstallPartition->DriveLetter,
4234                            SelectedLanguageId,
4235                            RegistryStatus);
4236     if (Error != ERROR_SUCCESS)
4237     {
4238         MUIDisplayError(Error, Ir, POPUP_WAIT_ENTER);
4239         return QUIT_PAGE;
4240     }
4241     else
4242     {
4243         CONSOLE_SetStatusText(MUIGetString(STRING_DONE));
4244         return BOOT_LOADER_PAGE;
4245     }
4246 }
4247 
4248 
4249 /*
4250  * Displays the BootLoaderPage.
4251  *
4252  * Next pages:
4253  *  SuccessPage (if RepairUpdate)
4254  *  BootLoaderHarddiskMbrPage
4255  *  BootLoaderHarddiskVbrPage
4256  *  BootLoaderFloppyPage
4257  *  SuccessPage
4258  *  QuitPage
4259  *
4260  * SIDEEFFECTS
4261  *  Calls RegInitializeRegistry
4262  *  Calls ImportRegistryFile
4263  *  Calls SetDefaultPagefile
4264  *  Calls SetMountedDeviceValues
4265  *
4266  * RETURNS
4267  *   Number of the next page.
4268  */
4269 static PAGE_NUMBER
4270 BootLoaderPage(PINPUT_RECORD Ir)
4271 {
4272     UCHAR PartitionType;
4273     BOOLEAN InstallOnFloppy;
4274     USHORT Line = 12;
4275     WCHAR PathBuffer[MAX_PATH];
4276 
4277     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
4278 
4279     ASSERT(SystemPartition->IsPartitioned && SystemPartition->PartitionNumber != 0);
4280 
4281     RtlFreeUnicodeString(&USetupData.SystemRootPath);
4282     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
4283             L"\\Device\\Harddisk%lu\\Partition%lu\\",
4284             SystemPartition->DiskEntry->DiskNumber,
4285             SystemPartition->PartitionNumber);
4286     RtlCreateUnicodeString(&USetupData.SystemRootPath, PathBuffer);
4287     DPRINT1("SystemRootPath: %wZ\n", &USetupData.SystemRootPath);
4288 
4289     PartitionType = SystemPartition->PartitionType;
4290 
4291     /* For unattended setup, skip MBR installation or install on floppy if needed */
4292     if (IsUnattendedSetup)
4293     {
4294         if ((USetupData.MBRInstallType == 0) ||
4295             (USetupData.MBRInstallType == 1))
4296         {
4297             goto Quit;
4298         }
4299     }
4300 
4301     /*
4302      * We may install an MBR or VBR, but before that, check whether
4303      * we need to actually install the VBR on floppy.
4304      */
4305     if (PartitionType == PARTITION_ENTRY_UNUSED)
4306     {
4307         DPRINT("Error: system partition invalid (unused)\n");
4308         InstallOnFloppy = TRUE;
4309     }
4310     else if (PartitionType == PARTITION_OS2BOOTMGR)
4311     {
4312         /* OS/2 boot manager partition */
4313         DPRINT("Found OS/2 boot manager partition\n");
4314         InstallOnFloppy = TRUE;
4315     }
4316     else if (PartitionType == PARTITION_LINUX)
4317     {
4318         /* Linux partition */
4319         DPRINT("Found Linux native partition (ext2/ext3/ReiserFS/BTRFS/etc)\n");
4320         InstallOnFloppy = FALSE;
4321     }
4322     else if (PartitionType == PARTITION_IFS)
4323     {
4324         /* NTFS partition */
4325         DPRINT("Found NTFS partition\n");
4326 
4327         // FIXME: Make it FALSE when we'll support NTFS installation!
4328         InstallOnFloppy = TRUE;
4329     }
4330     else if ((PartitionType == PARTITION_FAT_12) ||
4331              (PartitionType == PARTITION_FAT_16) ||
4332              (PartitionType == PARTITION_HUGE)   ||
4333              (PartitionType == PARTITION_XINT13) ||
4334              (PartitionType == PARTITION_FAT32)  ||
4335              (PartitionType == PARTITION_FAT32_XINT13))
4336     {
4337         DPRINT("Found FAT partition\n");
4338         InstallOnFloppy = FALSE;
4339     }
4340     else
4341     {
4342         /* Unknown partition */
4343         DPRINT("Unknown partition found\n");
4344         InstallOnFloppy = TRUE;
4345     }
4346 
4347     /* We should install on floppy */
4348     if (InstallOnFloppy)
4349     {
4350         USetupData.MBRInstallType = 1;
4351         goto Quit;
4352     }
4353 
4354     /* Is it an unattended install on hdd? */
4355     if (IsUnattendedSetup)
4356     {
4357         if ((USetupData.MBRInstallType == 2) ||
4358             (USetupData.MBRInstallType == 3))
4359         {
4360             goto Quit;
4361         }
4362     }
4363 
4364     MUIDisplayPage(BOOT_LOADER_PAGE);
4365     CONSOLE_InvertTextXY(8, Line, 60, 1);
4366 
4367     while (TRUE)
4368     {
4369         CONSOLE_ConInKey(Ir);
4370 
4371         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4372             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
4373         {
4374             CONSOLE_NormalTextXY(8, Line, 60, 1);
4375 
4376             Line++;
4377             if (Line < 12)
4378                 Line = 15;
4379 
4380             if (Line > 15)
4381                 Line = 12;
4382 
4383             CONSOLE_InvertTextXY(8, Line, 60, 1);
4384         }
4385         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4386                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
4387         {
4388             CONSOLE_NormalTextXY(8, Line, 60, 1);
4389 
4390             Line--;
4391             if (Line < 12)
4392                 Line = 15;
4393 
4394             if (Line > 15)
4395                 Line = 12;
4396 
4397             CONSOLE_InvertTextXY(8, Line, 60, 1);
4398         }
4399         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4400                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
4401         {
4402             CONSOLE_NormalTextXY(8, Line, 60, 1);
4403 
4404             Line = 12;
4405 
4406             CONSOLE_InvertTextXY(8, Line, 60, 1);
4407         }
4408         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4409                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
4410         {
4411             CONSOLE_NormalTextXY(8, Line, 60, 1);
4412 
4413             Line = 15;
4414 
4415             CONSOLE_InvertTextXY(8, Line, 60, 1);
4416         }
4417         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4418                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
4419         {
4420             if (ConfirmQuit(Ir))
4421                 return QUIT_PAGE;
4422 
4423             break;
4424         }
4425         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
4426         {
4427             if (Line == 12)
4428             {
4429                 /* Install on both MBR and VBR */
4430                 USetupData.MBRInstallType = 2;
4431                 break;
4432             }
4433             else if (Line == 13)
4434             {
4435                 /* Install on VBR only */
4436                 USetupData.MBRInstallType = 3;
4437                 break;
4438             }
4439             else if (Line == 14)
4440             {
4441                 /* Install on floppy */
4442                 USetupData.MBRInstallType = 1;
4443                 break;
4444             }
4445             else if (Line == 15)
4446             {
4447                 /* Skip MBR installation */
4448                 USetupData.MBRInstallType = 0;
4449                 break;
4450             }
4451 
4452             return BOOT_LOADER_PAGE;
4453         }
4454     }
4455 
4456 Quit:
4457     switch (USetupData.MBRInstallType)
4458     {
4459         /* Skip MBR installation */
4460         case 0:
4461             return SUCCESS_PAGE;
4462 
4463         /* Install on floppy */
4464         case 1:
4465             return BOOT_LOADER_FLOPPY_PAGE;
4466 
4467         /* Install on both MBR and VBR */
4468         case 2:
4469             return BOOT_LOADER_HARDDISK_MBR_PAGE;
4470 
4471         /* Install on VBR only */
4472         case 3:
4473             return BOOT_LOADER_HARDDISK_VBR_PAGE;
4474     }
4475 
4476     return BOOT_LOADER_PAGE;
4477 }
4478 
4479 
4480 /*
4481  * Displays the BootLoaderFloppyPage.
4482  *
4483  * Next pages:
4484  *  SuccessPage (At once)
4485  *  QuitPage
4486  *
4487  * SIDEEFFECTS
4488  *  Calls InstallFatBootcodeToFloppy()
4489  *
4490  * RETURNS
4491  *   Number of the next page.
4492  */
4493 static PAGE_NUMBER
4494 BootLoaderFloppyPage(PINPUT_RECORD Ir)
4495 {
4496     NTSTATUS Status;
4497 
4498     MUIDisplayPage(BOOT_LOADER_FLOPPY_PAGE);
4499 
4500 //  CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
4501 
4502     while (TRUE)
4503     {
4504         CONSOLE_ConInKey(Ir);
4505 
4506         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4507             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
4508         {
4509             if (ConfirmQuit(Ir))
4510                 return QUIT_PAGE;
4511 
4512             break;
4513         }
4514         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
4515         {
4516             Status = InstallFatBootcodeToFloppy(&USetupData.SourceRootPath,
4517                                                 &USetupData.DestinationArcPath);
4518             if (!NT_SUCCESS(Status))
4519             {
4520                 if (Status == STATUS_DEVICE_NOT_READY)
4521                     MUIDisplayError(ERROR_NO_FLOPPY, Ir, POPUP_WAIT_ENTER);
4522 
4523                 /* TODO: Print error message */
4524                 return BOOT_LOADER_FLOPPY_PAGE;
4525             }
4526 
4527             return SUCCESS_PAGE;
4528         }
4529     }
4530 
4531     return BOOT_LOADER_FLOPPY_PAGE;
4532 }
4533 
4534 
4535 /*
4536  * Displays the BootLoaderHarddiskVbrPage.
4537  *
4538  * Next pages:
4539  *  SuccessPage (At once)
4540  *  QuitPage
4541  *
4542  * SIDEEFFECTS
4543  *  Calls InstallVBRToPartition()
4544  *
4545  * RETURNS
4546  *   Number of the next page.
4547  */
4548 static PAGE_NUMBER
4549 BootLoaderHarddiskVbrPage(PINPUT_RECORD Ir)
4550 {
4551     NTSTATUS Status;
4552 
4553     // FIXME! We must not use the partition type, but instead use the partition FileSystem!!
4554     Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4555                                    &USetupData.SourceRootPath,
4556                                    &USetupData.DestinationArcPath,
4557                                    SystemPartition->PartitionType);
4558     if (!NT_SUCCESS(Status))
4559     {
4560         MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4561                         SystemPartition->FileSystem);
4562         return QUIT_PAGE;
4563     }
4564 
4565     return SUCCESS_PAGE;
4566 }
4567 
4568 
4569 /*
4570  * Displays the BootLoaderHarddiskMbrPage.
4571  *
4572  * Next pages:
4573  *  SuccessPage (At once)
4574  *  QuitPage
4575  *
4576  * SIDEEFFECTS
4577  *  Calls InstallVBRToPartition()
4578  *  Calls InstallMbrBootCodeToDisk()
4579  *
4580  * RETURNS
4581  *   Number of the next page.
4582  */
4583 static PAGE_NUMBER
4584 BootLoaderHarddiskMbrPage(PINPUT_RECORD Ir)
4585 {
4586     NTSTATUS Status;
4587     WCHAR DestinationDevicePathBuffer[MAX_PATH];
4588 
4589     /* Step 1: Write the VBR */
4590     // FIXME! We must not use the partition type, but instead use the partition FileSystem!!
4591     Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4592                                    &USetupData.SourceRootPath,
4593                                    &USetupData.DestinationArcPath,
4594                                    SystemPartition->PartitionType);
4595     if (!NT_SUCCESS(Status))
4596     {
4597         MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4598                         SystemPartition->FileSystem);
4599         return QUIT_PAGE;
4600     }
4601 
4602     /* Step 2: Write the MBR if the disk containing the system partition is not a super-floppy */
4603     if (!IsSuperFloppy(SystemPartition->DiskEntry))
4604     {
4605         RtlStringCchPrintfW(DestinationDevicePathBuffer, ARRAYSIZE(DestinationDevicePathBuffer),
4606                 L"\\Device\\Harddisk%d\\Partition0",
4607                 SystemPartition->DiskEntry->DiskNumber);
4608         Status = InstallMbrBootCodeToDisk(&USetupData.SystemRootPath,
4609                                           &USetupData.SourceRootPath,
4610                                           DestinationDevicePathBuffer);
4611         if (!NT_SUCCESS(Status))
4612         {
4613             DPRINT1("InstallMbrBootCodeToDisk() failed (Status %lx)\n", Status);
4614             MUIDisplayError(ERROR_INSTALL_BOOTCODE, Ir, POPUP_WAIT_ENTER, L"MBR");
4615             return QUIT_PAGE;
4616         }
4617     }
4618 
4619     return SUCCESS_PAGE;
4620 }
4621 
4622 
4623 /**
4624  * @name ProgressTimeOutStringHandler
4625  *
4626  * Handles the generation (displaying) of the timeout
4627  * countdown to the screen dynamically.
4628  *
4629  * @param   Bar
4630  *     A pointer to a progress bar.
4631  *
4632  * @param   AlwaysUpdate
4633  *     Constantly update the progress bar (boolean type).
4634  *
4635  * @param   Buffer
4636  *     A pointer to a string buffer.
4637  *
4638  * @param   cchBufferSize
4639  *     The buffer's size in number of characters.
4640  *
4641  * @return
4642  *     TRUE or FALSE on function termination.
4643  *
4644  */
4645 static
4646 BOOLEAN NTAPI
4647 ProgressTimeOutStringHandler(
4648     IN PPROGRESSBAR Bar,
4649     IN BOOLEAN AlwaysUpdate,
4650     OUT PSTR Buffer,
4651     IN SIZE_T cchBufferSize)
4652 {
4653     ULONG OldProgress = Bar->Progress;
4654 
4655     if (Bar->StepCount == 0)
4656     {
4657         Bar->Progress = 0;
4658     }
4659     else
4660     {
4661         Bar->Progress = Bar->StepCount - Bar->CurrentStep;
4662     }
4663 
4664     /* Build the progress string if it has changed */
4665     if (Bar->ProgressFormatText &&
4666         (AlwaysUpdate || (Bar->Progress != OldProgress)))
4667     {
4668         RtlStringCchPrintfA(Buffer, cchBufferSize,
4669                             Bar->ProgressFormatText, Bar->Progress / max(1, Bar->Width) + 1);
4670 
4671         return TRUE;
4672     }
4673 
4674     return FALSE;
4675 }
4676 
4677 /**
4678  * @name ProgressCountdown
4679  *
4680  * Displays and draws a red-coloured progress bar with a countdown.
4681  * When the timeout is reached, the flush page is displayed for reboot.
4682  *
4683  * @param   Ir
4684  *     A pointer to an input keyboard record.
4685  *
4686  * @param   TimeOut
4687  *     Initial countdown value in seconds.
4688  *
4689  * @return
4690  *     Nothing.
4691  *
4692  */
4693 static VOID
4694 ProgressCountdown(
4695     IN PINPUT_RECORD Ir,
4696     IN LONG TimeOut)
4697 {
4698     NTSTATUS Status;
4699     ULONG StartTime, BarWidth, TimerDiv;
4700     LONG TimeElapsed;
4701     LONG TimerValue, OldTimerValue;
4702     LARGE_INTEGER Timeout;
4703     PPROGRESSBAR ProgressBar;
4704     BOOLEAN RefreshProgress = TRUE;
4705 
4706     /* Bail out if the timeout is already zero */
4707     if (TimeOut <= 0)
4708         return;
4709 
4710     /* Create the timeout progress bar and set it up */
4711     ProgressBar = CreateProgressBarEx(13,
4712                                       26,
4713                                       xScreen - 13,
4714                                       yScreen - 20,
4715                                       10,
4716                                       24,
4717                                       TRUE,
4718                                       FOREGROUND_RED | BACKGROUND_BLUE,
4719                                       0,
4720                                       NULL,
4721                                       MUIGetString(STRING_REBOOTPROGRESSBAR),
4722                                       ProgressTimeOutStringHandler);
4723 
4724     BarWidth = max(1, ProgressBar->Width);
4725     TimerValue = TimeOut * BarWidth;
4726     ProgressSetStepCount(ProgressBar, TimerValue);
4727 
4728     StartTime = NtGetTickCount();
4729     CONSOLE_Flush();
4730 
4731     TimerDiv = 1000 / BarWidth;
4732     TimerDiv = max(1, TimerDiv);
4733     OldTimerValue = TimerValue;
4734     while (TRUE)
4735     {
4736         /* Decrease the timer */
4737 
4738         /*
4739          * Compute how much time the previous operations took.
4740          * This allows us in particular to take account for any time
4741          * elapsed if something slowed down.
4742          */
4743         TimeElapsed = NtGetTickCount() - StartTime;
4744         if (TimeElapsed >= TimerDiv)
4745         {
4746             /* Increase StartTime by steps of 1 / ProgressBar->Width seconds */
4747             TimeElapsed /= TimerDiv;
4748             StartTime += (TimerDiv * TimeElapsed);
4749 
4750             if (TimeElapsed <= TimerValue)
4751                 TimerValue -= TimeElapsed;
4752             else
4753                 TimerValue = 0;
4754 
4755             RefreshProgress = TRUE;
4756         }
4757 
4758         if (RefreshProgress)
4759         {
4760             ProgressSetStep(ProgressBar, OldTimerValue - TimerValue);
4761             RefreshProgress = FALSE;
4762         }
4763 
4764         /* Stop when the timer reaches zero */
4765         if (TimerValue <= 0)
4766             break;
4767 
4768         /* Check for user key presses */
4769 
4770         /*
4771          * If the timer is used, use a passive wait of maximum 1 second
4772          * while monitoring for incoming console input events, so that
4773          * we are still able to display the timing count.
4774          */
4775 
4776         /* Wait a maximum of 1 second for input events */
4777         TimeElapsed = NtGetTickCount() - StartTime;
4778         if (TimeElapsed < TimerDiv)
4779         {
4780             /* Convert the time to NT Format */
4781             Timeout.QuadPart = (TimerDiv - TimeElapsed) * -10000LL;
4782             Status = NtWaitForSingleObject(StdInput, FALSE, &Timeout);
4783         }
4784         else
4785         {
4786             Status = STATUS_TIMEOUT;
4787         }
4788 
4789         /* Check whether the input event has been signaled, or a timeout happened */
4790         if (Status == STATUS_TIMEOUT)
4791         {
4792             continue;
4793         }
4794         if (Status != STATUS_WAIT_0)
4795         {
4796             /* An error happened, bail out */
4797             DPRINT1("NtWaitForSingleObject() failed, Status 0x%08lx\n", Status);
4798             break;
4799         }
4800 
4801         /* Check for an ENTER key press */
4802         while (CONSOLE_ConInKeyPeek(Ir))
4803         {
4804             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
4805             {
4806                 /* Found it, stop waiting */
4807                 goto Exit;
4808             }
4809         }
4810     }
4811 
4812 Exit:
4813     /* Destroy the progress bar and quit */
4814     DestroyProgressBar(ProgressBar);
4815 }
4816 
4817 
4818 /*
4819  * Displays the QuitPage.
4820  *
4821  * Next pages:
4822  *  FlushPage (At once)
4823  *
4824  * SIDEEFFECTS
4825  *  Destroy the Lists
4826  *
4827  * RETURNS
4828  *   Number of the next page.
4829  */
4830 static PAGE_NUMBER
4831 QuitPage(PINPUT_RECORD Ir)
4832 {
4833     MUIDisplayPage(QUIT_PAGE);
4834 
4835     /* Destroy the NTOS installations list */
4836     if (NtOsInstallsList != NULL)
4837     {
4838         DestroyGenericList(NtOsInstallsList, TRUE);
4839         NtOsInstallsList = NULL;
4840     }
4841 
4842     /* Destroy the partition list */
4843     if (PartitionList != NULL)
4844     {
4845         DestroyPartitionList(PartitionList);
4846         PartitionList = NULL;
4847     }
4848 
4849     /* Reset the formatter machine state */
4850     TempPartition = NULL;
4851     FormatState = Start;
4852 
4853     /* Destroy the filesystem list */
4854     ResetFileSystemList();
4855 
4856     CONSOLE_SetStatusText(MUIGetString(STRING_REBOOTCOMPUTER2));
4857 
4858     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4859     ProgressCountdown(Ir, 15);
4860     return FLUSH_PAGE;
4861 }
4862 
4863 
4864 /*
4865  * Displays the SuccessPage.
4866  *
4867  * Next pages:
4868  *  FlushPage (At once)
4869  *
4870  * SIDEEFFECTS
4871  *  Destroy the Lists
4872  *
4873  * RETURNS
4874  *   Number of the next page.
4875  */
4876 static PAGE_NUMBER
4877 SuccessPage(PINPUT_RECORD Ir)
4878 {
4879     MUIDisplayPage(SUCCESS_PAGE);
4880 
4881     if (IsUnattendedSetup)
4882         return FLUSH_PAGE;
4883 
4884     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4885     ProgressCountdown(Ir, 15);
4886     return FLUSH_PAGE;
4887 }
4888 
4889 
4890 /*
4891  * Displays the FlushPage.
4892  *
4893  * Next pages:
4894  *  RebootPage (At once)
4895  *
4896  * RETURNS
4897  *   Number of the next page.
4898  */
4899 static PAGE_NUMBER
4900 FlushPage(PINPUT_RECORD Ir)
4901 {
4902     MUIDisplayPage(FLUSH_PAGE);
4903     return REBOOT_PAGE;
4904 }
4905 
4906 
4907 /*
4908  * The start routine and page management
4909  */
4910 NTSTATUS
4911 RunUSetup(VOID)
4912 {
4913     NTSTATUS Status;
4914     INPUT_RECORD Ir;
4915     PAGE_NUMBER Page;
4916     BOOLEAN Old;
4917 
4918     InfSetHeap(ProcessHeap);
4919 
4920     /* Tell the Cm this is a setup boot, and it has to behave accordingly */
4921     Status = NtInitializeRegistry(CM_BOOT_FLAG_SETUP);
4922     if (!NT_SUCCESS(Status))
4923         DPRINT1("NtInitializeRegistry() failed (Status 0x%08lx)\n", Status);
4924 
4925     /* Initialize the user-mode PnP manager */
4926     Status = InitializeUserModePnpManager(&USetupData.SetupInf);
4927     if (!NT_SUCCESS(Status))
4928     {
4929         // PrintString(??);
4930         DPRINT1("The user-mode PnP manager could not initialize (Status 0x%08lx), expect unavailable devices!\n", Status);
4931     }
4932 
4933     if (!CONSOLE_Init())
4934     {
4935         PrintString(MUIGetString(STRING_CONSOLEFAIL1));
4936         PrintString(MUIGetString(STRING_CONSOLEFAIL2));
4937         PrintString(MUIGetString(STRING_CONSOLEFAIL3));
4938 
4939         /* We failed to initialize the video, just quit the installer */
4940         return STATUS_APP_INIT_FAILURE;
4941     }
4942 
4943     /* Initialize Setup, phase 0 */
4944     InitializeSetup(&USetupData, 0);
4945     USetupData.ErrorRoutine = USetupErrorRoutine;
4946 
4947     /* Hide the cursor and clear the screen and keyboard buffer */
4948     CONSOLE_SetCursorType(TRUE, FALSE);
4949     CONSOLE_ClearScreen();
4950     CONSOLE_Flush();
4951 
4952     /* Global Initialization page */
4953     Page = SetupStartPage(&Ir);
4954 
4955     while (Page != REBOOT_PAGE && Page != RECOVERY_PAGE)
4956     {
4957         CONSOLE_ClearScreen();
4958         CONSOLE_Flush();
4959 
4960         // CONSOLE_SetUnderlinedTextXY(4, 3, " ReactOS " KERNEL_VERSION_STR " Setup ");
4961         // CONSOLE_Flush();
4962 
4963         switch (Page)
4964         {
4965             /* Language page */
4966             case LANGUAGE_PAGE:
4967                 Page = LanguagePage(&Ir);
4968                 break;
4969 
4970             /* Welcome page */
4971             case WELCOME_PAGE:
4972                 Page = WelcomePage(&Ir);
4973                 break;
4974 
4975             /* License page */
4976             case LICENSE_PAGE:
4977                 Page = LicensePage(&Ir);
4978                 break;
4979 
4980             /* Install pages */
4981             case INSTALL_INTRO_PAGE:
4982                 Page = InstallIntroPage(&Ir);
4983                 break;
4984 
4985 #if 0
4986             case SCSI_CONTROLLER_PAGE:
4987                 Page = ScsiControllerPage(&Ir);
4988                 break;
4989 
4990             case OEM_DRIVER_PAGE:
4991                 Page = OemDriverPage(&Ir);
4992                 break;
4993 #endif
4994 
4995             case DEVICE_SETTINGS_PAGE:
4996                 Page = DeviceSettingsPage(&Ir);
4997                 break;
4998 
4999             case COMPUTER_SETTINGS_PAGE:
5000                 Page = ComputerSettingsPage(&Ir);
5001                 break;
5002 
5003             case DISPLAY_SETTINGS_PAGE:
5004                 Page = DisplaySettingsPage(&Ir);
5005                 break;
5006 
5007             case KEYBOARD_SETTINGS_PAGE:
5008                 Page = KeyboardSettingsPage(&Ir);
5009                 break;
5010 
5011             case LAYOUT_SETTINGS_PAGE:
5012                 Page = LayoutSettingsPage(&Ir);
5013                 break;
5014 
5015             case SELECT_PARTITION_PAGE:
5016                 Page = SelectPartitionPage(&Ir);
5017                 break;
5018 
5019             case CREATE_PRIMARY_PARTITION_PAGE:
5020                 Page = CreatePrimaryPartitionPage(&Ir);
5021                 break;
5022 
5023             case CREATE_EXTENDED_PARTITION_PAGE:
5024                 Page = CreateExtendedPartitionPage(&Ir);
5025                 break;
5026 
5027             case CREATE_LOGICAL_PARTITION_PAGE:
5028                 Page = CreateLogicalPartitionPage(&Ir);
5029                 break;
5030 
5031             case CONFIRM_DELETE_SYSTEM_PARTITION_PAGE:
5032                 Page = ConfirmDeleteSystemPartitionPage(&Ir);
5033                 break;
5034 
5035             case DELETE_PARTITION_PAGE:
5036                 Page = DeletePartitionPage(&Ir);
5037                 break;
5038 
5039             case SELECT_FILE_SYSTEM_PAGE:
5040                 Page = SelectFileSystemPage(&Ir);
5041                 break;
5042 
5043             case FORMAT_PARTITION_PAGE:
5044                 Page = FormatPartitionPage(&Ir);
5045                 break;
5046 
5047             case CHECK_FILE_SYSTEM_PAGE:
5048                 Page = CheckFileSystemPage(&Ir);
5049                 break;
5050 
5051             case INSTALL_DIRECTORY_PAGE:
5052                 Page = InstallDirectoryPage(&Ir);
5053                 break;
5054 
5055             case PREPARE_COPY_PAGE:
5056                 Page = PrepareCopyPage(&Ir);
5057                 break;
5058 
5059             case FILE_COPY_PAGE:
5060                 Page = FileCopyPage(&Ir);
5061                 break;
5062 
5063             case REGISTRY_PAGE:
5064                 Page = RegistryPage(&Ir);
5065                 break;
5066 
5067             case BOOT_LOADER_PAGE:
5068                 Page = BootLoaderPage(&Ir);
5069                 break;
5070 
5071             case BOOT_LOADER_FLOPPY_PAGE:
5072                 Page = BootLoaderFloppyPage(&Ir);
5073                 break;
5074 
5075             case BOOT_LOADER_HARDDISK_MBR_PAGE:
5076                 Page = BootLoaderHarddiskMbrPage(&Ir);
5077                 break;
5078 
5079             case BOOT_LOADER_HARDDISK_VBR_PAGE:
5080                 Page = BootLoaderHarddiskVbrPage(&Ir);
5081                 break;
5082 
5083             /* Repair pages */
5084             case REPAIR_INTRO_PAGE:
5085                 Page = RepairIntroPage(&Ir);
5086                 break;
5087 
5088             case UPGRADE_REPAIR_PAGE:
5089                 Page = UpgradeRepairPage(&Ir);
5090                 break;
5091 
5092             case SUCCESS_PAGE:
5093                 Page = SuccessPage(&Ir);
5094                 break;
5095 
5096             case FLUSH_PAGE:
5097                 Page = FlushPage(&Ir);
5098                 break;
5099 
5100             case QUIT_PAGE:
5101                 Page = QuitPage(&Ir);
5102                 break;
5103 
5104             /* Virtual pages */
5105             case SETUP_INIT_PAGE:
5106             case REBOOT_PAGE:
5107             case RECOVERY_PAGE:
5108                 break;
5109 
5110             default:
5111                 break;
5112         }
5113     }
5114 
5115     /* Terminate the user-mode PnP manager */
5116     TerminateUserModePnpManager();
5117 
5118     /* Setup has finished */
5119     FinishSetup(&USetupData);
5120 
5121     if (Page == RECOVERY_PAGE)
5122         RecoveryConsole();
5123 
5124     FreeConsole();
5125 
5126     /* Reboot */
5127     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
5128     NtShutdownSystem(ShutdownReboot);
5129     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
5130 
5131     return STATUS_SUCCESS;
5132 }
5133 
5134 
5135 VOID NTAPI
5136 NtProcessStartup(PPEB Peb)
5137 {
5138     NTSTATUS Status;
5139     LARGE_INTEGER Time;
5140 
5141     RtlNormalizeProcessParams(Peb->ProcessParameters);
5142 
5143     ProcessHeap = Peb->ProcessHeap;
5144 
5145     NtQuerySystemTime(&Time);
5146 
5147     Status = RunUSetup();
5148 
5149     if (NT_SUCCESS(Status))
5150     {
5151         /*
5152          * Avoid a bugcheck if RunUSetup() finishes too quickly by implementing
5153          * a protective waiting.
5154          * This wait is needed because, since we are started as SMSS.EXE,
5155          * the NT kernel explicitly waits 5 seconds for the initial process
5156          * SMSS.EXE to initialize (as a protective measure), and otherwise
5157          * bugchecks with the code SESSION5_INITIALIZATION_FAILED.
5158          */
5159         Time.QuadPart += 50000000;
5160         NtDelayExecution(FALSE, &Time);
5161     }
5162     else
5163     {
5164         /* The installer failed to start: raise a hard error (crash the system/BSOD) */
5165         Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED,
5166                                   0, 0, NULL, 0, NULL);
5167     }
5168 
5169     NtTerminateProcess(NtCurrentProcess(), Status);
5170 }
5171 
5172 /* EOF */
5173