[SHELL32_APITEST] Follow-up to #6796 (25e2f5f)
[reactos.git] / boot / freeldr / freeldr / ntldr / setupldr.c
index 98b66e8..5762223 100644 (file)
 /*
- *  FreeLoader
- *
- *  Copyright (C) 2009       Aleksey Bragin  <aleksey@reactos.org>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * PROJECT:     FreeLoader
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:     Windows-compatible NT OS Setup Loader.
+ * COPYRIGHT:   Copyright 2009-2019 Aleksey Bragin <aleksey@reactos.org>
  */
 
 #include <freeldr.h>
-#include "winldr.h"
-
 #include <ndk/ldrtypes.h>
 #include <arc/setupblk.h>
+#include "winldr.h"
+#include "inffile.h"
+#include "ntldropts.h"
 
 #include <debug.h>
-
 DBG_DEFAULT_CHANNEL(WINDOWS);
-#define TAG_BOOT_OPTIONS 'pOtB'
 
 // TODO: Move to .h
-VOID AllocateAndInitLPB(PLOADER_PARAMETER_BLOCK *OutLoaderBlock);
+VOID
+AllocateAndInitLPB(
+    IN USHORT VersionToBoot,
+    OUT PLOADER_PARAMETER_BLOCK* OutLoaderBlock);
 
 static VOID
-SetupLdrLoadNlsData(PLOADER_PARAMETER_BLOCK LoaderBlock, HINF InfHandle, LPCSTR SearchPath)
+SetupLdrLoadNlsData(
+    _Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,
+    _In_ HINF InfHandle,
+    _In_ PCSTR SearchPath)
 {
+    BOOLEAN Success;
     INFCONTEXT InfContext;
-    LPCSTR AnsiName, OemName, LangName;
+    PCSTR AnsiData;
+    UNICODE_STRING AnsiFileName = {0};
+    UNICODE_STRING OemFileName = {0};
+    UNICODE_STRING LangFileName = {0}; // CaseTable
+    UNICODE_STRING OemHalFileName = {0};
 
     /* Get ANSI codepage file */
-    if (!InfFindFirstLine(InfHandle, "NLS", "AnsiCodepage", &InfContext))
+    if (!InfFindFirstLine(InfHandle, "NLS", "AnsiCodepage", &InfContext) ||
+        !InfGetDataField(&InfContext, 1, &AnsiData) ||
+        !RtlCreateUnicodeStringFromAsciiz(&AnsiFileName, AnsiData))
     {
-        ERR("Failed to find 'NLS/AnsiCodepage'\n");
+        ERR("Failed to find or get 'NLS/AnsiCodepage'\n");
         return;
     }
-    if (!InfGetDataField(&InfContext, 1, &AnsiName))
+
+    /* Get OEM codepage file */
+    if (!InfFindFirstLine(InfHandle, "NLS", "OemCodepage", &InfContext) ||
+        !InfGetDataField(&InfContext, 1, &AnsiData) ||
+        !RtlCreateUnicodeStringFromAsciiz(&OemFileName, AnsiData))
     {
-        ERR("Failed to get load options\n");
-        return;
+        ERR("Failed to find or get 'NLS/OemCodepage'\n");
+        goto Quit;
     }
 
-    /* Get OEM codepage file */
-    if (!InfFindFirstLine(InfHandle, "NLS", "OemCodepage", &InfContext))
+    /* Get the Unicode case table file */
+    if (!InfFindFirstLine(InfHandle, "NLS", "UnicodeCasetable", &InfContext) ||
+        !InfGetDataField(&InfContext, 1, &AnsiData) ||
+        !RtlCreateUnicodeStringFromAsciiz(&LangFileName, AnsiData))
     {
-        ERR("Failed to find 'NLS/AnsiCodepage'\n");
-        return;
+        ERR("Failed to find or get 'NLS/UnicodeCasetable'\n");
+        goto Quit;
     }
-    if (!InfGetDataField(&InfContext, 1, &OemName))
+
+    /* Get OEM HAL font file */
+    if (!InfFindFirstLine(InfHandle, "NLS", "OemHalFont", &InfContext) ||
+        !InfGetData(&InfContext, NULL, &AnsiData) ||
+        !RtlCreateUnicodeStringFromAsciiz(&OemHalFileName, AnsiData))
     {
-        ERR("Failed to get load options\n");
-        return;
+        WARN("Failed to find or get 'NLS/OemHalFont'\n");
+        /* Ignore, this is an optional file */
+        RtlInitEmptyUnicodeString(&OemHalFileName, NULL, 0);
     }
 
-    if (!InfFindFirstLine(InfHandle, "NLS", "UnicodeCasetable", &InfContext))
+    TRACE("NLS data: '%wZ' '%wZ' '%wZ' '%wZ'\n",
+          &AnsiFileName, &OemFileName, &LangFileName, &OemHalFileName);
+
+    /* Load NLS data */
+    Success = WinLdrLoadNLSData(LoaderBlock,
+                                SearchPath,
+                                &AnsiFileName,
+                                &OemFileName,
+                                &LangFileName,
+                                &OemHalFileName);
+    TRACE("NLS data loading %s\n", Success ? "successful" : "failed");
+    (VOID)Success;
+
+Quit:
+    RtlFreeUnicodeString(&OemHalFileName);
+    RtlFreeUnicodeString(&LangFileName);
+    RtlFreeUnicodeString(&OemFileName);
+    RtlFreeUnicodeString(&AnsiFileName);
+}
+
+static
+BOOLEAN
+SetupLdrInitErrataInf(
+    IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
+    IN HINF InfHandle,
+    IN PCSTR SystemRoot)
+{
+    INFCONTEXT InfContext;
+    PCSTR FileName;
+    ULONG FileSize;
+    PVOID PhysicalBase;
+    CHAR ErrataFilePath[MAX_PATH];
+
+    /* Retrieve the INF file name value */
+    if (!InfFindFirstLine(InfHandle, "BiosInfo", "InfName", &InfContext))
     {
-        ERR("Failed to find 'NLS/AnsiCodepage'\n");
-        return;
+        WARN("Failed to find 'BiosInfo/InfName'\n");
+        return FALSE;
     }
-    if (!InfGetDataField(&InfContext, 1, &LangName))
+    if (!InfGetDataField(&InfContext, 1, &FileName))
     {
-        ERR("Failed to get load options\n");
-        return;
+        WARN("Failed to read 'InfName' value\n");
+        return FALSE;
     }
 
-    TRACE("NLS data '%s' '%s' '%s'\n", AnsiName, OemName, LangName);
+    RtlStringCbCopyA(ErrataFilePath, sizeof(ErrataFilePath), SystemRoot);
+    RtlStringCbCatA(ErrataFilePath, sizeof(ErrataFilePath), FileName);
 
-#if DBG
+    /* Load the INF file */
+    PhysicalBase = WinLdrLoadModule(ErrataFilePath, &FileSize, LoaderRegistryData);
+    if (!PhysicalBase)
     {
-        BOOLEAN Success = WinLdrLoadNLSData(LoaderBlock, SearchPath, AnsiName, OemName, LangName);
-        TRACE("NLS data loading %s\n", Success ? "successful" : "failed");
-    }    
-#else
-    WinLdrLoadNLSData(LoaderBlock, SearchPath, AnsiName, OemName, LangName);
-#endif
+        WARN("Could not load '%s'\n", ErrataFilePath);
+        return FALSE;
+    }
 
