/*
 * refit/scan/loader.c
 *
 * Copyright (c) 2006-2010 Christoph Pfisterer
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *  * Neither the name of Christoph Pfisterer nor the names of the
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <Platform.h>
#include "../refit/lib.h"
#include "../libeg/libeg.h"
#include "loader.h"
#include "../cpp_foundation/XString.h"
#include "entry_scan.h"
#include "../Platform/Settings.h"
#include "../Platform/Hibernate.h"
#include "../refit/screen.h"
#include "../refit/menu.h"
#include "../Platform/Nvram.h"
#include "../Platform/APFS.h"
#include "../Platform/guid.h"
#include "../refit/lib.h"
#include "../gui/REFIT_MENU_SCREEN.h"
#include "../gui/REFIT_MAINMENU_SCREEN.h"
#include "../Settings/Self.h"
#include "../include/OSTypes.h"
#include "../Platform/BootOptions.h"
#include "../Platform/Volumes.h"
#include "../include/OSFlags.h"
#include "../libeg/XTheme.h"

#ifndef DEBUG_ALL
#define DEBUG_SCAN_LOADER 1
#else
#define DEBUG_SCAN_LOADER DEBUG_ALL
#endif

#if DEBUG_SCAN_LOADER == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_SCAN_LOADER, __VA_ARGS__)
#endif

const XStringW MACOSX_LOADER_PATH = L"\\System\\Library\\CoreServices\\boot.efi"_XSW;

const XStringW LINUX_ISSUE_PATH = L"\\etc\\issue"_XSW;
#define LINUX_BOOT_PATH L"\\boot"
#define LINUX_BOOT_ALT_PATH L"\\boot"
const XString8 LINUX_LOADER_PATH = "vmlinuz"_XS8;
const XStringW LINUX_FULL_LOADER_PATH = SWPrintf("%ls\\%s", LINUX_BOOT_PATH, LINUX_LOADER_PATH.c_str());
#define LINUX_LOADER_SEARCH_PATH L"vmlinuz*"
const XString8Array LINUX_DEFAULT_OPTIONS = Split<XString8Array>("ro add_efi_memmap quiet splash vt.handoff=7", " ");

#if defined(MDE_CPU_X64)
#define BOOT_LOADER_PATH L"\\EFI\\BOOT\\BOOTX64.efi"_XSW
#else
#define BOOT_LOADER_PATH L"\\EFI\\BOOT\\BOOTIA32.efi"_XSW
#endif


//extern LOADER_ENTRY *SubMenuKextInjectMgmt(LOADER_ENTRY *Entry);

// Linux loader path data
typedef struct LINUX_PATH_DATA
{
   CONST XStringW Path;
   CONST XStringW Title;
   CONST XStringW Icon;
   CONST XString8 Issue;
} LINUX_PATH_DATA;

typedef struct LINUX_ICON_DATA
{
   CONST CHAR16 *DirectoryName;
   CONST CHAR16 *IconName;
} LINUX_ICON_MAPPING;

STATIC LINUX_ICON_DATA LinuxIconMapping[] = {
    { L"LinuxMint", L"mint" },
    { L"arch_grub", L"arch" },
    { L"opensuse", L"suse" },
    { L"ORACLE", L"solaris" },
    { L"elementary", L"eos" },
    { L"pclinuxos", L"pclinux" },
    { L"mx18", L"mx" },
    { L"mx19", L"mx" }
};
STATIC CONST UINTN LinuxIconMappingCount = (sizeof(LinuxIconMapping) / sizeof(LinuxIconMapping[0]));

STATIC LINUX_PATH_DATA LinuxEntryData[] = {
#if defined(MDE_CPU_X64)
  //comment out all common names
//  { L"\\EFI\\grub\\grubx64.efi", L"Grub EFI boot menu", L"grub,linux" },
//  { L"\\EFI\\Gentoo\\grubx64.efi", L"Gentoo EFI boot menu", L"gentoo,linux", "Gentoo" },
  { L"\\EFI\\Gentoo\\kernelx64.efi"_XSW, L"Gentoo EFI kernel"_XSW, L"gentoo,linux"_XSW, ""_XS8 },
//  { L"\\EFI\\RedHat\\grubx64.efi", L"RedHat EFI boot menu", L"redhat,linux", "Redhat" },
//  { L"\\EFI\\debian\\grubx64.efi", L"Debian EFI boot menu", L"debian,linux", "Debian" },
//  { L"\\EFI\\kali\\grubx64.efi", L"Kali EFI boot menu", L"kali,linux", "Kali" },
//  { L"\\EFI\\ubuntu\\grubx64.efi", L"Ubuntu EFI boot menu", L"ubuntu,linux", "Ubuntu" },
//  { L"\\EFI\\kubuntu\\grubx64.efi", L"kubuntu EFI boot menu", L"kubuntu,linux", "kubuntu" },
//  { L"\\EFI\\LinuxMint\\grubx64.efi", L"Linux Mint EFI boot menu", L"mint,linux", "Linux Mint" },
//  { L"\\EFI\\Fedora\\grubx64.efi", L"Fedora EFI boot menu", L"fedora,linux", "Fedora" },
//  { L"\\EFI\\opensuse\\grubx64.efi", L"OpenSuse EFI boot menu", L"suse,linux", "openSUSE" },
//  { L"\\EFI\\arch\\grubx64.efi", L"ArchLinux EFI boot menu", L"arch,linux" },
//  { L"\\EFI\\arch_grub\\grubx64.efi", L"ArchLinux EFI boot menu", L"arch,linux" },
//  { L"\\EFI\\ORACLE\\grubx64.efi", L"Oracle Solaris EFI boot menu", L"solaris,linux", "Solaris" },
//  { L"\\EFI\\Endless\\grubx64.efi", L"EndlessOS EFI boot menu", L"endless,linux", "EndlessOS" },
//  { L"\\EFI\\antergos_grub\\grubx64.efi", L"Antergos EFI boot menu", L"antergos,linux", "Antergos" },
//  { L"\\EFI\\Deepin\\grubx64.efi", L"Deepin EFI boot menu", L"deepin,linux", "Deepin" },
//  { L"\\EFI\\elementary\\grubx64.efi", L"Elementary EFI boot menu", L"eos,linux", "Elementary" },
//  { L"\\EFI\\Manjaro\\grubx64.efi", L"Manjaro EFI boot menu", L"manjaro,linux", "Manjaro" },
//  { L"\\EFI\\xubuntu\\grubx64.efi", L"Xubuntu EFI boot menu", L"xubuntu,linux", "Xubuntu" },
//  { L"\\EFI\\zorin\\grubx64.efi", L"Zorin EFI boot menu", L"zorin,linux", "Zorin" },
  { L"\\EFI\\goofiboot\\goofibootx64.efi"_XSW, L"Solus EFI boot menu"_XSW, L"solus,linux"_XSW, "Solus"_XS8 },
//  { L"\\EFI\\centos\\grubx64.efi", L"CentOS EFI boot menu", L"centos,linux", "CentOS" },
//  { L"\\EFI\\pclinuxos\\grubx64.efi", L"PCLinuxOS EFI boot menu", L"pclinux,linux", "PCLinux" },
//  { L"\\EFI\\neon\\grubx64.efi", L"KDE Neon EFI boot menu", L"neon,linux", "KDE Neon" },
//  { L"\\EFI\\MX19\\grubx64.efi", L"MX Linux EFI boot menu", L"mx,linux", "MX Linux" },
//  { L"\\EFI\\parrot\\grubx64.efi", L"Parrot OS EFI boot menu", L"parrot,linux", "Parrot OS" },
#else
  //dont bother about 32bit compilation
  { L"\\EFI\\grub\\grub.efi", L"Grub EFI boot menu", L"grub,linux" },
  { L"\\EFI\\Gentoo\\grub.efi", L"Gentoo EFI boot menu", L"gentoo,linux", "Gentoo" },
  { L"\\EFI\\Gentoo\\kernel.efi", L"Gentoo EFI kernel", L"gentoo,linux" },
  { L"\\EFI\\RedHat\\grub.efi", L"RedHat EFI boot menu", L"redhat,linux", "Redhat" },
  { L"\\EFI\\debian\\grub.efi", L"Debian EFI boot menu", L"debian,linux", "Debian" },
  { L"\\EFI\\kali\\grub.efi", L"Kali EFI boot menu", L"kali,linux", "Kali" },
  { L"\\EFI\\ubuntu\\grub.efi", L"Ubuntu EFI boot menu", L"ubuntu,linux", "Ubuntu" },
  { L"\\EFI\\kubuntu\\grub.efi", L"kubuntu EFI boot menu", L"kubuntu,linux", "kubuntu" },
  { L"\\EFI\\LinuxMint\\grub.efi", L"Linux Mint EFI boot menu", L"mint,linux", "Linux Mint" },
  { L"\\EFI\\Fedora\\grub.efi", L"Fedora EFI boot menu", L"fedora,linux", "Fedora" },
  { L"\\EFI\\opensuse\\grub.efi", L"OpenSuse EFI boot menu", L"suse,linux", "openSUSE" },
  { L"\\EFI\\arch\\grub.efi", L"ArchLinux EFI boot menu", L"arch,linux" },
  { L"\\EFI\\arch_grub\\grub.efi", L"ArchLinux EFI boot menu", L"arch,linux" },
  { L"\\EFI\\ORACLE\\grub.efi", L"Oracle Solaris EFI boot menu", L"solaris,linux", "Solaris" },
  { L"\\EFI\\Endless\\grub.efi", L"EndlessOS EFI boot menu", L"endless,linux", "EndlessOS" },
  { L"\\EFI\\antergos_grub\\grub.efi", L"Antergos EFI boot menu", L"antergos,linux", "Antergos" },
  { L"\\EFI\\Deepin\\grub.efi", L"Deepin EFI boot menu", L"deepin,linux", "Deepin" },
  { L"\\EFI\\elementary\\grub.efi", L"Elementary EFI boot menu", L"eos,linux", "Elementary" },
  { L"\\EFI\\Manjaro\\grub.efi", L"Manjaro EFI boot menu", L"manjaro,linux", "Manjaro" },
  { L"\\EFI\\xubuntu\\grub.efi", L"Xubuntu EFI boot menu", L"xubuntu,linux", "Xubuntu" },
  { L"\\EFI\\zorin\\grub.efi", L"Zorin EFI boot menu", L"zorin,linux", "Zorin" },
  { L"\\EFI\\goofiboot\\goofiboot.efi", L"Solus EFI boot menu", L"solus,linux", "Solus" },
  { L"\\EFI\\centos\\grub.efi", L"CentOS EFI boot menu", L"centos,linux", "CentOS" },
  { L"\\EFI\\pclinuxos\\grub.efi", L"PCLinuxOS EFI boot menu", L"pclinux,linux", "PCLinux" },
  { L"\\EFI\\neon\\grub.efi", L"KDE Neon EFI boot menu", L"neon,linux", "KDE Neon" },
  { L"\\EFI\\MX19\\grub.efi", L"MX Linux EFI boot menu", L"mx,linux", "MX Linux" },
  { L"\\EFI\\parrot\\grub.efi", L"Parrot OS EFI boot menu", L"parrot,linux", "Parrot OS" },
#endif
  { L"\\EFI\\SuSe\\elilo.efi"_XSW, L"OpenSuse EFI boot menu"_XSW, L"suse,linux"_XSW, ""_XS8 },
};
STATIC CONST UINTN LinuxEntryDataCount = (sizeof(LinuxEntryData) / sizeof(LinuxEntryData[0]));

#if defined(ANDX86)
#if !defined(MDE_CPU_X64)
#undef ANDX86
#else
// ANDX86 loader path data
#define ANDX86_FINDLEN 3
typedef struct ANDX86_PATH_DATA
{
   CONST XStringW Path;
   CONST XStringW Title;
   CONST XStringW Icon;
   CONST XStringW Find[ANDX86_FINDLEN];
} ANDX86_PATH_DATA;

STATIC ANDX86_PATH_DATA AndroidEntryData[] = {
  //{ L"\\EFI\\boot\\grubx64.efi", L"Grub", L"grub,linux" },
  //{ L"\\EFI\\boot\\bootx64.efi", L"Grub", L"grub,linux" },
  { L"\\EFI\\remixos\\grubx64.efi"_XSW,         L"Remix"_XSW,     L"remix,grub,linux"_XSW,   { L"\\isolinux\\isolinux.bin"_XSW, L"\\initrd.img"_XSW, L"\\kernel"_XSW } },
  { L"\\EFI\\PhoenixOS\\boot\\grubx64.efi"_XSW, L"PhoenixOS"_XSW, L"phoenix,grub,linux"_XSW, { L"\\EFI\\PhoenixOS\\boot\\efi.img"_XSW, L"\\EFI\\PhoenixOS\\initrd.img"_XSW, L"\\EFI\\PhoenixOS\\kernel"_XSW } },
  { L"\\EFI\\boot\\grubx64.efi"_XSW,            L"Phoenix"_XSW,   L"phoenix,grub,linux"_XSW, { L"\\phoenix\\kernel"_XSW, L"\\phoenix\\initrd.img"_XSW, L"\\phoenix\\ramdisk.img"_XSW } },
  { L"\\EFI\\boot\\bootx64.efi"_XSW,            L"Chrome"_XSW,    L"chrome,grub,linux"_XSW,  { L"\\syslinux\\vmlinuz.A"_XSW, L"\\syslinux\\vmlinuz.B"_XSW, L"\\syslinux\\ldlinux.sys"_XSW} },

};
STATIC CONST UINTN AndroidEntryDataCount = (sizeof(AndroidEntryData) / sizeof(ANDX86_PATH_DATA));
#endif
#endif

CONST XStringW PaperBoot   = L"\\com.apple.boot.P\\boot.efi"_XSW;
CONST XStringW RockBoot    = L"\\com.apple.boot.R\\boot.efi"_XSW;
CONST XStringW ScissorBoot = L"\\com.apple.boot.S\\boot.efi"_XSW;

// OS X installer paths
CONST XStringW OSXInstallerPaths[] = {
  L"\\.IABootFiles\\boot.efi"_XSW, // 10.9 - 10.13.3
  L"\\Mac OS X Install Data\\boot.efi"_XSW, // 10.7
  L"\\OS X Install Data\\boot.efi"_XSW, // 10.8 - 10.11
  L"\\macOS Install Data\\boot.efi"_XSW, // 10.12 - 10.12.3
  L"\\macOS Install Data\\Locked Files\\Boot Files\\boot.efi"_XSW, // 10.12.4-10.15
  L"\\macOS Install Data\\Locked Files\\boot.efi"_XSW // 10.16+
};

STATIC CONST UINTN OSXInstallerPathsCount = (sizeof(OSXInstallerPaths) / sizeof(OSXInstallerPaths[0]));

STATIC INTN TimeCmp(IN EFI_TIME *Time1,
                    IN EFI_TIME *Time2)
{
   INTN Comparison;
   if (Time1 == NULL) {
     if (Time2 == NULL) {
       return 0;
     } else {
       return -1;
     }
   } else if (Time2 == NULL) {
     return 1;
   }
   Comparison = Time1->Year - Time2->Year;
   if (Comparison == 0) {
     Comparison = Time1->Month - Time2->Month;
     if (Comparison == 0) {
       Comparison = Time1->Day - Time2->Day;
       if (Comparison == 0) {
         Comparison = Time1->Hour - Time2->Hour;
         if (Comparison == 0) {
           Comparison = Time1->Minute - Time2->Minute;
           if (Comparison == 0) {
             Comparison = Time1->Second - Time2->Second;
             if (Comparison == 0) {
               Comparison = Time1->Nanosecond - Time2->Nanosecond;
             }
           }
         }
       }
     }
   }
   return Comparison;
}

UINT8 GetOSTypeFromPath(IN CONST XStringW& Path)
{
  if (Path.isEmpty()) {
    return OSTYPE_OTHER;
  }
  if ( Path.isEqualIC(MACOSX_LOADER_PATH)) {
    return OSTYPE_OSX;
  } else if ( Path.isEqualIC(OSXInstallerPaths[0]) ||
             ( Path.isEqualIC(OSXInstallerPaths[1])) ||
             ( Path.isEqualIC(OSXInstallerPaths[2])) ||
             ( Path.isEqualIC(OSXInstallerPaths[3])) ||
             ( Path.isEqualIC(OSXInstallerPaths[4])) ||
             ( Path.isEqualIC(RockBoot)) || ( Path.isEqualIC(PaperBoot)) || ( Path.isEqualIC(ScissorBoot)) /* ||
             (! Path.isEqualIC(L"\\.IABootFiles\\boot.efi") &&
            	Path.isEqualIC(L"\\.IAPhysicalMedia")) */ //what is it???
             ) {
    return OSTYPE_OSX_INSTALLER;
  } else if ( Path.isEqualIC(L"\\com.apple.recovery.boot\\boot.efi")) {
    return OSTYPE_RECOVERY;
  } else if (( Path.isEqualIC(L"\\EFI\\Microsoft\\Boot\\bootmgfw-orig.efi")) || //test first as orig
             ( Path.isEqualIC(L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi")) ||      //it can be Clover
    //         ( Path.isEqualIC(L"\\bootmgr.efi")) || //never worked, just extra icon in menu
             ( Path.isEqualIC(L"\\EFI\\MICROSOFT\\BOOT\\cdboot.efi"))) {
    return OSTYPE_WINEFI;
  } else if (LINUX_FULL_LOADER_PATH.isEqualIC(Path)) {
    return OSTYPE_LINEFI;
  } else if ( Path.containsIC("grubx64.efi") ) {
    return OSTYPE_LINEFI;
  } else {
    UINTN Index;
#if defined(ANDX86)
    Index = 0;
    while (Index < AndroidEntryDataCount) {
      if ( Path.isEqualIC(AndroidEntryData[Index].Path) ) {
        return OSTYPE_LIN;
      }
      ++Index;
    }
#endif
    Index = 0;
    while (Index < LinuxEntryDataCount) {
      if ( Path.isEqualIC(LinuxEntryData[Index].Path) ) {
        return OSTYPE_LIN;
      }
      ++Index;
    }
  }
  return OSTYPE_OTHER;
}

static const XStringW linux = L"linux"_XSW;

STATIC CONST XStringW& LinuxIconNameFromPath(IN CONST XStringW& Path,
                       const EFI_FILE_PROTOCOL *RootDir)
{
  UINTN Index;
#if defined(ANDX86)
  Index = 0;
  while (Index < AndroidEntryDataCount) {
    if ( Path.isEqualIC(AndroidEntryData[Index].Path) ) {
      return AndroidEntryData[Index].Icon;
    }
    ++Index;
  }
#endif
  
  //check not common names
  Index = 0;
  while (Index < LinuxEntryDataCount) {
    if ( Path.isEqualIC(LinuxEntryData[Index].Path) ) {
      return LinuxEntryData[Index].Icon;
    }
    ++Index;
  }
  
  // Try to open the linux issue
  if ((RootDir != NULL) && LINUX_FULL_LOADER_PATH.isEqualIC(Path)) {
    CHAR8 *Issue = NULL;
    UINTN  IssueLen = 0;
    if (!EFI_ERROR(egLoadFile(RootDir, LINUX_ISSUE_PATH.wc_str(), (UINT8 **)&Issue, &IssueLen)) && (Issue != NULL)) {
      if (IssueLen > 0) {
        for (Index = 0; Index < LinuxEntryDataCount; ++Index) {
          if ( LinuxEntryData[Index].Issue.notEmpty()  &&  AsciiStrStr(Issue, LinuxEntryData[Index].Issue.c_str()) != NULL ) {
            FreePool(Issue);
            return LinuxEntryData[Index].Icon;
          }
        }
      }
      FreePool(Issue);
    }
  }
  return linux;
}

STATIC CONST XString8 LinuxInitImagePath[] = {
   "initrd%s"_XS8,
   "initrd.img%s"_XS8,
   "initrd%s.img"_XS8,
   "initramfs%s"_XS8,
   "initramfs.img%s"_XS8,
   "initramfs%s.img"_XS8,
};
STATIC CONST UINTN LinuxInitImagePathCount = (sizeof(LinuxInitImagePath) / sizeof(LinuxInitImagePath[0]));

STATIC XString8Array LinuxKernelOptions(const EFI_FILE_PROTOCOL *Dir,
                                  IN CONST CHAR16            *Version,
                                  IN CONST CHAR16            *PartUUID,
                                  IN CONST XString8Array&           Options OPTIONAL)
{
  UINTN Index = 0;
  if ((Dir == NULL) || (PartUUID == NULL)) {
    return Options;
  }
  while (Index < LinuxInitImagePathCount) {
    XStringW InitRd = SWPrintf(LinuxInitImagePath[Index++].c_str(), (Version == NULL) ? L"" : Version);
    if (InitRd.notEmpty()) {
      if (FileExists(Dir, InitRd)) {
        XString8Array CustomOptions;
        CustomOptions.Add(S8Printf("root=/dev/disk/by-partuuid/%ls", PartUUID));
        CustomOptions.Add(S8Printf("initrd=%ls\\%ls", LINUX_BOOT_ALT_PATH, InitRd.wc_str()));
        CustomOptions.import(LINUX_DEFAULT_OPTIONS);
        CustomOptions.import(Options);
        return CustomOptions;
      }
    }
  }
  XString8Array CustomOptions;
  CustomOptions.Add(S8Printf("root=/dev/disk/by-partuuid/%ls", PartUUID));
  CustomOptions.import(LINUX_DEFAULT_OPTIONS);
  CustomOptions.import(Options);
  return CustomOptions;
}

STATIC XBool isFirstRootUUID(REFIT_VOLUME *Volume)
{
  UINTN         VolumeIndex;
  REFIT_VOLUME *scanedVolume;

  for (VolumeIndex = 0; VolumeIndex < Volumes.size(); VolumeIndex++) {
    scanedVolume = &Volumes[VolumeIndex];

    if (scanedVolume == Volume)
      return true;

    if ( scanedVolume->RootUUID == Volume->RootUUID )
      return false;

  }
  return true;
}

//Set Entry->VolName to .disk_label.contentDetails if it exists
STATIC EFI_STATUS GetOSXVolumeName(LOADER_ENTRY *Entry)
{
  EFI_STATUS  Status = EFI_NOT_FOUND;
  CONST CHAR16* targetNameFile = L"\\System\\Library\\CoreServices\\.disk_label.contentDetails";
  CHAR8*  fileBuffer;
  UINTN   fileLen = 0;
  if(FileExists(Entry->Volume->RootDir, targetNameFile)) {
    Status = egLoadFile(Entry->Volume->RootDir, targetNameFile, (UINT8 **)&fileBuffer, &fileLen);
    if(!EFI_ERROR(Status)) {
//      CHAR16  *tmpName;
      //Create null terminated string
//      targetString = (CHAR8*) A_llocateZeroPool(fileLen+1);
//      CopyMem( (void*)targetString, (void*)fileBuffer, fileLen);
//      DBG("found disk_label with contents:%s\n", targetString);

      //      NOTE: Sothor - This was never run. If we need this correct it and uncomment it.
      //      if (Entry->LoaderType == OSTYPE_OSX) {
      //        INTN i;
      //        //remove occurence number. eg: "vol_name 2" --> "vol_name"
      //        i=fileLen-1;
      //        while ((i>0) && (targetString[i]>='0') && (targetString[i]<='9')) {
      //          i--;
      //        }
      //        if (targetString[i] == ' ') {
      //          targetString[i] = 0;
      //        }
      //      }

//      //Convert to Unicode
//      tmpName = (CHAR16*)A_llocateZeroPool((fileLen+1)*2);
//      AsciiStrToUnicodeStrS(targetString, tmpName, fileLen);

      Entry->DisplayedVolName.strncpy(fileBuffer, fileLen);
      DBG("Created name:%ls\n", Entry->DisplayedVolName.wc_str());

      FreePool(fileBuffer);
    }
  }
  return Status;
}


MacOsVersion GetOSVersion(int LoaderType, const EFI_GUID& APFSTargetUUID, const REFIT_VOLUME* Volume, XString8* BuildVersionPtr)
{
  XString8   OSVersion;
  XString8   BuildVersion;
  EFI_STATUS Status      = EFI_NOT_FOUND;
  CHAR8*     PlistBuffer = NULL;
  UINTN      PlistLen;
  TagDict*     Dict        = NULL;
  const TagDict*     DictPointer = NULL;
  const TagStruct*     Prop        = NULL;

  if ( !Volume ) {
    return NullXString8;
  }

  if (OSTYPE_IS_OSX(LoaderType))
  {
    XString8 uuidPrefix;
    if ( APFSTargetUUID.notNull() ) uuidPrefix = S8Printf("\\%s", APFSTargetUUID.toXString8().c_str());

    XStringW plist = SWPrintf("%s\\System\\Library\\CoreServices\\SystemVersion.plist", uuidPrefix.c_str());
    if ( !FileExists(Volume->RootDir, plist) ) {
      plist = SWPrintf("%s\\System\\Library\\CoreServices\\ServerVersion.plist", uuidPrefix.c_str());
      if ( !FileExists(Volume->RootDir, plist) ) {
        plist.setEmpty();
      }
    }

    if ( plist.notEmpty() ) { // found macOS System
      Status = egLoadFile(Volume->RootDir, plist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
      if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
        Prop = Dict->propertyForKey("ProductVersion");
        if ( Prop != NULL ) {
          if ( !Prop->isString() ) {
            MsgLog("ATTENTION : property not string in ProductVersion\n");
          }else{
            if( Prop->getString()->stringValue().notEmpty() ) {
              OSVersion = Prop->getString()->stringValue();
            }
          }
        }
        Prop = Dict->propertyForKey("ProductBuildVersion");
        if ( Prop != NULL ) {
          if ( !Prop->isString() ) {
            MsgLog("ATTENTION : property not string in ProductBuildVersion\n");
          }else{
            if( Prop->getString()->stringValue().notEmpty() ) {
              BuildVersion = Prop->getString()->stringValue();
            }
          }
        }
        Dict->FreeTag();
      }
    }
  }

  if (OSTYPE_IS_OSX_INSTALLER (LoaderType)) {
    // Detect exact version for 2nd stage Installer (thanks to dmazar for this idea)
    // This should work for most installer cases. Rest cases will be read from boot.efi before booting.
    // Reworked by Sherlocks. 2018.04.12

    // 1st stage - 1
    // Check for plist - createinstallmedia/BaseSystem/InstallDVD/InstallESD

    XStringW InstallerPlist;

    if ( APFSTargetUUID.notNull() ) {
      InstallerPlist = SWPrintf("%s\\System\\Library\\CoreServices\\SystemVersion.plist", APFSTargetUUID.toXString8().c_str());
      if ( !FileExists(Volume->RootDir, InstallerPlist) ) InstallerPlist.setEmpty();
    }

    if ( InstallerPlist.isEmpty() ) {
      InstallerPlist = SWPrintf("\\.IABootFilesSystemVersion.plist"); // 10.9 - 10.13.3
      if (!FileExists(Volume->RootDir, InstallerPlist) && FileExists (Volume->RootDir, L"\\System\\Library\\CoreServices\\boot.efi") &&
          ((FileExists(Volume->RootDir, L"\\BaseSystem.dmg") && FileExists (Volume->RootDir, L"\\mach_kernel")) || // 10.7/10.8
           FileExists(Volume->RootDir, L"\\System\\Installation\\CDIS\\Mac OS X Installer.app") || // 10.6/10.7
           FileExists(Volume->RootDir, L"\\System\\Installation\\CDIS\\OS X Installer.app") || // 10.8 - 10.11
           FileExists(Volume->RootDir, L"\\System\\Installation\\CDIS\\macOS Installer.app") || // 10.12+
           FileExists(Volume->RootDir, L"\\.IAPhysicalMedia"))) { // 10.13.4+
        InstallerPlist = SWPrintf("\\System\\Library\\CoreServices\\SystemVersion.plist");
      }
    }
    if (FileExists (Volume->RootDir, InstallerPlist)) {
      Status = egLoadFile(Volume->RootDir, InstallerPlist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
      if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
        Prop = Dict->propertyForKey("ProductVersion");
        if ( Prop != NULL ) {
          if ( !Prop->isString() ) {
            MsgLog("ATTENTION : property not string in ProductVersion\n");
          }else{
            if( Prop->getString()->stringValue().notEmpty() ) {
              OSVersion = Prop->getString()->stringValue();
            }
          }
        }
        Prop = Dict->propertyForKey("ProductBuildVersion");
        if ( Prop != NULL ) {
          if ( !Prop->isString() ) {
            MsgLog("ATTENTION : property not string in ProductBuildVersion\n");
          }else{
            if( Prop->getString()->stringValue().notEmpty() ) {
              BuildVersion = Prop->getString()->stringValue();
            }
          }
        }
        Dict->FreeTag();
      }
    }

//    if ( OSVersion.isEmpty() )
//    {
//      if ( FileExists(Volume->RootDir, SWPrintf("\\%ls\\com.apple.installer\\BridgeVersion.plist", APFSTargetUUID.wc_str()).wc_str()) ) {
//        OSVersion = "11.0"_XS8;
//        // TODO so far, is there is a BridgeVersion.plist, it's version 11.0. Has to be improved with next releases.
//      }
//    }

    // 1st stage - 2
    // Check for plist - createinstallmedia/NetInstall
    if (OSVersion.isEmpty()) {
      InstallerPlist = SWPrintf("\\.IABootFiles\\com.apple.Boot.plist"); // 10.9 - ...
      if (FileExists (Volume->RootDir, InstallerPlist)) {
        Status = egLoadFile(Volume->RootDir, InstallerPlist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
        if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
          Prop = Dict->propertyForKey("Kernel Flags");
          if ( Prop != NULL ) {
            if ( !Prop->isString() ) {
              MsgLog("ATTENTION : property not string in Kernel Flags\n");
            }else{
              if ( Prop->getString()->stringValue().contains("Install%20macOS%20BigSur") || Prop->getString()->stringValue().contains("Install%20macOS%2011")) {
                OSVersion = "11"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%20Monterey") || Prop->getString()->stringValue().contains("Install%20macOS%2012")) {
                OSVersion = "12"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%20Ventura") || Prop->getString()->stringValue().contains("Install%20macOS%2013")) {
                OSVersion = "13"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%2010.16")) {
                OSVersion = "10.16"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%20Catalina") || Prop->getString()->stringValue().contains("Install%20macOS%2010.15")) {
                OSVersion = "10.15"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%20Mojave") || Prop->getString()->stringValue().contains("Install%20macOS%2010.14")) {
                OSVersion = "10.14"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%20High%20Sierra") || Prop->getString()->stringValue().contains("Install%20macOS%2010.13")) {
                OSVersion = "10.13"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20macOS%20Sierra") || Prop->getString()->stringValue().contains("Install%20OS%20hhX%2010.12")) {
                OSVersion = "10.12"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20OS%20hhX%20El%20Capitan") || Prop->getString()->stringValue().contains("Install%20OS%20hhX%2010.11")) {
                OSVersion = "10.11"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20OS%20hhX%20Yosemite") || Prop->getString()->stringValue().contains("Install%20OS%20hhX%2010.10")) {
                OSVersion = "10.10"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20OS%20hhX%20Mavericks.app")) {
                OSVersion = "10.9"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20OS%20hhX%20Mountain%20Lion")) {
                OSVersion = "10.8"_XS8;
              } else if ( Prop->getString()->stringValue().contains("Install%20Mac%20OS%20hhX%20Lion")) {
                OSVersion = "10.7"_XS8;
              }
            }
          }
        }
      }
    }

    // 2nd stage - 1
    // Check for plist - AppStore/createinstallmedia/startosinstall/Fusion Drive
    if (OSVersion.isEmpty()) {
      InstallerPlist = SWPrintf("\\macOS Install Data\\Locked Files\\Boot Files\\SystemVersion.plist"); // 10.12.4+
      if (!FileExists (Volume->RootDir, InstallerPlist)) {
        InstallerPlist = SWPrintf("\\macOS Install Data\\InstallInfo.plist"); // 10.12+
        if (!FileExists (Volume->RootDir, InstallerPlist)) {
          InstallerPlist = SWPrintf("\\com.apple.boot.R\\SystemVersion.plist)"); // 10.12+
          if (!FileExists (Volume->RootDir, InstallerPlist)) {
            InstallerPlist = SWPrintf("\\com.apple.boot.P\\SystemVersion.plist"); // 10.12+
            if (!FileExists (Volume->RootDir, InstallerPlist)) {
              InstallerPlist = SWPrintf("\\com.apple.boot.S\\SystemVersion.plist"); // 10.12+
              if (!FileExists (Volume->RootDir, InstallerPlist) &&
                  (FileExists (Volume->RootDir, L"\\com.apple.boot.R\\System\\Library\\PrelinkedKernels\\prelinkedkernel") ||
                   FileExists (Volume->RootDir, L"\\com.apple.boot.P\\System\\Library\\PrelinkedKernels\\prelinkedkernel") ||
                   FileExists (Volume->RootDir, L"\\com.apple.boot.S\\System\\Library\\PrelinkedKernels\\prelinkedkernel"))) {
                InstallerPlist = SWPrintf("\\System\\Library\\CoreServices\\SystemVersion.plist"); // 10.11
              }
            }
          }
        }
      }
      if (FileExists (Volume->RootDir, InstallerPlist)) {
        Status = egLoadFile(Volume->RootDir, InstallerPlist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
        if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
          Prop = Dict->propertyForKey("ProductVersion");
          if ( Prop != NULL ) {
            if ( !Prop->isString() ) {
              MsgLog("ATTENTION : property not string in ProductVersion\n");
            }else{
              if( Prop->getString()->stringValue().notEmpty() ) {
                OSVersion = Prop->getString()->stringValue();
              }
            }
          }
          Prop = Dict->propertyForKey("ProductBuildVersion");
          if ( Prop != NULL ) {
            if ( !Prop->isString() ) {
              MsgLog("ATTENTION : property not string in ProductBuildVersion\n");
            }else{
              if( Prop->getString()->stringValue().notEmpty() ) {
                BuildVersion = Prop->getString()->stringValue();
              }
            }
          }
          // In InstallInfo.plist, there is no a version key only when updating from AppStore in 10.13+
          // If use the startosinstall in 10.13+, this version key exists in InstallInfo.plist
          DictPointer = Dict->dictPropertyForKey("System Image Info"); // 10.12+
          if (DictPointer != NULL) {
            Prop = DictPointer->propertyForKey("version");
            if ( Prop != NULL ) {
              if ( !Prop->isString() ) {
                MsgLog("ATTENTION : property not string in version\n");
              }else{
                OSVersion = Prop->getString()->stringValue();
              }
            }
          }
          Dict->FreeTag();
        }
      }
    }

    // 2nd stage - 2
    // Check for ia.log - InstallESD/createinstallmedia/startosinstall
    // Implemented by Sherlocks
    if (OSVersion.isEmpty()) {
      CONST CHAR8  *s, *fileBuffer;
//      CHAR8  *Res5 = (__typeof__(Res5))AllocateZeroPool(5);
//      CHAR8  *Res6 = (__typeof__(Res6))AllocateZeroPool(6);
//      CHAR8  *Res7 = (__typeof__(Res7))AllocateZeroPool(7);
//      CHAR8  *Res8 = (__typeof__(Res8))AllocateZeroPool(8);
      UINTN  fileLen = 0;
      XStringW InstallerLog;
      InstallerLog = L"\\Mac OS X Install Data\\ia.log"_XSW; // 10.7
      if (!FileExists (Volume->RootDir, InstallerLog)) {
        InstallerLog = L"\\OS X Install Data\\ia.log"_XSW; // 10.8 - 10.11
        if (!FileExists (Volume->RootDir, InstallerLog)) {
          InstallerLog = L"\\macOS Install Data\\ia.log"_XSW; // 10.12+
        }
      }
      if (FileExists (Volume->RootDir, InstallerLog)) {
        Status = egLoadFile(Volume->RootDir, InstallerLog.wc_str(), (UINT8 **)&fileBuffer, &fileLen);
        if (!EFI_ERROR(Status)) {
          XString8 targetString;
          targetString.strncpy(fileBuffer, fileLen);
      //    s = SearchString(targetString, fileLen, "Running OS Build: Mac OS X ", 27);
          s = AsciiStrStr(targetString.c_str(), "Running OS Build: Mac OS X ");
          if (s[31] == ' ') {
            OSVersion.S8Printf("%c%c.%c\n", s[27], s[28], s[30]);
            if (s[38] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c\n", s[33], s[34], s[35], s[36], s[37]);
            } else if (s[39] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c%c\n", s[33], s[34], s[35], s[36], s[37], s[38]);
            }
          } else if (s[31] == '.') {
            OSVersion.S8Printf("%c%c.%c.%c\n", s[27], s[28], s[30], s[32]);
            if (s[40] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c\n", s[35], s[36], s[37], s[38], s[39]);
            } else if (s[41] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c%c\n", s[35], s[36], s[37], s[38], s[39], s[40]);
            }
          } else if (s[32] == ' ') {
            OSVersion.S8Printf("%c%c.%c%c\n", s[27], s[28], s[30], s[31]);
            if (s[39] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c\n", s[34], s[35], s[36], s[37], s[38]);
            } else if (s[40] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c%c\n", s[34], s[35], s[36], s[37], s[38], s[39]);
            } else if (s[41] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c%c%c\n", s[34], s[35], s[36], s[37], s[38], s[39], s[40]);
            }
          } else if (s[32] == '.') {
            OSVersion.S8Printf("%c%c.%c%c.%c\n", s[27], s[28], s[30], s[31], s[33]);
            if (s[41] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c\n", s[36], s[37], s[38], s[39], s[40]);
            } else if (s[42] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c%c\n", s[36], s[37], s[38], s[39], s[40], s[41]);
            } else if (s[43] == ')') {
              BuildVersion.S8Printf("%c%c%c%c%c%c%c\n", s[36], s[37], s[38], s[39], s[40], s[41], s[42]);
            }
          }
          FreePool(fileBuffer);
        }
      }
    }

    // 2nd stage - 3
    // Check for plist - Preboot of APFS
    if ( OSVersion.isEmpty() )
    {
      XStringW plist = L"\\macOS Install Data\\Locked Files\\Boot Files\\SystemVersion.plist"_XSW;
      if ( !FileExists(Volume->RootDir, plist) ) {
        plist.setEmpty();
      }

      if ( plist.notEmpty() ) { // found macOS System

        Status = egLoadFile(Volume->RootDir, plist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
        if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
          Prop = Dict->propertyForKey("ProductVersion");
          if ( Prop != NULL ) {
            if ( !Prop->isString() ) {
              MsgLog("ATTENTION : property not string in ProductVersion\n");
            }else{
              OSVersion = Prop->getString()->stringValue();
            }
          }
          Prop = Dict->propertyForKey("ProductBuildVersion");
          if ( Prop != NULL ) {
            if ( !Prop->isString() ) {
              MsgLog("ATTENTION : property not string in ProductBuildVersion\n");
            }else{
              BuildVersion = Prop->getString()->stringValue();
            }
          }
        }
        Dict->FreeTag();
      }
    }
  }

  if (OSTYPE_IS_OSX_RECOVERY (LoaderType)) {

    XString8 uuidPrefix;
    if ( APFSTargetUUID.notNull() ) uuidPrefix = S8Printf("\\%s", APFSTargetUUID.toXString8().c_str());

    XStringW plist = SWPrintf("%s\\SystemVersion.plist", uuidPrefix.c_str());
    if ( !FileExists(Volume->RootDir, plist) ) {
      plist = SWPrintf("%s\\ServerVersion.plist", uuidPrefix.c_str());
      if ( !FileExists(Volume->RootDir, plist) ) {
        plist = L"\\com.apple.recovery.boot\\SystemVersion.plist"_XSW;
        if ( !FileExists(Volume->RootDir, plist) ) {
          plist = L"\\com.apple.recovery.boot\\ServerVersion.plist"_XSW;
          if ( !FileExists(Volume->RootDir, plist) ) {
            plist.setEmpty();
          }
        }
      }
    }

    // Detect exact version for OS X Recovery
    if ( plist.notEmpty() ) { // found macOS System
      Status = egLoadFile(Volume->RootDir, plist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
      if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
        Prop = Dict->propertyForKey("ProductVersion");
        if ( Prop != NULL ) {
          if ( !Prop->isString() ) {
            MsgLog("ATTENTION : property not string in ProductVersion\n");
          }else{
            OSVersion = Prop->getString()->stringValue();
          }
        }
        Prop = Dict->propertyForKey("ProductBuildVersion");
        if ( Prop != NULL ) {
          if ( !Prop->isString() ) {
            MsgLog("ATTENTION : property not string in ProductBuildVersion\n");
          }else{
            BuildVersion = Prop->getString()->stringValue();
          }
        }
      }
      Dict->FreeTag();
    } else if (FileExists (Volume->RootDir, L"\\com.apple.recovery.boot\\boot.efi")) {
      // Special case - com.apple.recovery.boot/boot.efi exists but SystemVersion.plist doesn't --> 10.9 recovery
      OSVersion = "10.9"_XS8;
    }
  }

  if (PlistBuffer != NULL) {
    FreePool(PlistBuffer);
  }
  (*BuildVersionPtr).stealValueFrom(&BuildVersion);
  return OSVersion;
}

inline MacOsVersion GetOSVersion (IN LOADER_ENTRY *Entry) { return GetOSVersion(Entry->LoaderType, Entry->APFSTargetUUID, Entry->Volume, &Entry->BuildVersion); };

//constexpr XStringW iconMac = L"mac"_XSW;
CONST XStringW
GetOSIconName (const MacOsVersion& OSVersion)
{
  XStringW OSIconName;
  if (OSVersion.isEmpty()) {
    OSIconName = L"mac"_XSW;
  } else if (OSVersion.elementAt(0) == 13 ){
    // Ventura
    OSIconName = L"ventura,mac"_XSW;
  } else if (OSVersion.elementAt(0) == 12 ){
    // Monterey
    OSIconName = L"monterey,mac"_XSW;
  } else if ( (OSVersion.elementAt(0) == 10 && OSVersion.elementAt(1) == 16 ) ||
              (OSVersion.elementAt(0) == 11 /*&& OSVersion.elementAt(1) == 0*/ )
            ) {
    // Big Sur
    OSIconName = L"bigsur,mac"_XSW;
  } else if ( OSVersion.elementAt(0) == 10 ) {
    if ( OSVersion.elementAt(1) == 15 ) {
      // Catalina
      OSIconName = L"cata,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 14 ) {
      // Mojave
      OSIconName = L"moja,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 13 ) {
      // High Sierra
      OSIconName = L"hsierra,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 12 ) {
      // Sierra
      OSIconName = L"sierra,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 11 ) {
      // El Capitan
      OSIconName = L"cap,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 10 ) {
      // Yosemite
      OSIconName = L"yos,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 9 ) {
      // Mavericks
      OSIconName = L"mav,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 8 ) {
      // Mountain Lion
      OSIconName = L"cougar,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 7 ) {
      // Lion
      OSIconName = L"lion,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 6 ) {
      // Snow Leopard
      OSIconName = L"snow,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 5 ) {
      // Leopard
      OSIconName = L"leo,mac"_XSW;
    } else if ( OSVersion.elementAt(1) == 4 ) {
      // Tiger
      OSIconName = L"tiger,mac"_XSW;
    } else {
      OSIconName = L"mac"_XSW;
    }
  } else {
    OSIconName = L"mac"_XSW;
  }

  return OSIconName;
}

STATIC LOADER_ENTRY *CreateLoaderEntry(IN CONST XStringW& LoaderPath,
                                       IN CONST XString8Array& LoaderOptions,
                                       IN CONST XString8& FullTitle,
                                       IN CONST XString8& LoaderTitle,
                                       IN REFIT_VOLUME *Volume,
                                       IN XIcon *Image,
                                       IN XIcon *DriveImage,
                                       IN UINT8 OSType,
                                       IN UINT8 Flags,
                                       IN char32_t Hotkey,
                                       EFI_GRAPHICS_OUTPUT_BLT_PIXEL BootBgColor,
                                       IN UINT8 CustomBoot,
                                       IN const XImage& CustomLogo,
                                       IN const KERNEL_AND_KEXT_PATCHES* Patches,
                                       IN XBool CustomEntry)
{
  EFI_DEVICE_PATH       *LoaderDevicePath;
  XStringW               LoaderDevicePathString;
  XStringW               FilePathAsString;
//  CONST CHAR16          *OSIconName = NULL;
  CHAR16                ShortcutLetter;
  LOADER_ENTRY          *Entry;
  CONST CHAR8           *indent = "      ";

  // Check parameters are valid
  if ((LoaderPath.isEmpty()) || (Volume == NULL)) {
    return NULL;
  }

  // Get the loader device path
  LoaderDevicePath = FileDevicePath(Volume->DeviceHandle, LoaderPath);
  if (LoaderDevicePath == NULL) {
    return NULL;
  }
  LoaderDevicePathString = FileDevicePathToXStringW(LoaderDevicePath);
  if (LoaderDevicePathString.isEmpty()) {
    return NULL;
  }

  // Ignore this loader if it's self path
  XStringW selfDevicePathAsXStringW = FileDevicePathToXStringW(&self.getSelfDevicePath());
  if ( selfDevicePathAsXStringW == LoaderDevicePathString ) {
    DBG("%sskipped because path `%ls` is self path!\n", indent, LoaderDevicePathString.wc_str());
    return NULL;
  }
// DBG("OSType =%d\n", OSType);
  // DBG("prepare the menu entry\n");
  // prepare the menu entry
  Entry = new LOADER_ENTRY;

  if (!CustomEntry) {
    // Ignore this loader if it's device path is already present in another loader
      for (UINTN i = 0; i < MainMenu.Entries.size(); ++i) {
        REFIT_ABSTRACT_MENU_ENTRY& MainEntry = MainMenu.Entries[i];
        // Only want loaders
        if (MainEntry.getLOADER_ENTRY()) {
          if (StriCmp(MainEntry.getLOADER_ENTRY()->DevicePathString.wc_str(), LoaderDevicePathString.wc_str()) == 0) {
            DBG("%sskipped because path `%ls` already exists for another entry!\n", indent, LoaderDevicePathString.wc_str());
            return NULL;
          }
        }
      }
    // If this isn't a custom entry make sure it's not hidden by a custom entry
    for (size_t CustomIndex = 0 ; CustomIndex < GlobalConfig.CustomEntries.size() ; ++CustomIndex ) {
      CUSTOM_LOADER_ENTRY& Custom = GlobalConfig.CustomEntries[CustomIndex];
      if ( Custom.settings.Disabled ) continue; // before, disabled entries settings weren't loaded.
      // Check if the custom entry is hidden or disabled
      if ( OSFLAG_ISSET(Custom.getFlags(gSettings.SystemParameters.NoCaches), OSFLAG_DISABLED)  || Custom.settings.Hidden ) {

        INTN volume_match=0;
        INTN volume_type_match=0;
        INTN path_match=0;
        INTN type_match=0;

        // Check if volume match
        if (Custom.settings.Volume.notEmpty()) {
          // Check if the string matches the volume
          volume_match =
            ((StrStr(Volume->DevicePathString.wc_str(), Custom.settings.Volume.wc_str()) != NULL) ||
             ((Volume->VolName.notEmpty()) && (StrStr(Volume->VolName.wc_str(), Custom.settings.Volume.wc_str()) != NULL))) ? 1 : -1;
        }

        // Check if the volume_type match
        if (Custom.settings.VolumeType != 0) {
          volume_type_match = (((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0) ? 1 : -1;
        }

        // Check if the path match
        if (Custom.settings.Path.notEmpty()) {
          // Check if the loader path match
          path_match = (Custom.settings.Path.isEqualIC(LoaderPath)) ? 1 : -1;
        }

        // Check if the type match
        if (Custom.settings.Type != 0) {
          type_match = OSTYPE_COMPARE(Custom.settings.Type, OSType) ? 1 : -1;
        }

        if (volume_match == -1 || volume_type_match == -1 || path_match == -1 || type_match == -1 ) {
          UINTN add_comma = 0;

          DBG("%sNot match custom entry %zu: ", indent, CustomIndex);
          if (volume_match != 0) {
            DBG("Volume: %ls", volume_match == 1 ? L"match" : L"not match");
            add_comma++;
          }
          if (path_match != 0) {
            DBG("%lsPath: %ls",
                (add_comma ? L", " : L""),
                path_match == 1 ? L"match" : L"not match");
            add_comma++;
          }
          if (volume_type_match != 0) {
            DBG("%lsVolumeType: %ls",
                (add_comma ? L", " : L""),
                volume_type_match == 1 ? L"match" : L"not match");
            add_comma++;
          }
          if (type_match != 0) {
            DBG("%lsType: %ls",
                (add_comma ? L", " : L""),
                type_match == 1 ? L"match" : L"not match");
          }
          DBG("\n");
        } else {
          // Custom entry match
          DBG("%sHidden because matching custom entry %zu!\n", indent, CustomIndex);
          Entry->Hidden = true;
        }
      }
    }
  }
  Entry->Row = 0;
  Entry->Volume = Volume;

  if ( LoaderPath.length() >= 38 ) {
    if ( isPathSeparator(LoaderPath[0])  &&  isPathSeparator(LoaderPath[37]) ) {
			Entry->APFSTargetUUID.takeValueFrom(LoaderPath.subString(1, 36)); // if guid is not valid, APFSTargetUUID will be set to null
    }
  }

//  Entry->APFSTargetUUID = APFSTargetUUID;

  Entry->LoaderPath       = LoaderPath;
  Entry->DisplayedVolName = Volume->VolName;
  Entry->DevicePath       = LoaderDevicePath;
  Entry->DevicePathString = LoaderDevicePathString;
  Entry->Flags            = OSFLAG_SET(Flags, OSFLAG_USEGRAPHICS);

  if (OSFLAG_ISSET(Flags, OSFLAG_NODEFAULTARGS)) {
    Entry->LoadOptions  = LoaderOptions;
  }else{
    Entry->LoadOptions = Split<XString8Array>(gSettings.Boot.BootArgs, " ");
    Entry->LoadOptions.import(LoaderOptions);
  }
  //actions
  Entry->AtClick = ActionSelect;
  Entry->AtDoubleClick = ActionEnter;
  Entry->AtRightClick = ActionDetails;
  Entry->CustomBoot = CustomBoot;
  Entry->CustomLogo = CustomLogo; //could be an empty image

  Entry->LoaderType = OSType;
  Entry->BuildVersion.setEmpty();
#ifdef JIEF_DEBUG
if ( Entry->LoaderPath.contains("com.apple.installer") ) {
  DBG("%s", "");
}
if ( Entry->APFSTargetUUID.Data1 == 0x99999999 ) {
  DBG("%s", "");
}
#endif
  Entry->macOSVersion = GetOSVersion(Entry);
  DBG("%sOSVersion=%s \n", indent, Entry->macOSVersion.asString().c_str());
  // detect specific loaders
  XStringW OSIconName;
  ShortcutLetter = 0;

  switch (OSType) {
    case OSTYPE_OSX:
    case OSTYPE_RECOVERY:
    case OSTYPE_OSX_INSTALLER:
      OSIconName = GetOSIconName(Entry->macOSVersion);// Sothor - Get OSIcon name using OSVersion
      // apianti - force custom logo even when verbose
/* this is not needed, as this flag is also being unset when booting if -v is present (LoadOptions may change until then)
      if ( Entry->LoadOptions.containsIC("-v")  ) {
        // OSX is not booting verbose, so we can set console to graphics mode
        Entry->Flags = OSFLAG_UNSET(Entry->Flags, OSFLAG_USEGRAPHICS);
      }
*/
      if (OSType == OSTYPE_OSX && IsOsxHibernated(Entry)) {
        Entry->Flags = OSFLAG_SET(Entry->Flags, OSFLAG_HIBERNATED);
        DBG("%s  =>set entry as hibernated\n", indent);
      }
      //always unset checkFakeSmc for installer
      if (OSType == OSTYPE_OSX_INSTALLER){
//        Entry->Flags = OSFLAG_UNSET(Entry->Flags, OSFLAG_CHECKFAKESMC);
//        Entry->Flags = OSFLAG_SET(Entry->Flags, OSFLAG_WITHKEXTS);
      }
      ShortcutLetter = 'M';
      if ( Entry->DisplayedVolName.isEmpty() ) {
        // else no sense to override it with dubious name
        GetOSXVolumeName(Entry);
      }
      break;
    case OSTYPE_WIN:
      OSIconName = L"win"_XSW;
      ShortcutLetter = 'W';
      break;
    case OSTYPE_WINEFI:
      OSIconName = L"vista,win"_XSW;
      //ShortcutLetter = 'V';
      ShortcutLetter = 'W';
      break;
    case OSTYPE_LIN:
    case OSTYPE_LINEFI:
    // we already detected linux and have Path and Image
      Entry->LoaderType = OSType;
      OSIconName = L"linux"_XSW;
      if (Image == nullptr) {
        DBG("%slinux image not found\n", indent);
        OSIconName = LinuxIconNameFromPath(LoaderPath, Volume->RootDir); //something named "issue"
      }
      ShortcutLetter = 'L';
      break;
    //case OSTYPE_OTHER: 
    case OSTYPE_EFI:
      OSIconName = L"clover"_XSW;
      ShortcutLetter = 'E';
      Entry->LoaderType = OSTYPE_OTHER;
      break;
    default:
      OSIconName = L"unknown"_XSW;
      Entry->LoaderType = OSTYPE_OTHER;
      break;
  }
//DBG("OSIconName=%ls \n", OSIconName);
  Entry->Title = FullTitle;
  if (Entry->Title.isEmpty()  &&  Volume->VolLabel.notEmpty()) {
    if (Volume->VolLabel[0] == L'#') {
      Entry->Title.SWPrintf("Boot %ls from %ls", (!LoaderTitle.isEmpty()) ? XStringW(LoaderTitle).wc_str() : LoaderPath.basename().wc_str(), Volume->VolLabel.data(1));
    }else{
      Entry->Title.SWPrintf("Boot %ls from %ls", (!LoaderTitle.isEmpty()) ? XStringW(LoaderTitle).wc_str() : LoaderPath.basename().wc_str(), Volume->VolLabel.wc_str());
    }
  }

  XBool BootCampStyle = ThemeX.BootCampStyle;

  if ( Entry->Title.isEmpty()  &&  Entry->DisplayedVolName.isEmpty() ) {
    XStringW BasenameXW = XStringW(Basename(Volume->DevicePathString.wc_str()));
 //   DBG("encounter Entry->VolName ==%ls and StrLen(Entry->VolName) ==%llu\n",Entry->VolName, StrLen(Entry->VolName));
    if (BootCampStyle) {
      if (!LoaderTitle.isEmpty()) {
        Entry->Title = LoaderTitle;
      } else {
        Entry->Title = (BasenameXW.contains(L"-")) ? BasenameXW.subString(0,BasenameXW.indexOf(L"-") + 1) + L"..)" : BasenameXW;
      }
    } else {
      Entry->Title.SWPrintf("Boot %ls from %ls", (!LoaderTitle.isEmpty()) ? XStringW(LoaderTitle).wc_str() : LoaderPath.basename().wc_str(),
                            (BasenameXW.contains(L"-")) ? (BasenameXW.subString(0,BasenameXW.indexOf(L"-") + 1) + L"..)").wc_str() : BasenameXW.wc_str());
    }
  }
//  DBG("check Entry->Title \n");
  if ( Entry->Title.isEmpty() ) {
 //   DBG("encounter LoaderTitle ==%ls and Entry->VolName ==%ls\n", LoaderTitle.wc_str(), Entry->VolName);
    if (BootCampStyle) {
      if ((StriCmp(XStringW(LoaderTitle).wc_str(), L"macOS") == 0) || (StriCmp(XStringW(LoaderTitle).wc_str(), L"Recovery") == 0)) {
        Entry->Title.takeValueFrom(Entry->DisplayedVolName);
      } else {
        if (!LoaderTitle.isEmpty()) {
          Entry->Title = LoaderTitle;
        } else {
          Entry->Title = LoaderPath.basename();
        }
      }
    } else {
      Entry->Title.SWPrintf("Boot %ls from %ls", (!LoaderTitle.isEmpty()) ? XStringW(LoaderTitle).wc_str() : LoaderPath.basename().wc_str(),
                            Entry->DisplayedVolName.wc_str());
    }
  }
//  DBG("Entry->Title =%ls\n", Entry->Title.wc_str());
  // just an example that UI can show hibernated volume to the user
  // should be better to show it on entry image
  if (OSFLAG_ISSET(Entry->Flags, OSFLAG_HIBERNATED)) {
    Entry->Title.SWPrintf("%ls (hibernated)", Entry->Title.s());
  }

  Entry->ShortcutLetter = (Hotkey == 0) ? ShortcutLetter : Hotkey;

  // get custom volume icon if present
  if (gSettings.GUI.CustomIcons && FileExists(Volume->RootDir, L"\\.VolumeIcon.icns")){
    Entry->Image.Image.LoadIcns(Volume->RootDir, L"\\.VolumeIcon.icns", 128);
    if (!Entry->Image.Image.isEmpty()) {
      Entry->Image.setFilled();
      DBG("%susing VolumeIcon.icns image from Volume\n", indent);
    }    
  } else if (Image) {
    Entry->Image = *Image; //copy image from temporary storage
  } else {
    Entry->Image = ThemeX.LoadOSIcon(OSIconName);
  }
//  DBG("Load DriveImage\n");
  // Load DriveImage
  if (DriveImage) {
//    DBG("DriveImage presents\n");
    Entry->DriveImage = *DriveImage;
  } else {
    Entry->DriveImage = ScanVolumeDefaultIcon(Volume, Entry->LoaderType, Volume->DevicePath);
  }
//   DBG("HideBadges=%llu Volume=%ls ", ThemeX.HideBadges, Volume->VolName);
  if (ThemeX.HideBadges & HDBADGES_SHOW) {
    if (ThemeX.HideBadges & HDBADGES_SWAP) {
      Entry->BadgeImage.Image = XImage(Entry->DriveImage.Image, 0);
       DBG("%sShow badge as Drive.\n", indent);
    } else {
      Entry->BadgeImage.Image = XImage(Entry->Image.Image, 0);
       DBG("%sShow badge as OSImage.\n", indent);
    }
    if (!Entry->BadgeImage.Image.isEmpty()) {
      Entry->BadgeImage.setFilled();
    }
  }
  Entry->BootBgColor = BootBgColor;

//  Entry->KernelAndKextPatches = ((Patches == NULL) ? (KERNEL_AND_KEXT_PATCHES *)(((UINTN)&gSettings) + OFFSET_OF(SETTINGS_DATA, KernelAndKextPatches)) : Patches);
//  CopyKernelAndKextPatches(&Entry->KernelAndKextPatches, Patches == NULL ? &gSettings.KernelAndKextPatches : Patches);
  Entry->KernelAndKextPatches = Patches == NULL ? gSettings.KernelAndKextPatches : *Patches;
  
#ifdef DUMP_KERNEL_KEXT_PATCHES
  DumpKernelAndKextPatches(Entry->KernelAndKextPatches);
#endif
  DBG("%sLoader entry created for '%ls'\n", indent, Entry->DevicePathString.wc_str());
  return Entry;
}

void LOADER_ENTRY::AddDefaultMenu()
{
  XStringW     FileName;
//  CHAR16* TempOptions;
//  CHAR16            DiagsFileName[256];
  LOADER_ENTRY      *SubEntry;
//  REFIT_MENU_SCREEN *SubScreen;
//  REFIT_VOLUME      *Volume;
  UINT64             VolumeSize;
  EFI_GUID      Guid;
  XBool              KernelIs64BitOnly;
//  UINT64            os_version = AsciiOSVersionToUint64(OSVersion);

  constexpr LString8 quietLitteral = "quiet"_XS8;
  constexpr LString8 splashLitteral = "splash"_XS8;

  // Only kernels up to 10.7 have 32-bit mode
  KernelIs64BitOnly = (macOSVersion.isEmpty() ||
                       macOSVersion >= MacOsVersion("10.8"_XS8));
  
  const char* macOS = (macOSVersion.notEmpty() && macOSVersion < MacOsVersion("10.8"_XS8))? "Mac OS X" :
                      (macOSVersion.notEmpty() && macOSVersion < MacOsVersion("10.12"_XS8))? "OS X" : "macOS";

  FileName = LoaderPath.basename();

  // create the submenu
  SubScreen = new REFIT_MENU_SCREEN;
  SubScreen->Title.SWPrintf("Options for %ls on %ls", Title.wc_str(), DisplayedVolName.wc_str());

  SubScreen->TitleImage = Image;
  SubScreen->ID = LoaderType + 20; //wow
//    DBG("get anime for os=%lld\n", SubScreen->ID);
  SubScreen->GetAnime();
  VolumeSize = RShiftU64(MultU64x32(Volume->BlockIO->Media->LastBlock, Volume->BlockIO->Media->BlockSize), 20);
  SubScreen->AddMenuInfoLine_f("Volume size: %lluMb", VolumeSize);
  SubScreen->AddMenuInfoLine_f("%ls", FileDevicePathToXStringW(DevicePath).wc_str());
  Guid = FindGPTPartitionGuidInDevicePath(Volume->DevicePath);
  if ( Guid.notNull() ) {
    SubScreen->AddMenuInfoLine_f("UUID: %s", Guid.toXString8().c_str());
  }
  if ( Volume->ApfsFileSystemUUID.notNull() ||  APFSTargetUUID.notNull() ) {
    SubScreen->AddMenuInfoLine_f("APFS volume name: %ls", DisplayedVolName.wc_str());
  }
  if ( Volume->ApfsFileSystemUUID.notNull() ) {
    SubScreen->AddMenuInfoLine_f("APFS file system UUID: %s", Volume->ApfsFileSystemUUID.toXString8().c_str());
  }
  if ( Volume->ApfsContainerUUID.notNull() ) {
    SubScreen->AddMenuInfoLine_f("APFS container UUID: %s", Volume->ApfsContainerUUID.toXString8().c_str());
  }
  if ( APFSTargetUUID.notNull() ) {
    SubScreen->AddMenuInfoLine_f("APFS target UUID: %s", APFSTargetUUID.toXString8().c_str());
  }
  SubScreen->AddMenuInfoLine_f("Options: %s", LoadOptions.ConcatAll(" "_XS8).c_str());
  // loader-specific submenu entries
  if (LoaderType == OSTYPE_OSX ||
      LoaderType == OSTYPE_OSX_INSTALLER ||
      LoaderType == OSTYPE_RECOVERY) { // entries for Mac OS X
    SubScreen->AddMenuInfoLine_f("%s: %s", macOS, macOSVersion.asString().c_str());

    if (OSFLAG_ISSET(Flags, OSFLAG_HIBERNATED)) {
      SubEntry = getPartiallyDuplicatedEntry();
       SubEntry->Title.takeValueFrom("Cancel hibernate wake");
       SubEntry->Flags     = OSFLAG_UNSET(SubEntry->Flags, OSFLAG_HIBERNATED);
       SubScreen->AddMenuEntry(SubEntry, true);
    }

    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Boot %s with selected options", macOS);
    SubScreen->AddMenuEntry(SubEntry, true);
#if 0
    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Boot %s with injected kexts", macOS);
    SubEntry->Flags       = OSFLAG_UNSET(SubEntry->Flags, OSFLAG_CHECKFAKESMC);
    SubEntry->Flags       = OSFLAG_SET(SubEntry->Flags, OSFLAG_WITHKEXTS);
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Boot %s without injected kexts", macOS);
    SubEntry->Flags       = OSFLAG_UNSET(SubEntry->Flags, OSFLAG_CHECKFAKESMC);
    SubEntry->Flags       = OSFLAG_UNSET(SubEntry->Flags, OSFLAG_WITHKEXTS);
    SubScreen->AddMenuEntry(SubEntry, true);
#endif
    SubScreen->AddMenuEntry(SubMenuKextInjectMgmt(), true);
    SubScreen->AddMenuInfo_f("=== boot-args ===");
    if (!KernelIs64BitOnly) {
      if ( macOSVersion.notEmpty() && macOSVersion < MacOsVersion("10.8"_XS8) ) {
        SubScreen->AddMenuCheck("Mac OS X 32bit",   OPT_I386, 68);
      }
//      SubScreen->AddMenuCheck(XString8().SPrintf("%s 64bit", macOS).c_str(), OPT_X64,  68);
      SubScreen->AddMenuCheck((macOS + " 64bit"_XS8).c_str(), OPT_X64,  68);
    }
    SubScreen->AddMenuCheck("Verbose (-v)",                               OPT_VERBOSE, 68);
    // No Caches option works on 10.6 - 10.9
    if ( macOSVersion.notEmpty() && macOSVersion < MacOsVersion("10.10"_XS8) ) {
      SubScreen->AddMenuCheck("Without caches (-f)",                        OPT_NOCACHES, 68);
    }
    SubScreen->AddMenuCheck("Single User (-s)",                           OPT_SINGLE_USER, 68);
    SubScreen->AddMenuCheck("Safe Mode (-x)",                             OPT_SAFE, 68);
    SubScreen->AddMenuCheck("Disable KASLR (slide=0)",                    OPT_SLIDE, 68);
    SubScreen->AddMenuCheck("Set Nvidia to VESA (nv_disable=1)",          OPT_NVDISABLE, 68);
    SubScreen->AddMenuCheck("Use Nvidia WEB drivers (nvda_drv=1)",        OPT_NVWEBON, 68);
    SubScreen->AddMenuCheck("Disable PowerNap (darkwake=0)",              OPT_POWERNAPOFF, 68);
    SubScreen->AddMenuCheck("Use XNU CPUPM (-xcpm)",                      OPT_XCPM, 68);
//    SubScreen->AddMenuCheck("Disable Intel Idle Mode (-gux_no_idle)",     OPT_GNOIDLE, 68);
//    SubScreen->AddMenuCheck("Sleep Uses Shutdown (-gux_nosleep)",         OPT_GNOSLEEP, 68);
//    SubScreen->AddMenuCheck("Force No Msi Int (-gux_nomsi)",              OPT_GNOMSI, 68);
//    SubScreen->AddMenuCheck("EHC manage USB2 ports (-gux_defer_usb2)",    OPT_EHCUSB, 68);
    SubScreen->AddMenuCheck("Keep symbols on panic (keepsyms=1)",         OPT_KEEPSYMS, 68);
    SubScreen->AddMenuCheck("Don't reboot on panic (debug=0x100)",        OPT_DEBUG, 68);
    SubScreen->AddMenuCheck("Debug kexts (kextlog=0xffff)",               OPT_KEXTLOG, 68);
//    SubScreen->AddMenuCheck("Disable AppleALC (-alcoff)",                 OPT_APPLEALC, 68);
//    SubScreen->AddMenuCheck("Disable Shiki (-shikioff)",                  OPT_SHIKI, 68);

    if (gSettings.RtVariables.CsrActiveConfig == 0) {
      SubScreen->AddMenuCheck("No SIP", OSFLAG_NOSIP, 69);
    }
    
  } else if (LoaderType == OSTYPE_LINEFI) {
    XBool Quiet = LoadOptions.contains(quietLitteral);
    XBool WithSplash = LoadOptions.contains(splashLitteral);
    
    // default entry
    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Run %ls", FileName.wc_str());
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    if (Quiet) {
      SubEntry->Title.SWPrintf("%ls verbose", Title.s());
      SubEntry->LoadOptions.removeIC(quietLitteral);
    } else {
      SubEntry->Title.SWPrintf("%ls quiet", Title.s());
      SubEntry->LoadOptions.AddID(quietLitteral);
    }
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    if (WithSplash) {
      SubEntry->Title.SWPrintf("%ls without splash", Title.s());
      SubEntry->LoadOptions.removeIC(splashLitteral);
    } else {
      SubEntry->Title.SWPrintf("%ls with splash", Title.s());
      SubEntry->LoadOptions.AddID(splashLitteral);
    }
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    if (WithSplash) {
      if (Quiet) {
        SubEntry->Title.SWPrintf("%ls verbose without splash", Title.s());
        SubEntry->LoadOptions.removeIC(splashLitteral);
        SubEntry->LoadOptions.removeIC(quietLitteral);
      } else {
        SubEntry->Title.SWPrintf("%ls quiet without splash",Title.s());
        SubEntry->LoadOptions.removeIC(splashLitteral);
        SubEntry->LoadOptions.Add(quietLitteral);
      }
    } else if (Quiet) {
      SubEntry->Title.SWPrintf("%ls verbose with splash",Title.s());
      SubEntry->LoadOptions.removeIC(quietLitteral); //
      SubEntry->LoadOptions.AddID(splashLitteral);
    } else {
      SubEntry->Title.SWPrintf("%ls quiet with splash",Title.s());
      SubEntry->LoadOptions.AddID(quietLitteral);
      SubEntry->LoadOptions.AddID(splashLitteral);
    }
    SubScreen->AddMenuEntry(SubEntry, true);

  } else if ((LoaderType == OSTYPE_WIN) || (LoaderType == OSTYPE_WINEFI)) {
    // by default, skip the built-in selection and boot from hard disk only
    LoadOptions.setEmpty();
    LoadOptions.Add("-s"_XS8);
    LoadOptions.Add("-h"_XS8);
    
    // default entry
    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Run %ls", FileName.wc_str());
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.takeValueFrom("Boot Windows from Hard Disk");
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.takeValueFrom("Boot Windows from CD-ROM");
    LoadOptions.setEmpty();
    LoadOptions.Add("-s"_XS8);
    LoadOptions.Add("-c"_XS8);
    SubScreen->AddMenuEntry(SubEntry, true);

    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Run %ls in text mode", FileName.wc_str());
    SubEntry->Flags           = OSFLAG_UNSET(SubEntry->Flags, OSFLAG_USEGRAPHICS);
    LoadOptions.setEmpty();
    LoadOptions.Add("-v"_XS8);
    SubEntry->LoaderType      = OSTYPE_OTHER; // Sothor - Why are we using OSTYPE_OTHER here?
    SubScreen->AddMenuEntry(SubEntry, true);

  }else{
    // default entry
    SubEntry = getPartiallyDuplicatedEntry();
    SubEntry->Title.SWPrintf("Run %ls", FileName.wc_str());
    SubScreen->AddMenuEntry(SubEntry, true);
  }

//  SubScreen->AddMenuEntry(&MenuEntryReturn, false); //one-way ticket to avoid confusion
  // DBG("    Added '%ls': OSType='%d', OSVersion='%s'\n",Title,LoaderType,OSVersion);
}

LOADER_ENTRY* AddLoaderEntry(IN CONST XStringW& LoaderPath, IN CONST XString8Array& LoaderOptions,
                       IN CONST XStringW& FullTitle, IN CONST XStringW& LoaderTitle,
                       IN REFIT_VOLUME *Volume, IN XIcon *Image,
                       IN UINT8 OSType, IN UINT8 Flags)
{
  LOADER_ENTRY *Entry;

  if ((LoaderPath.isEmpty()) || (Volume == NULL) || (Volume->RootDir == NULL) || !FileExists(Volume->RootDir, LoaderPath)) {
    return NULL;
  }

  DBG("      AddLoaderEntry for Volume Name=%ls, idx=%zu\n", Volume->VolName.wc_str(), MainMenu.Entries.sizeIncludingHidden());
  if (OSFLAG_ISSET(Flags, OSFLAG_DISABLED)) {
    DBG("     skipped because entry is disabled\n");
    return NULL;
  }
//  if (!gSettings.ShowHiddenEntries && OSFLAG_ISSET(Flags, OSFLAG_HIDDEN)) {
//    DBG("        skipped because entry is hidden\n");
//    return NULL;
//  }
  //don't add hided entries
//  if (!gSettings.ShowHiddenEntries) {
//    for (HVi = 0; HVi < gSettings.HVCount; HVi++) {
//      if ( LoaderPath.containsIC(gSettings.GUI.HVHideStrings[HVi]) ) {
//        DBG("        hiding entry: %ls\n", LoaderPath.s());
//        return NULL;
//      }
//    }
//  }

  Entry = CreateLoaderEntry(LoaderPath, LoaderOptions, FullTitle, LoaderTitle, Volume, Image, NULL, OSType, Flags, 0, MenuBackgroundPixel, CUSTOM_BOOT_DISABLED, NullXImage, NULL, false);
  if (Entry != NULL) {
    if ((Entry->LoaderType == OSTYPE_OSX) ||
        (Entry->LoaderType == OSTYPE_OSX_INSTALLER ) ||
        (Entry->LoaderType == OSTYPE_RECOVERY)) {
//      if (gSettings.WithKexts) {
//        Entry->Flags = OSFLAG_SET(Entry->Flags, OSFLAG_WITHKEXTS);
//      }
//      if (gSettings.WithKextsIfNoFakeSMC) {
//        Entry->Flags = OSFLAG_SET(Entry->Flags, OSFLAG_CHECKFAKESMC);
//        Entry->Flags = OSFLAG_SET(Entry->Flags, OSFLAG_WITHKEXTS);
//      }
      if (gSettings.SystemParameters.NoCaches) {
        Entry->Flags = OSFLAG_SET(Entry->Flags, OSFLAG_NOCACHES);
      }
    }
    if ( Volume->Hidden ) {
      DBG("     hiding entry because volume is hidden: %ls\n", LoaderPath.s());
      Entry->Hidden = true;
    }else{
      for (size_t HVi = 0; HVi < gSettings.GUI.HVHideStrings.size(); HVi++) {
        if ( LoaderPath.containsIC(gSettings.GUI.HVHideStrings[HVi]) ) {
          DBG("     hiding entry: %ls\n", LoaderPath.s());
          Entry->Hidden = true;
        }
      }
    }
    //TODO there is a problem that Entry->Flags is unique while InputItems are global ;(
//    InputItems[69].IValue = Entry->Flags;
    Entry->AddDefaultMenu();
    DBG("      Menu entry added at index %zd\n", MainMenu.Entries.sizeIncludingHidden());
    MainMenu.AddMenuEntry(Entry, true);
    return Entry;
  }
  return NULL;
}

STATIC void LinuxScan(REFIT_VOLUME *Volume, UINT8 KernelScan, UINT8 Type, XStringW *CustomPath, XIcon *CustomImage)
{
  // When used for Regular Entries, all found entries will be added by AddLoaderEntry()
  // When used for Custom Entries (detected by CustomPath!=NULL), CustomPath+CustomImage will be set to the first entry found and execution will stop
  // Scanning is adjusted according to Type: OSTYPE_LIN will scan for linux loaders, OSTYPE_LINEFI will scan for linux kernels, unspecified will scan for both
  UINTN        Index;

  // check for linux loaders
  if (Type != OSTYPE_LINEFI) { // OSTYPE_LIN or unspecified
    //
    //----- Test common linux name and path like /EFI/ubuntu/grubx64.efi
    REFIT_DIR_ITER  DirIter;
    EFI_FILE_INFO  *DirEntry = NULL;
    DirIterOpen(Volume->RootDir, L"\\EFI", &DirIter);
    while (DirIterNext(&DirIter, 1, L"*", &DirEntry)) {
      if (DirEntry->FileName[0] == '.') {
        //DBG("Skip dot entries: %ls\n", DirEntry->FileName);
        continue;
      }
      XStringW File = SWPrintf("EFI\\%ls\\grubx64.efi", DirEntry->FileName);
      XStringW OSName = XStringW().takeValueFrom(DirEntry->FileName); // this is folder name, for example "ubuntu"
      OSName.lowerAscii(); // lowercase for icon name and title (first letter in title will be capitalized later)
      if (FileExists(Volume->RootDir, File)) {
        // check if nonstandard icon mapping is needed
        for (Index = 0; Index < LinuxIconMappingCount; ++Index) {
          if (StrCmp(OSName.wc_str(),LinuxIconMapping[Index].DirectoryName) == 0) {
            OSName = XStringW().takeValueFrom(LinuxIconMapping[Index].IconName);
            break;
          }
        }
        if (!CustomPath) {
          DBG("  found entry %ls,linux\n", OSName.wc_str());
        }
        XStringW LoaderTitle = OSName.subString(0,1); // capitalize first letter for title
        LoaderTitle.upperAscii();
        LoaderTitle += OSName.subString(1, OSName.length()) + L" Linux"_XSW;
        // Very few linux icons exist in IconNames, but these few may be preloaded, so check that first
        XIcon ImageX = ThemeX.GetIcon(L"os_"_XSW + OSName); //will the image be destroyed or rewritten by next image after the cycle end?
        if (ImageX.isEmpty()) {
          // no preloaded icon, try to load from dir
          ImageX.LoadXImage(&ThemeX.getThemeDir(), L"os_"_XSW + OSName);
        }
        if (CustomPath) { 
          *CustomPath = File;
          if (CustomImage) {
            *CustomImage = ImageX;
          }
          DirIterClose(&DirIter);
          return;
        } 
        AddLoaderEntry(File, NullXString8Array, L""_XSW, LoaderTitle, Volume,
                      (ImageX.isEmpty() ? NULL : &ImageX), OSTYPE_LIN, OSFLAG_NODEFAULTARGS);
      } //anyway continue search other entries
    }
    DirIterClose(&DirIter);

    // check for non-standard grub path
    for (Index = 0; Index < LinuxEntryDataCount; ++Index) {
      if (FileExists(Volume->RootDir, LinuxEntryData[Index].Path)) {
        XStringW OSIconName = XStringW().takeValueFrom(LinuxEntryData[Index].Icon);
        OSIconName = OSIconName.subString(0, OSIconName.indexOf(','));
        XIcon ImageX = ThemeX.GetIcon(L"os_"_XSW + OSIconName);
        if (ImageX.isEmpty()) {
          ImageX.LoadXImage(&ThemeX.getThemeDir(), L"os_"_XSW + OSIconName);
        }
        if (CustomPath) {
          *CustomPath = LinuxEntryData[Index].Path;
          if (CustomImage) {
            *CustomImage = ImageX;
          }
          return;
        }
        AddLoaderEntry(LinuxEntryData[Index].Path, NullXString8Array, L""_XSW, XStringW().takeValueFrom(LinuxEntryData[Index].Title), Volume,
                       (ImageX.isEmpty() ? NULL : &ImageX), OSTYPE_LIN, OSFLAG_NODEFAULTARGS);
      }
    }

  }

  if (Type != OSTYPE_LIN) { //OSTYPE_LINEFI or unspecified
    // check for linux kernels
    EFI_GUID PartGUID = FindGPTPartitionGuidInDevicePath(Volume->DevicePath);
    if ( PartGUID.notNull() && Volume->RootDir != NULL ) {
      REFIT_DIR_ITER  Iter;
      EFI_FILE_INFO  *FileInfo = NULL;
      EFI_TIME        PreviousTime;
      XStringW        Path;
      // CHAR16         *Options;
      // Get the partition UUID and make sure it's lower case
      CHAR16          PartUUID[40];
      ZeroMem(&PreviousTime, sizeof(EFI_TIME));
      snwprintf(PartUUID, sizeof(PartUUID), "%s", PartGUID.toXString8().c_str());
      StrToLower(PartUUID);
      // open the /boot directory (or whatever directory path)
      DirIterOpen(Volume->RootDir, LINUX_BOOT_PATH, &Iter);
  
      // Check which kernel scan to use
  
      // the following options can produce only a single option
      switch (KernelScan) {
        case KERNEL_SCAN_FIRST:
          // First kernel found only
          while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
            if (FileInfo != NULL) {
              if (FileInfo->FileSize == 0) {
                continue;
              }
              // get the kernel file path
              Path.SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
              // free the file info
              break;
            }
          }
          break;
        case KERNEL_SCAN_LAST:
          // Last kernel found only
          while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
            if (FileInfo != NULL) {
              if (FileInfo->FileSize > 0) {
                // get the kernel file path
                Path.SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
              }
            }
          }
          break;
        case KERNEL_SCAN_NEWEST:
          // Newest dated kernel only
          while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
            if (FileInfo != NULL) {
              if (FileInfo->FileSize > 0) {
                // get the kernel file path
                if ((PreviousTime.Year == 0) || (TimeCmp(&PreviousTime, &(FileInfo->ModificationTime)) < 0)) {
                  Path.SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
                  PreviousTime = FileInfo->ModificationTime;
                }
              }
            }
          }
          break;
        case KERNEL_SCAN_OLDEST:
          // Oldest dated kernel only
          while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
            if (FileInfo != NULL) {
              if (FileInfo->FileSize > 0) {
                // get the kernel file path
                if ((PreviousTime.Year == 0) || (TimeCmp(&PreviousTime, &(FileInfo->ModificationTime)) > 0)) {
                  Path.SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
                  PreviousTime = FileInfo->ModificationTime;
                }
              }
            }
          }
          break;
        case KERNEL_SCAN_MOSTRECENT:
          // most recent kernel version only
          while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
            if (FileInfo != NULL) {
              if (FileInfo->FileSize > 0) {
                // get the kernel file path
                XStringW NewPath = SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
                if ( Path < NewPath ) {
                   Path = NewPath;
                } else {
                    Path.setEmpty();
                }
              }
            }
          }
          break;
        case KERNEL_SCAN_EARLIEST:
          // earliest kernel version only
          while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
            if (FileInfo != NULL) {
              if (FileInfo->FileSize > 0) {
                // get the kernel file path
                XStringW NewPath = SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
                if ( Path > NewPath ) {
                  Path = NewPath;
                } else {
                  Path.setEmpty();
                }
              }
            }
          }
          break;
        case KERNEL_SCAN_NONE:
        default:
          // No kernel scan
          break;
      }

      // add the produced entry
      if (Path.notEmpty()) {
        if (CustomPath) {
          *CustomPath = Path;
          DirIterClose(&Iter);
          return;
        }
        XString8Array Options = LinuxKernelOptions(Iter.DirHandle, Basename(Path.wc_str()) + LINUX_LOADER_PATH.length(), PartUUID, NullXString8Array);
        // Add the entry
        AddLoaderEntry(Path, (Options.isEmpty()) ? LINUX_DEFAULT_OPTIONS : Options, L""_XSW, L""_XSW, Volume, NULL, OSTYPE_LINEFI, OSFLAG_NODEFAULTARGS);
        Path.setEmpty();
      }

      // the following produces multiple entries
      // custom entries has a different implementation, and does not use this code
      if (!CustomPath && KernelScan == KERNEL_SCAN_ALL) {
        // get all the filename matches
        while (DirIterNext(&Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo)) {
          if (FileInfo != NULL) {
            if (FileInfo->FileSize > 0) {
              // get the kernel file path
              Path.SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
              XString8Array Options = LinuxKernelOptions(Iter.DirHandle, Basename(Path.wc_str()) + LINUX_LOADER_PATH.length(), PartUUID, NullXString8Array);
              // Add the entry
              AddLoaderEntry(Path, (Options.isEmpty()) ? LINUX_DEFAULT_OPTIONS : Options, L""_XSW, L""_XSW, Volume, NULL, OSTYPE_LINEFI, OSFLAG_NODEFAULTARGS);
              Path.setEmpty();
            }
          }
        }
      }
      //close the directory
      DirIterClose(&Iter);
    }  
  }

}

