update for HEAD-2003021201
[reactos.git] / subsys / system / usetup / usetup.c
index 87d28fa..20d1883 100644 (file)
 #include <ddk/ntddk.h>
 #include <ntdll/rtl.h>
 
+#include <ntos/minmax.h>
+#include <reactos/resource.h>
+
 #include "usetup.h"
 #include "console.h"
 #include "partlist.h"
-
+#include "inicache.h"
+#include "filequeue.h"
+#include "progress.h"
+#include "bootsup.h"
 
 
 #define START_PAGE                     0
@@ -43,7 +49,8 @@
 #define PREPARE_COPY_PAGE              7
 #define INSTALL_DIRECTORY_PAGE         8
 #define FILE_COPY_PAGE                 9
-#define INIT_SYSTEM_PAGE               10
+#define REGISTRY_PAGE                  10
+#define BOOT_LOADER_PAGE               11
 
 #define REPAIR_INTRO_PAGE              20
 
 #define REBOOT_PAGE                    102
 
 
+typedef struct _COPYCONTEXT
+{
+  ULONG TotalOperations;
+  ULONG CompletedOperations;
+  PPROGRESS ProgressBar;
+} COPYCONTEXT, *PCOPYCONTEXT;
+
+
 /* GLOBALS ******************************************************************/
 
 HANDLE ProcessHeap;
 
-BOOL PartDataValid = FALSE;
+BOOLEAN PartDataValid;
 PARTDATA PartData;
 
-CHAR InstallDir[51];
+BOOLEAN ActivePartitionValid;
+PARTDATA ActivePartition;
 
 UNICODE_STRING SourcePath;
 UNICODE_STRING SourceRootPath;
 
+UNICODE_STRING InstallPath;
+UNICODE_STRING DestinationPath;
+UNICODE_STRING DestinationArcPath;
+UNICODE_STRING DestinationRootPath;
+
+UNICODE_STRING SystemRootPath; /* Path to the active partition */
+
+PINICACHE IniCache;
+
+HSPFILEQ SetupFileQueue = NULL;
+
 
 /* FUNCTIONS ****************************************************************/
 
@@ -88,6 +115,215 @@ PrintString(char* fmt,...)
 }
 
 
+static VOID
+PopupError(PCHAR Text,
+          PCHAR Status)
+{
+  SHORT xScreen;
+  SHORT yScreen;
+  SHORT yTop;
+  SHORT xLeft;
+  COORD coPos;
+  ULONG Written;
+  ULONG Length;
+  ULONG MaxLength;
+  ULONG Lines;
+  PCHAR p;
+  PCHAR pnext;
+  BOOLEAN LastLine;
+  SHORT Width;
+  SHORT Height;
+
+  /* Count text lines and longest line */
+  MaxLength = 0;
+  Lines = 0;
+  pnext = Text;
+  while (TRUE)
+  {
+    p = strchr(pnext, '\n');
+    if (p == NULL)
+    {
+      Length = strlen(pnext);
+      LastLine = TRUE;
+    }
+    else
+    {
+      Length = (ULONG)(p - pnext);
+      LastLine = FALSE;
+    }
+
+    Lines++;
+    if (Length > MaxLength)
+      MaxLength = Length;
+
+    if (LastLine == TRUE)
+      break;
+
+    pnext = p + 1;
+  }
+
+  /* Check length of status line */
+  if (Status != NULL)
+  {
+    Length = strlen(Status);
+    if (Length > MaxLength)
+      MaxLength = Length;
+  }
+
+  GetScreenSize(&xScreen, &yScreen);
+
+  Width = MaxLength + 4;
+  Height = Lines + 2;
+  if (Status != NULL)
+    Height += 2;
+
+  yTop = (yScreen - Height) / 2;
+  xLeft = (xScreen - Width) / 2;
+
+
+  /* Set screen attributes */
+  coPos.X = xLeft;
+  for (coPos.Y = yTop; coPos.Y < yTop + Height; coPos.Y++)
+  {
+    FillConsoleOutputAttribute(0x74,
+                              Width,
+                              coPos,
+                              &Written);
+  }
+
+  /* draw upper left corner */
+  coPos.X = xLeft;
+  coPos.Y = yTop;
+  FillConsoleOutputCharacter(0xDA, // '+',
+                            1,
+                            coPos,
+                            &Written);
+
+  /* draw upper edge */
+  coPos.X = xLeft + 1;
+  coPos.Y = yTop;
+  FillConsoleOutputCharacter(0xC4, // '-',
+                            Width - 2,
+                            coPos,
+                            &Written);
+
+  /* draw upper right corner */
+  coPos.X = xLeft + Width - 1;
+  coPos.Y = yTop;
+  FillConsoleOutputCharacter(0xBF, // '+',
+                            1,
+                            coPos,
+                            &Written);
+
+  /* Draw right edge, inner space and left edge */
+  for (coPos.Y = yTop + 1; coPos.Y < yTop + Height - 1; coPos.Y++)
+  {
+    coPos.X = xLeft;
+    FillConsoleOutputCharacter(0xB3, // '|',
+                              1,
+                              coPos,
+                              &Written);
+
+    coPos.X = xLeft + 1;
+    FillConsoleOutputCharacter(' ',
+                              Width - 2,
+                              coPos,
+                              &Written);
+
+    coPos.X = xLeft + Width - 1;
+    FillConsoleOutputCharacter(0xB3, // '|',
+                              1,
+                              coPos,
+                              &Written);
+  }
+
+  /* draw lower left corner */
+  coPos.X = xLeft;
+  coPos.Y = yTop + Height - 1;
+  FillConsoleOutputCharacter(0xC0, // '+',
+                            1,
+                            coPos,
+                            &Written);
+
+  /* draw lower edge */
+  coPos.X = xLeft + 1;
+  coPos.Y = yTop + Height - 1;
+  FillConsoleOutputCharacter(0xC4, // '-',
+                            Width - 2,
+                            coPos,
+                            &Written);
+
+  /* draw lower right corner */
+  coPos.X = xLeft + Width - 1;
+  coPos.Y = yTop + Height - 1;
+  FillConsoleOutputCharacter(0xD9, // '+',
+                            1,
+                            coPos,
+                            &Written);
+
+  /* Print message text */
+  coPos.Y = yTop + 1;
+  pnext = Text;
+  while (TRUE)
+  {
+    p = strchr(pnext, '\n');
+    if (p == NULL)
+    {
+      Length = strlen(pnext);
+      LastLine = TRUE;
+    }
+    else
+    {
+      Length = (ULONG)(p - pnext);
+      LastLine = FALSE;
+    }
+
+    if (Length != 0)
+    {
+      coPos.X = xLeft + 2;
+      WriteConsoleOutputCharacters(pnext,
+                                  Length,
+                                  coPos);
+    }
+
+    if (LastLine == TRUE)
+      break;
+
+    coPos.Y++;
+    pnext = p + 1;
+  }
+
+  /* Print separator line and status text */
+  if (Status != NULL)
+  {
+    coPos.Y = yTop + Height - 3;
+    coPos.X = xLeft;
+    FillConsoleOutputCharacter(0xC3, // '+',
+                              1,
+                              coPos,
+                              &Written);
+
+    coPos.X = xLeft + 1;
+    FillConsoleOutputCharacter(0xC4, // '-',
+                              Width - 2,
+                              coPos,
+                              &Written);
+
+    coPos.X = xLeft + Width - 1;
+    FillConsoleOutputCharacter(0xB4, // '+',
+                              1,
+                              coPos,
+                              &Written);
+
+    coPos.Y++;
+    coPos.X = xLeft + 2;
+    WriteConsoleOutputCharacters(Status,
+                                min(strlen(Status), Width - 4),
+                                coPos);
+  }
+}
+
+
 /*
  * Confirm quit setup
  * RETURNS
@@ -97,65 +333,15 @@ PrintString(char* fmt,...)
 static BOOL
 ConfirmQuit(PINPUT_RECORD Ir)
 {
-  SHORT xScreen;
-  SHORT yScreen;
-  SHORT yTop;
-  SHORT xLeft;
   BOOL Result = FALSE;
-  PUSHORT pAttributes = NULL;
-  PUCHAR pCharacters = NULL;
-  COORD Pos;
-
-  GetScreenSize(&xScreen, &yScreen);
-  yTop = (yScreen - 10) / 2;
-  xLeft = (xScreen - 52) / 2;
-
-  /* Save screen */
-#if 0
-  Pos.X = 0;
-  Pos.Y = 0;
-  pAttributes = (PUSHORT)RtlAllocateHeap(ProcessHeap,
-                                        0,
-                                        xScreen * yScreen * sizeof(USHORT));
-CHECKPOINT1;
-DPRINT1("pAttributes %p\n", pAttributes);
-  ReadConsoleOutputAttributes(pAttributes,
-                             xScreen * yScreen,
-                             Pos,
-                             NULL);
-CHECKPOINT1;
-  pCharacters = (PUCHAR)RtlAllocateHeap(ProcessHeap,
-                                       0,
-                                       xScreen * yScreen * sizeof(UCHAR));
-CHECKPOINT1;
-  ReadConsoleOutputCharacters(pCharacters,
-                             xScreen * yScreen,
-                             Pos,
-                             NULL);
-CHECKPOINT1;
-#endif
 
