xref: /reactos/base/setup/usetup/usetup.c (revision 77e6348f)
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                 static PCSTR s_pszCopying = NULL; /* Cached for speed */
3654 
3655                 /* Display copy message */
3656                 ASSERT(Param2 == FILEOP_COPY);
3657 
3658                 /* NOTE: When extracting from CABs the Source is the CAB name */
3659                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
3660                 if (DstFileName) ++DstFileName;
3661                 else DstFileName = FilePathInfo->Target;
3662 
3663                 if (!s_pszCopying)
3664                     s_pszCopying = MUIGetString(STRING_COPYING);
3665                 CONSOLE_SetStatusText(s_pszCopying, DstFileName);
3666 #ifdef __REACTOS__ /* HACK */
3667                 DoWatchDestFileName(DstFileName);
3668 #endif
3669             }
3670 
3671             SetupUpdateMemoryInfo(CopyContext, FALSE);
3672             break;
3673         }
3674 
3675         case SPFILENOTIFY_COPYERROR:
3676         {
3677             FilePathInfo = (PFILEPATHS_W)Param1;
3678 
3679             DPRINT1("An error happened while trying to copy file '%S' (error 0x%08lx), skipping it...\n",
3680                     FilePathInfo->Target, FilePathInfo->Win32Error);
3681             return FILEOP_SKIP;
3682         }
3683 
3684         case SPFILENOTIFY_ENDDELETE:
3685         case SPFILENOTIFY_ENDRENAME:
3686         case SPFILENOTIFY_ENDCOPY:
3687         {
3688             CopyContext->CompletedOperations++;
3689 
3690             /* SYSREG checkpoint */
3691             if (CopyContext->TotalOperations >> 1 == CopyContext->CompletedOperations)
3692                 DPRINT1("CHECKPOINT:HALF_COPIED\n");
3693 
3694             ProgressNextStep(CopyContext->ProgressBar);
3695             SetupUpdateMemoryInfo(CopyContext, FALSE);
3696             break;
3697         }
3698     }
3699 
3700     return FILEOP_DOIT;
3701 }
3702 
3703 
3704 /*
3705  * Displays the FileCopyPage.
3706  *
3707  * Next pages:
3708  *  RegistryPage(At once)
3709  *
3710  * SIDEEFFECTS
3711  *  Calls DoFileCopy
3712  *
3713  * RETURNS
3714  *   Number of the next page.
3715  */
3716 static PAGE_NUMBER
3717 FileCopyPage(PINPUT_RECORD Ir)
3718 {
3719     COPYCONTEXT CopyContext;
3720     UINT MemBarWidth;
3721 
3722     MUIDisplayPage(FILE_COPY_PAGE);
3723 
3724     /* Create context for the copy process */
3725     CopyContext.TotalOperations = 0;
3726     CopyContext.CompletedOperations = 0;
3727 
3728     /* Create the progress bar as well */
3729     CopyContext.ProgressBar = CreateProgressBar(13,
3730                                                 26,
3731                                                 xScreen - 13,
3732                                                 yScreen - 20,
3733                                                 10,
3734                                                 24,
3735                                                 TRUE,
3736                                                 MUIGetString(STRING_SETUPCOPYINGFILES));
3737 
3738     // fit memory bars to screen width, distribute them uniform
3739     MemBarWidth = (xScreen - 26) / 5;
3740     MemBarWidth -= MemBarWidth % 2;  // make even
3741     /* ATTENTION: The following progress bars are debug stuff, which should not be translated!! */
3742     /* Create the paged pool progress bar */
3743     CopyContext.MemoryBars[0] = CreateProgressBar(13,
3744                                                   40,
3745                                                   13 + MemBarWidth,
3746                                                   43,
3747                                                   13,
3748                                                   44,
3749                                                   FALSE,
3750                                                   "Kernel Pool");
3751 
3752     /* Create the non paged pool progress bar */
3753     CopyContext.MemoryBars[1] = CreateProgressBar((xScreen / 2)- (MemBarWidth / 2),
3754                                                   40,
3755                                                   (xScreen / 2) + (MemBarWidth / 2),
3756                                                   43,
3757                                                   (xScreen / 2)- (MemBarWidth / 2),
3758                                                   44,
3759                                                   FALSE,
3760                                                   "Kernel Cache");
3761 
3762     /* Create the global memory progress bar */
3763     CopyContext.MemoryBars[2] = CreateProgressBar(xScreen - 13 - MemBarWidth,
3764                                                   40,
3765                                                   xScreen - 13,
3766                                                   43,
3767                                                   xScreen - 13 - MemBarWidth,
3768                                                   44,
3769                                                   FALSE,
3770                                                   "Free Memory");
3771 
3772     /* Do the file copying */
3773     DoFileCopy(&USetupData, FileCopyCallback, &CopyContext);
3774 
3775     /* If we get here, we're done, so cleanup the progress bar */
3776     DestroyProgressBar(CopyContext.ProgressBar);
3777     DestroyProgressBar(CopyContext.MemoryBars[0]);
3778     DestroyProgressBar(CopyContext.MemoryBars[1]);
3779     DestroyProgressBar(CopyContext.MemoryBars[2]);
3780 
3781     /* Create the $winnt$.inf file */
3782     InstallSetupInfFile(&USetupData);
3783 
3784     /* Go display the next page */
3785     return REGISTRY_PAGE;
3786 }
3787 
3788 
3789 static VOID
3790 __cdecl
3791 RegistryStatus(IN REGISTRY_STATUS RegStatus, ...)
3792 {
3793     /* WARNING: Please keep this lookup table in sync with the resources! */
3794     static const UINT StringIDs[] =
3795     {
3796         STRING_DONE,                    /* Success */
3797         STRING_REGHIVEUPDATE,           /* RegHiveUpdate */
3798         STRING_IMPORTFILE,              /* ImportRegHive */
3799         STRING_DISPLAYSETTINGSUPDATE,   /* DisplaySettingsUpdate */
3800         STRING_LOCALESETTINGSUPDATE,    /* LocaleSettingsUpdate */
3801         STRING_ADDKBLAYOUTS,            /* KeybLayouts */
3802         STRING_KEYBOARDSETTINGSUPDATE,  /* KeybSettingsUpdate */
3803         STRING_CODEPAGEINFOUPDATE,      /* CodePageInfoUpdate */
3804     };
3805 
3806     va_list args;
3807 
3808     if (RegStatus < ARRAYSIZE(StringIDs))
3809     {
3810         va_start(args, RegStatus);
3811         CONSOLE_SetStatusTextV(MUIGetString(StringIDs[RegStatus]), args);
3812         va_end(args);
3813     }
3814     else
3815     {
3816         CONSOLE_SetStatusText("Unknown status %d", RegStatus);
3817     }
3818 }
3819 
3820 /*
3821  * Displays the RegistryPage.
3822  *
3823  * Next pages:
3824  *  SuccessPage (if RepairUpdate)
3825  *  BootLoaderPage (default)
3826  *  QuitPage
3827  *
3828  * SIDEEFFECTS
3829  *  Calls UpdateRegistry
3830  *
3831  * RETURNS
3832  *   Number of the next page.
3833  */
3834 static PAGE_NUMBER
3835 RegistryPage(PINPUT_RECORD Ir)
3836 {
3837     ULONG Error;
3838 
3839     MUIDisplayPage(REGISTRY_PAGE);
3840 
3841     Error = UpdateRegistry(&USetupData,
3842                            RepairUpdateFlag,
3843                            PartitionList,
3844                            InstallPartition->DriveLetter,
3845                            SelectedLanguageId,
3846                            RegistryStatus,
3847                            &s_SubstSettings);
3848     if (Error != ERROR_SUCCESS)
3849     {
3850         MUIDisplayError(Error, Ir, POPUP_WAIT_ENTER);
3851         return QUIT_PAGE;
3852     }
3853     else
3854     {
3855         CONSOLE_SetStatusText(MUIGetString(STRING_DONE));
3856         return BOOT_LOADER_PAGE;
3857     }
3858 }
3859 
3860 
3861 /*
3862  * Displays the BootLoaderPage.
3863  *
3864  * Next pages:
3865  *  SuccessPage (if RepairUpdate)
3866  *  BootLoaderHarddiskMbrPage
3867  *  BootLoaderHarddiskVbrPage
3868  *  BootLoaderFloppyPage
3869  *  SuccessPage
3870  *  QuitPage
3871  *
3872  * SIDEEFFECTS
3873  *  Calls RegInitializeRegistry
3874  *  Calls ImportRegistryFile
3875  *  Calls SetDefaultPagefile
3876  *  Calls SetMountedDeviceValues
3877  *
3878  * RETURNS
3879  *   Number of the next page.
3880  */
3881 static PAGE_NUMBER
3882 BootLoaderPage(PINPUT_RECORD Ir)
3883 {
3884     USHORT Line = 12;
3885     WCHAR PathBuffer[MAX_PATH];
3886 
3887     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
3888 
3889     /* We must have a supported system partition by now */
3890     ASSERT(SystemPartition && SystemPartition->IsPartitioned && SystemPartition->PartitionNumber != 0);
3891 
3892     RtlFreeUnicodeString(&USetupData.SystemRootPath);
3893     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
3894             L"\\Device\\Harddisk%lu\\Partition%lu\\",
3895             SystemPartition->DiskEntry->DiskNumber,
3896             SystemPartition->PartitionNumber);
3897     RtlCreateUnicodeString(&USetupData.SystemRootPath, PathBuffer);
3898     DPRINT1("SystemRootPath: %wZ\n", &USetupData.SystemRootPath);
3899 
3900     /* For unattended setup, skip MBR installation or install on floppy if needed */
3901     if (IsUnattendedSetup)
3902     {
3903         if ((USetupData.MBRInstallType == 0) ||
3904             (USetupData.MBRInstallType == 1))
3905         {
3906             goto Quit;
3907         }
3908     }
3909 
3910     /*
3911      * We may install an MBR or VBR, but before that, check whether
3912      * we need to actually install the VBR on floppy/removable media
3913      * if the system partition is not recognized.
3914      */
3915     if ((SystemPartition->DiskEntry->DiskStyle != PARTITION_STYLE_MBR) ||
3916         !IsRecognizedPartition(SystemPartition->PartitionType))
3917     {
3918         USetupData.MBRInstallType = 1;
3919         goto Quit;
3920     }
3921 
3922     /* Is it an unattended install on hdd? */
3923     if (IsUnattendedSetup)
3924     {
3925         if ((USetupData.MBRInstallType == 2) ||
3926             (USetupData.MBRInstallType == 3))
3927         {
3928             goto Quit;
3929         }
3930     }
3931 
3932     MUIDisplayPage(BOOT_LOADER_PAGE);
3933     CONSOLE_InvertTextXY(8, Line, 60, 1);
3934 
3935     while (TRUE)
3936     {
3937         CONSOLE_ConInKey(Ir);
3938 
3939         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3940             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
3941         {
3942             CONSOLE_NormalTextXY(8, Line, 60, 1);
3943 
3944             Line++;
3945             if (Line < 12)
3946                 Line = 15;
3947 
3948             if (Line > 15)
3949                 Line = 12;
3950 
3951             CONSOLE_InvertTextXY(8, Line, 60, 1);
3952         }
3953         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3954                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
3955         {
3956             CONSOLE_NormalTextXY(8, Line, 60, 1);
3957 
3958             Line--;
3959             if (Line < 12)
3960                 Line = 15;
3961 
3962             if (Line > 15)
3963                 Line = 12;
3964 
3965             CONSOLE_InvertTextXY(8, Line, 60, 1);
3966         }
3967         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3968                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
3969         {
3970             CONSOLE_NormalTextXY(8, Line, 60, 1);
3971 
3972             Line = 12;
3973 
3974             CONSOLE_InvertTextXY(8, Line, 60, 1);
3975         }
3976         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3977                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
3978         {
3979             CONSOLE_NormalTextXY(8, Line, 60, 1);
3980 
3981             Line = 15;
3982 
3983             CONSOLE_InvertTextXY(8, Line, 60, 1);
3984         }
3985         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3986                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3987         {
3988             if (ConfirmQuit(Ir))
3989                 return QUIT_PAGE;
3990 
3991             break;
3992         }
3993         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
3994         {
3995             if (Line == 12)
3996             {
3997                 /* Install on both MBR and VBR */
3998                 USetupData.MBRInstallType = 2;
3999                 break;
4000             }
4001             else if (Line == 13)
4002             {
4003                 /* Install on VBR only */
4004                 USetupData.MBRInstallType = 3;
4005                 break;
4006             }
4007             else if (Line == 14)
4008             {
4009                 /* Install on floppy */
4010                 USetupData.MBRInstallType = 1;
4011                 break;
4012             }
4013             else if (Line == 15)
4014             {
4015                 /* Skip MBR installation */
4016                 USetupData.MBRInstallType = 0;
4017                 break;
4018             }
4019 
4020             return BOOT_LOADER_PAGE;
4021         }
4022     }
4023 
4024 Quit:
4025     switch (USetupData.MBRInstallType)
4026     {
4027         /* Skip MBR installation */
4028         case 0:
4029             return SUCCESS_PAGE;
4030 
4031         /* Install on floppy */
4032         case 1:
4033             return BOOT_LOADER_FLOPPY_PAGE;
4034 
4035         /* Install on both MBR and VBR or VBR only */
4036         case 2:
4037         case 3:
4038             return BOOT_LOADER_INSTALLATION_PAGE;
4039     }
4040 
4041     return BOOT_LOADER_PAGE;
4042 }
4043 
4044 
4045 /*
4046  * Displays the BootLoaderFloppyPage.
4047  *
4048  * Next pages:
4049  *  SuccessPage (At once)
4050  *  QuitPage
4051  *
4052  * SIDEEFFECTS
4053  *  Calls InstallFatBootcodeToFloppy()
4054  *
4055  * RETURNS
4056  *   Number of the next page.
4057  */
4058 static PAGE_NUMBER
4059 BootLoaderFloppyPage(PINPUT_RECORD Ir)
4060 {
4061     NTSTATUS Status;
4062 
4063     MUIDisplayPage(BOOT_LOADER_FLOPPY_PAGE);
4064 
4065 //  CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
4066 
4067     while (TRUE)
4068     {
4069         CONSOLE_ConInKey(Ir);
4070 
4071         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4072             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
4073         {
4074             if (ConfirmQuit(Ir))
4075                 return QUIT_PAGE;
4076 
4077             break;
4078         }
4079         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
4080         {
4081             Status = InstallFatBootcodeToFloppy(&USetupData.SourceRootPath,
4082                                                 &USetupData.DestinationArcPath);
4083             if (!NT_SUCCESS(Status))
4084             {
4085                 if (Status == STATUS_DEVICE_NOT_READY)
4086                     MUIDisplayError(ERROR_NO_FLOPPY, Ir, POPUP_WAIT_ENTER);
4087 
4088                 /* TODO: Print error message */
4089                 return BOOT_LOADER_FLOPPY_PAGE;
4090             }
4091 
4092             return SUCCESS_PAGE;
4093         }
4094     }
4095 
4096     return BOOT_LOADER_FLOPPY_PAGE;
4097 }
4098 
4099 
4100 /*
4101  * Displays the BootLoaderInstallationPage.
4102  *
4103  * Next pages:
4104  *  SuccessPage (At once)
4105  *  QuitPage
4106  *
4107  * SIDEEFFECTS
4108  *  Calls InstallVBRToPartition() if VBR installation is chosen.
4109  *  Otherwise both InstallVBRToPartition() and InstallMbrBootCodeToDisk()
4110  *  are called if both MBR and VBR installation is chosen.
4111  *
4112  * RETURNS
4113  *   Number of the next page.
4114  */
4115 static PAGE_NUMBER
4116 BootLoaderInstallationPage(PINPUT_RECORD Ir)
4117 {
4118     NTSTATUS Status;
4119     WCHAR DestinationDevicePathBuffer[MAX_PATH];
4120 
4121     MUIDisplayPage(BOOT_LOADER_INSTALLATION_PAGE);
4122 
4123     if (USetupData.MBRInstallType == 2)
4124     {
4125         /* Step 1: Write the VBR */
4126         Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4127                                        &USetupData.SourceRootPath,
4128                                        &USetupData.DestinationArcPath,
4129                                        SystemPartition->FileSystem);
4130         if (!NT_SUCCESS(Status))
4131         {
4132             MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4133                             SystemPartition->FileSystem);
4134             return QUIT_PAGE;
4135         }
4136 
4137         /* Step 2: Write the MBR if the disk containing the system partition is not a super-floppy */
4138         if (!IsSuperFloppy(SystemPartition->DiskEntry))
4139         {
4140             RtlStringCchPrintfW(DestinationDevicePathBuffer, ARRAYSIZE(DestinationDevicePathBuffer),
4141                                 L"\\Device\\Harddisk%d\\Partition0",
4142                                 SystemPartition->DiskEntry->DiskNumber);
4143             Status = InstallMbrBootCodeToDisk(&USetupData.SystemRootPath,
4144                                               &USetupData.SourceRootPath,
4145                                               DestinationDevicePathBuffer);
4146             if (!NT_SUCCESS(Status))
4147             {
4148                 DPRINT1("InstallMbrBootCodeToDisk() failed (Status %lx)\n", Status);
4149                 MUIDisplayError(ERROR_INSTALL_BOOTCODE, Ir, POPUP_WAIT_ENTER, L"MBR");
4150                 return QUIT_PAGE;
4151             }
4152         }
4153     }
4154     else
4155     {
4156         Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4157                                        &USetupData.SourceRootPath,
4158                                        &USetupData.DestinationArcPath,
4159                                        SystemPartition->FileSystem);
4160         if (!NT_SUCCESS(Status))
4161         {
4162             MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4163                             SystemPartition->FileSystem);
4164             return QUIT_PAGE;
4165         }
4166     }
4167 
4168     return SUCCESS_PAGE;
4169 }
4170 
4171 
4172 /**
4173  * @name ProgressTimeOutStringHandler
4174  *
4175  * Handles the generation (displaying) of the timeout
4176  * countdown to the screen dynamically.
4177  *
4178  * @param   Bar
4179  *     A pointer to a progress bar.
4180  *
4181  * @param   AlwaysUpdate
4182  *     Constantly update the progress bar (boolean type).
4183  *
4184  * @param   Buffer
4185  *     A pointer to a string buffer.
4186  *
4187  * @param   cchBufferSize
4188  *     The buffer's size in number of characters.
4189  *
4190  * @return
4191  *     TRUE or FALSE on function termination.
4192  *
4193  */
4194 static
4195 BOOLEAN NTAPI
4196 ProgressTimeOutStringHandler(
4197     IN PPROGRESSBAR Bar,
4198     IN BOOLEAN AlwaysUpdate,
4199     OUT PSTR Buffer,
4200     IN SIZE_T cchBufferSize)
4201 {
4202     ULONG OldProgress = Bar->Progress;
4203 
4204     if (Bar->StepCount == 0)
4205     {
4206         Bar->Progress = 0;
4207     }
4208     else
4209     {
4210         Bar->Progress = Bar->StepCount - Bar->CurrentStep;
4211     }
4212 
4213     /* Build the progress string if it has changed */
4214     if (Bar->ProgressFormatText &&
4215         (AlwaysUpdate || (Bar->Progress != OldProgress)))
4216     {
4217         RtlStringCchPrintfA(Buffer, cchBufferSize,
4218                             Bar->ProgressFormatText, Bar->Progress / max(1, Bar->Width) + 1);
4219 
4220         return TRUE;
4221     }
4222 
4223     return FALSE;
4224 }
4225 
4226 /**
4227  * @name ProgressCountdown
4228  *
4229  * Displays and draws a red-coloured progress bar with a countdown.
4230  * When the timeout is reached, the flush page is displayed for reboot.
4231  *
4232  * @param   Ir
4233  *     A pointer to an input keyboard record.
4234  *
4235  * @param   TimeOut
4236  *     Initial countdown value in seconds.
4237  *
4238  * @return
4239  *     Nothing.
4240  *
4241  */
4242 static VOID
4243 ProgressCountdown(
4244     IN PINPUT_RECORD Ir,
4245     IN LONG TimeOut)
4246 {
4247     NTSTATUS Status;
4248     ULONG StartTime, BarWidth, TimerDiv;
4249     LONG TimeElapsed;
4250     LONG TimerValue, OldTimerValue;
4251     LARGE_INTEGER Timeout;
4252     PPROGRESSBAR ProgressBar;
4253     BOOLEAN RefreshProgress = TRUE;
4254 
4255     /* Bail out if the timeout is already zero */
4256     if (TimeOut <= 0)
4257         return;
4258 
4259     /* Create the timeout progress bar and set it up */
4260     ProgressBar = CreateProgressBarEx(13,
4261                                       26,
4262                                       xScreen - 13,
4263                                       yScreen - 20,
4264                                       10,
4265                                       24,
4266                                       TRUE,
4267                                       FOREGROUND_RED | BACKGROUND_BLUE,
4268                                       0,
4269                                       NULL,
4270                                       MUIGetString(STRING_REBOOTPROGRESSBAR),
4271                                       ProgressTimeOutStringHandler);
4272 
4273     BarWidth = max(1, ProgressBar->Width);
4274     TimerValue = TimeOut * BarWidth;
4275     ProgressSetStepCount(ProgressBar, TimerValue);
4276 
4277     StartTime = NtGetTickCount();
4278     CONSOLE_Flush();
4279 
4280     TimerDiv = 1000 / BarWidth;
4281     TimerDiv = max(1, TimerDiv);
4282     OldTimerValue = TimerValue;
4283     while (TRUE)
4284     {
4285         /* Decrease the timer */
4286 
4287         /*
4288          * Compute how much time the previous operations took.
4289          * This allows us in particular to take account for any time
4290          * elapsed if something slowed down.
4291          */
4292         TimeElapsed = NtGetTickCount() - StartTime;
4293         if (TimeElapsed >= TimerDiv)
4294         {
4295             /* Increase StartTime by steps of 1 / ProgressBar->Width seconds */
4296             TimeElapsed /= TimerDiv;
4297             StartTime += (TimerDiv * TimeElapsed);
4298 
4299             if (TimeElapsed <= TimerValue)
4300                 TimerValue -= TimeElapsed;
4301             else
4302                 TimerValue = 0;
4303 
4304             RefreshProgress = TRUE;
4305         }
4306 
4307         if (RefreshProgress)
4308         {
4309             ProgressSetStep(ProgressBar, OldTimerValue - TimerValue);
4310             RefreshProgress = FALSE;
4311         }
4312 
4313         /* Stop when the timer reaches zero */
4314         if (TimerValue <= 0)
4315             break;
4316 
4317         /* Check for user key presses */
4318 
4319         /*
4320          * If the timer is used, use a passive wait of maximum 1 second
4321          * while monitoring for incoming console input events, so that
4322          * we are still able to display the timing count.
4323          */
4324 
4325         /* Wait a maximum of 1 second for input events */
4326         TimeElapsed = NtGetTickCount() - StartTime;
4327         if (TimeElapsed < TimerDiv)
4328         {
4329             /* Convert the time to NT format */
4330             Timeout.QuadPart = (TimerDiv - TimeElapsed) * -10000LL;
4331             Status = NtWaitForSingleObject(StdInput, FALSE, &Timeout);
4332         }
4333         else
4334         {
4335             Status = STATUS_TIMEOUT;
4336         }
4337 
4338         /* Check whether the input event has been signaled, or a timeout happened */
4339         if (Status == STATUS_TIMEOUT)
4340         {
4341             continue;
4342         }
4343         if (Status != STATUS_WAIT_0)
4344         {
4345             /* An error happened, bail out */
4346             DPRINT1("NtWaitForSingleObject() failed, Status 0x%08lx\n", Status);
4347             break;
4348         }
4349 
4350         /* Check for an ENTER key press */
4351         while (CONSOLE_ConInKeyPeek(Ir))
4352         {
4353             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
4354             {
4355                 /* Found it, stop waiting */
4356                 goto Exit;
4357             }
4358         }
4359     }
4360 
4361 Exit:
4362     /* Destroy the progress bar and quit */
4363     DestroyProgressBar(ProgressBar);
4364 }
4365 
4366 
4367 /*
4368  * Displays the QuitPage.
4369  *
4370  * Next pages:
4371  *  FlushPage (At once)
4372  *
4373  * SIDEEFFECTS
4374  *  Destroy the Lists
4375  *
4376  * RETURNS
4377  *   Number of the next page.
4378  */
4379 static PAGE_NUMBER
4380 QuitPage(PINPUT_RECORD Ir)
4381 {
4382     MUIDisplayPage(QUIT_PAGE);
4383 
4384     /* Destroy the NTOS installations list */
4385     if (NtOsInstallsList != NULL)
4386     {
4387         DestroyGenericList(NtOsInstallsList, TRUE);
4388         NtOsInstallsList = NULL;
4389     }
4390 
4391     /* Destroy the partition list */
4392     if (PartitionList != NULL)
4393     {
4394         DestroyPartitionList(PartitionList);
4395         PartitionList = NULL;
4396     }
4397 
4398     /* Reset the formatter machine state */
4399     TempPartition = NULL;
4400     FormatState = Start;
4401 
4402     /* Destroy the filesystem list */
4403     ResetFileSystemList();
4404 
4405     CONSOLE_SetStatusText(MUIGetString(STRING_REBOOTCOMPUTER2));
4406 
4407     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4408     ProgressCountdown(Ir, 15);
4409     return FLUSH_PAGE;
4410 }
4411 
4412 
4413 /*
4414  * Displays the SuccessPage.
4415  *
4416  * Next pages:
4417  *  FlushPage (At once)
4418  *
4419  * SIDEEFFECTS
4420  *  Destroy the Lists
4421  *
4422  * RETURNS
4423  *   Number of the next page.
4424  */
4425 static PAGE_NUMBER
4426 SuccessPage(PINPUT_RECORD Ir)
4427 {
4428     MUIDisplayPage(SUCCESS_PAGE);
4429 
4430     if (IsUnattendedSetup)
4431         return FLUSH_PAGE;
4432 
4433     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4434     ProgressCountdown(Ir, 15);
4435     return FLUSH_PAGE;
4436 }
4437 
4438 
4439 /*
4440  * Displays the FlushPage.
4441  *
4442  * Next pages:
4443  *  RebootPage (At once)
4444  *
4445  * RETURNS
4446  *   Number of the next page.
4447  */
4448 static PAGE_NUMBER
4449 FlushPage(PINPUT_RECORD Ir)
4450 {
4451     MUIDisplayPage(FLUSH_PAGE);
4452     return REBOOT_PAGE;
4453 }
4454 
4455 
4456 /*
4457  * The start routine and page management
4458  */
4459 NTSTATUS
4460 RunUSetup(VOID)
4461 {
4462     NTSTATUS Status;
4463     INPUT_RECORD Ir;
4464     PAGE_NUMBER Page;
4465     BOOLEAN Old;
4466 
4467     InfSetHeap(ProcessHeap);
4468 
4469     /* Tell the Cm this is a setup boot, and it has to behave accordingly */
4470     Status = NtInitializeRegistry(CM_BOOT_FLAG_SETUP);
4471     if (!NT_SUCCESS(Status))
4472         DPRINT1("NtInitializeRegistry() failed (Status 0x%08lx)\n", Status);
4473 
4474     /* Initialize the user-mode PnP manager */
4475     Status = InitializeUserModePnpManager(&USetupData.SetupInf);
4476     if (!NT_SUCCESS(Status))
4477     {
4478         // PrintString(??);
4479         DPRINT1("The user-mode PnP manager could not initialize (Status 0x%08lx), expect unavailable devices!\n", Status);
4480     }
4481 
4482     if (!CONSOLE_Init())
4483     {
4484         PrintString(MUIGetString(STRING_CONSOLEFAIL1));
4485         PrintString(MUIGetString(STRING_CONSOLEFAIL2));
4486         PrintString(MUIGetString(STRING_CONSOLEFAIL3));
4487 
4488         /* We failed to initialize the video, just quit the installer */
4489         return STATUS_APP_INIT_FAILURE;
4490     }
4491 
4492     /* Initialize Setup, phase 0 */
4493     InitializeSetup(&USetupData, 0);
4494     USetupData.ErrorRoutine = USetupErrorRoutine;
4495 
4496     /* Hide the cursor and clear the screen and keyboard buffer */
4497     CONSOLE_SetCursorType(TRUE, FALSE);
4498     CONSOLE_ClearScreen();
4499     CONSOLE_Flush();
4500 
4501     /* Global Initialization page */
4502     Page = SetupStartPage(&Ir);
4503 
4504     while (Page != REBOOT_PAGE && Page != RECOVERY_PAGE)
4505     {
4506         CONSOLE_ClearScreen();
4507         CONSOLE_Flush();
4508 
4509         // CONSOLE_SetUnderlinedTextXY(4, 3, " ReactOS " KERNEL_VERSION_STR " Setup ");
4510 
4511         switch (Page)
4512         {
4513             /* Language page */
4514             case LANGUAGE_PAGE:
4515                 Page = LanguagePage(&Ir);
4516                 break;
4517 
4518             /* Welcome page */
4519             case WELCOME_PAGE:
4520                 Page = WelcomePage(&Ir);
4521                 break;
4522 
4523             /* License page */
4524             case LICENSE_PAGE:
4525                 Page = LicensePage(&Ir);
4526                 break;
4527 
4528             /* Install pages */
4529             case INSTALL_INTRO_PAGE:
4530                 Page = InstallIntroPage(&Ir);
4531                 break;
4532 
4533 #if 0
4534             case SCSI_CONTROLLER_PAGE:
4535                 Page = ScsiControllerPage(&Ir);
4536                 break;
4537 
4538             case OEM_DRIVER_PAGE:
4539                 Page = OemDriverPage(&Ir);
4540                 break;
4541 #endif
4542 
4543             case DEVICE_SETTINGS_PAGE:
4544                 Page = DeviceSettingsPage(&Ir);
4545                 break;
4546 
4547             case COMPUTER_SETTINGS_PAGE:
4548                 Page = ComputerSettingsPage(&Ir);
4549                 break;
4550 
4551             case DISPLAY_SETTINGS_PAGE:
4552                 Page = DisplaySettingsPage(&Ir);
4553                 break;
4554 
4555             case KEYBOARD_SETTINGS_PAGE:
4556                 Page = KeyboardSettingsPage(&Ir);
4557                 break;
4558 
4559             case LAYOUT_SETTINGS_PAGE:
4560                 Page = LayoutSettingsPage(&Ir);
4561                 break;
4562 
4563             /* Partitioning pages */
4564             case SELECT_PARTITION_PAGE:
4565                 Page = SelectPartitionPage(&Ir);
4566                 break;
4567 
4568             case CREATE_PRIMARY_PARTITION_PAGE:
4569                 Page = CreatePrimaryPartitionPage(&Ir);
4570                 break;
4571 
4572             case CREATE_EXTENDED_PARTITION_PAGE:
4573                 Page = CreateExtendedPartitionPage(&Ir);
4574                 break;
4575 
4576             case CREATE_LOGICAL_PARTITION_PAGE:
4577                 Page = CreateLogicalPartitionPage(&Ir);
4578                 break;
4579 
4580             case CONFIRM_DELETE_SYSTEM_PARTITION_PAGE:
4581                 Page = ConfirmDeleteSystemPartitionPage(&Ir);
4582                 break;
4583 
4584             case DELETE_PARTITION_PAGE:
4585                 Page = DeletePartitionPage(&Ir);
4586                 break;
4587 
4588             /* Filesystem partition operations pages */
4589             case SELECT_FILE_SYSTEM_PAGE:
4590                 Page = SelectFileSystemPage(&Ir);
4591                 break;
4592 
4593             case FORMAT_PARTITION_PAGE:
4594                 Page = FormatPartitionPage(&Ir);
4595                 break;
4596 
4597             case CHECK_FILE_SYSTEM_PAGE:
4598                 Page = CheckFileSystemPage(&Ir);
4599                 break;
4600 
4601             /* Installation pages */
4602             case INSTALL_DIRECTORY_PAGE:
4603                 Page = InstallDirectoryPage(&Ir);
4604                 break;
4605 
4606             case PREPARE_COPY_PAGE:
4607                 Page = PrepareCopyPage(&Ir);
4608                 break;
4609 
4610             case FILE_COPY_PAGE:
4611                 Page = FileCopyPage(&Ir);
4612                 break;
4613 
4614             case REGISTRY_PAGE:
4615                 Page = RegistryPage(&Ir);
4616                 break;
4617 
4618             /* Bootloader installation pages */
4619             case BOOT_LOADER_PAGE:
4620                 Page = BootLoaderPage(&Ir);
4621                 break;
4622 
4623             case BOOT_LOADER_FLOPPY_PAGE:
4624                 Page = BootLoaderFloppyPage(&Ir);
4625                 break;
4626 
4627             case BOOT_LOADER_INSTALLATION_PAGE:
4628                 Page = BootLoaderInstallationPage(&Ir);
4629                 break;
4630 
4631             /* Repair pages */
4632             case REPAIR_INTRO_PAGE:
4633                 Page = RepairIntroPage(&Ir);
4634                 break;
4635 
4636             case UPGRADE_REPAIR_PAGE:
4637                 Page = UpgradeRepairPage(&Ir);
4638                 break;
4639 
4640             case SUCCESS_PAGE:
4641                 Page = SuccessPage(&Ir);
4642                 break;
4643 
4644             case FLUSH_PAGE:
4645                 Page = FlushPage(&Ir);
4646                 break;
4647 
4648             case QUIT_PAGE:
4649                 Page = QuitPage(&Ir);
4650                 break;
4651 
4652             /* Virtual pages */
4653             case SETUP_INIT_PAGE:
4654             case REBOOT_PAGE:
4655             case RECOVERY_PAGE:
4656                 break;
4657 
4658             default:
4659                 break;
4660         }
4661     }
4662 
4663     /* Terminate the user-mode PnP manager */
4664     TerminateUserModePnpManager();
4665 
4666     /* Setup has finished */
4667     FinishSetup(&USetupData);
4668 
4669     if (Page == RECOVERY_PAGE)
4670         RecoveryConsole();
4671 
4672     FreeConsole();
4673 
4674     /* Reboot */
4675     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
4676     NtShutdownSystem(ShutdownReboot);
4677     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
4678 
4679     return STATUS_SUCCESS;
4680 }
4681 
4682 
4683 VOID NTAPI
4684 NtProcessStartup(PPEB Peb)
4685 {
4686     NTSTATUS Status;
4687     LARGE_INTEGER Time;
4688 
4689     RtlNormalizeProcessParams(Peb->ProcessParameters);
4690 
4691     ProcessHeap = Peb->ProcessHeap;
4692 
4693     NtQuerySystemTime(&Time);
4694 
4695     Status = RunUSetup();
4696 
4697     if (NT_SUCCESS(Status))
4698     {
4699         /*
4700          * Avoid a bugcheck if RunUSetup() finishes too quickly by implementing
4701          * a protective waiting.
4702          * This wait is needed because, since we are started as SMSS.EXE,
4703          * the NT kernel explicitly waits 5 seconds for the initial process
4704          * SMSS.EXE to initialize (as a protective measure), and otherwise
4705          * bugchecks with the code SESSION5_INITIALIZATION_FAILED.
4706          */
4707         Time.QuadPart += 50000000;
4708         NtDelayExecution(FALSE, &Time);
4709     }
4710     else
4711     {
4712         /* The installer failed to start: raise a hard error (crash the system/BSOD) */
4713         Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED,
4714                                   0, 0, NULL, 0, NULL);
4715     }
4716 
4717     NtTerminateProcess(NtCurrentProcess(), Status);
4718 }
4719 
4720 /* EOF */
4721