]> code.delx.au - refind/blobdiff - refind/lib.c
Improved submenu and information menu appearance over a full-screen
[refind] / refind / lib.c
index 048b301e099637e40ae5991802b8b326ff0af2b3..8235feab84d444375ef9febda95084f447f7008d 100644 (file)
@@ -93,8 +93,8 @@ UINTN            VolumesCount = 0;
 #define SAMPLE_SIZE 69632 /* 68 KiB -- ReiserFS superblock begins at 64 KiB */
 
 // Default names for volume badges (mini-icon to define disk type) and icons
-#define VOLUME_BADGE_NAME L".VolumeBadge.icns"
-#define VOLUME_ICON_NAME L".VolumeIcon.icns"
+#define VOLUME_BADGE_NAMES L".VolumeBadge.icns,.VolumeBadge.png"
+#define VOLUME_ICON_NAMES L".VolumeIcon.icns,.VolumeIcon.png"
 
 // functions
 
@@ -469,7 +469,7 @@ static UINT32 IdentifyFilesystemType(IN UINT8 *Buffer, IN UINTN BufferSize) {
    return FoundType;
 }
 
-static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootable)
+static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable)
 {
     EFI_STATUS              Status;
     UINT8                   Buffer[SAMPLE_SIZE];
@@ -624,7 +624,7 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl
         CheckError(Status, L"while reading boot sector");
 #endif
     }
-}
+} /* VOID ScanVolumeBootcode() */
 
 // default volume badge icon based on disk kind
 static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume)
@@ -645,17 +645,18 @@ static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume)
 // Return a string representing the input size in IEEE-1541 units.
 // The calling function is responsible for freeing the allocated memory.
 static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) {
-   float SizeInIeee;
-   UINTN Index = 0;
+   UINT64 SizeInIeee;
+   UINTN Index = 0, NumPrefixes;
    CHAR16 *Units, *Prefixes = L" KMGTPEZ";
    CHAR16 *TheValue;
 
-   TheValue = AllocateZeroPool(sizeof(CHAR16) * 80);
+   TheValue = AllocateZeroPool(sizeof(CHAR16) * 256);
    if (TheValue != NULL) {
-      SizeInIeee = (float) SizeInBytes;
-      while ((SizeInIeee > 1024.0) && (Index < (StrLen(Prefixes) - 1))) {
+      NumPrefixes = StrLen(Prefixes);
+      SizeInIeee = SizeInBytes;
+      while ((SizeInIeee > 1024) && (Index < (NumPrefixes - 1))) {
          Index++;
-         SizeInIeee /= 1024.0;
+         SizeInIeee /= 1024;
       } // while
       if (Prefixes[Index] == ' ') {
          Units = StrDuplicate(L"-byte");
@@ -663,7 +664,7 @@ static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) {
          Units = StrDuplicate(L"  iB");
          Units[1] = Prefixes[Index];
       } // if/else
-      SPrint(TheValue, 79, L"%d%s", (UINTN) SizeInIeee, Units);
+      SPrint(TheValue, 255, L"%ld%s", SizeInIeee, Units);
    } // if
    return TheValue;
 } // CHAR16 *SizeInSIUnits()
@@ -680,7 +681,7 @@ static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) {
    CHAR16                  *SISize, *TypeName;
 
    FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir);
-   if (FileSystemInfoPtr != NULL) {
+   if (FileSystemInfoPtr != NULL) { // we have filesystem information (size, label)....
        if ((FileSystemInfoPtr->VolumeLabel != NULL) && (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) {
           FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel);
        }
@@ -692,7 +693,7 @@ static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) {
           FoundName = NULL;
        } // if rEFInd HFS+ driver suspected
 
-       if (FoundName == NULL) { // filesystem has no name....
+       if (FoundName == NULL) { // filesystem has no name, so use fs type and size
           FoundName = AllocateZeroPool(sizeof(CHAR16) * 256);
           if (FoundName != NULL) {
              SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize);
@@ -703,10 +704,10 @@ static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) {
 
        FreePool(FileSystemInfoPtr);
 
-   } else {
+   } else { // fs driver not returning info; fall back on our own information....
       FoundName = AllocateZeroPool(sizeof(CHAR16) * 256);
       if (FoundName != NULL) {
-         TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; fn returns constant
+         TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant
          if (StrLen(TypeName) > 0)
             SPrint(FoundName, 255, L"%s volume", FSTypeName(Volume->FSType));
          else
@@ -726,7 +727,7 @@ static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) {
    return FoundName;
 } // static CHAR16 *GetVolumeName()
 
-VOID ScanVolume(IN OUT REFIT_VOLUME *Volume)
+VOID ScanVolume(REFIT_VOLUME *Volume)
 {
     EFI_STATUS              Status;
     EFI_DEVICE_PATH         *DevicePath, *NextDevicePath;
@@ -848,11 +849,10 @@ VOID ScanVolume(IN OUT REFIT_VOLUME *Volume)
     Volume->VolName = GetVolumeName(Volume);
 
     // get custom volume icon if present
-    if (FileExists(Volume->RootDir, VOLUME_BADGE_NAME))
-        Volume->VolBadgeImage = LoadIcns(Volume->RootDir, VOLUME_BADGE_NAME, 32);
-    if (FileExists(Volume->RootDir, VOLUME_ICON_NAME)) {
-       Volume->VolIconImage = LoadIcns(Volume->RootDir, VOLUME_ICON_NAME, 128);
-    }
+    if (!Volume->VolBadgeImage)
+       Volume->VolBadgeImage = LoadIcns(Volume->RootDir, VOLUME_BADGE_NAMES, 32);
+    if (!Volume->VolIconImage)
+       Volume->VolIconImage = LoadIcns(Volume->RootDir, VOLUME_ICON_NAMES, 128);
 } // ScanVolume()
 
 static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry)
@@ -915,20 +915,20 @@ static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_I
             }
         }
     }