-  /* Draw popup window */
-  SetTextXY(xLeft, yTop,
-           "+----------------------------------------------------+");
-  SetTextXY(xLeft, yTop + 1,
-           "| ReactOS 0.0.20 is not completely installed on your |");
-  SetTextXY(xLeft, yTop + 2,
-           "| computer. If you quit Setup now, you will need to  |");
-  SetTextXY(xLeft, yTop + 3,
-           "| run Setup again to install ReactOS.                |");
-  SetTextXY(xLeft, yTop + 4,
-           "|                                                    |");
-  SetTextXY(xLeft, yTop + 5,
-           "|   * Press ENTER to continue Setup.                 |");
-  SetTextXY(xLeft, yTop + 6,
-           "|   * Press F3 to quit Setup.                        |");
-  SetTextXY(xLeft, yTop + 7,
-           "+----------------------------------------------------+");
-  SetTextXY(xLeft, yTop + 8,
-           "| F3= Quit  ENTER = Continue                         |");
-  SetTextXY(xLeft, yTop + 9,
-           "+----------------------------------------------------+");
+  PopupError("ReactOS is not completely installed on your\n"
+            "computer. If you quit Setup now, you will need to\n"
+            "run Setup again to install ReactOS.\n"
+            "\n"
+            "  * Press ENTER to continue Setup.\n"
+            "  * Press F3 to quit Setup.",
+            "F3= Quit  ENTER = Continue");
 
   while(TRUE)
     {
@@ -174,34 +360,11 @@ CHECKPOINT1;
        }
     }
 
-  /* Restore screen */
-#if 0
-CHECKPOINT1;
-  WriteConsoleOutputAttributes(pAttributes,
-                              xScreen * yScreen,
-                              Pos,
-                              NULL);
-CHECKPOINT1;
-
-  WriteConsoleOutputCharacters(pCharacters,
-                              xScreen * yScreen,
-                              Pos);
-CHECKPOINT1;
-
-  RtlFreeHeap(ProcessHeap,
-             0,
-             pAttributes);
-  RtlFreeHeap(ProcessHeap,
-             0,
-             pCharacters);
-#endif
-
   return(Result);
 }
 
 
 
-
 /*
  * Start page
  * RETURNS
@@ -211,6 +374,12 @@ static ULONG
 StartPage(PINPUT_RECORD Ir)
 {
   NTSTATUS Status;
+  WCHAR FileNameBuffer[MAX_PATH];
+  UNICODE_STRING FileName;
+
+  PINICACHESECTION Section;
+  PWCHAR Value;
+
 
   SetStatusText("   Please wait...");
 
@@ -226,26 +395,91 @@ StartPage(PINPUT_RECORD Ir)
       PrintTextXY(6, 16, "SourceRootPath: '%wZ'", &SourceRootPath);
     }
 
-  /*
-   * FIXME: Open and load txtsetup.sif here. A pointer (or handle) to the
-   * ini data should be stored in a global variable.
-   * The full path to txtsetup.sif is created by appending '\txtsetup.sif'
-   * to the unicode string SourceRootPath.
-   */
 
-  SetStatusText("   ENTER = Continue");
+  /* Load txtsetup.sif from install media. */
+  wcscpy(FileNameBuffer, SourceRootPath.Buffer);
+  wcscat(FileNameBuffer, L"\\install\\txtsetup.sif");
+  RtlInitUnicodeString(&FileName,
+                      FileNameBuffer);
 
-  while(TRUE)
+  IniCache = NULL;
+  Status = IniCacheLoad(&IniCache,
+                       &FileName,
+                       TRUE);
+  if (!NT_SUCCESS(Status))
     {
-      ConInKey(Ir);
+      PopupError("Setup failed to load the file TXTSETUP.SIF.\n",
+                "ENTER = Reboot computer");
 
-      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      while(TRUE)
        {
-         return(INTRO_PAGE);
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+       }
+    }
+
+  /* Open 'Version' section */
+  Section = IniCacheGetSection(IniCache,
+                              L"Version");
+  if (Section == NULL)
+    {
+      PopupError("Setup found a corrupt TXTSETUP.SIF.\n",
+                "ENTER = Reboot computer");
+
+      while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+       }
+    }
+
+
+  /* Get pointer 'Signature' key */
+  Status = IniCacheGetKey(Section,
+                         L"Signature",
+                         &Value);
+  if (!NT_SUCCESS(Status))
+    {
+      PopupError("Setup found a corrupt TXTSETUP.SIF.\n",
+                "ENTER = Reboot computer");
+
+      while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+       }
+    }
+
+  /* Check 'Signature' string */
+  if (_wcsicmp(Value, L"$ReactOS$") != 0)
+    {
+      PopupError("Setup found an invalid signature in TXTSETUP.SIF.\n",
+                "ENTER = Reboot computer");
+
+      while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
        }
     }
 
-  return(START_PAGE);
+  return(INTRO_PAGE);
 }
 
 
@@ -258,9 +492,9 @@ RepairIntroPage(PINPUT_RECORD Ir)
 
   SetTextXY(6, 12, "The repair functions are not implemented yet.");
 
-  SetTextXY(8, 15, "\xf9  Press ESC to return to the main page.");
+  SetTextXY(8, 15, "\xfa  Press ESC to return to the main page.");
 
-  SetTextXY(8, 17, "\xf9  Press ENTER to reboot your computer.");
+  SetTextXY(8, 17, "\xfa  Press ENTER to reboot your computer.");
 
   SetStatusText("   ESC = Main page  ENTER = Reboot");
 
@@ -297,13 +531,13 @@ IntroPage(PINPUT_RECORD Ir)
   SetTextXY(6, 11, "This part of the setup copies the ReactOS Operating System to your");
   SetTextXY(6, 12, "computer and prepares the second part of the setup.");
 
-  SetTextXY(8, 15, "\xf9  Press ENTER to install ReactOS.");
+  SetTextXY(8, 15, "\xfa  Press ENTER to install ReactOS.");
 
-  SetTextXY(8, 17, "\xf9  Press E to start the emergency repair console.");
+  SetTextXY(8, 17, "\xfa  Press E to start the emergency repair console.");
 
-  SetTextXY(8, 19, "\xf9  Press R to repair ReactOS.");
+  SetTextXY(8, 19, "\xfa  Press R to repair ReactOS.");
 
-  SetTextXY(8, 21, "\xf9  Press F3 to quit without installing ReactOS.");
+  SetTextXY(8, 21, "\xfa  Press F3 to quit without installing ReactOS.");
 
 
   SetStatusText("   ENTER = Continue   F3 = Quit");
@@ -350,13 +584,12 @@ InstallIntroPage(PINPUT_RECORD Ir)
   SetTextXY(8, 14, "- Formatting partitions.");
   SetTextXY(8, 15, "- Support for non-FAT file systems.");
   SetTextXY(8, 16, "- Checking file systems.");
