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