xref: /reactos/base/setup/usetup/usetup.c (revision 3476cdae)
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     MUIDisplayPage(CHECK_FILE_SYSTEM_PAGE);
3161 
3162     if (PartitionList == NULL)
3163     {
3164         /* FIXME: show an error dialog */
3165         return QUIT_PAGE;
3166     }
3167 
3168     if (!GetNextUncheckedPartition(PartitionList, NULL, &PartEntry))
3169     {
3170         return INSTALL_DIRECTORY_PAGE;
3171     }
3172 
3173     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3174 
3175     DPRINT1("CheckFileSystemPage -- PartitionType: 0x%02X ; FileSystem: %S\n",
3176             PartEntry->PartitionType, (*PartEntry->FileSystem ? PartEntry->FileSystem : L"n/a"));
3177 
3178     /* Check the partition */
3179     Status = DoChkdsk(PartEntry);
3180     if (Status == STATUS_NOT_SUPPORTED)
3181     {
3182         /*
3183          * Partition checking is not supported with the current filesystem,
3184          * so disable FS checks on it.
3185          */
3186         PartEntry->NeedsCheck = FALSE;
3187 
3188         RtlStringCbPrintfA(Buffer,
3189                            sizeof(Buffer),
3190                            "Setup is currently unable to check a partition formatted in %S.\n"
3191                            "\n"
3192                            "  \x07  Press ENTER to continue Setup.\n"
3193                            "  \x07  Press F3 to quit Setup.",
3194                            PartEntry->FileSystem);
3195 
3196         PopupError(Buffer,
3197                    MUIGetString(STRING_QUITCONTINUE),
3198                    NULL, POPUP_WAIT_NONE);
3199 
3200         while (TRUE)
3201         {
3202             CONSOLE_ConInKey(Ir);
3203 
3204             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00 &&
3205                 Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)  /* F3 */
3206             {
3207                 if (ConfirmQuit(Ir))
3208                     return QUIT_PAGE;
3209                 else
3210                     return CHECK_FILE_SYSTEM_PAGE;
3211             }
3212             else if (Ir->Event.KeyEvent.uChar.AsciiChar == VK_RETURN) /* ENTER */
3213             {
3214                 return CHECK_FILE_SYSTEM_PAGE;
3215             }
3216         }
3217     }
3218     else if (!NT_SUCCESS(Status))
3219     {
3220         DPRINT1("ChkdskPartition() failed with status 0x%08lx\n", Status);
3221 
3222         RtlStringCbPrintfA(Buffer,
3223                            sizeof(Buffer),
3224                            "ChkDsk detected some disk errors.\n(Status 0x%08lx).\n",
3225                            Status);
3226 
3227         PopupError(Buffer,
3228                    MUIGetString(STRING_CONTINUE),
3229                    Ir, POPUP_WAIT_ENTER);
3230     }
3231 
3232     PartEntry->NeedsCheck = FALSE;
3233     return CHECK_FILE_SYSTEM_PAGE;
3234 }
3235 
3236 
3237 static BOOLEAN
3238 IsValidPath(
3239     IN PCWSTR InstallDir)
3240 {
3241     UINT i, Length;
3242 
3243     Length = wcslen(InstallDir);
3244 
3245     // TODO: Add check for 8.3 too.
3246 
3247     /* Path must be at least 2 characters long */
3248 //    if (Length < 2)
3249 //        return FALSE;
3250 
3251     /* Path must start with a backslash */
3252 //    if (InstallDir[0] != L'\\')
3253 //        return FALSE;
3254 
3255     /* Path must not end with a backslash */
3256     if (InstallDir[Length - 1] == L'\\')
3257         return FALSE;
3258 
3259     /* Path must not contain whitespace characters */
3260     for (i = 0; i < Length; i++)
3261     {
3262         if (iswspace(InstallDir[i]))
3263             return FALSE;
3264     }
3265 
3266     /* Path component must not end with a dot */
3267     for (i = 0; i < Length; i++)
3268     {
3269         if (InstallDir[i] == L'\\' && i > 0)
3270         {
3271             if (InstallDir[i - 1] == L'.')
3272                 return FALSE;
3273         }
3274     }
3275 
3276     if (InstallDir[Length - 1] == L'.')
3277         return FALSE;
3278 
3279     return TRUE;
3280 }
3281 
3282 
3283 /*
3284  * Displays the InstallDirectoryPage.
3285  *
3286  * Next pages:
3287  *  PrepareCopyPage
3288  *  QuitPage
3289  *
3290  * RETURNS
3291  *   Number of the next page.
3292  */
3293 static PAGE_NUMBER
3294 InstallDirectoryPage(PINPUT_RECORD Ir)
3295 {
3296     NTSTATUS Status;
3297     ULONG Length, Pos;
3298     WCHAR c;
3299     WCHAR InstallDir[MAX_PATH];
3300 
3301     /* We do not need the filesystem list anymore */
3302     ResetFileSystemList();
3303 
3304     if (PartitionList == NULL || InstallPartition == NULL)
3305     {
3306         /* FIXME: show an error dialog */
3307         return QUIT_PAGE;
3308     }
3309 
3310     // if (IsUnattendedSetup)
3311     if (RepairUpdateFlag)
3312         wcscpy(InstallDir, CurrentInstallation->PathComponent); // SystemNtPath
3313     else if (USetupData.InstallationDirectory[0])
3314         wcscpy(InstallDir, USetupData.InstallationDirectory);
3315     else
3316         wcscpy(InstallDir, L"\\ReactOS");
3317 
3318     /*
3319      * Check the validity of the predefined 'InstallDir'. If we are either
3320      * in unattended setup or in update/repair mode, and the installation path
3321      * is valid, just perform the installation. Otherwise (either in the case
3322      * of an invalid path, or we are in regular setup), display the UI and allow
3323      * the user to specify a new installation path.
3324      */
3325     if ((RepairUpdateFlag || IsUnattendedSetup) && IsValidPath(InstallDir))
3326     {
3327         Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition);
3328         if (!NT_SUCCESS(Status))
3329         {
3330             DPRINT1("InitDestinationPaths() failed. Status code: 0x%lx", Status);
3331             MUIDisplayError(ERROR_NO_BUILD_PATH, Ir, POPUP_WAIT_ENTER);
3332             return QUIT_PAGE;
3333         }
3334 
3335         /*
3336          * Check whether the user attempts to install ReactOS within the
3337          * installation source directory, or in a subdirectory thereof.
3338          * If so, fail with an error.
3339          */
3340         if (RtlPrefixUnicodeString(&USetupData.SourcePath, &USetupData.DestinationPath, TRUE))
3341         {
3342             MUIDisplayError(ERROR_SOURCE_DIR, Ir, POPUP_WAIT_ENTER);
3343             return INSTALL_DIRECTORY_PAGE;
3344         }
3345 
3346         return PREPARE_COPY_PAGE;
3347     }
3348 
3349     Length = wcslen(InstallDir);
3350     Pos = Length;
3351 
3352     MUIDisplayPage(INSTALL_DIRECTORY_PAGE);
3353     CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3354     CONSOLE_SetCursorXY(8 + Pos, 11);
3355     CONSOLE_SetCursorType(TRUE, TRUE);
3356 
3357     while (TRUE)
3358     {
3359         CONSOLE_ConInKey(Ir);
3360 
3361         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3362             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3363         {
3364             CONSOLE_SetCursorType(TRUE, FALSE);
3365 
3366             if (ConfirmQuit(Ir))
3367                 return QUIT_PAGE;
3368 
3369             CONSOLE_SetCursorType(TRUE, TRUE);
3370             break;
3371         }
3372         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3373                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DELETE))  /* DEL */
3374         {
3375             if (Pos < Length)
3376             {
3377                 memmove(&InstallDir[Pos],
3378                         &InstallDir[Pos + 1],
3379                         (Length - Pos - 1) * sizeof(WCHAR));
3380                 InstallDir[Length - 1] = UNICODE_NULL;
3381 
3382                 Length--;
3383                 CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3384                 CONSOLE_SetCursorXY(8 + Pos, 11);
3385             }
3386         }
3387         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3388                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
3389         {
3390             Pos = 0;
3391             CONSOLE_SetCursorXY(8 + Pos, 11);
3392         }
3393         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3394                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
3395         {
3396             Pos = Length;
3397             CONSOLE_SetCursorXY(8 + Pos, 11);
3398         }
3399         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3400                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_LEFT))  /* LEFT */
3401         {
3402             if (Pos > 0)
3403             {
3404                 Pos--;
3405                 CONSOLE_SetCursorXY(8 + Pos, 11);
3406             }
3407         }
3408         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3409                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT))  /* RIGHT */
3410         {
3411             if (Pos < Length)
3412             {
3413                 Pos++;
3414                 CONSOLE_SetCursorXY(8 + Pos, 11);
3415             }
3416         }
3417         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
3418         {
3419             CONSOLE_SetCursorType(TRUE, FALSE);
3420 
3421             /*
3422              * Check for the validity of the installation directory and pop up
3423              * an error if it is not the case. Then the user can fix its input.
3424              */
3425             if (!IsValidPath(InstallDir))
3426             {
3427                 MUIDisplayError(ERROR_DIRECTORY_NAME, Ir, POPUP_WAIT_ENTER);
3428                 return INSTALL_DIRECTORY_PAGE;
3429             }
3430 
3431             Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition);
3432             if (!NT_SUCCESS(Status))
3433             {
3434                 DPRINT1("InitDestinationPaths() failed. Status code: 0x%lx", Status);
3435                 MUIDisplayError(ERROR_NO_BUILD_PATH, Ir, POPUP_WAIT_ENTER);
3436                 return QUIT_PAGE;
3437             }
3438 
3439             /*
3440              * Check whether the user attempts to install ReactOS within the
3441              * installation source directory, or in a subdirectory thereof.
3442              * If so, fail with an error.
3443              */
3444             if (RtlPrefixUnicodeString(&USetupData.SourcePath, &USetupData.DestinationPath, TRUE))
3445             {
3446                 MUIDisplayError(ERROR_SOURCE_DIR, Ir, POPUP_WAIT_ENTER);
3447                 return INSTALL_DIRECTORY_PAGE;
3448             }
3449 
3450             return PREPARE_COPY_PAGE;
3451         }
3452         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x08) /* BACKSPACE */
3453         {
3454             if (Pos > 0)
3455             {
3456                 if (Pos < Length)
3457                     memmove(&InstallDir[Pos - 1],
3458                             &InstallDir[Pos],
3459                             (Length - Pos) * sizeof(WCHAR));
3460                 InstallDir[Length - 1] = UNICODE_NULL;
3461 
3462                 Pos--;
3463                 Length--;
3464                 CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3465                 CONSOLE_SetCursorXY(8 + Pos, 11);
3466             }
3467         }
3468         else if (isprint(Ir->Event.KeyEvent.uChar.AsciiChar))
3469         {
3470             if (Length < 50)
3471             {
3472                 c = (WCHAR)Ir->Event.KeyEvent.uChar.AsciiChar;
3473                 if (iswalpha(c) || iswdigit(c) || c == '.' || c == '\\' || c == '-' || c == '_')
3474                 {
3475                     if (Pos < Length)
3476                         memmove(&InstallDir[Pos + 1],
3477                                 &InstallDir[Pos],
3478                                 (Length - Pos) * sizeof(WCHAR));
3479                     InstallDir[Length + 1] = UNICODE_NULL;
3480                     InstallDir[Pos] = c;
3481 
3482                     Pos++;
3483                     Length++;
3484                     CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3485                     CONSOLE_SetCursorXY(8 + Pos, 11);
3486                 }
3487             }
3488         }
3489     }
3490 
3491     return INSTALL_DIRECTORY_PAGE;
3492 }
3493 
3494 
3495 // PSETUP_ERROR_ROUTINE
3496 static VOID
3497 __cdecl
3498 USetupErrorRoutine(
3499     IN PUSETUP_DATA pSetupData,
3500     ...)
3501 {
3502     INPUT_RECORD Ir;
3503     va_list arg_ptr;
3504 
3505     va_start(arg_ptr, pSetupData);
3506 
3507     if (pSetupData->LastErrorNumber >= ERROR_SUCCESS &&
3508         pSetupData->LastErrorNumber <  ERROR_LAST_ERROR_CODE)
3509     {
3510         // Note: the "POPUP_WAIT_ENTER" actually depends on the LastErrorNumber...
3511         MUIDisplayErrorV(pSetupData->LastErrorNumber, &Ir, POPUP_WAIT_ENTER, arg_ptr);
3512     }
3513 
3514     va_end(arg_ptr);
3515 }
3516 
3517 /*
3518  * Displays the PrepareCopyPage.
3519  *
3520  * Next pages:
3521  *  FileCopyPage(At once)
3522  *  QuitPage
3523  *
3524  * SIDEEFFECTS
3525  * Calls PrepareFileCopy
3526  *
3527  * RETURNS
3528  *   Number of the next page.
3529  */
3530 static PAGE_NUMBER
3531 PrepareCopyPage(PINPUT_RECORD Ir)
3532 {
3533     // ERROR_NUMBER ErrorNumber;
3534     BOOLEAN Success;
3535 
3536     MUIDisplayPage(PREPARE_COPY_PAGE);
3537 
3538     /* ErrorNumber = */ Success = PrepareFileCopy(&USetupData, NULL);
3539     if (/*ErrorNumber != ERROR_SUCCESS*/ !Success)
3540     {
3541         // MUIDisplayError(ErrorNumber, Ir, POPUP_WAIT_ENTER);
3542         return QUIT_PAGE;
3543     }
3544 
3545     return FILE_COPY_PAGE;
3546 }
3547 
3548 typedef struct _COPYCONTEXT
3549 {
3550     ULONG TotalOperations;
3551     ULONG CompletedOperations;
3552     PPROGRESSBAR ProgressBar;
3553     PPROGRESSBAR MemoryBars[4];
3554 } COPYCONTEXT, *PCOPYCONTEXT;
3555 
3556 static VOID
3557 SetupUpdateMemoryInfo(IN PCOPYCONTEXT CopyContext,
3558                       IN BOOLEAN First)
3559 {
3560     SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
3561 
3562     /* Get the memory information from the system */
3563     NtQuerySystemInformation(SystemPerformanceInformation,
3564                              &PerfInfo,
3565                              sizeof(PerfInfo),
3566                              NULL);
3567 
3568     /* Check if this is initial setup */
3569     if (First)
3570     {
3571         /* Set maximum limits to be total RAM pages */
3572         ProgressSetStepCount(CopyContext->MemoryBars[0], PerfInfo.CommitLimit);
3573         ProgressSetStepCount(CopyContext->MemoryBars[1], PerfInfo.CommitLimit);
3574         ProgressSetStepCount(CopyContext->MemoryBars[2], PerfInfo.CommitLimit);
3575     }
3576 
3577     /* Set current values */
3578     ProgressSetStep(CopyContext->MemoryBars[0], PerfInfo.PagedPoolPages + PerfInfo.NonPagedPoolPages);
3579     ProgressSetStep(CopyContext->MemoryBars[1], PerfInfo.ResidentSystemCachePage);
3580     ProgressSetStep(CopyContext->MemoryBars[2], PerfInfo.AvailablePages);
3581 }
3582 
3583 static UINT
3584 CALLBACK
3585 FileCopyCallback(PVOID Context,
3586                  UINT Notification,
3587                  UINT_PTR Param1,
3588                  UINT_PTR Param2)
3589 {
3590     PCOPYCONTEXT CopyContext = (PCOPYCONTEXT)Context;
3591     PFILEPATHS_W FilePathInfo;
3592     PCWSTR SrcFileName, DstFileName;
3593 
3594     switch (Notification)
3595     {
3596         case SPFILENOTIFY_STARTSUBQUEUE:
3597         {
3598             CopyContext->TotalOperations = (ULONG)Param2;
3599             CopyContext->CompletedOperations = 0;
3600             ProgressSetStepCount(CopyContext->ProgressBar,
3601                                  CopyContext->TotalOperations);
3602             SetupUpdateMemoryInfo(CopyContext, TRUE);
3603             break;
3604         }
3605 
3606         case SPFILENOTIFY_STARTDELETE:
3607         case SPFILENOTIFY_STARTRENAME:
3608         case SPFILENOTIFY_STARTCOPY:
3609         {
3610             FilePathInfo = (PFILEPATHS_W)Param1;
3611 
3612             if (Notification == SPFILENOTIFY_STARTDELETE)
3613             {
3614                 /* Display delete message */
3615                 ASSERT(Param2 == FILEOP_DELETE);
3616 
3617                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
3618                 if (DstFileName) ++DstFileName;
3619                 else DstFileName = FilePathInfo->Target;
3620 
3621                 CONSOLE_SetStatusText(MUIGetString(STRING_DELETING),
3622                                       DstFileName);
3623             }
3624             else if (Notification == SPFILENOTIFY_STARTRENAME)
3625             {
3626                 /* Display move/rename message */
3627                 ASSERT(Param2 == FILEOP_RENAME);
3628 
3629                 SrcFileName = wcsrchr(FilePathInfo->Source, L'\\');
3630                 if (SrcFileName) ++SrcFileName;
3631                 else SrcFileName = FilePathInfo->Source;
3632 
3633                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
3634                 if (DstFileName) ++DstFileName;
3635                 else DstFileName = FilePathInfo->Target;
3636 
3637                 if (!wcsicmp(SrcFileName, DstFileName))
3638                     Param2 = STRING_MOVING;
3639                 else
3640                     Param2 = STRING_RENAMING;
3641 
3642                 CONSOLE_SetStatusText(MUIGetString(Param2),
3643                                       SrcFileName, DstFileName);
3644             }
3645             else if (Notification == SPFILENOTIFY_STARTCOPY)
3646             {
3647                 /* Display copy message */
3648                 ASSERT(Param2 == FILEOP_COPY);
3649 
3650                 /* NOTE: When extracting from CABs the Source is the CAB name */
3651                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
3652                 if (DstFileName) ++DstFileName;
3653                 else DstFileName = FilePathInfo->Target;
3654 
3655                 CONSOLE_SetStatusText(MUIGetString(STRING_COPYING),
3656                                       DstFileName);
3657 #ifdef __REACTOS__ /* HACK */
3658                 DoWatchDestFileName(DstFileName);
3659 #endif
3660             }
3661 
3662             SetupUpdateMemoryInfo(CopyContext, FALSE);
3663             break;
3664         }
3665 
3666         case SPFILENOTIFY_COPYERROR:
3667         {
3668             FilePathInfo = (PFILEPATHS_W)Param1;
3669 
3670             DPRINT1("An error happened while trying to copy file '%S' (error 0x%08lx), skipping it...\n",
3671                     FilePathInfo->Target, FilePathInfo->Win32Error);
3672             return FILEOP_SKIP;
3673         }
3674 
3675         case SPFILENOTIFY_ENDDELETE:
3676         case SPFILENOTIFY_ENDRENAME:
3677         case SPFILENOTIFY_ENDCOPY:
3678         {
3679             CopyContext->CompletedOperations++;
3680 
3681             /* SYSREG checkpoint */
3682             if (CopyContext->TotalOperations >> 1 == CopyContext->CompletedOperations)
3683                 DPRINT1("CHECKPOINT:HALF_COPIED\n");
3684 
3685             ProgressNextStep(CopyContext->ProgressBar);
3686             SetupUpdateMemoryInfo(CopyContext, FALSE);
3687             break;
3688         }
3689     }
3690 
3691     return FILEOP_DOIT;
3692 }
3693 
3694 
3695 /*
3696  * Displays the FileCopyPage.
3697  *
3698  * Next pages:
3699  *  RegistryPage(At once)
3700  *
3701  * SIDEEFFECTS
3702  *  Calls DoFileCopy
3703  *
3704  * RETURNS
3705  *   Number of the next page.
3706  */
3707 static PAGE_NUMBER
3708 FileCopyPage(PINPUT_RECORD Ir)
3709 {
3710     COPYCONTEXT CopyContext;
3711     UINT MemBarWidth;
3712 
3713     MUIDisplayPage(FILE_COPY_PAGE);
3714 
3715     /* Create context for the copy process */
3716     CopyContext.TotalOperations = 0;
3717     CopyContext.CompletedOperations = 0;
3718 
3719     /* Create the progress bar as well */
3720     CopyContext.ProgressBar = CreateProgressBar(13,
3721                                                 26,
3722                                                 xScreen - 13,
3723                                                 yScreen - 20,
3724                                                 10,
3725                                                 24,
3726                                                 TRUE,
3727                                                 MUIGetString(STRING_SETUPCOPYINGFILES));
3728 
3729     // fit memory bars to screen width, distribute them uniform
3730     MemBarWidth = (xScreen - 26) / 5;
3731     MemBarWidth -= MemBarWidth % 2;  // make even
3732     /* ATTENTION: The following progress bars are debug stuff, which should not be translated!! */
3733     /* Create the paged pool progress bar */
3734     CopyContext.MemoryBars[0] = CreateProgressBar(13,
3735                                                   40,
3736                                                   13 + MemBarWidth,
3737                                                   43,
3738                                                   13,
3739                                                   44,
3740                                                   FALSE,
3741                                                   "Kernel Pool");
3742 
3743     /* Create the non paged pool progress bar */
3744     CopyContext.MemoryBars[1] = CreateProgressBar((xScreen / 2)- (MemBarWidth / 2),
3745                                                   40,
3746                                                   (xScreen / 2) + (MemBarWidth / 2),
3747                                                   43,
3748                                                   (xScreen / 2)- (MemBarWidth / 2),
3749                                                   44,
3750                                                   FALSE,
3751                                                   "Kernel Cache");
3752 
3753     /* Create the global memory progress bar */
3754     CopyContext.MemoryBars[2] = CreateProgressBar(xScreen - 13 - MemBarWidth,
3755                                                   40,
3756                                                   xScreen - 13,
3757                                                   43,
3758                                                   xScreen - 13 - MemBarWidth,
3759                                                   44,
3760                                                   FALSE,
3761                                                   "Free Memory");
3762 
3763     /* Do the file copying */
3764     DoFileCopy(&USetupData, FileCopyCallback, &CopyContext);
3765 
3766     /* If we get here, we're done, so cleanup the progress bar */
3767     DestroyProgressBar(CopyContext.ProgressBar);
3768     DestroyProgressBar(CopyContext.MemoryBars[0]);
3769     DestroyProgressBar(CopyContext.MemoryBars[1]);
3770     DestroyProgressBar(CopyContext.MemoryBars[2]);
3771 
3772     /* Create the $winnt$.inf file */
3773     InstallSetupInfFile(&USetupData);
3774 
3775     /* Go display the next page */
3776     return REGISTRY_PAGE;
3777 }
3778 
3779 
3780 static VOID
3781 __cdecl
3782 RegistryStatus(IN REGISTRY_STATUS RegStatus, ...)
3783 {
3784     /* WARNING: Please keep this lookup table in sync with the resources! */
3785     static const UINT StringIDs[] =
3786     {
3787         STRING_DONE,                    /* Success */
3788         STRING_REGHIVEUPDATE,           /* RegHiveUpdate */
3789         STRING_IMPORTFILE,              /* ImportRegHive */
3790         STRING_DISPLAYSETTINGSUPDATE,   /* DisplaySettingsUpdate */
3791         STRING_LOCALESETTINGSUPDATE,    /* LocaleSettingsUpdate */
3792         STRING_ADDKBLAYOUTS,            /* KeybLayouts */
3793         STRING_KEYBOARDSETTINGSUPDATE,  /* KeybSettingsUpdate */
3794         STRING_CODEPAGEINFOUPDATE,      /* CodePageInfoUpdate */
3795     };
3796 
3797     va_list args;
3798 
3799     if (RegStatus < ARRAYSIZE(StringIDs))
3800     {
3801         va_start(args, RegStatus);
3802         CONSOLE_SetStatusTextV(MUIGetString(StringIDs[RegStatus]), args);
3803         va_end(args);
3804     }
3805     else
3806     {
3807         CONSOLE_SetStatusText("Unknown status %d", RegStatus);
3808     }
3809 }
3810 
3811 /*
3812  * Displays the RegistryPage.
3813  *
3814  * Next pages:
3815  *  SuccessPage (if RepairUpdate)
3816  *  BootLoaderPage (default)
3817  *  QuitPage
3818  *
3819  * SIDEEFFECTS
3820  *  Calls UpdateRegistry
3821  *
3822  * RETURNS
3823  *   Number of the next page.
3824  */
3825 static PAGE_NUMBER
3826 RegistryPage(PINPUT_RECORD Ir)
3827 {
3828     ULONG Error;
3829 
3830     MUIDisplayPage(REGISTRY_PAGE);
3831 
3832     Error = UpdateRegistry(&USetupData,
3833                            RepairUpdateFlag,
3834                            PartitionList,
3835                            InstallPartition->DriveLetter,
3836                            SelectedLanguageId,
3837                            RegistryStatus,
3838                            &s_SubstSettings);
3839     if (Error != ERROR_SUCCESS)
3840     {
3841         MUIDisplayError(Error, Ir, POPUP_WAIT_ENTER);
3842         return QUIT_PAGE;
3843     }
3844     else
3845     {
3846         CONSOLE_SetStatusText(MUIGetString(STRING_DONE));
3847         return BOOT_LOADER_PAGE;
3848     }
3849 }
3850 
3851 
3852 /*
3853  * Displays the BootLoaderPage.
3854  *
3855  * Next pages:
3856  *  SuccessPage (if RepairUpdate)
3857  *  BootLoaderHarddiskMbrPage
3858  *  BootLoaderHarddiskVbrPage
3859  *  BootLoaderFloppyPage
3860  *  SuccessPage
3861  *  QuitPage
3862  *
3863  * SIDEEFFECTS
3864  *  Calls RegInitializeRegistry
3865  *  Calls ImportRegistryFile
3866  *  Calls SetDefaultPagefile
3867  *  Calls SetMountedDeviceValues
3868  *
3869  * RETURNS
3870  *   Number of the next page.
3871  */
3872 static PAGE_NUMBER
3873 BootLoaderPage(PINPUT_RECORD Ir)
3874 {
3875     USHORT Line = 12;
3876     WCHAR PathBuffer[MAX_PATH];
3877 
3878     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
3879 
3880     /* We must have a supported system partition by now */
3881     ASSERT(SystemPartition && SystemPartition->IsPartitioned && SystemPartition->PartitionNumber != 0);
3882 
3883     RtlFreeUnicodeString(&USetupData.SystemRootPath);
3884     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
3885             L"\\Device\\Harddisk%lu\\Partition%lu\\",
3886             SystemPartition->DiskEntry->DiskNumber,
3887             SystemPartition->PartitionNumber);
3888     RtlCreateUnicodeString(&USetupData.SystemRootPath, PathBuffer);
3889     DPRINT1("SystemRootPath: %wZ\n", &USetupData.SystemRootPath);
3890 
3891     /* For unattended setup, skip MBR installation or install on floppy if needed */
3892     if (IsUnattendedSetup)
3893     {
3894         if ((USetupData.MBRInstallType == 0) ||
3895             (USetupData.MBRInstallType == 1))
3896         {
3897             goto Quit;
3898         }
3899     }
3900 
3901     /*
3902      * We may install an MBR or VBR, but before that, check whether
3903      * we need to actually install the VBR on floppy/removable media
3904      * if the system partition is not recognized.
3905      */
3906     if ((SystemPartition->DiskEntry->DiskStyle != PARTITION_STYLE_MBR) ||
3907         !IsRecognizedPartition(SystemPartition->PartitionType))
3908     {
3909         USetupData.MBRInstallType = 1;
3910         goto Quit;
3911     }
3912 
3913     /* Is it an unattended install on hdd? */
3914     if (IsUnattendedSetup)
3915     {
3916         if ((USetupData.MBRInstallType == 2) ||
3917             (USetupData.MBRInstallType == 3))
3918         {
3919             goto Quit;
3920         }
3921     }
3922 
3923     MUIDisplayPage(BOOT_LOADER_PAGE);
3924     CONSOLE_InvertTextXY(8, Line, 60, 1);
3925 
3926     while (TRUE)
3927     {
3928         CONSOLE_ConInKey(Ir);
3929 
3930         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3931             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
3932         {
3933             CONSOLE_NormalTextXY(8, Line, 60, 1);
3934 
3935             Line++;
3936             if (Line < 12)
3937                 Line = 15;
3938 
3939             if (Line > 15)
3940                 Line = 12;
3941 
3942             CONSOLE_InvertTextXY(8, Line, 60, 1);
3943         }
3944         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3945                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
3946         {
3947             CONSOLE_NormalTextXY(8, Line, 60, 1);
3948 
3949             Line--;
3950             if (Line < 12)
3951                 Line = 15;
3952 
3953             if (Line > 15)
3954                 Line = 12;
3955 
3956             CONSOLE_InvertTextXY(8, Line, 60, 1);
3957         }
3958         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3959                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
3960         {
3961             CONSOLE_NormalTextXY(8, Line, 60, 1);
3962 
3963             Line = 12;
3964 
3965             CONSOLE_InvertTextXY(8, Line, 60, 1);
3966         }
3967         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3968                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
3969         {
3970             CONSOLE_NormalTextXY(8, Line, 60, 1);
3971 
3972             Line = 15;
3973 
3974             CONSOLE_InvertTextXY(8, Line, 60, 1);
3975         }
3976         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3977                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3978         {
3979             if (ConfirmQuit(Ir))
3980                 return QUIT_PAGE;
3981 
3982             break;
3983         }
3984         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
3985         {
3986             if (Line == 12)
3987             {
3988                 /* Install on both MBR and VBR */
3989                 USetupData.MBRInstallType = 2;
3990                 break;
3991             }
3992             else if (Line == 13)
3993             {
3994                 /* Install on VBR only */
3995                 USetupData.MBRInstallType = 3;
3996                 break;
3997             }
3998             else if (Line == 14)
3999             {
4000                 /* Install on floppy */
4001                 USetupData.MBRInstallType = 1;
4002                 break;
4003             }
4004             else if (Line == 15)
4005             {
4006                 /* Skip MBR installation */
4007                 USetupData.MBRInstallType = 0;
4008                 break;
4009             }
4010 
4011             return BOOT_LOADER_PAGE;
4012         }
4013     }
4014 
4015 Quit:
4016     switch (USetupData.MBRInstallType)
4017     {
4018         /* Skip MBR installation */
4019         case 0:
4020             return SUCCESS_PAGE;
4021 
4022         /* Install on floppy */
4023         case 1:
4024             return BOOT_LOADER_FLOPPY_PAGE;
4025 
4026         /* Install on both MBR and VBR */
4027         case 2:
4028             return BOOT_LOADER_HARDDISK_MBR_PAGE;
4029 
4030         /* Install on VBR only */
4031         case 3:
4032             return BOOT_LOADER_HARDDISK_VBR_PAGE;
4033     }
4034 
4035     return BOOT_LOADER_PAGE;
4036 }
4037 
4038 
4039 /*
4040  * Displays the BootLoaderFloppyPage.
4041  *
4042  * Next pages:
4043  *  SuccessPage (At once)
4044  *  QuitPage
4045  *
4046  * SIDEEFFECTS
4047  *  Calls InstallFatBootcodeToFloppy()
4048  *
4049  * RETURNS
4050  *   Number of the next page.
4051  */
4052 static PAGE_NUMBER
4053 BootLoaderFloppyPage(PINPUT_RECORD Ir)
4054 {
4055     NTSTATUS Status;
4056 
4057     MUIDisplayPage(BOOT_LOADER_FLOPPY_PAGE);
4058 
4059 //  CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
4060 
4061     while (TRUE)
4062     {
4063         CONSOLE_ConInKey(Ir);
4064 
4065         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4066             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
4067         {
4068             if (ConfirmQuit(Ir))
4069                 return QUIT_PAGE;
4070 
4071             break;
4072         }
4073         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
4074         {
4075             Status = InstallFatBootcodeToFloppy(&USetupData.SourceRootPath,
4076                                                 &USetupData.DestinationArcPath);
4077             if (!NT_SUCCESS(Status))
4078             {
4079                 if (Status == STATUS_DEVICE_NOT_READY)
4080                     MUIDisplayError(ERROR_NO_FLOPPY, Ir, POPUP_WAIT_ENTER);
4081 
4082                 /* TODO: Print error message */
4083                 return BOOT_LOADER_FLOPPY_PAGE;
4084             }
4085 
4086             return SUCCESS_PAGE;
4087         }
4088     }
4089 
4090     return BOOT_LOADER_FLOPPY_PAGE;
4091 }
4092 
4093 
4094 /*
4095  * Displays the BootLoaderHarddiskVbrPage.
4096  *
4097  * Next pages:
4098  *  SuccessPage (At once)
4099  *  QuitPage
4100  *
4101  * SIDEEFFECTS
4102  *  Calls InstallVBRToPartition()
4103  *
4104  * RETURNS
4105  *   Number of the next page.
4106  */
4107 static PAGE_NUMBER
4108 BootLoaderHarddiskVbrPage(PINPUT_RECORD Ir)
4109 {
4110     NTSTATUS Status;
4111 
4112     Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4113                                    &USetupData.SourceRootPath,
4114                                    &USetupData.DestinationArcPath,
4115                                    SystemPartition->FileSystem);
4116     if (!NT_SUCCESS(Status))
4117     {
4118         MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4119                         SystemPartition->FileSystem);
4120         return QUIT_PAGE;
4121     }
4122 
4123     return SUCCESS_PAGE;
4124 }
4125 
4126 
4127 /*
4128  * Displays the BootLoaderHarddiskMbrPage.
4129  *
4130  * Next pages:
4131  *  SuccessPage (At once)
4132  *  QuitPage
4133  *
4134  * SIDEEFFECTS
4135  *  Calls InstallVBRToPartition()
4136  *  Calls InstallMbrBootCodeToDisk()
4137  *
4138  * RETURNS
4139  *   Number of the next page.
4140  */
4141 static PAGE_NUMBER
4142 BootLoaderHarddiskMbrPage(PINPUT_RECORD Ir)
4143 {
4144     NTSTATUS Status;
4145     WCHAR DestinationDevicePathBuffer[MAX_PATH];
4146 
4147     /* Step 1: Write the VBR */
4148     Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4149                                    &USetupData.SourceRootPath,
4150                                    &USetupData.DestinationArcPath,
4151                                    SystemPartition->FileSystem);
4152     if (!NT_SUCCESS(Status))
4153     {
4154         MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4155                         SystemPartition->FileSystem);
4156         return QUIT_PAGE;
4157     }
4158 
4159     /* Step 2: Write the MBR if the disk containing the system partition is not a super-floppy */
4160     if (!IsSuperFloppy(SystemPartition->DiskEntry))
4161     {
4162         RtlStringCchPrintfW(DestinationDevicePathBuffer, ARRAYSIZE(DestinationDevicePathBuffer),
4163                 L"\\Device\\Harddisk%d\\Partition0",
4164                 SystemPartition->DiskEntry->DiskNumber);
4165         Status = InstallMbrBootCodeToDisk(&USetupData.SystemRootPath,
4166                                           &USetupData.SourceRootPath,
4167                                           DestinationDevicePathBuffer);
4168         if (!NT_SUCCESS(Status))
4169         {
4170             DPRINT1("InstallMbrBootCodeToDisk() failed (Status %lx)\n", Status);
4171             MUIDisplayError(ERROR_INSTALL_BOOTCODE, Ir, POPUP_WAIT_ENTER, L"MBR");
4172             return QUIT_PAGE;
4173         }
4174     }
4175 
4176     return SUCCESS_PAGE;
4177 }
4178 
4179 
4180 /**
4181  * @name ProgressTimeOutStringHandler
4182  *
4183  * Handles the generation (displaying) of the timeout
4184  * countdown to the screen dynamically.
4185  *
4186  * @param   Bar
4187  *     A pointer to a progress bar.
4188  *
4189  * @param   AlwaysUpdate
4190  *     Constantly update the progress bar (boolean type).
4191  *
4192  * @param   Buffer
4193  *     A pointer to a string buffer.
4194  *
4195  * @param   cchBufferSize
4196  *     The buffer's size in number of characters.
4197  *
4198  * @return
4199  *     TRUE or FALSE on function termination.
4200  *
4201  */
4202 static
4203 BOOLEAN NTAPI
4204 ProgressTimeOutStringHandler(
4205     IN PPROGRESSBAR Bar,
4206     IN BOOLEAN AlwaysUpdate,
4207     OUT PSTR Buffer,
4208     IN SIZE_T cchBufferSize)
4209 {
4210     ULONG OldProgress = Bar->Progress;
4211 
4212     if (Bar->StepCount == 0)
4213     {
4214         Bar->Progress = 0;
4215     }
4216     else
4217     {
4218         Bar->Progress = Bar->StepCount - Bar->CurrentStep;
4219     }
4220 
4221     /* Build the progress string if it has changed */
4222     if (Bar->ProgressFormatText &&
4223         (AlwaysUpdate || (Bar->Progress != OldProgress)))
4224     {
4225         RtlStringCchPrintfA(Buffer, cchBufferSize,
4226                             Bar->ProgressFormatText, Bar->Progress / max(1, Bar->Width) + 1);
4227 
4228         return TRUE;
4229     }
4230 
4231     return FALSE;
4232 }
4233 
4234 /**
4235  * @name ProgressCountdown
4236  *
4237  * Displays and draws a red-coloured progress bar with a countdown.
4238  * When the timeout is reached, the flush page is displayed for reboot.
4239  *
4240  * @param   Ir
4241  *     A pointer to an input keyboard record.
4242  *
4243  * @param   TimeOut
4244  *     Initial countdown value in seconds.
4245  *
4246  * @return
4247  *     Nothing.
4248  *
4249  */
4250 static VOID
4251 ProgressCountdown(
4252     IN PINPUT_RECORD Ir,
4253     IN LONG TimeOut)
4254 {
4255     NTSTATUS Status;
4256     ULONG StartTime, BarWidth, TimerDiv;
4257     LONG TimeElapsed;
4258     LONG TimerValue, OldTimerValue;
4259     LARGE_INTEGER Timeout;
4260     PPROGRESSBAR ProgressBar;
4261     BOOLEAN RefreshProgress = TRUE;
4262 
4263     /* Bail out if the timeout is already zero */
4264     if (TimeOut <= 0)
4265         return;
4266 
4267     /* Create the timeout progress bar and set it up */
4268     ProgressBar = CreateProgressBarEx(13,
4269                                       26,
4270                                       xScreen - 13,
4271                                       yScreen - 20,
4272                                       10,
4273                                       24,
4274                                       TRUE,
4275                                       FOREGROUND_RED | BACKGROUND_BLUE,
4276                                       0,
4277                                       NULL,
4278                                       MUIGetString(STRING_REBOOTPROGRESSBAR),
4279                                       ProgressTimeOutStringHandler);
4280 
4281     BarWidth = max(1, ProgressBar->Width);
4282     TimerValue = TimeOut * BarWidth;
4283     ProgressSetStepCount(ProgressBar, TimerValue);
4284 
4285     StartTime = NtGetTickCount();
4286     CONSOLE_Flush();
4287 
4288     TimerDiv = 1000 / BarWidth;
4289     TimerDiv = max(1, TimerDiv);
4290     OldTimerValue = TimerValue;
4291     while (TRUE)
4292     {
4293         /* Decrease the timer */
4294 
4295         /*
4296          * Compute how much time the previous operations took.
4297          * This allows us in particular to take account for any time
4298          * elapsed if something slowed down.
4299          */
4300         TimeElapsed = NtGetTickCount() - StartTime;
4301         if (TimeElapsed >= TimerDiv)
4302         {
4303             /* Increase StartTime by steps of 1 / ProgressBar->Width seconds */
4304             TimeElapsed /= TimerDiv;
4305             StartTime += (TimerDiv * TimeElapsed);
4306 
4307             if (TimeElapsed <= TimerValue)
4308                 TimerValue -= TimeElapsed;
4309             else
4310                 TimerValue = 0;
4311 
4312             RefreshProgress = TRUE;
4313         }
4314 
4315         if (RefreshProgress)
4316         {
4317             ProgressSetStep(ProgressBar, OldTimerValue - TimerValue);
4318             RefreshProgress = FALSE;
4319         }
4320 
4321         /* Stop when the timer reaches zero */
4322         if (TimerValue <= 0)
4323             break;
4324 
4325         /* Check for user key presses */
4326 
4327         /*
4328          * If the timer is used, use a passive wait of maximum 1 second
4329          * while monitoring for incoming console input events, so that
4330          * we are still able to display the timing count.
4331          */
4332 
4333         /* Wait a maximum of 1 second for input events */
4334         TimeElapsed = NtGetTickCount() - StartTime;
4335         if (TimeElapsed < TimerDiv)
4336         {
4337             /* Convert the time to NT format */
4338             Timeout.QuadPart = (TimerDiv - TimeElapsed) * -10000LL;
4339             Status = NtWaitForSingleObject(StdInput, FALSE, &Timeout);
4340         }
4341         else
4342         {
4343             Status = STATUS_TIMEOUT;
4344         }
4345 
4346         /* Check whether the input event has been signaled, or a timeout happened */
4347         if (Status == STATUS_TIMEOUT)
4348         {
4349             continue;
4350         }
4351         if (Status != STATUS_WAIT_0)
4352         {
4353             /* An error happened, bail out */
4354             DPRINT1("NtWaitForSingleObject() failed, Status 0x%08lx\n", Status);
4355             break;
4356         }
4357 
4358         /* Check for an ENTER key press */
4359         while (CONSOLE_ConInKeyPeek(Ir))
4360         {
4361             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
4362             {
4363                 /* Found it, stop waiting */
4364                 goto Exit;
4365             }
4366         }
4367     }
4368 
4369 Exit:
4370     /* Destroy the progress bar and quit */
4371     DestroyProgressBar(ProgressBar);
4372 }
4373 
4374 
4375 /*
4376  * Displays the QuitPage.
4377  *
4378  * Next pages:
4379  *  FlushPage (At once)
4380  *
4381  * SIDEEFFECTS
4382  *  Destroy the Lists
4383  *
4384  * RETURNS
4385  *   Number of the next page.
4386  */
4387 static PAGE_NUMBER
4388 QuitPage(PINPUT_RECORD Ir)
4389 {
4390     MUIDisplayPage(QUIT_PAGE);
4391 
4392     /* Destroy the NTOS installations list */
4393     if (NtOsInstallsList != NULL)
4394     {
4395         DestroyGenericList(NtOsInstallsList, TRUE);
4396         NtOsInstallsList = NULL;
4397     }
4398 
4399     /* Destroy the partition list */
4400     if (PartitionList != NULL)
4401     {
4402         DestroyPartitionList(PartitionList);
4403         PartitionList = NULL;
4404     }
4405 
4406     /* Reset the formatter machine state */
4407     TempPartition = NULL;
4408     FormatState = Start;
4409 
4410     /* Destroy the filesystem list */
4411     ResetFileSystemList();
4412 
4413     CONSOLE_SetStatusText(MUIGetString(STRING_REBOOTCOMPUTER2));
4414 
4415     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4416     ProgressCountdown(Ir, 15);
4417     return FLUSH_PAGE;
4418 }
4419 
4420 
4421 /*
4422  * Displays the SuccessPage.
4423  *
4424  * Next pages:
4425  *  FlushPage (At once)
4426  *
4427  * SIDEEFFECTS
4428  *  Destroy the Lists
4429  *
4430  * RETURNS
4431  *   Number of the next page.
4432  */
4433 static PAGE_NUMBER
4434 SuccessPage(PINPUT_RECORD Ir)
4435 {
4436     MUIDisplayPage(SUCCESS_PAGE);
4437 
4438     if (IsUnattendedSetup)
4439         return FLUSH_PAGE;
4440 
4441     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4442     ProgressCountdown(Ir, 15);
4443     return FLUSH_PAGE;
4444 }
4445 
4446 
4447 /*
4448  * Displays the FlushPage.
4449  *
4450  * Next pages:
4451  *  RebootPage (At once)
4452  *
4453  * RETURNS
4454  *   Number of the next page.
4455  */
4456 static PAGE_NUMBER
4457 FlushPage(PINPUT_RECORD Ir)
4458 {
4459     MUIDisplayPage(FLUSH_PAGE);
4460     return REBOOT_PAGE;
4461 }
4462 
4463 
4464 /*
4465  * The start routine and page management
4466  */
4467 NTSTATUS
4468 RunUSetup(VOID)
4469 {
4470     NTSTATUS Status;
4471     INPUT_RECORD Ir;
4472     PAGE_NUMBER Page;
4473     BOOLEAN Old;
4474 
4475     InfSetHeap(ProcessHeap);
4476 
4477     /* Tell the Cm this is a setup boot, and it has to behave accordingly */
4478     Status = NtInitializeRegistry(CM_BOOT_FLAG_SETUP);
4479     if (!NT_SUCCESS(Status))
4480         DPRINT1("NtInitializeRegistry() failed (Status 0x%08lx)\n", Status);
4481 
4482     /* Initialize the user-mode PnP manager */
4483     Status = InitializeUserModePnpManager(&USetupData.SetupInf);
4484     if (!NT_SUCCESS(Status))
4485     {
4486         // PrintString(??);
4487         DPRINT1("The user-mode PnP manager could not initialize (Status 0x%08lx), expect unavailable devices!\n", Status);
4488     }
4489 
4490     if (!CONSOLE_Init())
4491     {
4492         PrintString(MUIGetString(STRING_CONSOLEFAIL1));
4493         PrintString(MUIGetString(STRING_CONSOLEFAIL2));
4494         PrintString(MUIGetString(STRING_CONSOLEFAIL3));
4495 
4496         /* We failed to initialize the video, just quit the installer */
4497         return STATUS_APP_INIT_FAILURE;
4498     }
4499 
4500     /* Initialize Setup, phase 0 */
4501     InitializeSetup(&USetupData, 0);
4502     USetupData.ErrorRoutine = USetupErrorRoutine;
4503 
4504     /* Hide the cursor and clear the screen and keyboard buffer */
4505     CONSOLE_SetCursorType(TRUE, FALSE);
4506     CONSOLE_ClearScreen();
4507     CONSOLE_Flush();
4508 
4509     /* Global Initialization page */
4510     Page = SetupStartPage(&Ir);
4511 
4512     while (Page != REBOOT_PAGE && Page != RECOVERY_PAGE)
4513     {
4514         CONSOLE_ClearScreen();
4515         CONSOLE_Flush();
4516 
4517         // CONSOLE_SetUnderlinedTextXY(4, 3, " ReactOS " KERNEL_VERSION_STR " Setup ");
4518 
4519         switch (Page)
4520         {
4521             /* Language page */
4522             case LANGUAGE_PAGE:
4523                 Page = LanguagePage(&Ir);
4524                 break;
4525 
4526             /* Welcome page */
4527             case WELCOME_PAGE:
4528                 Page = WelcomePage(&Ir);
4529                 break;
4530 
4531             /* License page */
4532             case LICENSE_PAGE:
4533                 Page = LicensePage(&Ir);
4534                 break;
4535 
4536             /* Install pages */
4537             case INSTALL_INTRO_PAGE:
4538                 Page = InstallIntroPage(&Ir);
4539                 break;
4540 
4541 #if 0
4542             case SCSI_CONTROLLER_PAGE:
4543                 Page = ScsiControllerPage(&Ir);
4544                 break;
4545 
4546             case OEM_DRIVER_PAGE:
4547                 Page = OemDriverPage(&Ir);
4548                 break;
4549 #endif
4550 
4551             case DEVICE_SETTINGS_PAGE:
4552                 Page = DeviceSettingsPage(&Ir);
4553                 break;
4554 
4555             case COMPUTER_SETTINGS_PAGE:
4556                 Page = ComputerSettingsPage(&Ir);
4557                 break;
4558 
4559             case DISPLAY_SETTINGS_PAGE:
4560                 Page = DisplaySettingsPage(&Ir);
4561                 break;
4562 
4563             case KEYBOARD_SETTINGS_PAGE:
4564                 Page = KeyboardSettingsPage(&Ir);
4565                 break;
4566 
4567             case LAYOUT_SETTINGS_PAGE:
4568                 Page = LayoutSettingsPage(&Ir);
4569                 break;
4570 
4571             /* Partitioning pages */
4572             case SELECT_PARTITION_PAGE:
4573                 Page = SelectPartitionPage(&Ir);
4574                 break;
4575 
4576             case CREATE_PRIMARY_PARTITION_PAGE:
4577                 Page = CreatePrimaryPartitionPage(&Ir);
4578                 break;
4579 
4580             case CREATE_EXTENDED_PARTITION_PAGE:
4581                 Page = CreateExtendedPartitionPage(&Ir);
4582                 break;
4583 
4584             case CREATE_LOGICAL_PARTITION_PAGE:
4585                 Page = CreateLogicalPartitionPage(&Ir);
4586                 break;
4587 
4588             case CONFIRM_DELETE_SYSTEM_PARTITION_PAGE:
4589                 Page = ConfirmDeleteSystemPartitionPage(&Ir);
4590                 break;
4591 
4592             case DELETE_PARTITION_PAGE:
4593                 Page = DeletePartitionPage(&Ir);
4594                 break;
4595 
4596             /* Filesystem partition operations pages */
4597             case SELECT_FILE_SYSTEM_PAGE:
4598                 Page = SelectFileSystemPage(&Ir);
4599                 break;
4600 
4601             case FORMAT_PARTITION_PAGE:
4602                 Page = FormatPartitionPage(&Ir);
4603                 break;
4604 
4605             case CHECK_FILE_SYSTEM_PAGE:
4606                 Page = CheckFileSystemPage(&Ir);
4607                 break;
4608 
4609             /* Installation pages */
4610             case INSTALL_DIRECTORY_PAGE:
4611                 Page = InstallDirectoryPage(&Ir);
4612                 break;
4613 
4614             case PREPARE_COPY_PAGE:
4615                 Page = PrepareCopyPage(&Ir);
4616                 break;
4617 
4618             case FILE_COPY_PAGE:
4619                 Page = FileCopyPage(&Ir);
4620                 break;
4621 
4622             case REGISTRY_PAGE:
4623                 Page = RegistryPage(&Ir);
4624                 break;
4625 
4626             /* Bootloader installation pages */
4627             case BOOT_LOADER_PAGE:
4628                 Page = BootLoaderPage(&Ir);
4629                 break;
4630 
4631             case BOOT_LOADER_FLOPPY_PAGE:
4632                 Page = BootLoaderFloppyPage(&Ir);
4633                 break;
4634 
4635             case BOOT_LOADER_HARDDISK_MBR_PAGE:
4636                 Page = BootLoaderHarddiskMbrPage(&Ir);
4637                 break;
4638 
4639             case BOOT_LOADER_HARDDISK_VBR_PAGE:
4640                 Page = BootLoaderHarddiskVbrPage(&Ir);
4641                 break;
4642 
4643             /* Repair pages */
4644             case REPAIR_INTRO_PAGE:
4645                 Page = RepairIntroPage(&Ir);
4646                 break;
4647 
4648             case UPGRADE_REPAIR_PAGE:
4649                 Page = UpgradeRepairPage(&Ir);
4650                 break;
4651 
4652             case SUCCESS_PAGE:
4653                 Page = SuccessPage(&Ir);
4654                 break;
4655 
4656             case FLUSH_PAGE:
4657                 Page = FlushPage(&Ir);
4658                 break;
4659 
4660             case QUIT_PAGE:
4661                 Page = QuitPage(&Ir);
4662                 break;
4663 
4664             /* Virtual pages */
4665             case SETUP_INIT_PAGE:
4666             case REBOOT_PAGE:
4667             case RECOVERY_PAGE:
4668                 break;
4669 
4670             default:
4671                 break;
4672         }
4673     }
4674 
4675     /* Terminate the user-mode PnP manager */
4676     TerminateUserModePnpManager();
4677 
4678     /* Setup has finished */
4679     FinishSetup(&USetupData);
4680 
4681     if (Page == RECOVERY_PAGE)
4682         RecoveryConsole();
4683 
4684     FreeConsole();
4685 
4686     /* Reboot */
4687     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
4688     NtShutdownSystem(ShutdownReboot);
4689     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
4690 
4691     return STATUS_SUCCESS;
4692 }
4693 
4694 
4695 VOID NTAPI
4696 NtProcessStartup(PPEB Peb)
4697 {
4698     NTSTATUS Status;
4699     LARGE_INTEGER Time;
4700 
4701     RtlNormalizeProcessParams(Peb->ProcessParameters);
4702 
4703     ProcessHeap = Peb->ProcessHeap;
4704 
4705     NtQuerySystemTime(&Time);
4706 
4707     Status = RunUSetup();
4708 
4709     if (NT_SUCCESS(Status))
4710     {
4711         /*
4712          * Avoid a bugcheck if RunUSetup() finishes too quickly by implementing
4713          * a protective waiting.
4714          * This wait is needed because, since we are started as SMSS.EXE,
4715          * the NT kernel explicitly waits 5 seconds for the initial process
4716          * SMSS.EXE to initialize (as a protective measure), and otherwise
4717          * bugchecks with the code SESSION5_INITIALIZATION_FAILED.
4718          */
4719         Time.QuadPart += 50000000;
4720         NtDelayExecution(FALSE, &Time);
4721     }
4722     else
4723     {
4724         /* The installer failed to start: raise a hard error (crash the system/BSOD) */
4725         Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED,
4726                                   0, 0, NULL, 0, NULL);
4727     }
4728 
4729     NtTerminateProcess(NtCurrentProcess(), Status);
4730 }
4731 
4732 /* EOF */
4733