-  SetTextXY(8, 17, "- Installing the bootloader.");
 
 
 
-  SetTextXY(8, 21, "\xf9  Press ENTER to install ReactOS.");
+  SetTextXY(8, 21, "\xfa  Press ENTER to install ReactOS.");
 
-  SetTextXY(8, 23, "\xf9  Press F3 to quit without installing ReactOS.");
+  SetTextXY(8, 23, "\xfa  Press F3 to quit without installing ReactOS.");
 
 
   SetStatusText("   ENTER = Continue   F3 = Quit");
@@ -385,6 +618,7 @@ InstallIntroPage(PINPUT_RECORD Ir)
 static ULONG
 SelectPartitionPage(PINPUT_RECORD Ir)
 {
+  WCHAR PathBuffer[MAX_PATH];
   PPARTLIST PartList;
   SHORT xScreen;
   SHORT yScreen;
@@ -392,13 +626,16 @@ SelectPartitionPage(PINPUT_RECORD Ir)
   SetTextXY(6, 8, "The list below shows existing partitions and unused disk");
   SetTextXY(6, 9, "space for new partitions.");
 
-  SetTextXY(8, 11, "\xf9  Press UP or DOWN to select a list entry.");
-  SetTextXY(8, 13, "\xf9  Press ENTER to install ReactOS onto the selected partition.");
-  SetTextXY(8, 15, "\xf9  Press C to create a new partition.");
-  SetTextXY(8, 17, "\xf9  Press D to delete an existing partition.");
+  SetTextXY(8, 11, "\xfa  Press UP or DOWN to select a list entry.");
+  SetTextXY(8, 13, "\xfa  Press ENTER to install ReactOS onto the selected partition.");
+  SetTextXY(8, 15, "\xfa  Press C to create a new partition.");
+  SetTextXY(8, 17, "\xfa  Press D to delete an existing partition.");
 
   SetStatusText("   Please wait...");
 
+  RtlFreeUnicodeString(&DestinationPath);
+  RtlFreeUnicodeString(&DestinationRootPath);
+
   GetScreenSize(&xScreen, &yScreen);
 
   PartList = CreatePartitionList(2, 19, xScreen - 3, yScreen - 3);
@@ -436,8 +673,28 @@ SelectPartitionPage(PINPUT_RECORD Ir)
        }
       else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
        {
-         PartDataValid = GetPartitionData(PartList, &PartData);
+         PartDataValid = GetSelectedPartition(PartList,
+                                              &PartData);
+         ActivePartitionValid = GetActiveBootPartition(PartList,
+                                                       &ActivePartition);
          DestroyPartitionList(PartList);
+
+         RtlFreeUnicodeString(&DestinationRootPath);
+         swprintf(PathBuffer,
+                  L"\\Device\\Harddisk%lu\\Partition%lu",
+                  PartData.DiskNumber,
+                  PartData.PartNumber);
+         RtlCreateUnicodeString(&DestinationRootPath,
+                                PathBuffer);
+
+         RtlFreeUnicodeString(&SystemRootPath);
+         swprintf(PathBuffer,
+                  L"\\Device\\Harddisk%lu\\Partition%lu",
+                  ActivePartition.DiskNumber,
+                  ActivePartition.PartNumber);
+         RtlCreateUnicodeString(&SystemRootPath,
+                                PathBuffer);
+
          return(SELECT_FILE_SYSTEM_PAGE);
        }
 
@@ -512,30 +769,32 @@ SelectFileSystemPage(PINPUT_RECORD Ir)
       PartType = "Unknown";
     }
 
-  SetTextXY(6, 8, "ReactOS will be installed");
+  SetTextXY(6, 8, "Setup will install ReactOS on");
+
+  PrintTextXY(8, 10, "Partition %lu (%I64u %s) %s on",
+             PartData.PartNumber,
+             PartSize,
+             PartUnit,
+             PartType);
 
-  PrintTextXY(8, 9, "on Harddisk %lu (%I64u %s), Port=%hu, Bus=%hu, Id=%hu.",
+  PrintTextXY(8, 12, "Harddisk %lu (%I64u %s), Port=%hu, Bus=%hu, Id=%hu (%wZ).",
              PartData.DiskNumber,
              DiskSize,
              DiskUnit,
              PartData.Port,
              PartData.Bus,
-             PartData.Id);
+             PartData.Id,
+             &PartData.DriverName);
 
-  PrintTextXY(8, 10, "on Partition %lu (%I64u %s) %s",
-             PartData.PartNumber,
-             PartSize,
-             PartUnit,
-             PartType);
 
-  SetTextXY(6, 13, "Select a file system for the partition from the list below.");
+  SetTextXY(6, 17, "Select a file system for the partition from the list below.");
 
-  SetTextXY(8, 15, "\xf9  Press UP or DOWN to select a file system.");
-  SetTextXY(8, 17, "\xf9  Press ENTER to format the partition.");
-  SetTextXY(8, 19, "\xf9  Press ESC to select another partition.");
+  SetTextXY(8, 19, "\xfa  Press UP or DOWN to select a file system.");
+  SetTextXY(8, 21, "\xfa  Press ENTER to format the partition.");
+  SetTextXY(8, 23, "\xfa  Press ESC to select another partition.");
 
   /* FIXME: use a real list later */
-  SetInvertedTextXY(6, 22, " Keep current file system (no changes) ");
+  SetInvertedTextXY(6, 26, " Keep current file system (no changes) ");
 
 
   SetStatusText("   ENTER = Continue   ESC = Cancel   F3 = Quit");
@@ -602,21 +861,56 @@ CheckFileSystemPage(PINPUT_RECORD Ir)
 static ULONG
 InstallDirectoryPage(PINPUT_RECORD Ir)
 {
+  PINICACHESECTION Section;
+  WCHAR PathBuffer[MAX_PATH];
+  WCHAR InstallDir[51];
+  PWCHAR DefaultPath;
   ULONG Length;
+  NTSTATUS Status;
+
+  /* Open 'SetupData' section */
+  Section = IniCacheGetSection(IniCache,
+                              L"SetupData");
+  if (Section == NULL)
+    {
+      PopupError("Setup failed to find the 'SetupData' section\n"
+                "in TXTSETUP.SIF.\n",
+                "ENTER = Reboot computer");
+
+      while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+       }
+    }
+
+  /* Read the 'DefaultPath' key */
+  Status = IniCacheGetKey(Section,
+                         L"DefaultPath",
+                         &DefaultPath);
+  if (!NT_SUCCESS(Status))
+    {
+      wcscpy(InstallDir, L"\\reactos");
+    }
+  else
+    {
+      wcscpy(InstallDir, DefaultPath);
+    }
+  Length = wcslen(InstallDir);
 
   SetTextXY(6, 8, "Setup installs ReactOS files onto the selected partition. Choose a");
   SetTextXY(6, 9, "directory where you want ReactOS to be installed:");
 
-  strcpy(InstallDir, "\\reactos");
-  Length = strlen(InstallDir);
-
   SetInputTextXY(8, 11, 51, InstallDir);
 
   SetTextXY(6, 14, "To change the suggested directory, press BACKSPACE to delete");
   SetTextXY(6, 15, "characters and then type the directory where you want ReactOS to");
   SetTextXY(6, 16, "be installed.");
 
-
   SetStatusText("   ENTER = Continue   F3 = Quit");
 
   while(TRUE)