-    /* TODO: Load OEM HAL font */
-    // Value "OemHalFont"
+    LoaderBlock->Extension->EmInfFileImage = PaToVa(PhysicalBase);
+    LoaderBlock->Extension->EmInfFileSize  = FileSize;
+
+    return TRUE;
 }
 
 static VOID
-SetupLdrScanBootDrivers(PLIST_ENTRY BootDriverListHead, HINF InfHandle, LPCSTR SearchPath)
+SetupLdrScanBootDrivers(
+    _Inout_ PLIST_ENTRY BootDriverListHead,
+    _In_ HINF InfHandle,
+    _In_ PCSTR SearchPath)
 {
     INFCONTEXT InfContext, dirContext;
+    PCSTR Media, DriverName, dirIndex, ImagePath;
     BOOLEAN Success;
-    LPCSTR Media, DriverName, dirIndex, ImagePath;
-    WCHAR ServiceName[256];
-    WCHAR ImagePathW[256];
+    WCHAR ImagePathW[MAX_PATH];
+    WCHAR DriverNameW[256];
+
+    UNREFERENCED_PARAMETER(SearchPath);
 
-    /* Open inf section */
+    /* Open INF section */
     if (!InfFindFirstLine(InfHandle, "SourceDisksFiles", NULL, &InfContext))
-        return;
+        goto Quit;
 
     /* Load all listed boot drivers */
     do
@@ -112,58 +163,339 @@ SetupLdrScanBootDrivers(PLIST_ENTRY BootDriverListHead, HINF InfHandle, LPCSTR S
                 InfFindFirstLine(InfHandle, "Directories", dirIndex, &dirContext) &&
                 InfGetDataField(&dirContext, 1, &ImagePath))
             {
-                /* Convert name to widechar */
-                swprintf(ServiceName, L"%S", DriverName);
-
                 /* Prepare image path */
-                swprintf(ImagePathW, L"%S", ImagePath);
-                wcscat(ImagePathW, L"\\");
-                wcscat(ImagePathW, ServiceName);
+                RtlStringCbPrintfW(ImagePathW, sizeof(ImagePathW),
+                                   L"%S\\%S", ImagePath, DriverName);
 
-                /* Remove .sys extension */
-                ServiceName[wcslen(ServiceName) - 4] = 0;
+                /* Convert name to unicode and remove .sys extension */
+                RtlStringCbPrintfW(DriverNameW, sizeof(DriverNameW),
+                                   L"%S", DriverName);
+                DriverNameW[wcslen(DriverNameW) - 4] = UNICODE_NULL;
 
                 /* Add it to the list */
                 Success = WinLdrAddDriverToList(BootDriverListHead,
-                                                L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\",
+                                                FALSE,
+                                                DriverNameW,
                                                 ImagePathW,
-                                                ServiceName);
+                                                NULL,
+                                                SERVICE_ERROR_NORMAL,
+                                                -1);
                 if (!Success)
                 {
-                    ERR("Could not add boot driver '%s', '%s'\n", SearchPath, DriverName);
-                    return;
+                    ERR("Could not add boot driver '%s'\n", DriverName);
+                    /* Ignore and continue adding other drivers */
                 }
             }
         }
     } while (InfFindNextLine(&InfContext, &InfContext));