#define Paper 1
#define Rock  2
#define Scissor 4

void AddPRSEntry(REFIT_VOLUME *Volume)
{
  INTN WhatBoot = 0;
  //CONST INTN Paper = 1;
  //CONST INTN Rock = 2;
  //CONST INTN Scissor = 4;

  WhatBoot |= FileExists(Volume->RootDir, RockBoot)?Rock:0;
  WhatBoot |= FileExists(Volume->RootDir, PaperBoot)?Paper:0;
  WhatBoot |= FileExists(Volume->RootDir, ScissorBoot)?Scissor:0;
  switch (WhatBoot) {
    case Paper:
    case (Paper | Rock):
      AddLoaderEntry(PaperBoot, NullXString8Array, L""_XSW, L"macOS InstallP"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0);
      break;
    case Scissor:
    case (Paper | Scissor):
      AddLoaderEntry(ScissorBoot, NullXString8Array, L""_XSW, L"macOS InstallS"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0);
      break;
    case Rock:
    case (Rock | Scissor):
    case (Rock | Scissor | Paper):
      AddLoaderEntry(RockBoot, NullXString8Array, L""_XSW, L"macOS InstallR"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0);
      break;

    default:
      break;
  }
}
#undef Paper
#undef Rock
#undef Scissor

XString8 GetAuthRootDmg(const EFI_FILE& dir, const XStringW& path)
{
  XString8 returnValue;

  XStringW plist = SWPrintf("%ls\\com.apple.Boot.plist", path.wc_str());
  if ( !FileExists(dir, plist) ) return NullXString8;

  CHAR8*     PlistBuffer = NULL;
  UINTN      PlistLen;
  TagDict*     Dict        = NULL;
  const TagStruct*     Prop        = NULL;

  EFI_STATUS Status = egLoadFile(&dir, plist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
  if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS)
  {
    Prop = Dict->propertyForKey("Kernel Flags");
    if ( Prop != NULL ) {
      if ( !Prop->isString() ) {
        MsgLog("ATTENTION : Kernel Flags not string in ProductVersion\n");
      }else{
        if( Prop->getString()->stringValue().notEmpty() ) {
          const XString8& kernelFlags = Prop->getString()->stringValue();
          size_t idx = kernelFlags.indexOf("auth-root-dmg");
          if ( idx == MAX_XSIZE ) return NullXString8;
          idx += strlen("auth-root-dmg");
          while ( idx < kernelFlags.length()  &&  IS_BLANK(kernelFlags[idx]) ) ++idx;
          if ( kernelFlags[idx] == '=' ) ++idx;
          else return NullXString8;
          while ( idx < kernelFlags.length()  &&  IS_BLANK(kernelFlags[idx]) ) ++idx;
          if ( kernelFlags.isEqualAtIC(idx, "file://"_XS8) ) idx += strlen("file://");
          size_t idxEnd = idx;
          while ( idxEnd < kernelFlags.length()  &&  !IS_BLANK(kernelFlags[idxEnd]) ) ++idxEnd;
          returnValue = kernelFlags.subString(idx, idxEnd - idx);
        }
      }
    }
  }
  if ( PlistBuffer ) FreePool(PlistBuffer);
  return returnValue;
}