@@ -632,6 +926,35 @@ InstallDirectoryPage(PINPUT_RECORD Ir)
        }
       else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
        {
+         /* Create 'InstallPath' string */
+         RtlFreeUnicodeString(&InstallPath);
+         RtlCreateUnicodeString(&InstallPath,
+                                InstallDir);
+
+         /* Create 'DestinationPath' string */
+         RtlFreeUnicodeString(&DestinationPath);
+         wcscpy(PathBuffer,
+                DestinationRootPath.Buffer);
+         if (InstallDir[0] != L'\\')
+           wcscat(PathBuffer,
+                  L"\\");
+         wcscat(PathBuffer, InstallDir);
+         RtlCreateUnicodeString(&DestinationPath,
+                                PathBuffer);
+
+         /* Create 'DestinationArcPath' */
+         RtlFreeUnicodeString(&DestinationArcPath);
+         swprintf(PathBuffer,
+                  L"multi(0)disk(0)rdisk(%lu)partition(%lu)",
+                  PartData.DiskNumber,
+                  PartData.PartNumber);
+         if (InstallDir[0] != L'\\')
+           wcscat(PathBuffer,
+                  L"\\");
+         wcscat(PathBuffer, InstallDir);
+         RtlCreateUnicodeString(&DestinationArcPath,
+                                PathBuffer);
+
          return(PREPARE_COPY_PAGE);
        }
       else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x08) /* BACKSPACE */
@@ -647,7 +970,7 @@ InstallDirectoryPage(PINPUT_RECORD Ir)
        {
          if (Length < 50)
            {
-             InstallDir[Length] = Ir->Event.KeyEvent.uChar.AsciiChar;
+             InstallDir[Length] = (WCHAR)Ir->Event.KeyEvent.uChar.AsciiChar;
              Length++;
              InstallDir[Length] = 0;
              SetInputTextXY(8, 11, 51, InstallDir);
@@ -662,152 +985,234 @@ InstallDirectoryPage(PINPUT_RECORD Ir)
 static ULONG
 PrepareCopyPage(PINPUT_RECORD Ir)
 {
-  OBJECT_ATTRIBUTES ObjectAttributes;
-  IO_STATUS_BLOCK IoStatusBlock;
-  CHAR PathBuffer[MAX_PATH];
-  UNICODE_STRING PathName;
-  HANDLE DirectoryHandle;
-  NTSTATUS Status;
-  PCHAR End;
+  WCHAR PathBuffer[MAX_PATH];
+  PINICACHESECTION DirSection;
+  PINICACHESECTION FilesSection;
+  PINICACHEITERATOR Iterator;
+  PWCHAR KeyName;
+  PWCHAR KeyValue;
   ULONG Length;
-  ULONG i;
+  NTSTATUS Status;
 
-  PCHAR Dirs[]= {
-    "System32",
-    "System32\\Config",
-    "System32\\Drivers",
-    "Inf",
-    "Help",
-    "Fonts",
-    NULL};
+  PWCHAR FileKeyName;
+  PWCHAR FileKeyValue;
+  PWCHAR DirKeyName;
+  PWCHAR DirKeyValue;
 
   SetTextXY(6, 8, "Setup prepares your computer for copying the ReactOS files. ");
 
 
-  SetTextXY(8, 12, "Build file copy list");
+//  SetTextXY(8, 12, "Build file copy list");
 
-  SetTextXY(8, 14, "Create directories");
+//  SetTextXY(8, 14, "Create directories");
 
-  SetStatusText("   Please wait...");
+//  SetStatusText("   Please wait...");
 
 
-  /* build the file copy list */
+  /*
+   * Build the file copy list
+   */
+  SetStatusText("   Building the file copy list...");
+//  SetInvertedTextXY(8, 12, "Build file copy list");
 
-  SetInvertedTextXY(8, 12, "Build file copy list");
 
-  /* FIXME: build that list */
+  /* Open 'Directories' section */
+  DirSection = IniCacheGetSection(IniCache,
+                                 L"Directories");
+  if (DirSection == NULL)
+  {
+    PopupError("Setup failed to find the 'Directories' section\n"
+              "in TXTSETUP.SIF.\n",
+              "ENTER = Reboot computer");
 
-  SetTextXY(8, 12, "Build file copy list");
-  SetHighlightedTextXY(50, 12, "Done");
+      while(TRUE)
+      {
+       ConInKey(Ir);
 
+       if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+       {
+         return(QUIT_PAGE);
+       }
+      }
+  }
+
+  /* Open 'SourceFiles' section */
+  FilesSection = IniCacheGetSection(IniCache,
+                                   L"SourceFiles");
+  if (FilesSection == NULL)
+  {
+    PopupError("Setup failed to find the 'SourceFiles' section\n"
+              "in TXTSETUP.SIF.\n",
+              "ENTER = Reboot computer");
+
+      while(TRUE)
+      {
+       ConInKey(Ir);
+
+       if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+       {
+         return(QUIT_PAGE);
+       }
+      }
+  }
 
-  /* create directories */
-  SetInvertedTextXY(8, 14, "Create directories");
 
+  /* Create the file queue */
+  SetupFileQueue = SetupOpenFileQueue();
+  if (SetupFileQueue == NULL)
+  {
+    PopupError("Setup failed to open the copy file queue.\n",
+              "ENTER = Reboot computer");
+
+      while(TRUE)
+      {
+       ConInKey(Ir);
+
+       if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+       {
+         return(QUIT_PAGE);
+       }
+      }
+  }
 
   /*
-   * FIXME: Enumerate the ini section 'Directories' and create all "relative" directories
+   * Enumerate the files in the 'SourceFiles' section
+   * and add them to the file queue.
    */
+  Iterator = IniCacheFindFirstValue(FilesSection,
+                                   &FileKeyName,
+                                   &FileKeyValue);
+  if (Iterator != NULL)
+  {
+    do
+    {
+      DPRINT1("FileKeyName: '%S'  FileKeyValue: '%S'\n", FileKeyName, FileKeyValue);
 
+      /* Lookup target directory */
+      Status = IniCacheGetKey(DirSection,
+                             FileKeyValue,
+                             &DirKeyValue);
+      if (!NT_SUCCESS(Status))
+      {
+       /* FIXME: Handle error! */
+       DPRINT1("IniCacheGetKey() failed (Status 0x%lX)\n", Status);
+      }
+
+      if (SetupQueueCopy(SetupFileQueue,
+                        SourceRootPath.Buffer,
+                        L"\\install",
+                        FileKeyName,
+                        DirKeyValue,
+                        NULL) == FALSE)
+      {
+       /* FIXME: Handle error! */
+       DPRINT1("SetupQueueCopy() failed\n");
+      }
+    }
+    while (IniCacheFindNextValue(Iterator, &FileKeyName, &FileKeyValue));
 
-  /* create the systemroot directory */
-  sprintf(PathBuffer,
-         "\\Device\\Harddisk%lu\\Partition%lu",
-         PartData.DiskNumber,
-         PartData.PartNumber);
-  if (InstallDir[0] != '\\')
-    strcat(PathBuffer, "\\");
-  strcat(PathBuffer, InstallDir);
+    IniCacheFindClose(Iterator);
+  }
+
+  /* Report that the file queue has been built */
+//  SetTextXY(8, 12, "Build file copy list");
+//  SetHighlightedTextXY(50, 12, "Done");
+
+  /* create directories */
+  SetStatusText("   Creating directories...");
+//  SetInvertedTextXY(8, 14, "Create directories");
 
-  /* remove trailing backslash */
-  Length = strlen(PathBuffer);
+
+  /*
+   * FIXME:
+   * Install directories like '\reactos\test' are not handled yet.
+   */
+
+  /* Get destination path */
+  wcscpy(PathBuffer, DestinationPath.Buffer);
+
+  /* Remove trailing backslash */
+  Length = wcslen(PathBuffer);
   if ((Length > 0) && (PathBuffer[Length - 1] == '\\'))
     PathBuffer[Length - 1] = 0;
 
-  RtlCreateUnicodeStringFromAsciiz(&PathName,
-                                  PathBuffer);
-
-  ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
-  ObjectAttributes.RootDirectory = NULL;
-  ObjectAttributes.ObjectName = &PathName;
-  ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE | OBJ_INHERIT;
-  ObjectAttributes.SecurityDescriptor = NULL;
-  ObjectAttributes.SecurityQualityOfService = NULL;
-
-  Status = NtCreateFile(&DirectoryHandle,
-                       DIRECTORY_ALL_ACCESS,
-                       &ObjectAttributes,
-                       &IoStatusBlock,
-                       NULL,
-                       FILE_ATTRIBUTE_DIRECTORY,
-                       0,
-                       FILE_CREATE,
-                       FILE_DIRECTORY_FILE,
-                       NULL,
-                       0);
-  if (!NT_SUCCESS(Status))
+  /* Create the install directory */
+  Status = CreateDirectory(PathBuffer);
+  if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
+  {
+    DPRINT("Creating directory '%S' failed: Status = 0x%08lx", PathBuffer, Status);
+
+    PopupError("Setup could not create the install directory.",
+              "ENTER = Reboot computer");
+
+    while(TRUE)
     {
-      PrintTextXY(6, 25, "Creating directory failed: Status = 0x%08lx", Status);
+      ConInKey(Ir);
 
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
     }
-  else
+  }
+
+
+  /* Enumerate the directory values and create the subdirectories */
+  Iterator = IniCacheFindFirstValue(DirSection,
+                                   &KeyName,
+                                   &KeyValue);
+  if (Iterator != NULL)
+  {
+    do
     {
-      PrintTextXY(6, 25, "Created directory.");
-      NtClose (DirectoryHandle);
-    }
+      if (KeyValue[0] == L'\\' && KeyValue[1] != 0)
+      {
+        DPRINT("Absolute Path: '%S'\n", KeyValue);
 
+       wcscpy(PathBuffer, DestinationRootPath.Buffer);
+       wcscat(PathBuffer, KeyValue);
 
-  RtlFreeUnicodeString(&PathName);
+       DPRINT("FullPath: '%S'\n", PathBuffer);
+      }
+      else if (KeyValue[0] != L'\\')
+      {
+       DPRINT("RelativePath: '%S'\n", KeyValue);
+       wcscpy(PathBuffer, DestinationPath.Buffer);
+       wcscat(PathBuffer, L"\\");
+       wcscat(PathBuffer, KeyValue);
 
+       DPRINT("FullPath: '%S'\n", PathBuffer);
 
-  /* create the subdirectories */
+       Status = CreateDirectory(PathBuffer);
+       if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_COLLISION)
+         {
+           DPRINT("Creating directory '%S' failed: Status = 0x%08lx", PathBuffer, Status);
 
-  /* append backslash and init end pointer */
-  strcat(PathBuffer, "\\");
-  Length = strlen(PathBuffer);
-  End = &PathBuffer[Length];
+           PopupError("Setup could not create install directories.",
+                      "ENTER = Reboot computer");
 
-  for (i = 0; Dirs[i] != NULL; i++)
-    {
-      strcpy(End, Dirs[i]);
-
-
-      RtlCreateUnicodeStringFromAsciiz(&PathName,
-                                      PathBuffer);
-
-
-      ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
-      ObjectAttributes.RootDirectory = NULL;
-      ObjectAttributes.ObjectName = &PathName;
-      ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE | OBJ_INHERIT;
-      ObjectAttributes.SecurityDescriptor = NULL;
-      ObjectAttributes.SecurityQualityOfService = NULL;
-
-      Status = NtCreateFile(&DirectoryHandle,
-                           DIRECTORY_ALL_ACCESS,
-                           &ObjectAttributes,
-                           &IoStatusBlock,
-                           NULL,
-                           FILE_ATTRIBUTE_DIRECTORY,
-                           0,
-                           FILE_CREATE,
-                           FILE_DIRECTORY_FILE,
-                           NULL,
-                           0);
-      if (!NT_SUCCESS(Status))
-       {
-         PrintTextXY(6, 25, "Creating directory failed: Status = 0x%08lx", Status);
-       }
-      else
-       {
-         PrintTextXY(6, 25, "Created directory.");
-         NtClose (DirectoryHandle);
-       }
+           while(TRUE)
+           {
+             ConInKey(Ir);
 
-      RtlFreeUnicodeString(&PathName);
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               IniCacheFindClose(Iterator);
+               return(QUIT_PAGE);
+             }
+           }
+         }
+      }
     }
+    while (IniCacheFindNextValue(Iterator, &KeyName, &KeyValue));
 
+    IniCacheFindClose(Iterator);
+  }
+
+
+  return(FILE_COPY_PAGE);
 
+#if 0
   SetTextXY(8, 14, "Create directories");
   SetHighlightedTextXY(50, 14, "Done");
 
@@ -832,16 +1237,81 @@ PrepareCopyPage(PINPUT_RECORD Ir)
     }
 
   return(PREPARE_COPY_PAGE);
