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