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