+#endif
+}
+
+
+static ULONG
+FileCopyCallback(PVOID Context,
+                ULONG Notification,
+                PVOID Param1,
+                PVOID Param2)
+{
+  PCOPYCONTEXT CopyContext;
+
+  CopyContext = (PCOPYCONTEXT)Context;
+
+  switch (Notification)
+  {
+    case SPFILENOTIFY_STARTSUBQUEUE:
+      CopyContext->TotalOperations = (ULONG)Param2;
+      ProgressSetStepCount(CopyContext->ProgressBar,
+                          CopyContext->TotalOperations);
+      break;
+
+    case SPFILENOTIFY_STARTCOPY:
+      /* Display copy message */
+      PrintTextXYN(6, 16, 60, "Copying file: %S", (PWSTR)Param1);
+
+      PrintTextXYN(6, 18, 60, "File %lu of %lu",
+                  CopyContext->CompletedOperations + 1,
+                  CopyContext->TotalOperations);
+      break;
+
+
+    case SPFILENOTIFY_ENDCOPY:
+      CopyContext->CompletedOperations++;
+      ProgressNextStep(CopyContext->ProgressBar);
+      break;
+  }
+
+  return(0);
 }
 
 
 static ULONG
 FileCopyPage(PINPUT_RECORD Ir)
 {
+  COPYCONTEXT CopyContext;
+  SHORT xScreen;
+  SHORT yScreen;
+
+  SetStatusText("   Please wait...");
 
   SetTextXY(6, 8, "Copying files");
 
+  GetScreenSize(&xScreen, &yScreen);
+
+  CopyContext.TotalOperations = 0;
+  CopyContext.CompletedOperations = 0;
+  CopyContext.ProgressBar = CreateProgressBar(6,
+                                             yScreen - 14,
+                                             xScreen - 7,
+                                             yScreen - 10);
+
+  SetupCommitFileQueue(SetupFileQueue,
+                      DestinationRootPath.Buffer,
+                      InstallPath.Buffer,
+                      (PSP_FILE_CALLBACK)FileCopyCallback,
+                      &CopyContext);
+
+  SetupCloseFileQueue(SetupFileQueue);
+
+  DestroyProgressBar(CopyContext.ProgressBar);
 
+  return(REGISTRY_PAGE);
+
+#if 0
   SetStatusText("   ENTER = Continue   F3 = Quit");
 
   while(TRUE)
@@ -857,123 +1327,775 @@ FileCopyPage(PINPUT_RECORD Ir)
        }
       else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
        {
-         return(INIT_SYSTEM_PAGE);
+         return(REGISTRY_PAGE);
        }
     }
 
   return(FILE_COPY_PAGE);
+#endif
 }
 
 