+
+Quit:
+    /* Finally, add the boot filesystem driver to the list */
+    if (BootFileSystem)
+    {
+        TRACE("Adding filesystem driver %S\n", BootFileSystem);
+        Success = WinLdrAddDriverToList(BootDriverListHead,
+                                        FALSE,
+                                        BootFileSystem,
+                                        NULL,
+                                        L"Boot File System",
+                                        SERVICE_ERROR_CRITICAL,
+                                        -1);
+        if (!Success)
+            ERR("Failed to add filesystem driver %S\n", BootFileSystem);
+    }
+    else
+    {
+        TRACE("No required filesystem driver\n");
+    }
 }
 
 
 /* SETUP STARTER **************************************************************/
 
+/*
+ * Update the options in the buffer pointed by LoadOptions, of maximum size
+ * BufferSize, by first removing any specified options, and then adding any
+ * other ones.
+ *
+ * OptionsToAdd is a NULL-terminated array of string buffer pointers that
+ *    specify the options to be added into LoadOptions. Whether they are
+ *    prepended or appended to LoadOptions is controlled via the Append
+ *    parameter. The options are added in the order specified by the array.
+ *
+ * OptionsToRemove is a NULL-terminated array of string buffer pointers that
+ *    specify the options to remove from LoadOptions. Specifying also there
+ *    any options to add, has the effect of removing from LoadOptions any
+ *    duplicates of the options to be added, before adding them later into
+ *    LoadOptions. The options are removed in the order specified by the array.
+ *
+ * The options string buffers in the OptionsToRemove array have the format:
+ *    "/option1 /option2[=] ..."
+ *
+ * An option in the OptionsToRemove list with a trailing '=' or ':' designates
+ * an option in LoadOptions with user-specific data appended after the sign.
+ * When such an option is being removed from LoadOptions, all the appended
+ * data is also removed until the next option.
+ */
+VOID
+NtLdrUpdateLoadOptions(
+    IN OUT PSTR LoadOptions,
+    IN ULONG BufferSize,
+    IN BOOLEAN Append,
+    IN PCSTR OptionsToAdd[] OPTIONAL,
+    IN PCSTR OptionsToRemove[] OPTIONAL)
+{
+    PCSTR NextOptions, NextOpt;
+    PSTR Options, Option;
+    ULONG NextOptLength;
+    ULONG OptionLength;
+
+    if (!LoadOptions || (BufferSize == 0))
+        return;
+    // ASSERT(strlen(LoadOptions) + 1 <= BufferSize);
+
+    /* Loop over the options to remove */
+    for (; OptionsToRemove && *OptionsToRemove; ++OptionsToRemove)
+    {
+        NextOptions = *OptionsToRemove;
+        while ((NextOpt = NtLdrGetNextOption(&NextOptions, &NextOptLength)))
+        {
+            /* Scan the load options */
+            Options = LoadOptions;
+            while ((Option = (PSTR)NtLdrGetNextOption((PCSTR*)&Options, &OptionLength)))
+            {
+                /*
+                 * Check whether the option to find exactly matches the current
+                 * load option, or is a prefix thereof if this is an option with
+                 * appended data.
+                 */
+                if ((OptionLength >= NextOptLength) &&
+                    (_strnicmp(Option, NextOpt, NextOptLength) == 0))
+                {
+                    if ((OptionLength == NextOptLength) ||
+                        (NextOpt[NextOptLength-1] == '=') ||
+                        (NextOpt[NextOptLength-1] == ':'))
+                    {
+                        /* Eat any skipped option or whitespace separators */
+                        while ((Option > LoadOptions) &&
+                               (Option[-1] == '/' ||
+                                Option[-1] == ' ' ||
+                                Option[-1] == '\t'))
+                        {
+                            --Option;
+                        }
+
+                        /* If the option was not preceded by a whitespace
+                         * separator, insert one and advance the pointer. */
+                        if ((Option > LoadOptions) &&
+                            (Option[-1] != ' ') &&
+                            (Option[-1] != '\t') &&
+                            (*Options != '\0') /* &&
+                            ** Not necessary since NtLdrGetNextOption() **
+                            ** stripped any leading separators.         **
+                            (*Options != ' ') &&
+                            (*Options != '\t') */)
+                        {
+                            *Option++ = ' ';
+                        }
+
+                        /* Move the remaining options back, erasing the current one */
+                        ASSERT(Option <= Options);
+                        RtlMoveMemory(Option,
+                                      Options,
+                                      (strlen(Options) + 1) * sizeof(CHAR));
+
+                        /* Reset the iterator */
+                        Options = Option;
+                    }
+                }
+            }
+        }
+    }
+
+    /* Now loop over the options to add */
+    for (; OptionsToAdd && *OptionsToAdd; ++OptionsToAdd)
+    {
+        NtLdrAddOptions(LoadOptions,
+                        BufferSize,
+                        Append,
+                        *OptionsToAdd);
+    }
+}
+
+
+/*
+ * List of options and their corresponding higher priority ones,
+ * that are either checked before any other ones, or whose name
+ * includes another option name as a subset (e.g. NODEBUG vs. DEBUG).
+ * See also https://geoffchappell.com/notes/windows/boot/editoptions.htm
+ */
+static const struct
+{
+    PCSTR Options;
+    PCSTR ExtraOptions;
+    PCSTR HigherPriorOptions;
+} HighPriorOptionsMap[] =
+{
+    /* NODEBUG has a higher precedence than DEBUG */
+    {"/DEBUG/DEBUG=", NULL, "/NODEBUG"},
+
+    /* When using SCREEN debug port, we need boot video */
+    {"/DEBUGPORT=SCREEN", NULL, "/NOGUIBOOT"},
+
+    /* DETECTHAL has a higher precedence than HAL= or KERNEL= */
+    {"/HAL=/KERNEL=", NULL, "/DETECTHAL"},
+
+    /* NOPAE has a higher precedence than PAE */
+    {"/PAE", NULL, "/NOPAE"},
+
+    /* NOEXECUTE(=) has a higher precedence than EXECUTE */
+    {"/EXECUTE", "/NOEXECUTE=ALWAYSOFF", "/NOEXECUTE/NOEXECUTE="},
+    /* NOEXECUTE(=) options are self-excluding and
+     * some have higher precedence than others. */
+    {"/NOEXECUTE/NOEXECUTE=", NULL, "/NOEXECUTE/NOEXECUTE="},
+
+    /* SAFEBOOT(:) options are self-excluding */
+    {"/SAFEBOOT/SAFEBOOT:", NULL, "/SAFEBOOT/SAFEBOOT:"},
+};
+
+#define TAG_BOOT_OPTIONS 'pOtB'
+
+VOID
+NtLdrGetHigherPriorityOptions(
+    IN PCSTR BootOptions,
+    OUT PSTR* ExtraOptions,
+    OUT PSTR* HigherPriorityOptions)
+{
+    ULONG i;
+    PCSTR NextOptions, NextOpt;
+    ULONG NextOptLength;
+    SIZE_T ExtraOptsSize = 0;
+    SIZE_T HighPriorOptsSize = 0;
+
+    /* Masks specifying the presence (TRUE) or absence (FALSE) of the options */
+    BOOLEAN Masks[RTL_NUMBER_OF(HighPriorOptionsMap)];
+
+    /* Just return if we cannot return anything */
+    if (!ExtraOptions && !HigherPriorityOptions)
+        return;
+
+    if (ExtraOptions)
+        *ExtraOptions = NULL;
+    if (HigherPriorityOptions)
+        *HigherPriorityOptions = NULL;
+
+    /* Just return if no initial options were given */
+    if (!BootOptions || !*BootOptions)
+        return;
+
+    /* Determine the presence of the colliding options, and the
+     * maximum necessary sizes for the pointers to be allocated. */
+    RtlZeroMemory(Masks, sizeof(Masks));
+    for (i = 0; i < RTL_NUMBER_OF(HighPriorOptionsMap); ++i)
+    {
+        /* Loop over the given options to search for */
+        NextOptions = HighPriorOptionsMap[i].Options;
+        while ((NextOpt = NtLdrGetNextOption(&NextOptions, &NextOptLength)))
+        {
+            /* If any of these options are present... */
+            if (NtLdrGetOptionExN(BootOptions, NextOpt, NextOptLength, NULL))
+            {
+                /* ... set the mask, retrieve the sizes and stop looking for these options */
+                Masks[i] = TRUE;
+                if (ExtraOptions && HighPriorOptionsMap[i].ExtraOptions)
+                {
+                    ExtraOptsSize += strlen(HighPriorOptionsMap[i].ExtraOptions) * sizeof(CHAR);
+                }
+                if (HigherPriorityOptions && HighPriorOptionsMap[i].HigherPriorOptions)
+                {
+                    HighPriorOptsSize += strlen(HighPriorOptionsMap[i].HigherPriorOptions) * sizeof(CHAR);
+                }
+                break;
+            }
+        }
+    }
+    /* Count the NULL-terminator */
+    if (ExtraOptions)
+        ExtraOptsSize += sizeof(ANSI_NULL);
+    if (HigherPriorityOptions)
+        HighPriorOptsSize += sizeof(ANSI_NULL);
+
+    /* Allocate the string pointers */
+    if (ExtraOptions)
+    {
+        *ExtraOptions = FrLdrHeapAlloc(ExtraOptsSize, TAG_BOOT_OPTIONS);
+        if (!*ExtraOptions)
+            return;
+    }
+    if (HigherPriorityOptions)
+    {
+        *HigherPriorityOptions = FrLdrHeapAlloc(HighPriorOptsSize, TAG_BOOT_OPTIONS);
+        if (!*HigherPriorityOptions)
+        {
+            if (ExtraOptions)
+            {
+                FrLdrHeapFree(*ExtraOptions, TAG_BOOT_OPTIONS);
+                *ExtraOptions = NULL;
+            }
+            return;
+        }
+    }
+
+    /* Initialize the strings */
+    if (ExtraOptions)
+        *(*ExtraOptions) = '\0';
+    if (HigherPriorityOptions)
+        *(*HigherPriorityOptions) = '\0';
+
+    /* Go through the masks that determine the options to check */
+    for (i = 0; i < RTL_NUMBER_OF(HighPriorOptionsMap); ++i)
+    {
+        if (Masks[i])
+        {
+            /* Retrieve the strings */
+            if (ExtraOptions && HighPriorOptionsMap[i].ExtraOptions)
+            {
+                RtlStringCbCatA(*ExtraOptions,
+                                ExtraOptsSize,
+                                HighPriorOptionsMap[i].ExtraOptions);
+            }
+            if (HigherPriorityOptions && HighPriorOptionsMap[i].HigherPriorOptions)
+            {
+                RtlStringCbCatA(*HigherPriorityOptions,
+                                HighPriorOptsSize,
+                                HighPriorOptionsMap[i].HigherPriorOptions);
+            }
+        }
+    }
+}
+
+
 ARC_STATUS
 LoadReactOSSetup(
     IN ULONG Argc,
     IN PCHAR Argv[],
     IN PCHAR Envp[])
 {
+    ARC_STATUS Status;
     PCSTR ArgValue;
-    PCHAR File;
-    CHAR FileName[512];
-    CHAR BootPath[512];
-    CHAR BootOptions2[256];
-    LPCSTR LoadOptions;
-    LPSTR BootOptions;
+    PCSTR SystemPartition;
+    PCSTR SystemPath;
+    PSTR FileName;
+    ULONG FileNameLength;
     BOOLEAN BootFromFloppy;
     BOOLEAN Success;
-    ULONG i, ErrorLine;
     HINF InfHandle;
     INFCONTEXT InfContext;
+    ULONG i, ErrorLine;
     PLOADER_PARAMETER_BLOCK LoaderBlock;
     PSETUP_LOADER_BLOCK SetupBlock;
-    LPCSTR SystemPath;
+    CHAR BootPath[MAX_PATH];
+    CHAR FilePath[MAX_PATH];
+    CHAR UserBootOptions[256];
+    PCSTR BootOptions;
 
-    static LPCSTR SourcePaths[] =
+    static PCSTR SourcePaths[] =
     {
         "", /* Only for floppy boot */
 #if defined(_M_IX86)
@@ -177,10 +509,31 @@ LoadReactOSSetup(
         NULL
     };
 
-    UiDrawStatusText("Setup is loading...");
+    /* Retrieve the (mandatory) boot type */
+    ArgValue = GetArgumentValue(Argc, Argv, "BootType");
+    if (!ArgValue || !*ArgValue)
+    {
+        ERR("No 'BootType' value, aborting!\n");
+        return EINVAL;
+    }
+    if (_stricmp(ArgValue, "ReactOSSetup") != 0)
+    {
+        ERR("Unknown 'BootType' value '%s', aborting!\n", ArgValue);
+        return EINVAL;
+    }
 
+    /* Retrieve the (mandatory) system partition */
+    SystemPartition = GetArgumentValue(Argc, Argv, "SystemPartition");
+    if (!SystemPartition || !*SystemPartition)
+    {
+        ERR("No 'SystemPartition' specified, aborting!\n");
+        return EINVAL;
+    }
+
+    /* Let the user know we started loading */
     UiDrawBackdrop();
-    UiDrawProgressBarCenter(1, 100, "Loading ReactOS Setup...");
+    UiDrawStatusText("Setup is loading...");
+    UiDrawProgressBarCenter("Loading ReactOS Setup...");
 
     /* Retrieve the system path */
     *BootPath = ANSI_NULL;
@@ -192,13 +545,12 @@ LoadReactOSSetup(
     else
     {
         /*
-         * IMPROVE: I don't want to call MachDiskGetBootPath here as a
-         * default choice because I can call it after (see few lines below).
+         * IMPROVE: I don't want to use the SystemPartition here as a
+         * default choice because I can do it after (see few lines below).
          * Instead I reset BootPath here so that we can build the full path
          * using the general code from below.
          */
-        // MachDiskGetBootPath(BootPath, sizeof(BootPath));
-        // RtlStringCbCopyA(BootPath, sizeof(BootPath), ArgValue);
+        // RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
         *BootPath = ANSI_NULL;
     }
 
@@ -211,38 +563,55 @@ LoadReactOSSetup(
     if (strrchr(BootPath, ')') == NULL)
     {
         /* Temporarily save the boot path */
-        RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
+        RtlStringCbCopyA(FilePath, sizeof(FilePath), BootPath);
 
-        /* This is not a full path. Use the current (i.e. boot) device. */
-        MachDiskGetBootPath(BootPath, sizeof(BootPath));
+        /* This is not a full path: prepend the SystemPartition */
+        RtlStringCbCopyA(BootPath, sizeof(BootPath), SystemPartition);
 
         /* Append a path separator if needed */
-        if (*FileName != '\\' && *FileName != '/')
+        if (*FilePath != '\\' && *FilePath != '/')
             RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
 
         /* Append the remaining path */
-        RtlStringCbCatA(BootPath, sizeof(BootPath), FileName);
+        RtlStringCbCatA(BootPath, sizeof(BootPath), FilePath);
     }
 
-    /* Append a backslash if needed */
+    /* Append a path separator if needed */
     if (!*BootPath || BootPath[strlen(BootPath) - 1] != '\\')
         RtlStringCbCatA(BootPath, sizeof(BootPath), "\\");
 
     TRACE("BootPath: '%s'\n", BootPath);
 
-    /* Retrieve the boot options */
-    *BootOptions2 = ANSI_NULL;
-    ArgValue = GetArgumentValue(Argc, Argv, "Options");
-    if (ArgValue)
-        RtlStringCbCopyA(BootOptions2, sizeof(BootOptions2), ArgValue);
+    /*
+     * Retrieve the boot options. Any options present here will supplement or
+     * override those that will be specified in TXTSETUP.SIF's OsLoadOptions.
+     */
+    BootOptions = GetArgumentValue(Argc, Argv, "Options");
+    if (!BootOptions)
+        BootOptions = "";
 
-    TRACE("BootOptions: '%s'\n", BootOptions2);
+    TRACE("BootOptions: '%s'\n", BootOptions);
+
+    /* Check if a RAM disk file was given */
+    FileName = (PSTR)NtLdrGetOptionEx(BootOptions, "RDPATH=", &FileNameLength);
+    if (FileName && (FileNameLength > 7))
+    {
+        /* Load the RAM disk */
+        Status = RamDiskInitialize(FALSE, BootOptions, SystemPartition);
+        if (Status != ESUCCESS)
+        {
+            FileName += 7; FileNameLength -= 7;
+            UiMessageBox("Failed to load RAM disk file '%.*s'",
+                         FileNameLength, FileName);
+            return Status;
+        }
+    }
 
     /* Check if we booted from floppy */
     BootFromFloppy = strstr(BootPath, "fdisk") != NULL;
 
-    /* Open 'txtsetup.sif' from any of source paths */
-    File = BootPath + strlen(BootPath);
+    /* Open 'txtsetup.sif' from any of the source paths */
+    FileName = BootPath + strlen(BootPath);
     for (i = BootFromFloppy ? 0 : 1; ; i++)
     {
         SystemPath = SourcePaths[i];
@@ -251,10 +620,11 @@ LoadReactOSSetup(
             UiMessageBox("Failed to open txtsetup.sif");
             return ENOENT;
         }
-        RtlStringCbCopyA(File, sizeof(BootPath) - (File - BootPath)*sizeof(CHAR), SystemPath);
-        RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
-        RtlStringCbCatA(FileName, sizeof(FileName), "txtsetup.sif");
-        if (InfOpenFile(&InfHandle, FileName, &ErrorLine))
+        FileNameLength = (ULONG)(sizeof(BootPath) - (FileName - BootPath)*sizeof(CHAR));
+        RtlStringCbCopyA(FileName, FileNameLength, SystemPath);
+        RtlStringCbCopyA(FilePath, sizeof(FilePath), BootPath);
+        RtlStringCbCatA(FilePath, sizeof(FilePath), "txtsetup.sif");
+        if (InfOpenFile(&InfHandle, FilePath, &ErrorLine))
         {
             break;
         }
@@ -262,68 +632,158 @@ LoadReactOSSetup(
 
     TRACE("BootPath: '%s', SystemPath: '%s'\n", BootPath, SystemPath);
 
-    /* Get Load options - debug and non-debug */
-    if (!InfFindFirstLine(InfHandle, "SetupData", "OsLoadOptions", &InfContext))
-    {
-        ERR("Failed to find 'SetupData/OsLoadOptions'\n");
-        return EINVAL;
-    }
-
-    if (!InfGetDataField(&InfContext, 1, &LoadOptions))
-    {
-        ERR("Failed to get load options\n");
-        return EINVAL;
-    }
+    // UseLocalSif = NtLdrGetOption(BootOptions, "USELOCALSIF");
 
-#if DBG
-    /* Get debug load options and use them */
-    if (InfFindFirstLine(InfHandle, "SetupData", "DbgOsLoadOptions", &InfContext))
+    if (NtLdrGetOption(BootOptions, "SIFOPTIONSOVERRIDE"))
     {
-        LPCSTR DbgLoadOptions;
+        PCSTR OptionsToRemove[2] = {"SIFOPTIONSOVERRIDE", NULL};
 
-        if (InfGetDataField(&InfContext, 1, &DbgLoadOptions))
-            LoadOptions = DbgLoadOptions;
-    }
-#endif
+        /* Do not use any load options from TXTSETUP.SIF, but
+         * use instead those passed from the command line. */
+        RtlStringCbCopyA(UserBootOptions, sizeof(UserBootOptions), BootOptions);
 
-    /* Copy loadoptions (original string will be freed) */
-    BootOptions = FrLdrTempAlloc(strlen(LoadOptions) + 1, TAG_BOOT_OPTIONS);
-    ASSERT(BootOptions);
-    strcpy(BootOptions, LoadOptions);
+        /* Remove the private switch from the options */
+        NtLdrUpdateLoadOptions(UserBootOptions,
+                               sizeof(UserBootOptions),
+                               FALSE,
+                               NULL,
+                               OptionsToRemove);
 
-    TRACE("BootOptions: '%s'\n", BootOptions);
-
-    /* Check if a ramdisk file was given */
-    File = strstr(BootOptions2, "/RDPATH=");
-    if (File)
+        BootOptions = UserBootOptions;
+    }
+    else // if (!*BootOptions || NtLdrGetOption(BootOptions, "SIFOPTIONSADD"))
     {
-        /* Copy the file name and everything else after it */
-        RtlStringCbCopyA(FileName, sizeof(FileName), File + 8);
+        PCSTR LoadOptions = NULL;
+        PCSTR DbgLoadOptions = NULL;
+        PSTR ExtraOptions, HigherPriorityOptions;
+        PSTR OptionsToAdd[3];
+        PSTR OptionsToRemove[4];
+
+        /* Load the options from TXTSETUP.SIF */
+        if (InfFindFirstLine(InfHandle, "SetupData", "OsLoadOptions", &InfContext))
+        {
+            if (!InfGetDataField(&InfContext, 1, &LoadOptions))
+                WARN("Failed to get load options\n");
+        }
+
+#if !DBG
+        /* Non-debug mode: get the debug load options only if /DEBUG was specified
+         * in the Argv command-line options (was e.g. added to the options when
+         * the user selected "Debugging Mode" in the advanced boot menu). */
+        if (NtLdrGetOption(BootOptions, "DEBUG") ||
+            NtLdrGetOption(BootOptions, "DEBUG="))
+        {
+#else
+        /* Debug mode: always get the debug load options */
+#endif
+        if (InfFindFirstLine(InfHandle, "SetupData", "SetupDebugOptions", &InfContext))
+        {
+            if (!InfGetDataField(&InfContext, 1, &DbgLoadOptions))
+                WARN("Failed to get debug load options\n");
+        }
+        /* If none was found, default to enabling debugging */
+        if (!DbgLoadOptions)
+            DbgLoadOptions = "/DEBUG";
+#if !DBG
+        }
+#endif
 
-        /* Null-terminate */
-        *strstr(FileName, " ") = ANSI_NULL;
+        /* Initialize the load options with those from TXTSETUP.SIF */
+        *UserBootOptions = ANSI_NULL;
+        if (LoadOptions && *LoadOptions)
+            RtlStringCbCopyA(UserBootOptions, sizeof(UserBootOptions), LoadOptions);
 
-        /* Load the ramdisk */
-        if (!RamDiskLoadVirtualFile(FileName))
+        /* Merge the debug load options if any */
+        if (DbgLoadOptions)
         {
-            UiMessageBox("Failed to load RAM disk file %s", FileName);
-            return ENOENT;
+            RtlZeroMemory(OptionsToAdd, sizeof(OptionsToAdd));
+            RtlZeroMemory(OptionsToRemove, sizeof(OptionsToRemove));
+
+            /*
+             * Retrieve any option patterns that we should remove from the
+             * SIF load options because they are of higher precedence than
+             * those specified in the debug load options to be added.
+             * Also always remove NODEBUG (even if the debug load options
+             * do not contain explicitly the DEBUG option), since we want
+             * to have debugging enabled if possible.
+             */
+            OptionsToRemove[0] = "/NODEBUG";
+            NtLdrGetHigherPriorityOptions(DbgLoadOptions,
+                                          &ExtraOptions,
+                                          &HigherPriorityOptions);
+            OptionsToAdd[1] = (ExtraOptions ? ExtraOptions : "");
+            OptionsToRemove[1] = (HigherPriorityOptions ? HigherPriorityOptions : "");
+
+            /*
+             * Prepend the debug load options, so that in case it contains
+             * redundant options with respect to the SIF load options, the
+             * former can take precedence over the latter.
+             */
+            OptionsToAdd[0] = (PSTR)DbgLoadOptions;
+            OptionsToRemove[2] = (PSTR)DbgLoadOptions;
+            NtLdrUpdateLoadOptions(UserBootOptions,
+                                   sizeof(UserBootOptions),
+                                   FALSE,
+                                   (PCSTR*)OptionsToAdd,
+                                   (PCSTR*)OptionsToRemove);
+
+            if (ExtraOptions)
+                FrLdrHeapFree(ExtraOptions, TAG_BOOT_OPTIONS);
+            if (HigherPriorityOptions)
+                FrLdrHeapFree(HigherPriorityOptions, TAG_BOOT_OPTIONS);
         }
+
+        RtlZeroMemory(OptionsToAdd, sizeof(OptionsToAdd));
+        RtlZeroMemory(OptionsToRemove, sizeof(OptionsToRemove));
+
+        /*
+         * Retrieve any option patterns that we should remove from the
+         * SIF load options because they are of higher precedence than
+         * those specified in the options to be added.
+         */
+        NtLdrGetHigherPriorityOptions(BootOptions,
+                                      &ExtraOptions,
+                                      &HigherPriorityOptions);
+        OptionsToAdd[1] = (ExtraOptions ? ExtraOptions : "");
+        OptionsToRemove[0] = (HigherPriorityOptions ? HigherPriorityOptions : "");
+
+        /* Finally, prepend the user-specified options that
+         * take precedence over those from TXTSETUP.SIF. */
+        OptionsToAdd[0] = (PSTR)BootOptions;
+        OptionsToRemove[1] = (PSTR)BootOptions;
+        NtLdrUpdateLoadOptions(UserBootOptions,
+                               sizeof(UserBootOptions),
+                               FALSE,
+                               (PCSTR*)OptionsToAdd,
+                               (PCSTR*)OptionsToRemove);
+
+        if (ExtraOptions)
+            FrLdrHeapFree(ExtraOptions, TAG_BOOT_OPTIONS);
+        if (HigherPriorityOptions)
+            FrLdrHeapFree(HigherPriorityOptions, TAG_BOOT_OPTIONS);
+
+        BootOptions = UserBootOptions;
     }
 
-    /* Allocate and minimalist-initialize LPB */
-    AllocateAndInitLPB(&LoaderBlock);
+    TRACE("BootOptions: '%s'\n", BootOptions);
+
+    /* Handle the SOS option */
+    SosEnabled = !!NtLdrGetOption(BootOptions, "SOS");
+    if (SosEnabled)
+        UiResetForSOS();
+
+    /* Allocate and minimally-initialize the Loader Parameter Block */
+    AllocateAndInitLPB(_WIN32_WINNT_WS03, &LoaderBlock);
 
-    /* Allocate and initialize setup loader block */
+    /* Allocate and initialize the setup loader block */
     SetupBlock = &WinLdrSystemBlock->SetupBlock;
     LoaderBlock->SetupLdrBlock = SetupBlock;
 
     /* Set textmode setup flag */
     SetupBlock->Flags = SETUPLDR_TEXT_MODE;
 
-    /* Load the system hive "setupreg.hiv" for setup */
-    UiDrawBackdrop();
-    UiDrawProgressBarCenter(15, 100, "Loading setup system hive...");
+    /* Load the "setupreg.hiv" setup system hive */
+    UiUpdateProgressBar(15, "Loading setup system hive...");
     Success = WinLdrInitSystemHive(LoaderBlock, BootPath, TRUE);
     TRACE("Setup SYSTEM hive %s\n", (Success ? "loaded" : "not loaded"));
     /* Bail out if failure */
@@ -331,9 +791,14 @@ LoadReactOSSetup(
         return ENOEXEC;
 
     /* Load NLS data, they are in the System32 directory of the installation medium */
-    RtlStringCbCopyA(FileName, sizeof(FileName), BootPath);
-    RtlStringCbCatA(FileName, sizeof(FileName), "system32\\");
-    SetupLdrLoadNlsData(LoaderBlock, InfHandle, FileName);
+    RtlStringCbCopyA(FilePath, sizeof(FilePath), BootPath);
+    RtlStringCbCatA(FilePath, sizeof(FilePath), "system32\\");
+    SetupLdrLoadNlsData(LoaderBlock, InfHandle, FilePath);
+
+    /* Load the Firmware Errata file from the installation medium */
+    Success = SetupLdrInitErrataInf(LoaderBlock, InfHandle, BootPath);
+    TRACE("Firmware Errata file %s\n", (Success ? "loaded" : "not loaded"));
+    /* Not necessarily fatal if not found - carry on going */
 
     // UiDrawStatusText("Press F6 if you need to install a 3rd-party SCSI or RAID driver...");
 
@@ -345,10 +810,9 @@ LoadReactOSSetup(
 
     UiDrawStatusText("The Setup program is starting...");
 
-    /* Load ReactOS Setup */
+    /* Finish loading */
     return LoadAndBootWindowsCommon(_WIN32_WINNT_WS03,
                                     LoaderBlock,
                                     BootOptions,
-                                    BootPath,
-                                    TRUE);
+                                    BootPath);
 }