-}
+} /* VOID ScanExtendedPartition() */
 
 VOID ScanVolumes(VOID)
 {
     EFI_STATUS              Status;
-    UINTN                   HandleCount = 0;
-    UINTN                   HandleIndex;
     EFI_HANDLE              *Handles;
     REFIT_VOLUME            *Volume, *WholeDiskVolume;
-    UINTN                   VolumeIndex, VolumeIndex2;
     MBR_PARTITION_INFO      *MbrTable;
+    UINTN                   HandleCount = 0;
+    UINTN                   HandleIndex;
+    UINTN                   VolumeIndex, VolumeIndex2;
     UINTN                   PartitionIndex;
+    UINTN                   SectorSum, i, VolNumber = 0;
     UINT8                   *SectorBuffer1, *SectorBuffer2;
-    UINTN                   SectorSum, i;
 
     MyFreePool(Volumes);
     Volumes = NULL;
@@ -948,6 +948,10 @@ VOID ScanVolumes(VOID)
         Volume = AllocateZeroPool(sizeof(REFIT_VOLUME));
         Volume->DeviceHandle = Handles[HandleIndex];
         ScanVolume(Volume);
+        if (Volume->IsReadable)
+           Volume->VolNumber = VolNumber++;
+        else
+           Volume->VolNumber = VOL_UNREADABLE;
 
         AddListElement((VOID ***) &Volumes, &VolumesCount, Volume);
 
@@ -1030,7 +1034,7 @@ VOID ScanVolumes(VOID)
             MyFreePool(SectorBuffer2);
         }
 
-    }
+    } // for
 } /* VOID ScanVolumes() */
 
 static VOID UninitVolumes(VOID)
@@ -1315,20 +1319,22 @@ CHAR16 * Basename(IN CHAR16 *Path)
     return FileName;
 }
 
-// Replaces a filename extension of ".efi" with the specified string
-// (Extension). If the input Path doesn't end in ".efi", Extension
-// is added to the existing filename.
-VOID ReplaceEfiExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension)
-{
-    UINTN PathLen;
-
-    PathLen = StrLen(Path);
-    // Note: Do StriCmp() twice to work around Gigabyte Hybrid EFI case-sensitivity bug....
-    if ((PathLen >= 4) && ((StriCmp(&Path[PathLen - 4], L".efi") == 0) || (StriCmp(&Path[PathLen - 4], L".EFI") == 0))) {
-       Path[PathLen - 4] = 0;
-    } // if
-    StrCat(Path, Extension);
-} // VOID ReplaceEfiExtension()
+// Remove the .efi extension from FileName -- for instance, if FileName is
+// "fred.efi", returns "fred". If the filename contains no .efi extension,
+// returns a copy of the original input.
+CHAR16 * StripEfiExtension(CHAR16 *FileName) {
+   UINTN  Length;
+   CHAR16 *Copy = NULL;
+
+   if ((FileName != NULL) && ((Copy = StrDuplicate(FileName)) != NULL)) {
+      Length = StrLen(Copy);
+      // Note: Do StriCmp() twice to work around Gigabyte Hybrid EFI case-sensitivity bug....
+      if ((Length >= 4) && ((StriCmp(&Copy[Length - 4], L".efi") == 0) || (StriCmp(&Copy[Length - 4], L".EFI") == 0))) {
+         Copy[Length - 4] = 0;
+      } // if
+   } // if
+   return Copy;
+} // CHAR16 * StripExtension()
 
 //
 // memory string search