-#if 0
-static NTSTATUS
-UpdateSystemRootLink(VOID)
+
+static ULONG
+RegistryPage(PINPUT_RECORD Ir)
 {
   OBJECT_ATTRIBUTES ObjectAttributes;
-  UNICODE_STRING LinkName;
-  UNICODE_STRING TargetName;
-  CHAR TargetBuffer[MAX_PATH];
-  HANDLE Handle;
+  UNICODE_STRING KeyName;
+  UNICODE_STRING ValueName;
+  HANDLE KeyHandle;
   NTSTATUS Status;
 
-  RtlInitUnicodeString(&LinkName,
-                      L"\\SystemRoot");
+  SetTextXY(6, 8, "Setup initializes system settings");
+
+
+//  SetTextXY(6, 12, "Create registry hives");
+
+//  SetTextXY(6, 14, "Update registry hives");
 
+
+//  SetStatusText("   Please wait...");
+
+  SetStatusText("   Creating registry hives...");
+
+  /* Create the 'secret' InstallPath key */
+  RtlInitUnicodeStringFromLiteral(&KeyName,
+                                 L"\\Registry\\Machine\\HARDWARE");
   InitializeObjectAttributes(&ObjectAttributes,
-                            &LinkName,
-                            OBJ_OPENLINK,
+                            &KeyName,
+                            OBJ_CASE_INSENSITIVE,
                             NULL,
                             NULL);
+  Status =  NtOpenKey(&KeyHandle,
+                     KEY_ALL_ACCESS,
+                     &ObjectAttributes);
+  if (!NT_SUCCESS(Status))
+  {
+    DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
+    PopupError("Setup failed to open the HARDWARE registry key.",
+              "ENTER = Reboot computer");
 
-  Status = NtOpenSymbolicLinkObject(&Handle,
-                                   SYMBOLIC_LINK_ALL_ACCESS,
-                                   &ObjectAttributes);
+      while(TRUE)
+      {
+       ConInKey(Ir);
+
+       if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+       {
+         return(QUIT_PAGE);
+       }
+      }
+  }
+
+  RtlInitUnicodeStringFromLiteral(&ValueName,
+                                 L"InstallPath");
+
+  Status = NtSetValueKey(KeyHandle,
+                        &ValueName,
+                        0,
+                        REG_SZ,
+                        (PVOID)DestinationPath.Buffer,
+                        DestinationPath.Length);
+  NtClose(KeyHandle);
   if (!NT_SUCCESS(Status))
-    return(Status);
+  {
+    DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
+    PopupError("Setup failed to set the \'InstallPath\' registry value.",
+              "ENTER = Reboot computer");
 
-  Status = NtMakeTemporaryObject(Handle);
-  NtClose(Handle);
+      while(TRUE)
+      {
+       ConInKey(Ir);
+
+       if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+       {
+         return(QUIT_PAGE);
+       }
+      }
+  }
+
+  /* Create the standard hives */
+  Status = NtInitializeRegistry(TRUE);
   if (!NT_SUCCESS(Status))
-    return(Status);
+  {
+    DPRINT1("NtInitializeRegistry() failed (Status %lx)\n", Status);
+    PopupError("Setup failed to initialize the registry.",
+              "ENTER = Reboot computer");
 
-  sprintf(TargetBuffer,
-         "\\Device\\Harddisk%lu\\Partition%lu",
-         PartData.DiskNumber,
-         PartData.PartNumber);
-  if (InstallDir[0] != '\\')
-    strcat(TargetBuffer, "\\");
-  strcat(TargetBuffer, InstallDir);
+      while(TRUE)
+      {
+       ConInKey(Ir);
 
-  RtlCreateUnicodeStringFromAsciiz(&TargetName,
-                                  TargetBuffer);
+       if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+       {
+         return(QUIT_PAGE);
+       }
+      }
+  }
 
-  Status = NtCreateSymbolicLinkObject(&Handle,
-                                     SYMBOLIC_LINK_ALL_ACCESS,
-                                     &ObjectAttributes,
-                                     &TargetName);
 
-  RtlFreeUnicodeString(&TargetName);
+  /* Update registry */
+  SetStatusText("   Updating registry hives...");
 
-  if (!NT_SUCCESS(Status))
-    return(Status);
+  /* FIXME: Create key '\Registry\Machine\System\Setup' */
 
-  NtClose(Handle);
+  /* FIXME: Create value 'SystemSetupInProgress' */
 
-  return(STATUS_SUCCESS);
-}
+
+  return(BOOT_LOADER_PAGE);
+
+#if 0
+  SetStatusText("   ENTER = Continue   F3 = Quit");
+
+  while(TRUE)
+  {
+    ConInKey(Ir);
+
+    if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
+       (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
+    {
+      if (ConfirmQuit(Ir) == TRUE)
+       return(QUIT_PAGE);
+      break;
+    }
+    else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
+    {
+      return(BOOT_LOADER_PAGE);
+    }
+  }
+
+  return(REGISTRY_PAGE);
 #endif
+}
 
 
 static ULONG
