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