MacOsVersion GetMacOSVersionFromFolder(const EFI_FILE& dir, const XStringW& path)
{
  MacOsVersion macOSVersion;

  XStringW plist = SWPrintf("%ls\\SystemVersion.plist", path.wc_str());
  if ( !FileExists(dir, plist) ) {
    plist = SWPrintf("%ls\\ServerVersion.plist", path.wc_str());
    if ( !FileExists(dir, plist) ) {
      plist.setEmpty();
    }
  }

  if ( plist.notEmpty() ) { // found macOS System
    CHAR8*     PlistBuffer = NULL;
    UINTN      PlistLen;
    TagDict*     Dict        = NULL;
    const TagStruct*     Prop        = NULL;

    EFI_STATUS Status = egLoadFile(&dir, plist.wc_str(), (UINT8 **)&PlistBuffer, &PlistLen);
    if (!EFI_ERROR(Status) && PlistBuffer != NULL && ParseXML(PlistBuffer, &Dict, 0) == EFI_SUCCESS) {
      Prop = Dict->propertyForKey("ProductVersion");
      if ( Prop != NULL ) {
        if ( !Prop->isString() ) {
          MsgLog("ATTENTION : property not string in ProductVersion\n");
        }else{
          if( Prop->getString()->stringValue().notEmpty() ) {
            macOSVersion = Prop->getString()->stringValue();
          }
        }
      }
    }
    if ( PlistBuffer ) FreePool(PlistBuffer);
  }
  return macOSVersion;
}