-InitSystemPage(PINPUT_RECORD Ir)
+BootLoaderPage(PINPUT_RECORD Ir)
 {
-#if 0
+  WCHAR SrcPath[MAX_PATH];
+  WCHAR DstPath[MAX_PATH];
+  PINICACHE IniCache;
+  PINICACHESECTION IniSection;
   NTSTATUS Status;
-#endif
 
-  SetTextXY(6, 8, "Initializing system settings");
+  SetTextXY(6, 8, "Installing the boot loader");
 
+  SetStatusText("   Please wait...");
 
-  SetTextXY(6, 12, "Create registry hives");
+  if (ActivePartitionValid == FALSE)
+  {
+    DPRINT1("Error: no active partition found\n");
+    PopupError("Setup could not find an active partiton\n",
+              "ENTER = Reboot computer");
 
-  SetTextXY(6, 14, "Update registry hives");
+    while(TRUE)
+    {
+      ConInKey(Ir);
 
-  SetTextXY(6, 16, "Install/update boot manager");
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
+    }
+  }
 
-  SetStatusText("   Please wait...");
+  if (ActivePartition.PartType == PARTITION_ENTRY_UNUSED)
+  {
+    DPRINT1("Error: active partition invalid (unused)\n");
+    PopupError("The active partition is unused (invalid).\n",
+              "ENTER = Reboot computer");
 
-#if 0
-  /*
-   * Initialize registry
-   */
+    while(TRUE)
+    {
+      ConInKey(Ir);
 
-  /* Update 'SystemRoot' link */
-  Status = UpdateSystemRootLink();
-  if (!NT_SUCCESS(Status))
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
+    }
+  }
+
+  if (ActivePartition.PartType == 0x0A)
+  {
+    /* OS/2 boot manager partition */
+    DPRINT1("Found OS/2 boot manager partition\n");
+    PopupError("Setup found an OS/2 boot manager partiton.\n"
+              "The OS/2 boot manager is not supported yet!",
+              "ENTER = Reboot computer");
+
+    while(TRUE)
     {
+      ConInKey(Ir);
 
-      PrintTextXY(6, 25, "UpdateSystemRootLink() failed (Status = 0x%08lx)", Status);
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
     }
+  }
+  else if (ActivePartition.PartType == 0x83)
+  {
+    /* Linux ext2 partition */
+    DPRINT1("Found Linux ext2 partition\n");
+    PopupError("Setup found a Linux ext2 partiton.\n"
+              "Linux ext2 partitions are not supported yet!",
+              "ENTER = Reboot computer");
+
+    while(TRUE)
+    {
+      ConInKey(Ir);
 
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
+    }
+  }
+  else if (ActivePartition.PartType == PARTITION_IFS)
+  {
+      /* NTFS partition */
+    DPRINT1("Found NTFS partition\n");
+    PopupError("Setup found an NTFS partiton.\n"
+              "NTFS partitions are not supported yet!",
+              "ENTER = Reboot computer");
+
+    while(TRUE)
+    {
+      ConInKey(Ir);
 
-  Status = NtInitializeRegistry(TRUE);
-  if (!NT_SUCCESS(Status))
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
+    }
+  }
+  else if ((ActivePartition.PartType == PARTITION_FAT_12) ||
+          (ActivePartition.PartType == PARTITION_FAT_16) ||
+          (ActivePartition.PartType == PARTITION_HUGE) ||
+          (ActivePartition.PartType == PARTITION_XINT13) ||
+          (ActivePartition.PartType == PARTITION_FAT32) ||
+          (ActivePartition.PartType == PARTITION_FAT32_XINT13))
+  {
+    /* FAT or FAT32 partition */
+    DPRINT1("System path: '%wZ'\n", &SystemRootPath);
+
+    if (DoesFileExist(SystemRootPath.Buffer, L"ntldr") == TRUE ||
+       DoesFileExist(SystemRootPath.Buffer, L"boot.ini") == TRUE)
     {
+      /* Search root directory for 'ntldr' and 'boot.ini'. */
+      DPRINT1("Found Microsoft Windows NT/2000/XP boot loader\n");
+
+      /* Copy FreeLoader to the boot partition */
+      wcscpy(SrcPath, SourceRootPath.Buffer);
+      wcscat(SrcPath, L"\\loader\\freeldr.sys");
+      wcscpy(DstPath, SystemRootPath.Buffer);
+      wcscat(DstPath, L"\\freeldr.sys");
+
+      DPRINT1("Copy: %S ==> %S\n", SrcPath, DstPath);
+      Status = SetupCopyFile(SrcPath, DstPath);
+      if (!NT_SUCCESS(Status))
+      {
+       DPRINT1("SetupCopyFile() failed (Status %lx)\n", Status);
+       PopupError("Setup failed to copy 'freeldr.sys'.",
+                  "ENTER = Reboot computer");
+
+       while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+         {
+           return(QUIT_PAGE);
+         }
+       }
+      }
+
+      /* Create or update freeldr.ini */
+      if (DoesFileExist(SystemRootPath.Buffer, L"freeldr.ini") == FALSE)
+      {
+       /* Create new 'freeldr.ini' */
+       DPRINT1("Create new 'freeldr.ini'\n");
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\freeldr.ini");
+
+       Status = CreateFreeLoaderIniForReactos(DstPath,
+                                              DestinationArcPath.Buffer);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("CreateFreeLoaderIniForReactos() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to create 'freeldr.ini'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+
+       /* Install new bootcode */
+       if ((ActivePartition.PartType == PARTITION_FAT32) ||
+           (ActivePartition.PartType == PARTITION_FAT32_XINT13))
+       {
+         /* Install FAT32 bootcode */
+         wcscpy(SrcPath, SourceRootPath.Buffer);
+         wcscat(SrcPath, L"\\loader\\fat32.bin");
+         wcscpy(DstPath, SystemRootPath.Buffer);
+         wcscat(DstPath, L"\\bootsect.ros");
+
+         DPRINT1("Install FAT32 bootcode: %S ==> %S\n", SrcPath, DstPath);
+         Status = InstallFat32BootCodeToFile(SrcPath,
+                                             DstPath,
+                                             SystemRootPath.Buffer);
+         if (!NT_SUCCESS(Status))
+         {
+           DPRINT1("InstallFat32BootCodeToFile() failed (Status %lx)\n", Status);
+           PopupError("Setup failed to install the FAT32 bootcode.",
+                      "ENTER = Reboot computer");
+
+           while(TRUE)
+           {
+             ConInKey(Ir);
 
-      PrintTextXY(6, 26, "NtInitializeRegistry() failed (Status = 0x%08lx)", Status);
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               return(QUIT_PAGE);
+             }
+           }
+         }
+       }
+       else
+       {
+         /* Install FAT16 bootcode */
+         wcscpy(SrcPath, SourceRootPath.Buffer);
+         wcscat(SrcPath, L"\\loader\\fat.bin");
+         wcscpy(DstPath, SystemRootPath.Buffer);
+         wcscat(DstPath, L"\\bootsect.ros");
+
+         DPRINT1("Install FAT bootcode: %S ==> %S\n", SrcPath, DstPath);
+         Status = InstallFat16BootCodeToFile(SrcPath,
+                                             DstPath,
+                                             SystemRootPath.Buffer);
+         if (!NT_SUCCESS(Status))
+         {
+           DPRINT1("InstallFat16BootCodeToFile() failed (Status %lx)\n", Status);
+           PopupError("Setup failed to install the FAT bootcode.",
+                      "ENTER = Reboot computer");
+
+           while(TRUE)
+           {
+             ConInKey(Ir);
+
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               return(QUIT_PAGE);
+             }
+           }
+         }
+       }
+
+       /* Update 'boot.ini' */
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\boot.ini");
+
+       DPRINT1("Update 'boot.ini': %S\n", DstPath);
+       Status = UpdateBootIni(DstPath,
+                              L"C:\\bootsect.ros",
+                              L"\"ReactOS\"");
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("UpdateBootIni() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to update \'boot.ini\'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+      }
+      else
+      {
+       /* Update existing 'freeldr.ini' */
+       DPRINT1("Update existing 'freeldr.ini'\n");
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\freeldr.ini");
+
+       Status = UpdateFreeLoaderIni(DstPath,
+                                    DestinationArcPath.Buffer);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("UpdateFreeLoaderIni() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to update 'freeldr.ini'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+      }
     }
-#endif
+    else if (DoesFileExist(SystemRootPath.Buffer, L"io.sys") == TRUE ||
+            DoesFileExist(SystemRootPath.Buffer, L"msdos.sys") == TRUE)
+    {
+      /* Search for root directory for 'io.sys' and 'msdos.sys'. */
+      DPRINT1("Found Microsoft DOS or Windows 9x boot loader\n");
 
-  /*
-   * Update registry
-   */
+      /* Copy FreeLoader to the boot partition */
+      wcscpy(SrcPath, SourceRootPath.Buffer);
+      wcscat(SrcPath, L"\\loader\\freeldr.sys");
+      wcscpy(DstPath, SystemRootPath.Buffer);
+      wcscat(DstPath, L"\\freeldr.sys");
 
-  /* FIXME: Create key '\Registry\Machine\System\Setup' */
+      DPRINT("Copy: %S ==> %S\n", SrcPath, DstPath);
+      Status = SetupCopyFile(SrcPath, DstPath);
+      if (!NT_SUCCESS(Status))
+      {
+       DPRINT1("SetupCopyFile() failed (Status %lx)\n", Status);
+       PopupError("Setup failed to copy 'freeldr.sys'.",
+                  "ENTER = Reboot computer");
 
-  /* FIXME: Create value 'SystemSetupInProgress' */
+       while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+         {
+           return(QUIT_PAGE);
+         }
+       }
+      }
+
+      /* Create or update 'freeldr.ini' */
+      if (DoesFileExist(SystemRootPath.Buffer, L"freeldr.ini") == FALSE)
+      {
+       /* Create new 'freeldr.ini' */
+       DPRINT1("Create new 'freeldr.ini'\n");
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\freeldr.ini");
+
+       Status = CreateFreeLoaderIniForDos(DstPath,
+                                          DestinationArcPath.Buffer);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("CreateFreeLoaderIniForDos() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to create 'freeldr.ini'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+
+       /* Save current bootsector as 'BOOTSECT.DOS' */
+       wcscpy(SrcPath, SystemRootPath.Buffer);
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\bootsect.dos");
+
+       DPRINT1("Save bootsector: %S ==> %S\n", SrcPath, DstPath);
+       Status = SaveCurrentBootSector(SrcPath,
+                                      DstPath);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("SaveCurrentBootSector() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to save the current bootsector.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+
+       /* Install new bootsector */
+       if ((ActivePartition.PartType == PARTITION_FAT32) ||
+           (ActivePartition.PartType == PARTITION_FAT32_XINT13))
+       {
+         wcscpy(SrcPath, SourceRootPath.Buffer);
+         wcscat(SrcPath, L"\\loader\\fat32.bin");
+
+         DPRINT1("Install FAT32 bootcode: %S ==> %S\n", SrcPath, SystemRootPath.Buffer);
+         Status = InstallFat32BootCodeToDisk(SrcPath,
+                                             SystemRootPath.Buffer);
+         if (!NT_SUCCESS(Status))
+         {
+           DPRINT1("InstallFat32BootCodeToDisk() failed (Status %lx)\n", Status);
+           PopupError("Setup failed to install the FAT32 bootcode.",
+                      "ENTER = Reboot computer");
+
+           while(TRUE)
+           {
+             ConInKey(Ir);
+
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               return(QUIT_PAGE);
+             }
+           }
+         }
+       }
+       else
+       {
+         wcscpy(SrcPath, SourceRootPath.Buffer);
+         wcscat(SrcPath, L"\\loader\\fat.bin");
+
+         DPRINT1("Install FAT bootcode: %S ==> %S\n", SrcPath, SystemRootPath.Buffer);
+         Status = InstallFat16BootCodeToDisk(SrcPath,
+                                             SystemRootPath.Buffer);
+         if (!NT_SUCCESS(Status))
+         {
+           DPRINT1("InstallFat16BootCodeToDisk() failed (Status %lx)\n", Status);
+           PopupError("Setup failed to install the FAT bootcode.",
+                      "ENTER = Reboot computer");
+
+           while(TRUE)
+           {
+             ConInKey(Ir);
+
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               return(QUIT_PAGE);
+             }
+           }
+         }
+       }
+      }
+      else
+      {
+       /* Update existing 'freeldr.ini' */
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\freeldr.ini");
+
+       Status = UpdateFreeLoaderIni(DstPath,
+                                    DestinationArcPath.Buffer);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("UpdateFreeLoaderIni() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to update 'freeldr.ini'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+      }
+    }
+    else
+    {
+      /* No or unknown boot loader */
+      DPRINT1("No or unknown boot loader found\n");
+
+      /* Copy FreeLoader to the boot partition */
+      wcscpy(SrcPath, SourceRootPath.Buffer);
+      wcscat(SrcPath, L"\\loader\\freeldr.sys");
+      wcscpy(DstPath, SystemRootPath.Buffer);
+      wcscat(DstPath, L"\\freeldr.sys");
+
+      DPRINT1("Copy: %S ==> %S\n", SrcPath, DstPath);
+      Status = SetupCopyFile(SrcPath, DstPath);
+      if (!NT_SUCCESS(Status))
+      {
+       DPRINT1("SetupCopyFile() failed (Status %lx)\n", Status);
+       PopupError("Setup failed to copy 'freeldr.sys'.",
+                  "ENTER = Reboot computer");
+
+       while(TRUE)
+       {
+         ConInKey(Ir);
+
+         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)       /* ENTER */
+         {
+           return(QUIT_PAGE);
+         }
+       }
+      }
+
+      /* Create or update 'freeldr.ini' */
+      if (DoesFileExist(SystemRootPath.Buffer, L"freeldr.ini") == FALSE)
+      {
+       /* Create new freeldr.ini */
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\freeldr.ini");
+
+       DPRINT1("Copy: %S ==> %S\n", SrcPath, DstPath);
+       Status = CreateFreeLoaderIniForReactos(DstPath,
+                                              DestinationArcPath.Buffer);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("CreateFreeLoaderIniForReactos() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to create \'freeldr.ini\'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+
+       /* Save current bootsector as 'BOOTSECT.OLD' */
+       wcscpy(SrcPath, SystemRootPath.Buffer);
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\bootsect.old");
+
+       DPRINT1("Save bootsector: %S ==> %S\n", SrcPath, DstPath);
+       Status = SaveCurrentBootSector(SrcPath,
+                                      DstPath);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("SaveCurrentBootSector() failed (Status %lx)\n", Status);
+         PopupError("Setup failed save the current bootsector.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+
+       /* Install new bootsector */
+       if ((ActivePartition.PartType == PARTITION_FAT32) ||
+           (ActivePartition.PartType == PARTITION_FAT32_XINT13))
+       {
+         wcscpy(SrcPath, SourceRootPath.Buffer);
+         wcscat(SrcPath, L"\\loader\\fat32.bin");
+
+         DPRINT1("Install FAT32 bootcode: %S ==> %S\n", SrcPath, SystemRootPath.Buffer);
+         Status = InstallFat32BootCodeToDisk(SrcPath,
+                                             SystemRootPath.Buffer);
+         if (!NT_SUCCESS(Status))
+         {
+           DPRINT1("InstallFat32BootCodeToDisk() failed (Status %lx)\n", Status);
+           PopupError("Setup failed to install the FAT32 bootcode.",
+                      "ENTER = Reboot computer");
+
+           while(TRUE)
+           {
+             ConInKey(Ir);
+
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               return(QUIT_PAGE);
+             }
+           }
+         }
+       }
+       else
+       {
+         wcscpy(SrcPath, SourceRootPath.Buffer);
+         wcscat(SrcPath, L"\\loader\\fat.bin");
+
+         DPRINT1("Install FAT bootcode: %S ==> %S\n", SrcPath, SystemRootPath.Buffer);
+         Status = InstallFat16BootCodeToDisk(SrcPath,
+                                             SystemRootPath.Buffer);
+         if (!NT_SUCCESS(Status))
+         {
+           DPRINT1("InstallFat16BootCodeToDisk() failed (Status %lx)\n", Status);
+           PopupError("Setup failed to install the FAT bootcode.",
+                      "ENTER = Reboot computer");
+
+           while(TRUE)
+           {
+             ConInKey(Ir);
 
+             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)   /* ENTER */
+             {
+               return(QUIT_PAGE);
+             }
+           }
+         }
+       }
+      }
+      else
+      {
+       /* Update existing 'freeldr.ini' */
+       wcscpy(DstPath, SystemRootPath.Buffer);
+       wcscat(DstPath, L"\\freeldr.ini");
+
+       Status = UpdateFreeLoaderIni(DstPath,
+                                    DestinationArcPath.Buffer);
+       if (!NT_SUCCESS(Status))
+       {
+         DPRINT1("UpdateFreeLoaderIni() failed (Status %lx)\n", Status);
+         PopupError("Setup failed to update 'freeldr.ini'.",
+                    "ENTER = Reboot computer");
+
+         while(TRUE)
+         {
+           ConInKey(Ir);
+
+           if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)     /* ENTER */
+           {
+             return(QUIT_PAGE);
+           }
+         }
+       }
+      }
+    }
+  }
+  else
+  {
+    /* Unknown partition */
+    DPRINT1("Unknown partition found\n");
+    PopupError("Setup found an unknown partiton type.\n"
+              "This partition type is not supported!",
+              "ENTER = Reboot computer");
+
+    while(TRUE)
+    {
+      ConInKey(Ir);
+
+      if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
+      {
+       return(QUIT_PAGE);
+      }
+    }
+  }
 
+  return(SUCCESS_PAGE);
 
+#if 0
   SetStatusText("   ENTER = Continue   F3 = Quit");
 
   while(TRUE)
@@ -993,17 +2115,19 @@ InitSystemPage(PINPUT_RECORD Ir)
        }
     }
 
