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