[FREELDR] Limit the usage of DiskStopFloppyMotor() in hardware/platform-specific...
[reactos.git] / boot / freeldr / freeldr / oslist.c
index 1d03a48..3a05ebd 100644 (file)
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+/* INCLUDES *******************************************************************/
+
 #include <freeldr.h>
 
-#define TAG_STRING ' rtS'
+#include <debug.h>
+DBG_DEFAULT_CHANNEL(INIFILE);
+
 #define TAG_OS_ITEM 'tISO'
 
+/* FUNCTIONS ******************************************************************/
+
 static PCSTR CopyString(PCSTR Source)
 {
     PSTR Dest;
 
     if (!Source)
         return NULL;
+
     Dest = FrLdrHeapAlloc(strlen(Source) + 1, TAG_STRING);
     if (Dest)
-    {
         strcpy(Dest, Source);
-    }
 
     return Dest;
 }
 
-OperatingSystemItem* InitOperatingSystemList(ULONG* OperatingSystemCountPointer)
+OperatingSystemItem*
+InitOperatingSystemList(
+    IN ULONG_PTR FrLdrSectionId,
+    OUT PULONG OperatingSystemCount,
+    OUT PULONG DefaultOperatingSystem)
 {
-    ULONG Idx;
-    CHAR SettingName[260];
-    CHAR SettingValue[260];
-    ULONG_PTR SectionId;
+    ULONG DefaultOS = 0;
+    PCSTR DefaultOSName = NULL;
+    CHAR  DefaultOSText[80];
+
+    OperatingSystemItem* Items;
+    ULONG Count;
+    ULONG i;
+    ULONG_PTR OsSectionId, SectionId;
     PCHAR TitleStart, TitleEnd;
     PCSTR OsLoadOptions;
-    ULONG Count;
-    OperatingSystemItem* Items;
+    BOOLEAN HadSection;
+    BOOLEAN HadNoBootType;
+    CHAR SettingName[260];
+    CHAR SettingValue[260];
+    CHAR BootType[80];
+    CHAR TempBuffer[sizeof(SettingValue)/sizeof(CHAR)];
 
-    //
-    // Open the [FreeLoader] section
-    //
-    if (!IniOpenSection("Operating Systems", &SectionId))
-    {
+    /* Open the [Operating Systems] section */
+    if (!IniOpenSection("Operating Systems", &OsSectionId))
         return NULL;
-    }
 
-    //
-    // Count number of operating systems in the section
-    //
-    Count = IniGetNumSectionItems(SectionId);
+    /* Count the number of operating systems in the section */
+    Count = IniGetNumSectionItems(OsSectionId);
 
-    //
-    // Allocate memory to hold operating system lists
-    //
+    /* Allocate memory to hold operating system lists */
     Items = FrLdrHeapAlloc(Count * sizeof(OperatingSystemItem), TAG_OS_ITEM);
     if (!Items)
-    {
         return NULL;
+
+    /* Retrieve which OS is the default one */
+    DefaultOSName = CmdLineGetDefaultOS();
+    if (!DefaultOSName || !*DefaultOSName)
+    {
+        if ((FrLdrSectionId != 0) &&
+            IniReadSettingByName(FrLdrSectionId, "DefaultOS", DefaultOSText, sizeof(DefaultOSText)))
+        {
+            DefaultOSName = DefaultOSText;
+        }
     }
 
-    //
-    // Now loop through and read the operating system section and display names
-    //
-    for (Idx = 0; Idx < Count; Idx++)
+    /* Now loop through the operating system section and load each item */
+    for (i = 0; i < Count; ++i)
     {
-        IniReadSettingByNumber(SectionId, Idx, SettingName, sizeof(SettingName), SettingValue, sizeof(SettingValue));
+        IniReadSettingByNumber(OsSectionId, i,
+                               SettingName, sizeof(SettingName),
+                               SettingValue, sizeof(SettingValue));
+        if (!*SettingName)
+        {
+            ERR("Invalid OS entry %lu, skipping.\n", i);
+            continue;
+        }
 
-        //
-        // Search start and end of the title
-        //
-        OsLoadOptions = NULL;
+        /* Retrieve the start and end of the title */
         TitleStart = SettingValue;
-        while (*TitleStart == ' ' || *TitleStart == '"')
-            TitleStart++;
+        /* Trim any leading whitespace and quotes */
+        while (*TitleStart == ' ' || *TitleStart == '\t' || *TitleStart == '"')
+            ++TitleStart;
         TitleEnd = TitleStart;
-        if (*TitleEnd != ANSI_NULL)
-            TitleEnd++;
+        /* Go up to the first last quote */
         while (*TitleEnd != ANSI_NULL && *TitleEnd != '"')
-            TitleEnd++;
-        if (*TitleEnd != ANSI_NULL)
+            ++TitleEnd;
+
+        /* NULL-terminate the title */
+        if (*TitleEnd)
+            *TitleEnd++ = ANSI_NULL; // Skip the quote too.
+
+        /* Retrieve the options after the quoted title */
+        if (*TitleEnd)
         {
-            *TitleEnd = ANSI_NULL;
-            OsLoadOptions = TitleEnd + 1;
+            /* Trim any trailing whitespace and quotes */
+            while (*TitleEnd == ' ' || *TitleEnd == '\t' || *TitleEnd == '"')
+                ++TitleEnd;
+        }
+        OsLoadOptions = (*TitleEnd ? TitleEnd : NULL);
+
+        // TRACE("\n"
+              // "SettingName   = '%s'\n"
+              // "TitleStart    = '%s'\n"
+              // "OsLoadOptions = '%s'\n",
+              // SettingName, TitleStart, OsLoadOptions);
+
+        /* Find the default OS item while we haven't got one */
+        if (DefaultOSName && _stricmp(DefaultOSName, SettingName) == 0)
+        {
+            DefaultOS = i;
+            DefaultOSName = NULL; // We have found the first one, don't search for others.
+        }
+
+        /*
+         * Determine whether this is a legacy operating system entry of the form:
+         *
+         * [Operating Systems]
+         * ArcOsLoadPartition="LoadIdentifier" /List /of /Options
+         *
+         * and if so, convert it into a new operating system INI entry:
+         *
+         * [Operating Systems]
+         * SectionIdentifier="LoadIdentifier"
+         *
+         * [SectionIdentifier]
+         * BootType=...
+         * SystemPath=ArcOsLoadPartition
+         * Options=/List /of /Options
+         *
+         * The "BootType" value is heuristically determined from the form of
+         * the ArcOsLoadPartition: if this is an ARC path, the "BootType" value
+         * is "Windows", otherwise if this is a DOS path the "BootType" value
+         * is "BootSector". This ensures backwards-compatibility with NTLDR.
+         */
+
+        /* Try to open the operating system section in the .ini file */
+        SectionId = 0;
+        HadSection = IniOpenSection(SettingName, &SectionId);
+        if (HadSection)
+        {
+            /* This is a new OS entry: try to read the boot type */
+            IniReadSettingByName(SectionId, "BootType", BootType, sizeof(BootType));
+        }
+        else
+        {
+            /* This is a legacy OS entry: no explicit BootType specified, we will infer one */
+            *BootType = ANSI_NULL;
+        }
+
+        /* Check whether we have got a BootType value; if not, try to infer one */
+        HadNoBootType = (*BootType == ANSI_NULL);
+        if (HadNoBootType)
+        {
+#ifdef _M_IX86
+            ULONG FileId;
+            if (ArcOpen(SettingName, OpenReadOnly, &FileId) == ESUCCESS)
+            {
+                ArcClose(FileId);
+                strcpy(BootType, "BootSector");
+            }
+            else
+#endif
+            {
+                strcpy(BootType, "Windows");
+            }
+        }
+
+        /* This is a legacy OS entry: convert it into a new OS entry */
+        if (!HadSection)
+        {
+            TIMEINFO* TimeInfo;
+
+            /* Save the system path from the original SettingName (overwritten below) */
+            RtlStringCbCopyA(TempBuffer, sizeof(TempBuffer), SettingName);
+
+            /* Generate a unique section name */
+            TimeInfo = ArcGetTime();
+            if (_stricmp(BootType, "BootSector") == 0)
+            {
+                RtlStringCbPrintfA(SettingName, sizeof(SettingName),
+                                   "BootSectorFile%u%u%u%u%u%u",
+                                   TimeInfo->Year, TimeInfo->Day, TimeInfo->Month,
+                                   TimeInfo->Hour, TimeInfo->Minute, TimeInfo->Second);
+            }
+            else if (_stricmp(BootType, "Windows") == 0)
+            {
+                RtlStringCbPrintfA(SettingName, sizeof(SettingName),
+                                   "Windows%u%u%u%u%u%u",
+                                   TimeInfo->Year, TimeInfo->Day, TimeInfo->Month,
+                                   TimeInfo->Hour, TimeInfo->Minute, TimeInfo->Second);
+            }
+            else
+            {
+                ASSERT(FALSE);
+            }
+
+            /* Add the section */
+            if (!IniAddSection(SettingName, &SectionId))
+            {
+                ERR("Could not convert legacy OS entry %lu, skipping.\n", i);
+                continue;
+            }
+
+            /* Add the system path */
+            if (_stricmp(BootType, "BootSector") == 0)
+            {
+                if (!IniAddSettingValueToSection(SectionId, "BootSectorFile", TempBuffer))
+                {
+                    ERR("Could not convert legacy OS entry %lu, skipping.\n", i);
+                    continue;
+                }
+            }
+            else if (_stricmp(BootType, "Windows") == 0)
+            {
+                if (!IniAddSettingValueToSection(SectionId, "SystemPath", TempBuffer))
+                {
+                    ERR("Could not convert legacy OS entry %lu, skipping.\n", i);
+                    continue;
+                }
+            }
+            else
+            {
+                ASSERT(FALSE);
+            }
+
+            /* Add the OS options */
+            if (OsLoadOptions && !IniAddSettingValueToSection(SectionId, "Options", OsLoadOptions))
+            {
+                ERR("Could not convert legacy OS entry %lu, skipping.\n", i);
+                continue;
+            }
+        }
+
+        /* Add or modify the BootType if needed */
+        if (HadNoBootType && !IniModifySettingValue(SectionId, "BootType", BootType))
+        {
+            ERR("Could not fixup the BootType entry for OS '%s', ignoring.\n", SettingName);
+        }
+
+        /*
+         * If this is a new OS entry, but some options were given appended to
+         * the OS entry item, append them instead to the "Options=" value.
+         */
+        if (HadSection && OsLoadOptions && *OsLoadOptions)
+        {
+            /* Read the original "Options=" value */
+            *TempBuffer = ANSI_NULL;
+            if (!IniReadSettingByName(SectionId, "Options", TempBuffer, sizeof(TempBuffer)))
+                TRACE("No 'Options' value found for OS '%s', ignoring.\n", SettingName);
+
+            /* Concatenate the options together */
+            RtlStringCbCatA(TempBuffer, sizeof(TempBuffer), " ");
+            RtlStringCbCatA(TempBuffer, sizeof(TempBuffer), OsLoadOptions);
+
+            /* Save them */
+            if (!IniModifySettingValue(SectionId, "Options", TempBuffer))
+                ERR("Could not modify the options for OS '%s', ignoring.\n", SettingName);
         }
 
-        //
-        // Copy the system partition, identifier and options
-        //
-        Items[Idx].SystemPartition = CopyString(SettingName);
-        Items[Idx].LoadIdentifier = CopyString(TitleStart);
-        Items[Idx].OsLoadOptions = CopyString(OsLoadOptions);
+        /* Copy the OS section ID and its identifier */
+        Items[i].SectionId = SectionId;
+        Items[i].LoadIdentifier = CopyString(TitleStart);
+        // TRACE("We did Items[%lu]: SectionName = '%s' (SectionId = 0x%p), LoadIdentifier = '%s'\n",
+              // i, SettingName, Items[i].SectionId, Items[i].LoadIdentifier);
     }
 
-    //
-    // Return success
-    //
-    *OperatingSystemCountPointer = Count;
+    /* Return success */
+    *OperatingSystemCount = Count;
+    *DefaultOperatingSystem = DefaultOS;
     return Items;
 }