-  return(INIT_SYSTEM_PAGE);
+  return(BOOT_LOADER_PAGE);
+#endif
 }
 
 
+
 static ULONG
 QuitPage(PINPUT_RECORD Ir)
 {
   SetTextXY(10, 6, "ReactOS is not completely installed");
 
   SetTextXY(10, 8, "Remove floppy disk from Drive A: and");
-  SetTextXY(10, 9, "all CD-ROMs from CD-Drive.");
+  SetTextXY(10, 9, "all CD-ROMs from CD-Drives.");
 
   SetTextXY(10, 11, "Press ENTER to reboot your computer.");
 
@@ -1066,12 +2190,24 @@ NtProcessStartup(PPEB Peb)
                       0,0,0,0,0);
     }
 
+  PartDataValid = FALSE;
+
+  /* Initialize global unicode strings */
+  RtlInitUnicodeString(&SourcePath, NULL);
+  RtlInitUnicodeString(&SourceRootPath, NULL);
+  RtlInitUnicodeString(&InstallPath, NULL);
+  RtlInitUnicodeString(&DestinationPath, NULL);
+  RtlInitUnicodeString(&DestinationArcPath, NULL);
+  RtlInitUnicodeString(&DestinationRootPath, NULL);
+  RtlInitUnicodeString(&SystemRootPath, NULL);
+
+
   Page = START_PAGE;
   while (Page != REBOOT_PAGE)
     {
       ClearScreen();
 
-      SetUnderlinedTextXY(4, 3, " ReactOS 0.0.20 Setup ");
+      SetUnderlinedTextXY(4, 3, " ReactOS " KERNEL_VERSION_STR " Setup ");
 
       switch (Page)
        {
@@ -1124,8 +2260,12 @@ NtProcessStartup(PPEB Peb)
            Page = FileCopyPage(&Ir);
            break;
 
-         case INIT_SYSTEM_PAGE:
-           Page = InitSystemPage(&Ir);
+         case REGISTRY_PAGE:
+           Page = RegistryPage(&Ir);
+           break;
+
+         case BOOT_LOADER_PAGE:
+           Page = BootLoaderPage(&Ir);
            break;