void ScanLoader(void)
{
  //DBG("Scanning loaders...\n");
  DbgHeader("ScanLoader");
   
  for (UINTN VolumeIndex = 0; VolumeIndex < Volumes.size(); VolumeIndex++)
  {
    REFIT_VOLUME* Volume = &Volumes[VolumeIndex];
    if (Volume->RootDir == NULL) { // || Volume->VolName == NULL)
      //DBG(", no file system\n", VolumeIndex);
      continue;
    }
    DBG("- [%02llu]: '%ls'", VolumeIndex, Volume->VolName.wc_str());
    if (Volume->VolName.isEmpty()) {
      Volume->VolName = L"Unknown"_XSW;
    }

    // skip volume if its kind is configured as disabled
    if (((1ull<<Volume->DiskKind) & GlobalConfig.DisableFlags) != 0)
    {
      DBG(", flagged disable\n");
      continue;
    }

    DBG("\n");

    if ( Volume->ApfsContainerUUID.notNull() ) DBG("    ApfsContainerUUID=%s\n", Volume->ApfsContainerUUID.toXString8().c_str());
    if ( Volume->ApfsFileSystemUUID.notNull() ) DBG("    ApfsFileSystemUUID=%s\n", Volume->ApfsFileSystemUUID.toXString8().c_str());


    // check for Mac OS X Install Data
    // 1st stage - createinstallmedia
    if (FileExists(Volume->RootDir, L"\\.IABootFiles\\boot.efi")) {
      if (FileExists(Volume->RootDir, L"\\Install OS X Mavericks.app") ||
          FileExists(Volume->RootDir, L"\\Install OS X Yosemite.app") ||
          FileExists(Volume->RootDir, L"\\Install OS X El Capitan.app")) {
        AddLoaderEntry(L"\\.IABootFiles\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.9 - 10.11
      } else {
        AddLoaderEntry(L"\\.IABootFiles\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.12 - 10.13.3
      }
    } else if (FileExists(Volume->RootDir, L"\\.IAPhysicalMedia") && FileExists(Volume->RootDir, MACOSX_LOADER_PATH)) {
      AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.13.4+
    }
    // 2nd stage - InstallESD/AppStore/startosinstall/Fusion Drive
     // 10.7
    AddLoaderEntry(L"\\Mac OS X Install Data\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"Mac OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.7
    // 10.8 - 10.11
    AddLoaderEntry(L"\\OS X Install Data\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.8 - 10.11
    // 10.12 - 10.12.3
    AddLoaderEntry(L"\\macOS Install Data\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.12 - 10.12.3
    // 10.12.4-10.15
    AddLoaderEntry(L"\\macOS Install Data\\Locked Files\\Boot Files\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0);
    // Big Sur install must be via Preboot. Next line must stay commented.
    // AddLoaderEntry(L"\\macOS Install Data\\Locked Files\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 11+
    AddPRSEntry(Volume); // 10.12+

    // Netinstall
    AddLoaderEntry(L"\\NetInstall macOS High Sierra.nbi\\i386\\booter"_XSW, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0);
    // Use standard location for boot.efi, according to the install files is present
    // That file indentifies a DVD/ESD/BaseSystem/Fusion Drive Install Media, so when present, check standard path to avoid entry duplication
    if (FileExists(Volume->RootDir, MACOSX_LOADER_PATH)) {
      if (FileExists(Volume->RootDir, L"\\System\\Installation\\CDIS\\Mac OS X Installer.app")) {
        // InstallDVD/BaseSystem
        AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"Mac OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.6/10.7
      } else if (FileExists(Volume->RootDir, L"\\System\\Installation\\CDIS\\OS X Installer.app")) {
        // BaseSystem
        AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.8 - 10.11
      } else if (FileExists(Volume->RootDir, L"\\System\\Installation\\CDIS\\macOS Installer.app")) {
        // BaseSystem
        AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"macOS Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.12+
      } else if (FileExists(Volume->RootDir, L"\\BaseSystem.dmg") && FileExists(Volume->RootDir, L"\\mach_kernel")) {
        // InstallESD
        if (FileExists(Volume->RootDir, L"\\MacOSX_Media_Background.png")) {
          AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"Mac OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.7
        } else {
          AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.8
        }
      } else if (FileExists(Volume->RootDir, L"\\com.apple.boot.R\\System\\Library\\PrelinkedKernels\\prelinkedkernel") ||
                 FileExists(Volume->RootDir, L"\\com.apple.boot.P\\System\\Library\\PrelinkedKernels\\prelinkedkernel") ||
                 FileExists(Volume->RootDir, L"\\com.apple.boot.S\\System\\Library\\PrelinkedKernels\\prelinkedkernel")) {
        if (StriStr(Volume->VolName.wc_str(), L"Recovery") != NULL) {
          // FileVault of HFS+
          // TODO: need info for 10.11 and lower
          AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"macOS FileVault"_XSW, Volume, NULL, OSTYPE_OSX, 0); // 10.12+
        } else {
          // Fusion Drive
          AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"OS X Install"_XSW, Volume, NULL, OSTYPE_OSX_INSTALLER, 0); // 10.11
        }
      } else if (!FileExists(Volume->RootDir, L"\\.IAPhysicalMedia")) {
        // Installed
        if (EFI_ERROR(GetRootUUID(Volume)) || isFirstRootUUID(Volume)) {
          if (!FileExists(Volume->RootDir, L"\\System\\Library\\CoreServices\\NotificationCenter.app") && !FileExists(Volume->RootDir, L"\\System\\Library\\CoreServices\\Siri.app")) {
            AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"Mac OS X"_XSW, Volume, NULL, OSTYPE_OSX, 0); // 10.6 - 10.7
          } else if (FileExists(Volume->RootDir, L"\\System\\Library\\CoreServices\\NotificationCenter.app") && !FileExists(Volume->RootDir, L"\\System\\Library\\CoreServices\\Siri.app")) {
            AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"OS X"_XSW, Volume, NULL, OSTYPE_OSX, 0); // 10.8 - 10.11
          } else {
            MacOsVersion macOSVersion;
            if ( Volume->ApfsFileSystemUUID.notNull() && (Volume->ApfsRole & APPLE_APFS_VOLUME_ROLE_SYSTEM) != 0 )
            {
              macOSVersion = GetMacOSVersionFromFolder(*Volume->RootDir, L"\\System\\Library\\CoreServices"_XSW);
            }
            if ( macOSVersion < MacOsVersion("11"_XS8) ) {
              AddLoaderEntry(MACOSX_LOADER_PATH, NullXString8Array, L""_XSW, L"macOS"_XSW, Volume, NULL, OSTYPE_OSX, 0); // 10.12+
            }
          }
        }
      }
    }

    // check for Mac OS X Recovery Boot
    AddLoaderEntry(L"\\com.apple.recovery.boot\\boot.efi"_XSW, NullXString8Array, L""_XSW, L"Recovery"_XSW, Volume, NULL, OSTYPE_RECOVERY, 0);

    // Sometimes, on some systems (HP UEFI, if Win is installed first)
    // it is needed to get rid of bootmgfw.efi to allow starting of
    // Clover as /efi/boot/bootx64.efi from HD. We can do that by renaming
    // bootmgfw.efi to bootmgfw-orig.efi
    AddLoaderEntry(L"\\EFI\\microsoft\\Boot\\bootmgfw-orig.efi"_XSW, NullXString8Array, L""_XSW, L"Microsoft EFI"_XSW, Volume, NULL, OSTYPE_WINEFI, 0);
    // check for Microsoft boot loader/menu
    // If there is bootmgfw-orig.efi, then do not check for bootmgfw.efi
    // since on some systems this will actually be CloverX64.efi
    // renamed to bootmgfw.efi
    AddLoaderEntry(L"\\EFI\\microsoft\\Boot\\bootmgfw.efi"_XSW, NullXString8Array, L""_XSW, L"Microsoft EFI Boot"_XSW, Volume, NULL, OSTYPE_WINEFI, 0);
    // check for Microsoft boot loader/menu. This entry is redundant so excluded
    // AddLoaderEntry(L"\\bootmgr.efi", L"", L"Microsoft EFI mgrboot", Volume, NULL, OSTYPE_WINEFI, 0);
    // check for Microsoft boot loader/menu on CDROM
    if (!AddLoaderEntry(L"\\EFI\\MICROSOFT\\BOOT\\cdboot.efi"_XSW, NullXString8Array, L""_XSW, L"Microsoft EFI cdboot"_XSW, Volume, NULL, OSTYPE_WINEFI, 0)) {
      AddLoaderEntry(L"\\EFI\\MICROSOFT\\BOOT\\CDBOOT.EFI"_XSW, NullXString8Array, L""_XSW, L"Microsoft EFI CDBOOT"_XSW, Volume, NULL, OSTYPE_WINEFI, 0);
    }


#if defined(ANDX86)
    if (true) { //gSettings.AndroidScan
      // check for Android loaders
      for (UINTN Index = 0; Index < AndroidEntryDataCount; ++Index) {
        UINTN aIndex, aFound;
      if (FileExists(Volume->RootDir, AndroidEntryData[Index].Path)) {
          aFound = 0;
          for (aIndex = 0; aIndex < ANDX86_FINDLEN; ++aIndex) {
            if ((AndroidEntryData[Index].Find[aIndex].isEmpty()) || FileExists(Volume->RootDir, AndroidEntryData[Index].Find[aIndex])) ++aFound;
          }
          if (aFound && (aFound == aIndex)) {
            XIcon ImageX;
            XStringW IconXSW = XStringW().takeValueFrom(AndroidEntryData[Index].Icon);
            ImageX.LoadXImage(&ThemeX.getThemeDir(), (L"os_"_XSW + IconXSW.subString(0, IconXSW.indexOf(','))).wc_str());
            AddLoaderEntry(AndroidEntryData[Index].Path, NullXString8Array, L""_XSW, XStringW().takeValueFrom(AndroidEntryData[Index].Title), Volume,
                           (ImageX.isEmpty() ? NULL : &ImageX), OSTYPE_LIN, OSFLAG_NODEFAULTARGS);
          }
        }
      }
    }
#endif

    if (gSettings.GUI.Scan.LinuxScan) {
      LinuxScan(Volume, gSettings.GUI.Scan.KernelScan, 0, NULL, NULL);
    }

    //     DBG("search for  optical UEFI\n");
    if (Volume->DiskKind == DISK_KIND_OPTICAL) {
      AddLoaderEntry(BOOT_LOADER_PATH, NullXString8Array, L""_XSW, L"UEFI optical"_XSW, Volume, NULL, OSTYPE_OTHER, 0);
    }
    //     DBG("search for internal UEFI\n");
    if (Volume->DiskKind == DISK_KIND_INTERNAL) {
      LOADER_ENTRY* le = AddLoaderEntry(BOOT_LOADER_PATH, NullXString8Array, L""_XSW, L"UEFI internal"_XSW, Volume, NULL, OSTYPE_OTHER, 0);
      if ( le ) {
        DBG("     hiding entry because DiskKind is DISK_KIND_INTERNAL: %ls\n", le->LoaderPath.s());
        le->Hidden = true;
      }
    }
    //    DBG("search for external UEFI\n");
    if (Volume->DiskKind == DISK_KIND_EXTERNAL) {
      LOADER_ENTRY* le = AddLoaderEntry(BOOT_LOADER_PATH, NullXString8Array, L""_XSW, L"UEFI external"_XSW, Volume, NULL, OSTYPE_OTHER, 0);
      if ( le ) {
        DBG("     hiding entry because DiskKind is DISK_KIND_EXTERNAL: %ls\n", le->LoaderPath.s());
        le->Hidden = true;
      }
    }

//DBG("Volume->ApfsTargetUUIDArray.size()=%zd\n", Volume->ApfsTargetUUIDArray.size());
    if ( Volume->ApfsTargetUUIDArray.size() > 0 ) {

      for (UINTN i = 0; i < Volume->ApfsTargetUUIDArray.size(); i++)
      {
        const EFI_GUID& ApfsTargetUUID = Volume->ApfsTargetUUIDArray[i];
        DBG("    APFSTargetUUID=%s\n", ApfsTargetUUID.toXString8().c_str());
        XStringW FullTitle;
        XStringW FullTitleRecovery;
        XStringW FullTitleInstaller;
        XStringW LoaderTitle;
        XStringW LoaderTitleInstaller;

        // Find the "target" volume.
        REFIT_VOLUME* targetVolume = Volumes.getVolumeWithApfsContainerUUIDAndFileSystemUUID(Volume->ApfsContainerUUID, ApfsTargetUUID);
        // If targetVolume is found, and it's a data partition, try to find the system partition that goes with it.
//DBG("targetVolume=%d\n", targetVolume ? 1 : 0);
        if ( targetVolume ) {
          if ( (targetVolume->ApfsRole & APPLE_APFS_VOLUME_ROLE_DATA) != 0 ) {
            for (size_t VolumeIndex2 = 0; VolumeIndex2 < Volumes.size(); VolumeIndex2++) {
              REFIT_VOLUME* Volume2 = &Volumes[VolumeIndex2];
//DBG("idx=%zu  name %ls  uuid=%s \n", VolumeIndex2, Volume2->VolName.wc_str(), Volume2->ApfsFileSystemUUID.c_str());
              if ( Volume2->ApfsContainerUUID == targetVolume->ApfsContainerUUID ) {
                if ( (Volume2->ApfsRole & APPLE_APFS_VOLUME_ROLE_SYSTEM) != 0 ) {
                  if ( !targetVolume ) {
                    targetVolume = Volume2;
                  }else{
                    // More than one system partition in container. I don't know how to select which one is supposed to pair with this
                    targetVolume = NULL; // we'll try .disk_label.contentDetails
                    break; // we need to escape the loop after bootVolume = NULL;
                  }
                }
              }
            }
          }
        }
        // If targetVolume is not found, and it's not a recovery, find the preboot volume from the same container
//DBG("2) targetVolume=%d\n", targetVolume ? 1 : 0);
        if ( !targetVolume ) {
          REFIT_VOLUME* bootVolume = Volume;
          if ( (Volume->ApfsRole & APPLE_APFS_VOLUME_ROLE_RECOVERY) != 0 ) {
            for (size_t VolumeIndex2 = 0; VolumeIndex2 < Volumes.size(); VolumeIndex2++) {
              REFIT_VOLUME* Volume2 = &Volumes[VolumeIndex2];
//DBG("idx=%zu  name %ls  uuid=%s \n", VolumeIndex2, Volume2->VolName.wc_str(), Volume2->ApfsFileSystemUUID.c_str());
              if ( (Volume2->ApfsRole & APPLE_APFS_VOLUME_ROLE_PREBOOT) != 0 ) {
                if ( Volume2->ApfsContainerUUID == Volume->ApfsContainerUUID ) {
                  bootVolume = Volume2;
                  break;
                }
              }
            }
          }
          if ( bootVolume ) {
            XStringW targetNameFile;
            CHAR8*  fileBuffer;
            UINTN   fileLen = 0;
            targetNameFile.SWPrintf("%s\\System\\Library\\CoreServices\\.disk_label.contentDetails", ApfsTargetUUID.toXString8().c_str());
            if ( FileExists(bootVolume->RootDir, targetNameFile) ) {
              EFI_STATUS Status = egLoadFile(bootVolume->RootDir, targetNameFile.wc_str(), (UINT8 **)&fileBuffer, &fileLen);
              if(!EFI_ERROR(Status)) {
                FullTitle.SWPrintf("Boot Mac OS from %.*s", (int)fileLen, fileBuffer);
                FullTitleRecovery.SWPrintf("Boot Mac OS Recovery for %.*s", (int)fileLen, fileBuffer);
                FullTitleInstaller.SWPrintf("Boot Mac OS Install for %.*s", (int)fileLen, fileBuffer);
                if ( fileLen < MAX_INT32 ) {
                  DBG("      contentDetails name:%.*s\n", (int)fileLen, fileBuffer);
                }
                FreePool(fileBuffer);
              }
            }
          }
        }
        if ( FullTitle.isEmpty() ) {
          if ( targetVolume ) {
            FullTitle.SWPrintf("Boot Mac OS from %ls", targetVolume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());
            FullTitleRecovery.SWPrintf("Boot Mac OS Recovery for %ls", targetVolume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());
            FullTitleInstaller.SWPrintf("Boot Mac OS Install for %ls", targetVolume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());
          }else{
            FullTitle.SWPrintf("Boot Mac OS");
            FullTitleRecovery.SWPrintf("Boot Mac OS Recovery");
            FullTitleInstaller.SWPrintf("Mac OS Install");
          }
        }
        /*MacOsVersion macOSVersion = GetMacOSVersionFromFolder(*Volume->RootDir, SWPrintf("\\%s\\System\\Library\\CoreServices", ApfsTargetUUID.c_str()));
        if ( macOSVersion.notEmpty() && macOSVersion < MacOsVersion("11"_XS8) )*/ FullTitle.SWCatf(" via %ls", Volume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());
        AddLoaderEntry(SWPrintf("\\%s\\System\\Library\\CoreServices\\boot.efi", ApfsTargetUUID.toXString8().c_str()), NullXString8Array, FullTitle, LoaderTitle, Volume, NULL, OSTYPE_OSX, 0);

        //Try to add Recovery APFS entry
        /*macOSVersion = GetMacOSVersionFromFolder(*Volume->RootDir, SWPrintf("\\%s", ApfsTargetUUID.c_str()));
        if ( macOSVersion.notEmpty() && macOSVersion < MacOsVersion("11"_XS8) )*/ FullTitleRecovery.SWCatf(" via %ls", Volume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());
        if (!AddLoaderEntry(SWPrintf("\\%s\\boot.efi", Volume->ApfsTargetUUIDArray[i].toXString8().c_str()), NullXString8Array, FullTitleRecovery, L""_XSW, Volume, NULL, OSTYPE_RECOVERY, 0)) {
          //Try to add Recovery APFS entry as dmg
          AddLoaderEntry(SWPrintf("\\%s\\BaseSystem.dmg", Volume->ApfsTargetUUIDArray[i].toXString8().c_str()), NullXString8Array, FullTitleRecovery, L""_XSW, Volume, NULL, OSTYPE_RECOVERY, 0);
        }
       //Try to add macOS install entry
        /*macOSVersion = GetMacOSVersionFromFolder(*Volume->RootDir, SWPrintf("\\%s\\com.apple.installer", ApfsTargetUUID.c_str()));
        if ( macOSVersion.notEmpty() && macOSVersion < MacOsVersion("11"_XS8) )*/ FullTitleInstaller.SWCatf(" via %ls", Volume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());

        XString8 installerPath = SWPrintf("\\%s\\com.apple.installer", Volume->ApfsTargetUUIDArray[i].toXString8().c_str());
        if ( FileExists(Volume->RootDir, installerPath) ) {
          XString8 rootDmg = GetAuthRootDmg(*Volume->RootDir, installerPath);
          rootDmg.replaceAll("%20"_XS8, " "_XS8);
//          while ( rootDmg.notEmpty()  &&  rootDmg.startWith('/') ) rootDmg.deleteCharsAtPos(0, 1);
          rootDmg.replaceAll('/', '\\');
          REFIT_VOLUME* targetInstallVolume = Volumes.getVolumeWithApfsContainerUUIDAndFileSystemUUID(Volume->ApfsContainerUUID, Volume->ApfsTargetUUIDArray[i]);
          if ( targetInstallVolume ) {
            EFI_FILE_PROTOCOL* TestFile;
            EFI_STATUS Status = targetInstallVolume->RootDir->Open(targetInstallVolume->RootDir, &TestFile, L"\\", EFI_FILE_MODE_READ, 0);
            if ( EFI_ERROR(Status) ) TestFile = NULL; // if the root of the volume can't be opened (most likely encrypted), add the installer anyway.
            if ( rootDmg.isEmpty()  ||  EFI_ERROR(Status)  ||  FileExists(*targetInstallVolume->RootDir, rootDmg) ) { // rootDmg empty is accepted, to be compatible with previous code
              AddLoaderEntry(SWPrintf("\\%s\\com.apple.installer\\boot.efi", Volume->ApfsTargetUUIDArray[i].toXString8().c_str()), NullXString8Array, FullTitleInstaller, LoaderTitleInstaller, Volume, NULL, OSTYPE_OSX_INSTALLER, 0);
            }else{
              DBG("    Dead installer entry found (installer dmg boot file not found : '%s')\n", rootDmg.c_str());
            }
            if ( TestFile != NULL ) TestFile->Close(TestFile);
          }else{
            DBG("    Dead installer entry found (target volume not found : '%s')\n", Volume->ApfsTargetUUIDArray[i].toXString8().c_str());
          }
        }
      }
    }
  }

  DBG("Entries list before ordering\n");
  for (size_t idx = 0; idx < MainMenu.Entries.sizeIncludingHidden(); idx++) {
    if ( MainMenu.Entries.ElementAt(idx).getLOADER_ENTRY() ) {
      DBG("    Entry %zd : %ls%s\n", idx, MainMenu.Entries.ElementAt(idx).Title.wc_str(), MainMenu.Entries.ElementAt(idx).Hidden ? " (hidden)" : "");
    }else{
      DBG("    Entry %zd : %ls%s\n", idx, MainMenu.Entries.ElementAt(idx).Title.wc_str(), MainMenu.Entries.ElementAt(idx).Hidden ? " (hidden)" : "");
    }
  }

  // Hide redundant preboot partition
  for (size_t entryIdx1 = 0; entryIdx1 < MainMenu.Entries.sizeIncludingHidden(); entryIdx1++)
  {
    LOADER_ENTRY* loaderEntry1Ptr = MainMenu.Entries.ElementAt(entryIdx1).getLOADER_ENTRY();
    if ( !loaderEntry1Ptr ) continue;
    LOADER_ENTRY& loaderEntry1 = *loaderEntry1Ptr;

    if ( ( loaderEntry1.LoaderType == OSTYPE_OSX || loaderEntry1.LoaderType == OSTYPE_OSX_INSTALLER )  &&  loaderEntry1.APFSTargetUUID.notNull() )
    {
      size_t entryIdx2 = MainMenu.Entries.getApfsLoaderIdx(loaderEntry1.Volume->ApfsContainerUUID, loaderEntry1.APFSTargetUUID, loaderEntry1.LoaderType);
      if ( entryIdx2 != SIZE_T_MAX ) {
        DBG("Hiding entry %zd because of entry %zd\n", entryIdx1, entryIdx2);
        loaderEntry1.Hidden = true;
      }
    }
  }



  typedef struct EntryIdx {
    size_t idx;
    REFIT_ABSTRACT_MENU_ENTRY* entry;
    EntryIdx(size_t _idx, REFIT_ABSTRACT_MENU_ENTRY* _entry) : idx(_idx), entry(_entry) {};
  } EntryIdx;

  XObjArray<EntryIdx> EntriesArrayTmp;

  for (size_t idx = 0; idx < MainMenu.Entries.sizeIncludingHidden(); idx++) {
    if ( MainMenu.Entries.ElementAt(idx).getLOADER_ENTRY() ) {
      if ( MainMenu.Entries.ElementAt(idx).getLOADER_ENTRY()->APFSTargetUUID.notNull() ) {
//        DBG("Add in EntriesArrayTmp at index %zd  Entry %zd : %ls\n", EntriesArrayTmp.size(), idx, MainMenu.Entries.ElementAt(idx).Title.wc_str());
        EntriesArrayTmp.AddReference(new EntryIdx(idx, &MainMenu.Entries.ElementAt(idx)), true);
      }
    }
  }

  XBool hasMovedSomething;

  // Re-order preboot partition
  do {
    hasMovedSomething = false;
    for (size_t idx = 0; !hasMovedSomething && idx < EntriesArrayTmp.size(); )
    {
      LOADER_ENTRY* loaderEntry1Ptr = EntriesArrayTmp.ElementAt(idx).entry->getLOADER_ENTRY();
      if ( !loaderEntry1Ptr ) {
        EntriesArrayTmp.RemoveAtIndex(idx);
        // do not increment idx
        continue;
      }
      LOADER_ENTRY& loaderEntry1 = *loaderEntry1Ptr;

      if ( loaderEntry1.LoaderType == OSTYPE_OSX  &&  (loaderEntry1.Volume->ApfsRole & APPLE_APFS_VOLUME_ROLE_PREBOOT) != 0 )
      {
        size_t prebootIdx = MainMenu.Entries.getIdx(loaderEntry1Ptr);
        if ( prebootIdx == SIZE_T_MAX ) {
        	log_technical_bug("%s : prebootIdx == SIZE_T_MAX", __PRETTY_FUNCTION__);
        }else{
          size_t idxMain = MainMenu.Entries.getApfsLoaderIdx(loaderEntry1.Volume->ApfsContainerUUID, loaderEntry1.APFSTargetUUID, OSTYPE_OSX);
          if ( idxMain != SIZE_T_MAX && idxMain != prebootIdx+1 ) {
            DBG("Move preboot entry %zu before system %zu\n", prebootIdx, idxMain);
            MainMenu.Entries.moveBefore(prebootIdx, idxMain); // this will move preboot entry just before main
            EntriesArrayTmp.RemoveAtIndex(idx);
            hasMovedSomething = true;
          }
        }
      }
      ++idx;
    }
  } while ( hasMovedSomething );

  // Re-order installer partition
  do {
    hasMovedSomething = false;
    for (size_t idx = 0; !hasMovedSomething && idx < EntriesArrayTmp.size(); )
    {
      LOADER_ENTRY* loaderEntry1Ptr = EntriesArrayTmp.ElementAt(idx).entry->getLOADER_ENTRY();
      if ( !loaderEntry1Ptr ) {
        EntriesArrayTmp.RemoveAtIndex(idx);
        // do not increment idx
        continue;
      }
      LOADER_ENTRY& loaderEntry1 = *loaderEntry1Ptr;

      if ( loaderEntry1.LoaderType == OSTYPE_OSX_INSTALLER )
      {
        size_t installerIdx = MainMenu.Entries.getIdx(loaderEntry1Ptr);
        if ( installerIdx == SIZE_T_MAX ) {
          log_technical_bug("%s : installerIdx == SIZE_T_MAX", __PRETTY_FUNCTION__);
        }else{
          size_t idxPreboot = MainMenu.Entries.getApfsPrebootLoaderIdx(loaderEntry1.Volume->ApfsContainerUUID, loaderEntry1.APFSTargetUUID, OSTYPE_OSX);
          if ( idxPreboot != SIZE_T_MAX ) {
            if ( idxPreboot != installerIdx + 1 ) {
              DBG("Move installer entry %zu before preboot %zu\n", EntriesArrayTmp.ElementAt(idx).idx, idxPreboot);
              MainMenu.Entries.moveBefore(installerIdx, idxPreboot); // this will move preboot entry just before main
              EntriesArrayTmp.RemoveAtIndex(idx);
              hasMovedSomething = true;
            }
          }else{
            size_t idxMain = MainMenu.Entries.getApfsLoaderIdx(loaderEntry1.Volume->ApfsContainerUUID, loaderEntry1.APFSTargetUUID, OSTYPE_OSX);
            if ( idxMain != SIZE_T_MAX ) {
              if ( idxMain != installerIdx+1 ) {
                DBG("Move installer entry %zu before system %zu\n", EntriesArrayTmp.ElementAt(idx).idx, idxMain);
                MainMenu.Entries.moveBefore(installerIdx, idxMain); // this will move preboot entry just before main
                EntriesArrayTmp.RemoveAtIndex(idx);
                hasMovedSomething = true;
              }
            }
          }
        }
      }
      ++idx;
    }
  } while ( hasMovedSomething );

  // Re-order recovery partition
  do {
    hasMovedSomething = false;
    for (size_t idx = 0; !hasMovedSomething && idx < EntriesArrayTmp.size(); )
    {
      LOADER_ENTRY* loaderEntry1Ptr = EntriesArrayTmp.ElementAt(idx).entry->getLOADER_ENTRY();
      if ( !loaderEntry1Ptr ) {
        EntriesArrayTmp.RemoveAtIndex(idx);
        // do not increment idx
        continue;
      }
      LOADER_ENTRY& loaderEntry1 = *loaderEntry1Ptr;

      if ( loaderEntry1.LoaderType == OSTYPE_RECOVERY  &&  (loaderEntry1.Volume->ApfsRole & APPLE_APFS_VOLUME_ROLE_RECOVERY) != 0 )
      {
        size_t recoveryIdx = MainMenu.Entries.getIdx(loaderEntry1Ptr);
        if ( recoveryIdx == SIZE_T_MAX ) {
          log_technical_bug("%s : recoveryIdx == SIZE_T_MAX", __PRETTY_FUNCTION__);
        }else{
          size_t idxMain = MainMenu.Entries.getApfsLoaderIdx(loaderEntry1.Volume->ApfsContainerUUID, loaderEntry1.APFSTargetUUID, OSTYPE_OSX);
          if ( idxMain != SIZE_T_MAX ) {
            if ( idxMain + 1 != recoveryIdx ) {
              DBG("Move recovery entry %zu after system %zu\n", EntriesArrayTmp.ElementAt(idx).idx, idxMain);
              MainMenu.Entries.moveAfter(recoveryIdx, idxMain); // this will move preboot entry just before main
              EntriesArrayTmp.RemoveAtIndex(idx);
              hasMovedSomething = true;
            }
          }else{
            size_t idxPreboot = MainMenu.Entries.getApfsPrebootLoaderIdx(loaderEntry1.Volume->ApfsContainerUUID, loaderEntry1.APFSTargetUUID, OSTYPE_OSX);
            if ( idxPreboot != SIZE_T_MAX ) {
              if ( idxPreboot + 1 != recoveryIdx ) {
                DBG("Move recovery entry %zu after preboot %zu\n", EntriesArrayTmp.ElementAt(idx).idx, idxPreboot);
                MainMenu.Entries.moveAfter(recoveryIdx, idxPreboot); // this will move preboot entry just before main
                EntriesArrayTmp.RemoveAtIndex(idx);
                hasMovedSomething = true;
              }
            }
          }
        }
      }
      ++idx;
    }
  } while ( hasMovedSomething );


  DBG("Entries list after ordering\n");
  for (size_t idx = 0; idx < MainMenu.Entries.sizeIncludingHidden(); idx++) {
 //   if ( MainMenu.Entries.ElementAt(idx).getLOADER_ENTRY() ) {
 //     DBG("  Entry %zd : %ls%s\n", idx, MainMenu.Entries.ElementAt(idx).Title.wc_str(), MainMenu.Entries.ElementAt(idx).Hidden ? " (hidden)" : "");
 //   }else{
      DBG("  Entry %zd : %ls%s\n", idx, MainMenu.Entries.ElementAt(idx).Title.wc_str(), MainMenu.Entries.ElementAt(idx).Hidden ? " (hidden)" : "");
 //   }
  }

}

STATIC void AddCustomSubEntry(REFIT_VOLUME   *Volume,
                           IN UINTN                       CustomIndex,
                           IN const XStringW&             CustomPath,
                           UINT8 parentType,
                           IN const CUSTOM_LOADER_SUBENTRY&  Custom,
                           IN const XStringW&             DefaultEntrySettings,
                           IN REFIT_MENU_SCREEN          *SubMenu)
{
//  UINTN           VolumeIndex;
//  REFIT_VOLUME   *Volume;
//  REFIT_DIR_ITER  SIter;
//  REFIT_DIR_ITER *Iter = &SIter;
//  CHAR16          PartUUID[40];
//  XStringW        CustomPath = _CustomPath;

  if ( CustomPath.isEmpty() ) panic("BUG : CustomPath is empty");
  if ( SubMenu == NULL ) panic("BUG : this must be a sub entry");

//  if (FindCustomPath && (Custom.settings.Type != OSTYPE_LINEFI) && (Custom.settings.Type != OSTYPE_LIN)) {
////    DBG("Custom %lsentry %llu skipped because it didn't have a ", IsSubEntry ? L"sub " : L"", CustomIndex);
////    if (Custom.Type == 0) {
////      DBG("Type.\n");
////    } else {
////      DBG("Path.\n");
////    }
//    return;
//  }

  if ( Custom.settings.Disabled ) {
//    DBG("Custom %lsentry %llu skipped because it is disabled.\n", IsSubEntry ? L"sub " : L"", CustomIndex);
    return;
  }

//  if (!gSettings.ShowHiddenEntries && OSFLAG_ISSET(Custom.settings.Flags, OSFLAG_HIDDEN)) {
//    DBG("Custom %lsentry %llu skipped because it is hidden.\n", IsSubEntry ? L"sub " : L"", CustomIndex);
//    return;
//  }

#if 0  //if someone want to debug this
  DBG("Custom %lsentry %llu ", IsSubEntry ? L"sub " : L"", CustomIndex);
  //  if (Custom.settings.Title) {
  DBG("Title:\"%ls\" ", Custom.settings.Title.wc_str());
  //  }
  //  if (Custom.settings.FullTitle) {
  DBG("FullTitle:\"%ls\" ", Custom.settings.FullTitle.wc_str());
  //  }
  if (CustomPath) {
    DBG("Path:\"%ls\" ", CustomPath);
  }
  if (Custom.settings.Options != NULL) {
    DBG("Options:\"%ls\" ", Custom.settings.Options);
  }
  DBG("Type:%d Flags:0x%hhX matching ", Custom.settings.Type, Custom.settings.Flags);
  if (Custom.settings.Volume) {
    DBG("Volume:\"%ls\"\n", Custom.settings.Volume);
  } else {
    DBG("all volumes\n");
  }
#endif

    LOADER_ENTRY        *Entry = NULL;

    if ((Volume == NULL) || (Volume->RootDir == NULL)) {
      return;
    }
    if (Volume->VolName.isEmpty()) {
      Volume->VolName = L"Unknown"_XSW;
    }

//    do { // when not scanning for kernels, this loop will execute only once
      XString8Array CustomOptions = Custom.getLoadOptions();

      UINT8 newCustomFlags = Custom.getFlags(gSettings.SystemParameters.NoCaches);

      // Create an entry for this volume
      Entry = CreateLoaderEntry(CustomPath, CustomOptions, Custom.getFullTitle(), Custom.getTitle(), Volume,
                                NULL, NULL,
                                parentType, newCustomFlags, 0, {0,0,0,0}, 0, NullXImage,
                                /*(KERNEL_AND_KEXT_PATCHES *)(((UINTN)Custom) + OFFSET_OF(CUSTOM_LOADER_ENTRY, KernelAndKextPatches))*/ NULL, true);
      if (Entry != NULL) {
//        if ( Custom.settings.Settings.notEmpty() ) DBG("Custom settings: %ls.plist will %s be applied\n", Custom.settings.Settings.wc_str(), Custom.settings.CommonSettings?"not":"");
//        if (!Custom.settings.CommonSettings) {
//          Entry->Settings = DefaultEntrySettings;
//        }
        if (OSFLAG_ISUNSET(newCustomFlags, OSFLAG_NODEFAULTMENU)) {
          Entry->AddDefaultMenu();
//        } else if (Custom.SubEntries.notEmpty()) {
//          UINTN CustomSubIndex = 0;
//          // Add subscreen
//          REFIT_MENU_SCREEN *SubScreen = new REFIT_MENU_SCREEN;
//          SubScreen->Title.SWPrintf("Boot Options for %ls on %ls", (Custom.settings.Title.notEmpty()) ? Custom.settings.Title.wc_str() : CustomPath.wc_str(), Entry->DisplayedVolName.wc_str());
//          SubScreen->TitleImage = Entry->Image;
//          SubScreen->ID = Custom.settings.Type + 20;
//          SubScreen->GetAnime();
//          VolumeSize = RShiftU64(MultU64x32(Volume->BlockIO->Media->LastBlock, Volume->BlockIO->Media->BlockSize), 20);
//          SubScreen->AddMenuInfoLine_f("Volume size: %lldMb", VolumeSize);
//          SubScreen->AddMenuInfoLine_f("%ls", FileDevicePathToXStringW(Entry->DevicePath).wc_str());
//          if (Guid) {
//            SubScreen->AddMenuInfoLine_f("UUID: %s", Guid.toXString8().c_str());
//          }
//          SubScreen->AddMenuInfoLine_f("Options: %s", Entry->LoadOptions.ConcatAll(" "_XS8).c_str());
//          DBG("Create sub entries\n");
//          for (size_t CustomSubEntryIndex = 0 ; CustomSubEntryIndex < Custom.SubEntries.size() ; ++CustomSubEntryIndex ) {
//            const CUSTOM_LOADER_SUBENTRY& CustomSubEntry = Custom.SubEntries[CustomSubEntryIndex];
//            if ( CustomSubEntry.settings.Settings.isEmpty() ) {
//              AddCustomSubEntry(Volume, CustomSubIndex++, CustomSubEntry.settings.Path.notEmpty() ? CustomSubEntry.settings.Path : CustomPath, CustomSubEntry, Custom.settings.Settings, SubScreen);
//            }else{
//              AddCustomSubEntry(Volume, CustomSubIndex++, CustomSubEntry.settings.Path.notEmpty() ? CustomSubEntry.settings.Path : CustomPath, CustomSubEntry, CustomSubEntry.settings.Settings, SubScreen);
//            }
//          }
//          SubScreen->AddMenuEntry(&MenuEntryReturn, true);
//          Entry->SubScreen = SubScreen;
        }
        SubMenu->AddMenuEntry(Entry, true);
//        Entry->Hidden = Custom.settings.Hidden;
//        if ( Custom.settings.Hidden ) DBG("     hiding entry because Custom.settings.Hidden\n");
      }
//    } while (FindCustomPath && Custom.settings.Type == OSTYPE_LINEFI && Custom.settings.KernelScan == KERNEL_SCAN_ALL); // repeat loop only for kernel scanning

//    // Close the kernel boot directory
//    if (FindCustomPath && Custom.settings.Type == OSTYPE_LINEFI) {
//      DirIterClose(Iter);
//    }
//  }

}

STATIC void AddCustomEntry(IN UINTN                       CustomIndex,
                           IN const XStringW&             _CustomPath,
                           IN const CUSTOM_LOADER_ENTRY&  Custom,
                           IN const XStringW&             DefaultEntrySettings,
                           IN REFIT_MENU_SCREEN          *SubMenu)
{
  UINTN           VolumeIndex;
  REFIT_VOLUME   *Volume;
  REFIT_DIR_ITER  SIter;
  REFIT_DIR_ITER *Iter = &SIter;
  CHAR16          PartUUID[40];
  XStringW        CustomPath = _CustomPath;
  XBool           FindCustomPath = (CustomPath.isEmpty());

  if ( SubMenu != NULL ) panic("Call AddCustomSubEntry instead");
  
  if (FindCustomPath && (Custom.settings.Type != OSTYPE_LINEFI) && (Custom.settings.Type != OSTYPE_LIN)) {
//    DBG("Custom %lsentry %llu skipped because it didn't have a ", IsSubEntry ? L"sub " : L"", CustomIndex);
//    if (Custom.Type == 0) {
//      DBG("Type.\n");
//    } else {
//      DBG("Path.\n");
//    }
    return;
  }

  if (OSFLAG_ISSET(Custom.getFlags(gSettings.SystemParameters.NoCaches), OSFLAG_DISABLED)) {
//    DBG("Custom %lsentry %llu skipped because it is disabled.\n", IsSubEntry ? L"sub " : L"", CustomIndex);
    return;
  }

//  if (!gSettings.ShowHiddenEntries && OSFLAG_ISSET(Custom.settings.Flags, OSFLAG_HIDDEN)) {
//    DBG("Custom %lsentry %llu skipped because it is hidden.\n", IsSubEntry ? L"sub " : L"", CustomIndex);
//    return;
//  }

#if 0  //if someone want to debug this
  DBG("Custom %lsentry %llu ", IsSubEntry ? L"sub " : L"", CustomIndex);
  //  if (Custom.settings.Title) {
  DBG("Title:\"%ls\" ", Custom.settings.Title.wc_str());
  //  }
  //  if (Custom.settings.FullTitle) {
  DBG("FullTitle:\"%ls\" ", Custom.settings.FullTitle.wc_str());
  //  }
  if (CustomPath) {
    DBG("Path:\"%ls\" ", CustomPath);
  }
  if (Custom.settings.Options != NULL) {
    DBG("Options:\"%ls\" ", Custom.settings.Options);
  }
  DBG("Type:%d Flags:0x%hhX matching ", Custom.settings.Type, Custom.settings.Flags);
  if (Custom.settings.Volume) {
    DBG("Volume:\"%ls\"\n", Custom.settings.Volume);
  } else {
    DBG("all volumes\n");
  }
#endif

  for (VolumeIndex = 0; VolumeIndex < Volumes.size(); ++VolumeIndex) {
    LOADER_ENTRY        *Entry = NULL;
    XIcon Image = Custom.Image;
    XIcon DriveImage = Custom.DriveImage;

    EFI_GUID        Guid;
    UINT64               VolumeSize;

    Volume = &Volumes[VolumeIndex];
    if ((Volume == NULL) || (Volume->RootDir == NULL)) {
      continue;
    }
    if (Volume->VolName.isEmpty()) {
      Volume->VolName = L"Unknown"_XSW;
    }

    DBG("    Checking volume \"%ls\" (%ls) ... ", Volume->VolName.wc_str(), Volume->DevicePathString.wc_str());

    // skip volume if its kind is configured as disabled
    if (((1ull<<Volume->DiskKind) & GlobalConfig.DisableFlags) != 0) {
      DBG("skipped because media is disabled\n");
      continue;
    }

    if (Custom.settings.VolumeType != 0 && ((1<<Volume->DiskKind) & Custom.settings.VolumeType) == 0) {
      DBG("skipped because media is ignored\n");
      continue;
    }
    
    // Check the volume is readable and the entry exists on the volume
    if (Volume->RootDir == NULL) {
      DBG("skipped because filesystem is not readable\n");
      continue;
    }

    if (Volume->Hidden) {
      DBG("skipped because volume is hidden\n");
      continue;
    }

    
    // Check for exact volume matches (devicepath / volumelabel)
    if (Custom.settings.Volume.notEmpty()) {
      if ((StrStr(Volume->DevicePathString.wc_str(), Custom.settings.Volume.wc_str()) == NULL) &&
          ((Volume->VolName.isEmpty()) || (StrStr(Volume->VolName.wc_str(), Custom.settings.Volume.wc_str()) == NULL))) {
        XBool CustomEntryFound = false;
        //..\VenMedia(BE74FCF7-0B7C-49F3-9147-01F4042E6842,E97E25EA28F4DF46AAD44CC3F12E28D3)
        EFI_DEVICE_PATH *MediaPath = Clover_FindDevicePathNodeWithType(Volume->DevicePath, MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP);
        if (MediaPath) {
          EFI_GUID MediaPathGuid = *(EFI_GUID *)&((VENDOR_DEVICE_PATH_WITH_DATA*)MediaPath)->VendorDefinedData;
          XStringW MediaPathGuidStr = MediaPathGuid.toXStringW();
          //       DBG("  checking '%ls'\n", MediaPathGuidStr.wc_str());
          if (StrStr(Custom.settings.Volume.wc_str(), MediaPathGuidStr.wc_str())) {
            DBG("   - found entry for volume '%ls', '%ls'\n", Custom.settings.Volume.wc_str(), MediaPathGuidStr.wc_str());
            CustomEntryFound = true;
          } else {
            DBG("  - search volume '%ls', but MediaPath '%ls' \n", Custom.settings.Volume.wc_str(), MediaPathGuidStr.wc_str());
          }
        }
        if (!CustomEntryFound) {
          DBG("skipped because volume does not match\n");
          continue;
        }
      }
    }


    Guid = FindGPTPartitionGuidInDevicePath(Volume->DevicePath);
    if (FindCustomPath) {
      // Get the partition UUID and make sure it's lower case
      if ( Guid.isNull() ) {
        DBG("skipped because volume does not have partition uuid\n");
        continue;
      }
      snwprintf(PartUUID, sizeof(PartUUID), "%s", Guid.toXString8().c_str());
      StrToLower(PartUUID);

      // search for standard/nonstandard linux uefi paths, and all kernel scan options that != KERNEL_SCAN_ALL
      if (Custom.settings.Type == OSTYPE_LIN || Custom.settings.KernelScan != KERNEL_SCAN_ALL) {
        LinuxScan(Volume, Custom.settings.KernelScan, Custom.settings.Type, &CustomPath, &Image);
      }
      if (Custom.settings.Type == OSTYPE_LINEFI) {
        // Open the boot directory to determine linux loadoptions when found item, or kernels when KERNEL_SCAN_ALL
        DirIterOpen(Volume->RootDir, LINUX_BOOT_PATH, Iter);
      }
    } else if (!FileExists(Volume->RootDir, CustomPath)) {
      DBG("skipped because path '%ls' does not exist\n", CustomPath.wc_str());
      continue;
    }

    // Change to custom image if needed
    if (Image.isEmpty() && Custom.settings.dgetImagePath().notEmpty()) {
      Image.LoadXImage(&ThemeX.getThemeDir(), Custom.settings.dgetImagePath());
      if (Image.isEmpty()) {
        Image.LoadXImage(&ThemeX.getThemeDir(), L"os_"_XSW + Custom.settings.dgetImagePath());
        if (Image.isEmpty()) {
          Image.LoadXImage(&self.getCloverDir(), Custom.settings.dgetImagePath());
          if (Image.isEmpty()) {
            Image.LoadXImage(&self.getSelfVolumeRootDir(), Custom.settings.dgetImagePath());
            if (Image.isEmpty()) {
              Image.LoadXImage(Volume->RootDir, Custom.settings.dgetImagePath());
            }
          }
        }
      }
    }

    // Change to custom drive image if needed
    if (DriveImage.isEmpty() && Custom.settings.dgetDriveImagePath().notEmpty()) {
      DriveImage.LoadXImage(&ThemeX.getThemeDir(), Custom.settings.dgetDriveImagePath());
      if (DriveImage.isEmpty()) {
        DriveImage.LoadXImage(&self.getCloverDir(), Custom.settings.dgetImagePath());
        if (DriveImage.isEmpty()) {
          DriveImage.LoadXImage(&self.getSelfVolumeRootDir(), Custom.settings.dgetImagePath());
          if (DriveImage.isEmpty()) {
            DriveImage.LoadXImage(Volume->RootDir, Custom.settings.dgetImagePath());
          }
        }
      }
    }

    do { // when not scanning for kernels, this loop will execute only once
      XString8Array CustomOptions = Custom.getLoadOptions();

      // for LINEFI with option KERNEL_SCAN_ALL, use this loop to search for kernels
      if (FindCustomPath && Custom.settings.Type == OSTYPE_LINEFI && Custom.settings.KernelScan == KERNEL_SCAN_ALL) {
        EFI_FILE_INFO *FileInfo = NULL;
        // Get the next kernel path or stop looking
        if (!DirIterNext(Iter, 2, LINUX_LOADER_SEARCH_PATH, &FileInfo) || (FileInfo == NULL)) {
          DBG("\n");
          break;
        }
        // who knows....
        if (FileInfo->FileSize == 0) {
          continue;
        }
        // get the kernel file path
        CustomPath.SWPrintf("%ls\\%ls", LINUX_BOOT_PATH, FileInfo->FileName);
      }
      if (CustomPath.isEmpty()) {
        DBG("skipped\n");
        break;
      }

      UINT8 newCustomFlags = Custom.getFlags(gSettings.SystemParameters.NoCaches);

      // Check to make sure if we should update linux custom options or not
      if (FindCustomPath && Custom.settings.Type == OSTYPE_LINEFI && OSFLAG_ISUNSET(Custom.getFlags(gSettings.SystemParameters.NoCaches), OSFLAG_NODEFAULTARGS)) {
        // Find the init ram image and select root
        CustomOptions = LinuxKernelOptions(Iter->DirHandle, Basename(CustomPath.wc_str()) + LINUX_LOADER_PATH.length(), PartUUID, Custom.getLoadOptions());
        newCustomFlags = OSFLAG_SET(Custom.getFlags(gSettings.SystemParameters.NoCaches), OSFLAG_NODEFAULTARGS);
      }

      // Check to make sure that this entry is not hidden or disabled by another custom entry
      if (true) {
        XBool BetterMatch = false;
        for (size_t i = 0 ; i < GlobalConfig.CustomEntries.size() ; ++i ) {
          CUSTOM_LOADER_ENTRY& CustomEntry = GlobalConfig.CustomEntries[i];
          if ( CustomEntry.settings.Disabled ) continue; // before, disabled entries settings weren't loaded.
          // Don't match against this custom
          if (&CustomEntry == &Custom) {
            continue;
          }
          // Can only match the same types
          if (Custom.settings.Type != CustomEntry.settings.Type) {
            continue;
          }
          // Check if the volume string matches
          if (Custom.settings.Volume != CustomEntry.settings.Volume) {
            if (CustomEntry.settings.Volume.isEmpty()) {
              // Less precise volume match
              if (Custom.settings.Path != CustomEntry.settings.Path) {
                // Better path match
                BetterMatch = ((CustomEntry.settings.Path.notEmpty()) && CustomPath.isEqual(CustomEntry.settings.Path) &&
                               ((Custom.settings.VolumeType == CustomEntry.settings.VolumeType) ||
                                ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0));
              }
            } else if ((StrStr(Volume->DevicePathString.wc_str(), Custom.settings.Volume.wc_str()) == NULL) &&
                       ((Volume->VolName.isEmpty()) || (StrStr(Volume->VolName.wc_str(), Custom.settings.Volume.wc_str()) == NULL))) {
              if (Custom.settings.Volume.isEmpty()) {
                // More precise volume match
                if (Custom.settings.Path != CustomEntry.settings.Path) {
                  // Better path match
                  BetterMatch = ((CustomEntry.settings.Path.notEmpty()) && CustomPath.isEqual(CustomEntry.settings.Path) &&
                                 ((Custom.settings.VolumeType == CustomEntry.settings.VolumeType) ||
                                  ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0));
                } else if (Custom.settings.VolumeType != CustomEntry.settings.VolumeType) {
                  // More precise volume type match
                  BetterMatch = ((Custom.settings.VolumeType == 0) &&
                                 ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0);
                } else {
                  // Better match
                  BetterMatch = true;
                }
              // Duplicate volume match
              } else if (Custom.settings.Path != CustomEntry.settings.Path) {
                // Better path match
                BetterMatch = ((CustomEntry.settings.Path.notEmpty()) && CustomPath.isEqual(CustomEntry.settings.Path) &&
                               ((Custom.settings.VolumeType == CustomEntry.settings.VolumeType) ||
                                ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0));
              // Duplicate path match
              } else if (Custom.settings.VolumeType != CustomEntry.settings.VolumeType) {
                // More precise volume type match
                BetterMatch = ((Custom.settings.VolumeType == 0) &&
                               ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0);
              } else {
                // Duplicate entry
                BetterMatch = (i <= CustomIndex);
              }
            }
          // Duplicate volume match
          } else if (Custom.settings.Path != CustomEntry.settings.Path) {
            if (CustomEntry.settings.Path.isEmpty()) {
              // Less precise path match
              BetterMatch = ((Custom.settings.VolumeType != CustomEntry.settings.VolumeType) &&
                             ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0);
            } else if (CustomPath.isEqual(CustomEntry.settings.Path)) {
              if (Custom.settings.Path.isEmpty()) {
                // More precise path and volume type match
                BetterMatch = ((Custom.settings.VolumeType == CustomEntry.settings.VolumeType) ||
                               ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0);
              } else if (Custom.settings.VolumeType != CustomEntry.settings.VolumeType) {
                // More precise volume type match
                BetterMatch = ((Custom.settings.VolumeType == 0) &&
                               ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0);
              } else {
                // Duplicate entry
                BetterMatch = (i <= CustomIndex);
              }
            }
          // Duplicate path match
          } else if (Custom.settings.VolumeType != CustomEntry.settings.VolumeType) {
            // More precise volume type match
            BetterMatch = ((Custom.settings.VolumeType == 0) &&
                           ((1ull<<Volume->DiskKind) & Custom.settings.VolumeType) != 0);
          } else {
            // Duplicate entry
            BetterMatch = (i <= CustomIndex);
          }
          if (BetterMatch) {
            DBG("skipped because custom entry %zu is a better match and will produce a duplicate entry\n", i);
            break;
          }
        }
        if (BetterMatch) {
          continue;
        }
      }

      DBG("match!\n");
      // Create an entry for this volume
      Entry = CreateLoaderEntry(CustomPath, CustomOptions, Custom.settings.FullTitle, Custom.settings.dgetTitle(), Volume,
                                (Image.isEmpty() ? NULL : &Image), (DriveImage.isEmpty() ? NULL : &DriveImage),            
                                Custom.settings.Type, newCustomFlags, Custom.settings.Hotkey, Custom.settings.BootBgColor, Custom.CustomLogoType, Custom.CustomLogoImage,
                                /*(KERNEL_AND_KEXT_PATCHES *)(((UINTN)Custom) + OFFSET_OF(CUSTOM_LOADER_ENTRY, KernelAndKextPatches))*/ NULL, true);
      if (Entry != NULL) {
        if ( Custom.settings.Settings.notEmpty() ) {
          DBG("Custom settings: %ls.plist will %s be applied\n", Custom.settings.Settings.wc_str(), Custom.settings.CommonSettings?"not":"");
        }
        if (!Custom.settings.CommonSettings) {
          Entry->Settings = DefaultEntrySettings;
        }
        if (Custom.settings.ForceTextMode) {
          Entry->Flags = OSFLAG_UNSET(Entry->Flags, OSFLAG_USEGRAPHICS);
        }
        if (OSFLAG_ISUNSET(newCustomFlags, OSFLAG_NODEFAULTMENU)) {
          Entry->AddDefaultMenu();
        } else if (Custom.SubEntries.notEmpty()) {
          UINTN CustomSubIndex = 0;
          // Add subscreen
          REFIT_MENU_SCREEN *SubScreen = new REFIT_MENU_SCREEN;
          SubScreen->Title.SWPrintf("Boot Options for %ls on %ls", (Custom.settings.dgetTitle().notEmpty()) ? XStringW(Custom.settings.dgetTitle()).wc_str() : CustomPath.wc_str(), Entry->DisplayedVolName.wc_str());
          SubScreen->TitleImage = Entry->Image;
          SubScreen->ID = Custom.settings.Type + 20;
          SubScreen->GetAnime();
          VolumeSize = RShiftU64(MultU64x32(Volume->BlockIO->Media->LastBlock, Volume->BlockIO->Media->BlockSize), 20);
          SubScreen->AddMenuInfoLine_f("Volume size: %lldMb", VolumeSize);
          SubScreen->AddMenuInfoLine_f("%ls", FileDevicePathToXStringW(Entry->DevicePath).wc_str());
          if ( Guid.notNull() ) {
            SubScreen->AddMenuInfoLine_f("UUID: %s", Guid.toXString8().c_str());
          }
          SubScreen->AddMenuInfoLine_f("Options: %s", Entry->LoadOptions.ConcatAll(" "_XS8).c_str());
          DBG("Create sub entries\n");
          for (size_t CustomSubEntryIndex = 0 ; CustomSubEntryIndex < Custom.SubEntries.size() ; ++CustomSubEntryIndex ) {
            const CUSTOM_LOADER_SUBENTRY& CustomSubEntry = Custom.SubEntries[CustomSubEntryIndex];
//            if ( CustomSubEntry.settings.Settings.isEmpty() ) {
              AddCustomSubEntry(Volume, CustomSubIndex++, Custom.settings.Path.notEmpty() ? Custom.settings.Path : CustomPath, Custom.settings.Type, CustomSubEntry, Custom.settings.Settings, SubScreen);
//            }else{
//              AddCustomSubEntry(Volume, CustomSubIndex++, CustomSubEntry.settings.Path.notEmpty() ? CustomSubEntry.settings.Path : CustomPath, CustomSubEntry, CustomSubEntry.settings.Settings, SubScreen);
//            }
          }
          SubScreen->AddMenuEntry(&MenuEntryReturn, true);
          Entry->SubScreen = SubScreen;
        }
//        if (IsSubEntry)
//          SubMenu->AddMenuEntry(Entry, true);
//        else
          MainMenu.AddMenuEntry(Entry, true);

        Entry->Hidden = Custom.settings.Hidden;
        if ( Custom.settings.Hidden ) DBG("     hiding entry because Custom.settings.Hidden\n");
      }
    } while (FindCustomPath && Custom.settings.Type == OSTYPE_LINEFI && Custom.settings.KernelScan == KERNEL_SCAN_ALL); // repeat loop only for kernel scanning

    // Close the kernel boot directory
    if (FindCustomPath && Custom.settings.Type == OSTYPE_LINEFI) {
      DirIterClose(Iter);
    }
  }

}

// Add custom entries
void AddCustomEntries(void)
{
  if (GlobalConfig.CustomEntries.isEmpty()) return;

  //DBG("Custom entries start\n");
  DbgHeader("AddCustomEntries");
  // Traverse the custom entries
  for (size_t i = 0 ; i < GlobalConfig.CustomEntries.size(); ++i) {
    CUSTOM_LOADER_ENTRY& Custom = GlobalConfig.CustomEntries[i];
    DBG("- [00]: '%s'\n", Custom.settings.FullTitle.isEmpty() ? Custom.settings.dgetTitle().c_str() : Custom.settings.FullTitle.c_str() );
    if ( Custom.settings.Disabled ) {
      DBG("  Disabled\n");
      continue; // before, disabled entries settings weren't loaded.
    }
    if ((Custom.settings.Path.isEmpty()) && (Custom.settings.Type != 0)) {
      if (OSTYPE_IS_OSX(Custom.settings.Type)) {
        AddCustomEntry(i, MACOSX_LOADER_PATH, Custom, Custom.settings.Settings, NULL);
      } else if (OSTYPE_IS_OSX_RECOVERY(Custom.settings.Type)) {
        AddCustomEntry(i, L"\\com.apple.recovery.boot\\boot.efi"_XSW, Custom, Custom.settings.Settings, NULL);
      } else if (OSTYPE_IS_OSX_INSTALLER(Custom.settings.Type)) {
        UINTN Index = 0;
        while (Index < OSXInstallerPathsCount) {
          AddCustomEntry(i, OSXInstallerPaths[Index++], Custom, Custom.settings.Settings, NULL);
        }
      } else if (OSTYPE_IS_WINDOWS(Custom.settings.Type)) {
        AddCustomEntry(i, L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"_XSW, Custom, Custom.settings.Settings, NULL);
      } else if (OSTYPE_IS_LINUX(Custom.settings.Type)) {
#if defined(ANDX86)
        for (UINTN Index = 0; Index < AndroidEntryDataCount; ++Index) {
          AddCustomEntry(i, AndroidEntryData[Index].Path, Custom, Custom.settings.Settings, NULL);
        }
#endif
        AddCustomEntry(i, NullXStringW, Custom, Custom.settings.Settings, NULL);
      } else if (Custom.settings.Type == OSTYPE_LINEFI) {
        AddCustomEntry(i, NullXStringW, Custom, Custom.settings.Settings, NULL);
      } else {
        AddCustomEntry(i, BOOT_LOADER_PATH, Custom, Custom.settings.Settings, NULL);
      }
    } else {
      AddCustomEntry(i, Custom.settings.Path, Custom, Custom.settings.Settings, NULL);
    }
  }
  //DBG("Custom entries finish\n");
}