@@ -1375,8 +1381,8 @@ BOOLEAN StriSubCmp(IN CHAR16 *SmallStr, IN CHAR16 *BigStr) {
 
 // Merges two strings, creating a new one and returning a pointer to it.
 // If AddChar != 0, the specified character is placed between the two original
-// strings (unless the first string is NULL). The original input string
-// *First is de-allocated and replaced by the new merged string.
+// strings (unless the first string is NULL or empty). The original input
+// string *First is de-allocated and replaced by the new merged string.
 // This is similar to StrCat, but safer and more flexible because
 // MergeStrings allocates memory that's the correct size for the
 // new merged string, so it can take a NULL *First and it cleans
@@ -1392,6 +1398,10 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar) {
       Length2 = StrLen(Second);
    NewString = AllocatePool(sizeof(CHAR16) * (Length1 + Length2 + 2));
    if (NewString != NULL) {
+      if ((*First != NULL) && (StrLen(*First) == 0)) {
+         MyFreePool(*First);
+         *First = NULL;
+      }
       NewString[0] = L'\0';
       if (*First != NULL) {
          StrCat(NewString, *First);
@@ -1515,6 +1525,39 @@ VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **Devi
    MyFreePool(DeviceString);
 } // VOID FindVolumeAndFilename()
 
+// Splits a volume/filename string (e.g., "fs0:\EFI\BOOT") into separate
+// volume and filename components (e.g., "fs0" and "\EFI\BOOT"), returning
+// the filename component in the original *Path variable and the split-off
+// volume component in the *VolName variable.
+// Returns TRUE if both components are found, FALSE otherwise.
+BOOLEAN SplitVolumeAndFilename(IN OUT CHAR16 **Path, OUT CHAR16 **VolName) {
+   UINTN i = 0, Length;
+   CHAR16 *Filename;
+
+   if (*Path == NULL)
+      return FALSE;
+
+   if (*VolName != NULL) {
+      MyFreePool(*VolName);
+      *VolName = NULL;
+   }
+
+   Length = StrLen(*Path);
+   while ((i < Length) && ((*Path)[i] != L':')) {
+      i++;
+   } // while
+
+   if (i < Length) {
+      Filename = StrDuplicate((*Path) + i + 1);
+      (*Path)[i] = 0;
+      *VolName = *Path;
+      *Path = Filename;
+      return TRUE;
+   } else {
+      return FALSE;
+   }
+} // BOOLEAN SplitVolumeAndFilename()
+
 // Returns all the digits in the input string, including intervening
 // non-digit characters. For instance, if InString is "foo-3.3.4-7.img",
 // this function returns "3.3.4-7". If InString contains no digits,
@@ -1598,7 +1641,7 @@ BOOLEAN IsIn(IN CHAR16 *SmallString, IN CHAR16 *List) {
 
 // Implement FreePool the way it should have been done to begin with, so that
 // it doesn't throw an ASSERT message if fed a NULL pointer....
-VOID MyFreePool(IN OUT VOID *Pointer) {
+VOID MyFreePool(IN VOID *Pointer) {
    if (Pointer != NULL)
       FreePool(Pointer);
 }
@@ -1631,18 +1674,18 @@ BOOLEAN EjectMedia(VOID) {
 } // VOID EjectMedia()
 
 
-// // Return the GUID as a string, suitable for display to the user. Note that the calling
-// // function is responsible for freeing the allocated memory.
-// CHAR16 * GuidAsString(EFI_GUID *GuidData) {
-//    CHAR16 *TheString;
-// 
-//    TheString = AllocateZeroPool(42 * sizeof(CHAR16));
-//    if (TheString != 0) {
-//       SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
-//               (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3,
-//               (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2],
-//               (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5],
-//               (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]);
-//    }
-//    return TheString;
-// } // GuidAsString(EFI_GUID *GuidData)
+// Return the GUID as a string, suitable for display to the user. Note that the calling
+// function is responsible for freeing the allocated memory.
+CHAR16 * GuidAsString(EFI_GUID *GuidData) {
+   CHAR16 *TheString;
+
+   TheString = AllocateZeroPool(42 * sizeof(CHAR16));
+   if (TheString != 0) {
+      SPrint (TheString, 82, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+              (UINTN)GuidData->Data1, (UINTN)GuidData->Data2, (UINTN)GuidData->Data3,
+              (UINTN)GuidData->Data4[0], (UINTN)GuidData->Data4[1], (UINTN)GuidData->Data4[2],
+              (UINTN)GuidData->Data4[3], (UINTN)GuidData->Data4[4], (UINTN)GuidData->Data4[5],
+              (UINTN)GuidData->Data4[6], (UINTN)GuidData->Data4[7]);
+   }
+   return TheString;
+} // GuidAsString(EFI_GUID *GuidData)