/*
 * refit/main.c
 * Main code for the boot menu
 *
 * 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 "entry_scan.h"
//#include "Platform.h"
//#include "../include/Handle.h"
#include "nanosvg.h"

#include "Version.h"

#ifndef DEBUG_ALL
#define DEBUG_MAIN 1
#else
#define DEBUG_MAIN DEBUG_ALL
#endif

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

#ifndef HIBERNATE
#define HIBERNATE 0
#endif


#ifndef CHECK_SMC
#define CHECK_SMC 0
#endif


// variables
#ifdef FIRMWARE_REVISION
CHAR16 *gFirmwareRevision = FIRMWARE_REVISION;
#else
CHAR16 *gFirmwareRevision = NULL;
#endif

BOOLEAN                 gGuiIsReady     = FALSE;
BOOLEAN                 gThemeNeedInit  = TRUE;
BOOLEAN                 DoHibernateWake = FALSE;
BOOLEAN                 APFSSupport     = FALSE;

//extern EFI_HANDLE              gImageHandle;
//extern EFI_SYSTEM_TABLE*       gST;
//extern EFI_BOOT_SERVICES*      gBS;
//extern EFI_DXE_SERVICES*       gDS;
EFI_RUNTIME_SERVICES*   gRS;

DRIVERS_FLAGS gDriversFlags;  //the initializer is not needed for global variables

EMU_VARIABLE_CONTROL_PROTOCOL *gEmuVariableControl = NULL;

EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL* SimpleTextEx;
EFI_KEY_DATA KeyData;

extern VOID HelpRefit(VOID);
extern VOID AboutRefit(VOID);
extern BOOLEAN BooterPatch(IN UINT8 *BooterData, IN UINT64 BooterSize, LOADER_ENTRY *Entry);

extern UINTN                ThemesNum;
extern CHAR16               *ThemesList[];
extern UINTN                 ConfigsNum;
extern CHAR16                *ConfigsList[];
extern UINTN                 DsdtsNum;
extern CHAR16                *DsdtsList[];
extern UINTN                 AudioNum;
extern HDA_OUTPUTS           AudioList[20];
extern EFI_AUDIO_IO_PROTOCOL *AudioIo;


static EFI_STATUS LoadEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
                                    IN CHAR16 *ImageTitle,
                                    OUT UINTN *ErrorInStep,
                                    OUT EFI_HANDLE *NewImageHandle)
{
  EFI_STATUS              Status, ReturnStatus;
  EFI_HANDLE              ChildImageHandle = 0;
  UINTN                   DevicePathIndex;
  CHAR16                  ErrorInfo[256];

  DBG("Loading %s", ImageTitle);
  if (ErrorInStep != NULL) {
    *ErrorInStep = 0;
  }
  if (NewImageHandle != NULL) {
    *NewImageHandle = NULL;
  }

  // load the image into memory
  ReturnStatus = Status = EFI_NOT_FOUND;  // in case the list is empty
  for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
    ReturnStatus = Status = gBS->LoadImage(FALSE, SelfImageHandle, DevicePaths[DevicePathIndex], NULL, 0, &ChildImageHandle);
    DBG("  status=%r", Status);
    if (ReturnStatus != EFI_NOT_FOUND)
      break;
  }
  UnicodeSPrint(ErrorInfo, 512, L"while loading %s", ImageTitle);
  if (CheckError(Status, ErrorInfo)) {
    if (ErrorInStep != NULL)
      *ErrorInStep = 1;
    PauseForKey(L"press any key");
    goto bailout;
  }

  if (!EFI_ERROR(ReturnStatus)) { //why unload driver?!
    if (NewImageHandle != NULL) {
      *NewImageHandle = ChildImageHandle;
    }
    goto bailout;
  }

  // unload the image, we don't care if it works or not...
  Status = gBS->UnloadImage(ChildImageHandle);
bailout:
  DBG("\n");
  return ReturnStatus;
}


static EFI_STATUS StartEFILoadedImage(IN EFI_HANDLE ChildImageHandle,
                                    IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
                                    IN CHAR16 *ImageTitle,
                                    OUT UINTN *ErrorInStep)
{
  EFI_STATUS              Status, ReturnStatus;
  EFI_LOADED_IMAGE        *ChildLoadedImage;
  CHAR16                  ErrorInfo[256];
  CHAR16                  *FullLoadOptions = NULL;

//  DBG("Starting %s\n", ImageTitle);
  if (ErrorInStep != NULL) {
    *ErrorInStep = 0;
  }
  ReturnStatus = Status = EFI_NOT_FOUND;  // in case no image handle was specified
  if (ChildImageHandle == NULL) {
    if (ErrorInStep != NULL) *ErrorInStep = 1;
    goto bailout;
  }

  // set load options
  if (LoadOptions != NULL) {
    ReturnStatus = Status = gBS->HandleProtocol(ChildImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ChildLoadedImage);
    if (CheckError(Status, L"while getting a LoadedImageProtocol handle")) {
      if (ErrorInStep != NULL)
        *ErrorInStep = 2;
      goto bailout_unload;
    }

    if (LoadOptionsPrefix != NULL) {
      FullLoadOptions = PoolPrint(L"%s %s ", LoadOptionsPrefix, LoadOptions);
      // NOTE: That last space is also added by the EFI shell and seems to be significant
      //  when passing options to Apple's boot.efi...
      LoadOptions = FullLoadOptions;
    }
    // NOTE: We also include the terminating null in the length for safety.
    ChildLoadedImage->LoadOptions = (VOID *)LoadOptions;
    ChildLoadedImage->LoadOptionsSize = (UINT32)StrSize(LoadOptions);
    //((UINT32)StrLen(LoadOptions) + 1) * sizeof(CHAR16);
//    DBG("Using load options '%s'\n", LoadOptions);
  }
  //DBG("Image loaded at: %p\n", ChildLoadedImage->ImageBase);
  //PauseForKey(L"continue");
  
  // close open file handles
  UninitRefitLib();

  // turn control over to the image
  //
  // Before calling the image, enable the Watchdog Timer for
  // the 5 Minute period - Slice - NO! For slow driver and slow disk we need more
  //
  gBS->SetWatchdogTimer (600, 0x0000, 0x00, NULL);

  ReturnStatus = Status = gBS->StartImage(ChildImageHandle, NULL, NULL);
  //
  // Clear the Watchdog Timer after the image returns
  //
  gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);

  //PauseForKey(L"Returned from StartImage\n");

  // control returns here when the child image calls Exit()
  if (ImageTitle) {
    UnicodeSPrint(ErrorInfo, 512, L"returned from %s", ImageTitle);
  }

  if (CheckError(Status, ErrorInfo)) {
    if (ErrorInStep != NULL)
      *ErrorInStep = 3;
  }
  if (!EFI_ERROR(ReturnStatus)) { //why unload driver?!
    goto bailout;
  }

bailout_unload:
  // unload the image, we don't care if it works or not...
  Status = gBS->UnloadImage(ChildImageHandle);
  if (FullLoadOptions != NULL)
    FreePool(FullLoadOptions);
bailout:
  return ReturnStatus;
}


static EFI_STATUS LoadEFIImage(IN EFI_DEVICE_PATH *DevicePath,
                                IN CHAR16 *ImageTitle,
                                OUT UINTN *ErrorInStep,
                                OUT EFI_HANDLE *NewImageHandle)
{
  EFI_DEVICE_PATH *DevicePaths[2];

#ifdef ENABLE_SECURE_BOOT
  // Verify secure boot policy
  if (gSettings.SecureBoot && gSettings.SecureBootSetupMode) {
    // Only verify if in forced secure boot mode
    EFI_STATUS Status = VerifySecureBootImage(DevicePath);
    if (EFI_ERROR(Status)) {
      return Status;
    }
  }
#endif // ENABLE_SECURE_BOOT

  // Load the image now
  DevicePaths[0] = DevicePath;
  DevicePaths[1] = NULL;
  return LoadEFIImageList(DevicePaths, ImageTitle, ErrorInStep, NewImageHandle);
}


static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
                                IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
                                IN CHAR16 *ImageTitle,
                                OUT UINTN *ErrorInStep,
                                OUT EFI_HANDLE *NewImageHandle)
{
  EFI_STATUS Status;
  EFI_HANDLE ChildImageHandle = NULL;

  Status = LoadEFIImage(DevicePath, ImageTitle, ErrorInStep, &ChildImageHandle);
  if (!EFI_ERROR(Status)) {
    Status = StartEFILoadedImage(ChildImageHandle, LoadOptions, LoadOptionsPrefix, ImageTitle, ErrorInStep);
  }

  if (NewImageHandle != NULL) {
      *NewImageHandle = ChildImageHandle;
  }
  return Status;
}

/*
static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
                                IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
                                IN CHAR16 *ImageTitle,
                                OUT UINTN *ErrorInStep,
                                OUT EFI_HANDLE *NewImageHandle)
{
  EFI_STATUS Status;
  EFI_HANDLE ChildImageHandle = NULL;

  Status = LoadEFIImageList(DevicePaths, ImageTitle, ErrorInStep, &ChildImageHandle);
  if (!EFI_ERROR(Status)) {
    Status = StartEFILoadedImage(ChildImageHandle, LoadOptions, LoadOptionsPrefix, ImageTitle, ErrorInStep);
  }

  if (NewImageHandle != NULL) {
      *NewImageHandle = ChildImageHandle;
  }
  return Status;
}
*/

static CHAR8 *SearchString (
  IN  CHAR8       *Source,
  IN  UINT64      SourceSize,
  IN  CHAR8       *Search,
  IN  UINTN       SearchSize
  )
{
  CHAR8 *End = Source + SourceSize;

  while (Source < End) {
    if (CompareMem(Source, Search, SearchSize) == 0) {
      return Source;
    } else {
      Source++;
    }
  }
  return NULL;
}
#ifdef DUMP_KERNEL_KEXT_PATCHES
VOID DumpKernelAndKextPatches(KERNEL_AND_KEXT_PATCHES *Patches)
{
  if (!Patches) {
    DBG("Kernel and Kext Patches null pointer\n");
    return;
  }
  DBG("Kernel and Kext Patches at %p:\n", Patches);
  DBG("\tAllowed: %c\n", gSettings.KextPatchesAllowed ? 'y' : 'n');
  DBG("\tDebug: %c\n", Patches->KPDebug ? 'y' : 'n');
  DBG("\tKernelCpu: %c\n", Patches->KPKernelCpu ? 'y' : 'n');
  DBG("\tKernelLapic: %c\n", Patches->KPKernelLapic ? 'y' : 'n');
  DBG("\tKernelXCPM: %c\n", Patches->KPKernelXCPM ? 'y' : 'n');
  DBG("\tKernelPm: %c\n", Patches->KPKernelPm ? 'y' : 'n');
  DBG("\tAppleIntelCPUPM: %c\n", Patches->KPAppleIntelCPUPM ? 'y' : 'n');
  DBG("\tAppleRTC: %c\n", Patches->KPAppleRTC ? 'y' : 'n');
  // Dell smbios truncate fix
  DBG("\tDellSMBIOSPatch: %c\n", Patches->KPDELLSMBIOS ? 'y' : 'n');
  DBG("\tFakeCPUID: 0x%x\n", Patches->FakeCPUID);
  DBG("\tATIController: %s\n", (Patches->KPATIConnectorsController == NULL) ? L"null": Patches->KPATIConnectorsController);
  DBG("\tATIDataLength: %d\n", Patches->KPATIConnectorsDataLen);
  DBG("\t%d Kexts to load\n", Patches->NrForceKexts);
  if (Patches->ForceKexts) {
    INTN i = 0;
    for (; i < Patches->NrForceKexts; ++i) {
       DBG("\t  KextToLoad[%d]: %s\n", i, Patches->ForceKexts[i]);
    }
  }
  DBG("\t%d Kexts to patch\n", Patches->NrKexts);
  if (Patches->KextPatches) {
    INTN i = 0;
    for (; i < Patches->NrKexts; ++i) {
       if (Patches->KextPatches[i].IsPlistPatch) {
          DBG("\t  KextPatchPlist[%d]: %d bytes, %a\n", i, Patches->KextPatches[i].DataLen, Patches->KextPatches[i].Name);
       } else {
          DBG("\t  KextPatch[%d]: %d bytes, %a\n", i, Patches->KextPatches[i].DataLen, Patches->KextPatches[i].Name);
       }
    }
  }
}
#endif
VOID FilterKextPatches(IN LOADER_ENTRY *Entry) //zzzz
{
  if (gSettings.KextPatchesAllowed && (Entry->KernelAndKextPatches->KextPatches != NULL) && Entry->KernelAndKextPatches->NrKexts) {
    INTN i;
    DBG("Filtering KextPatches:\n");
    for (i = 0; i < Entry->KernelAndKextPatches->NrKexts; i++) {
      DBG(" - [%02d]: %a :: %a :: [OS: %a | MatchOS: %a | MatchBuild: %a]",
        i,
        Entry->KernelAndKextPatches->KextPatches[i].Label,
        Entry->KernelAndKextPatches->KextPatches[i].IsPlistPatch ? "PlistPatch" : "BinPatch",
        Entry->OSVersion,
        Entry->KernelAndKextPatches->KextPatches[i].MatchOS ? Entry->KernelAndKextPatches->KextPatches[i].MatchOS : "All",
        Entry->KernelAndKextPatches->KextPatches[i].MatchBuild != NULL ? Entry->KernelAndKextPatches->KextPatches[i].MatchBuild : "All"
      );
      if (!Entry->KernelAndKextPatches->KextPatches[i].MenuItem.BValue) {
        DBG(" ==> disabled by user\n");
        continue;
      }
      
      if ((Entry->BuildVersion != NULL) && (Entry->KernelAndKextPatches->KextPatches[i].MatchBuild != NULL)) {
        Entry->KernelAndKextPatches->KextPatches[i].MenuItem.BValue = IsPatchEnabled(Entry->KernelAndKextPatches->KextPatches[i].MatchBuild, Entry->BuildVersion);
        DBG(" ==> %a\n", Entry->KernelAndKextPatches->KextPatches[i].MenuItem.BValue ? "allowed" : "not allowed");
        continue; 
      }

      Entry->KernelAndKextPatches->KextPatches[i].MenuItem.BValue = IsPatchEnabled(Entry->KernelAndKextPatches->KextPatches[i].MatchOS, Entry->OSVersion);
      DBG(" ==> %a\n", Entry->KernelAndKextPatches->KextPatches[i].MenuItem.BValue ? "allowed" : "not allowed");
    }
  }
}

VOID FilterKernelPatches(IN LOADER_ENTRY *Entry)
{
  if (gSettings.KernelPatchesAllowed && (Entry->KernelAndKextPatches->KernelPatches != NULL) && Entry->KernelAndKextPatches->NrKernels) {
    INTN i = 0;
    DBG("Filtering KernelPatches:\n");
    for (; i < Entry->KernelAndKextPatches->NrKernels; ++i) {
      DBG(" - [%02d]: %a :: [OS: %a | MatchOS: %a | MatchBuild: %a]",
        i,
        Entry->KernelAndKextPatches->KernelPatches[i].Label,
        Entry->OSVersion,
        Entry->KernelAndKextPatches->KernelPatches[i].MatchOS ? Entry->KernelAndKextPatches->KernelPatches[i].MatchOS : "All",
        Entry->KernelAndKextPatches->KernelPatches[i].MatchBuild != NULL ? Entry->KernelAndKextPatches->KernelPatches[i].MatchBuild : "no"
      );
      if (!Entry->KernelAndKextPatches->KernelPatches[i].MenuItem.BValue) {
        DBG(" ==> disabled by user\n");
        continue;
      }

      if ((Entry->BuildVersion != NULL) && (Entry->KernelAndKextPatches->KernelPatches[i].MatchBuild != NULL)) {
        Entry->KernelAndKextPatches->KernelPatches[i].MenuItem.BValue = IsPatchEnabled(Entry->KernelAndKextPatches->KernelPatches[i].MatchBuild, Entry->BuildVersion);
        DBG(" ==> %a by build\n", Entry->KernelAndKextPatches->KernelPatches[i].MenuItem.BValue ? "allowed" : "not allowed");
        continue; 
      }

      Entry->KernelAndKextPatches->KernelPatches[i].MenuItem.BValue = IsPatchEnabled(Entry->KernelAndKextPatches->KernelPatches[i].MatchOS, Entry->OSVersion);
      DBG(" ==> %a by OS\n", Entry->KernelAndKextPatches->KernelPatches[i].MenuItem.BValue ? "allowed" : "not allowed");
    }
  }
}

VOID FilterBootPatches(IN LOADER_ENTRY *Entry)
{
  if ((Entry->KernelAndKextPatches->BootPatches != NULL) && Entry->KernelAndKextPatches->NrBoots) {
    INTN i = 0;
    DBG("Filtering BootPatches:\n");
    for (; i < Entry->KernelAndKextPatches->NrBoots; ++i) {
      DBG(" - [%02d]: %a :: [OS: %a | MatchOS: %a | MatchBuild: %a]",
          i,
          Entry->KernelAndKextPatches->BootPatches[i].Label,
          Entry->OSVersion,
          Entry->KernelAndKextPatches->BootPatches[i].MatchOS ? Entry->KernelAndKextPatches->BootPatches[i].MatchOS : "All",
          Entry->KernelAndKextPatches->BootPatches[i].MatchBuild != NULL ? Entry->KernelAndKextPatches->BootPatches[i].MatchBuild : "no"
          );
      if (!Entry->KernelAndKextPatches->BootPatches[i].MenuItem.BValue) {
        DBG(" ==> disabled by user\n");
        continue;
      }

      if ((Entry->BuildVersion != NULL) && (Entry->KernelAndKextPatches->BootPatches[i].MatchBuild != NULL)) {
        Entry->KernelAndKextPatches->BootPatches[i].MenuItem.BValue = IsPatchEnabled(Entry->KernelAndKextPatches->BootPatches[i].MatchBuild, Entry->BuildVersion);
        DBG(" ==> %a by build\n", Entry->KernelAndKextPatches->BootPatches[i].MenuItem.BValue ? "allowed" : "not allowed");
        continue;
      }
 
      Entry->KernelAndKextPatches->BootPatches[i].MenuItem.BValue = IsPatchEnabled(Entry->KernelAndKextPatches->BootPatches[i].MatchOS, Entry->OSVersion);
      DBG(" ==> %a by OS\n", Entry->KernelAndKextPatches->BootPatches[i].MenuItem.BValue ? "allowed" : "not allowed");
  
    }
  }
}

VOID ReadSIPCfg()
{
  UINT32 csrCfg = gSettings.CsrActiveConfig & CSR_VALID_FLAGS;
  CHAR16 *csrLog = AllocateZeroPool(SVALUE_MAX_SIZE);

  if (csrCfg & CSR_ALLOW_UNTRUSTED_KEXTS)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, L"CSR_ALLOW_UNTRUSTED_KEXTS");
  if (csrCfg & CSR_ALLOW_UNRESTRICTED_FS)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_UNRESTRICTED_FS"));
  if (csrCfg & CSR_ALLOW_TASK_FOR_PID)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_TASK_FOR_PID"));
  if (csrCfg & CSR_ALLOW_KERNEL_DEBUGGER)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_KERNEL_DEBUGGER"));
  if (csrCfg & CSR_ALLOW_APPLE_INTERNAL)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_APPLE_INTERNAL"));
  if (csrCfg & CSR_ALLOW_UNRESTRICTED_DTRACE)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_UNRESTRICTED_DTRACE"));
  if (csrCfg & CSR_ALLOW_UNRESTRICTED_NVRAM)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_UNRESTRICTED_NVRAM"));
  if (csrCfg & CSR_ALLOW_DEVICE_CONFIGURATION)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_DEVICE_CONFIGURATION"));
  if (csrCfg & CSR_ALLOW_ANY_RECOVERY_OS)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_ANY_RECOVERY_OS"));
  if (csrCfg & CSR_ALLOW_UNAPPROVED_KEXTS)
    StrCatS(csrLog, SVALUE_MAX_SIZE/2, PoolPrint(L"%a%a", StrLen(csrLog) ? " | " : "", "CSR_ALLOW_UNAPPROVED_KEXTS"));
    
  if (StrLen(csrLog)) {
    DBG("CSR_CFG: %s\n", csrLog);
  }

  FreePool(csrLog);
}

//
// Null ConOut OutputString() implementation - for blocking
// text output from boot.efi when booting in graphics mode
//
EFI_STATUS EFIAPI
NullConOutOutputString(IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, IN CHAR16 *String) {
  return EFI_SUCCESS;
}

//
// EFI OS loader functions
//
EG_PIXEL DarkBackgroundPixel  = { 0x0, 0x0, 0x0, 0xFF };

static VOID StartLoader(IN LOADER_ENTRY *Entry)
{
  EFI_STATUS              Status;
  EFI_TEXT_STRING         ConOutOutputString = 0;
  EFI_HANDLE              ImageHandle = NULL;
  EFI_LOADED_IMAGE        *LoadedImage = NULL;
  CHAR8                   *InstallerVersion;
  TagPtr                  dict = NULL;
  UINTN                   i;
  NSVGfont                *font, *nextFont;

//  DBG("StartLoader() start\n");
  DbgHeader("StartLoader");
  if (Entry->Settings) {
    DBG("Entry->Settings: %s\n", Entry->Settings);
    Status = LoadUserSettings(SelfRootDir, Entry->Settings, &dict);
    if (!EFI_ERROR(Status)) {
      DBG(" - found custom settings for this entry: %s\n", Entry->Settings);
      gBootChanged = TRUE;
      Status = GetUserSettings(SelfRootDir, dict);
      if (EFI_ERROR(Status)) {
        DBG(" - ... but: %r\n", Status);
      } else {
        if ((gSettings.CpuFreqMHz > 100) && (gSettings.CpuFreqMHz < 20000)) {
          gCPUStructure.MaxSpeed      = gSettings.CpuFreqMHz;
        }
        //CopyMem (Entry->KernelAndKextPatches,
        //         &gSettings.KernelAndKextPatches,
        //         sizeof(KERNEL_AND_KEXT_PATCHES));
        //DBG("Custom KernelAndKextPatches copyed to started entry\n");
      }
    } else {
      DBG(" - [!] LoadUserSettings failed: %r\n", Status);
    }
  }
  
  DBG("Finally: ExternalClock=%ldMHz BusSpeed=%ldkHz CPUFreq=%ldMHz",
  				DivU64x32(gCPUStructure.ExternalClock + kilo - 1, kilo),
  				DivU64x32(gCPUStructure.FSBFrequency + kilo - 1, kilo),
          gCPUStructure.MaxSpeed);
  if (gSettings.QPI) {
    DBG(" QPI: hw.busfrequency=%ldHz\n", MultU64x32(gSettings.QPI, Mega));
  } else {
    // to match the value of hw.busfrequency in the terminal
    DBG(" PIS: hw.busfrequency=%ldHz\n", MultU64x32(LShiftU64(DivU64x32(gCPUStructure.ExternalClock + kilo - 1, kilo), 2), Mega));
  }
  
  //Free memory
  for (i = 0; i < ThemesNum; i++) {
    if (ThemesList[i]) {
      FreePool(ThemesList[i]);
      ThemesList[i] = NULL;
    }
  }
  for (i = 0; i < ConfigsNum; i++) {
    if (ConfigsList[i]) {
      FreePool(ConfigsList[i]);
      ConfigsList[i] = NULL;
    }
  }
  for (i = 0; i < DsdtsNum; i++) {
    if (DsdtsList[i]) {
      FreePool(DsdtsList[i]);
      DsdtsList[i] = NULL;
    }
  }
  FreeMenu(&OptionMenu);
  //there is a place to free memory
  // GuiAnime
  // mainParser
  // BuiltinIcons
  // OSIcons
  font = fontsDB;
  while (font) {
    nextFont = font->next;
    nsvg__deleteFont(font);
    font = nextFont;
  }
  nsvg__deleteParser(mainParser);

  
  //DumpKernelAndKextPatches(Entry->KernelAndKextPatches);

  // Load image into memory (will be started later)
  Status = LoadEFIImage(Entry->DevicePath, Basename(Entry->LoaderPath), NULL, &ImageHandle);
  if (EFI_ERROR(Status)) {
    DBG("Image is not loaded, status=%r\n", Status);
    return; // no reason to continue if loading image failed
  }

  egClearScreen(Entry->BootBgColor ? Entry->BootBgColor : &DarkBackgroundPixel);
//  KillMouse();

//  if (Entry->LoaderType == OSTYPE_OSX) {
  if (OSTYPE_IS_OSX(Entry->LoaderType) ||
      OSTYPE_IS_OSX_RECOVERY(Entry->LoaderType) ||
      OSTYPE_IS_OSX_INSTALLER(Entry->LoaderType)) {

    // To display progress bar properly (especially in FV2 mode) boot.efi needs to be in graphics mode.
    // Unfortunately many UEFI implementations change the resolution when SetMode happens.
    // This is not what boot.efi expects, and it freely calls SetMode at its will.
    // As a result we see progress bar at improper resolution and the background is also missing (10.12.x+).
    //
    // libeg already has a workaround for SetMode behaviour, so we extend it for boot.efi support.
    // The approach tries to be  follows:
    // 1. Ensure we have graphics mode set (since it is a must in the future).
    // 2. Request text mode for boot.efi, which it expects by default (here a SetMode libeg hack will trigger
    //    on problematic UEFI implementations like AMI).
    egSetGraphicsModeEnabled(TRUE);
    egSetGraphicsModeEnabled(FALSE);

    DBG("GetOSVersion:");

    //needed for boot.efi patcher
    Status = gBS->HandleProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
    // Correct OSVersion if it was not found
    // This should happen only for 10.7-10.9 OSTYPE_OSX_INSTALLER
    // For these cases, take OSVersion from loaded boot.efi image in memory
    if (/*Entry->LoaderType == OSTYPE_OSX_INSTALLER ||*/ !Entry->OSVersion) {

      if (!EFI_ERROR(Status)) {
        // version in boot.efi appears as "Mac OS X 10.?"
        /*
          Start OSName Mac OS X 10.12 End OSName Start OSVendor Apple Inc. End
        */
        InstallerVersion = SearchString(LoadedImage->ImageBase, LoadedImage->ImageSize, "Mac OS X ", 9);

        if (InstallerVersion != NULL) { // string was found
          InstallerVersion += 9; // advance to version location

          if (AsciiStrnCmp(InstallerVersion, "10.7", 4) &&
              AsciiStrnCmp(InstallerVersion, "10.8", 4) &&
              AsciiStrnCmp(InstallerVersion, "10.9", 4) &&
              AsciiStrnCmp(InstallerVersion, "10.10", 5) &&
              AsciiStrnCmp(InstallerVersion, "10.11", 5) &&
              AsciiStrnCmp(InstallerVersion, "10.12", 5) &&
              AsciiStrnCmp(InstallerVersion, "10.13", 5) &&
              AsciiStrnCmp(InstallerVersion, "10.14", 5) &&
              AsciiStrnCmp(InstallerVersion, "10.15", 5)) {   
            InstallerVersion = NULL; // flag known version was not found
          }
          if (InstallerVersion != NULL) { // known version was found in image
            if (Entry->OSVersion != NULL) {
              FreePool(Entry->OSVersion);
            }
            Entry->OSVersion = AllocateCopyPool(AsciiStrLen(InstallerVersion)+1, InstallerVersion);
            Entry->OSVersion[AsciiStrLen(InstallerVersion)] = '\0';
//            DBG("Corrected OSVersion: %a\n", Entry->OSVersion);
          }
        }
      }

      if (Entry->BuildVersion != NULL) {
        FreePool(Entry->BuildVersion);
        Entry->BuildVersion = NULL;
      }
    }

    if (Entry->BuildVersion != NULL) {
      DBG(" %a (%a)\n", Entry->OSVersion, Entry->BuildVersion);
    } else {
      DBG(" %a\n", Entry->OSVersion);
    }

    if (Entry->OSVersion && (AsciiOSVersionToUint64(Entry->OSVersion) >= AsciiOSVersionToUint64("10.11"))) {
      if (OSFLAG_ISSET(Entry->Flags, OSFLAG_NOSIP)) {
        gSettings.CsrActiveConfig = (UINT32)0x37F;
        gSettings.BooterConfig = 0x28;
      }
      ReadSIPCfg();
    }

    FilterKextPatches(Entry);
    FilterKernelPatches(Entry);
    FilterBootPatches(Entry);
    if (LoadedImage && !BooterPatch(LoadedImage->ImageBase, LoadedImage->ImageSize, Entry)) {
      DBG("Will not patch boot.efi\n");
    }

    // Set boot argument for kernel if no caches, this should force kernel loading
    if (OSFLAG_ISSET(Entry->Flags, OSFLAG_NOCACHES) &&
        (StriStr(Entry->LoadOptions, L"Kernel=") == NULL)) {
      CHAR16 *KernelLocation = NULL;
      CHAR16 *TempOptions;

      if (Entry->OSVersion && AsciiOSVersionToUint64(Entry->OSVersion) <= AsciiOSVersionToUint64("10.9")) {
        KernelLocation =L"\"Kernel=/mach_kernel\"";
      } else {
        // used for 10.10, 10.11, and new version.
        KernelLocation =L"\"Kernel=/System/Library/Kernels/kernel\"";
      }

      TempOptions = AddLoadOption(Entry->LoadOptions, KernelLocation);
      FreePool(Entry->LoadOptions);
      Entry->LoadOptions = TempOptions;
    }

    //we are booting OSX - restore emulation if it's not installed before g boot.efi
    if (gEmuVariableControl != NULL) {
        gEmuVariableControl->InstallEmulation(gEmuVariableControl);
    }

    // first patchACPI and find PCIROOT and RTC
    // but before ACPI patch we need smbios patch
	CheckEmptyFB();
    PatchSmbios();
//    DBG("PatchACPI\n");
    PatchACPI(Entry->Volume, Entry->OSVersion);

    // If KPDebug is true boot in verbose mode to see the debug messages
    if ((Entry->KernelAndKextPatches != NULL) && Entry->KernelAndKextPatches->KPDebug) {
      CHAR16 *TempOptions = AddLoadOption(Entry->LoadOptions, L"-v");
      FreePool(Entry->LoadOptions);
      Entry->LoadOptions = TempOptions;
    }

    DbgHeader("RestSetup macOS");

//    DBG("SetDevices\n");
    SetDevices(Entry);
//    DBG("SetFSInjection\n");
    SetFSInjection(Entry);
    //PauseForKey(L"SetFSInjection");
//    DBG("SetVariablesForOSX\n");
    SetVariablesForOSX(Entry);
//    DBG("SetVariablesForOSX\n");
    EventsInitialize(Entry);
//    DBG("FinalizeSmbios\n");
    FinalizeSmbios();

    SetCPUProperties();

    if (OSFLAG_ISSET(Entry->Flags, OSFLAG_HIBERNATED)) {
      DoHibernateWake = PrepareHibernation(Entry->Volume);
    }
    SetupDataForOSX(DoHibernateWake);
    

    if (gDriversFlags.AptioFixLoaded &&
        !DoHibernateWake &&
        !StrStr(Entry->LoadOptions, L"slide=")) {
      // Add slide=0 argument for ML+ if not present
      CHAR16 *TempOptions = AddLoadOption(Entry->LoadOptions, L"slide=0");
      FreePool(Entry->LoadOptions);
      Entry->LoadOptions = TempOptions;
    }
     
      
    /**
     * syscl - append "-xcpm" argument conditionally if set KernelXCPM on Intel Haswell+ low-end CPUs
     */
    if ((Entry->KernelAndKextPatches != NULL) && Entry->KernelAndKextPatches->KPKernelXCPM &&
        gCPUStructure.Vendor == CPU_VENDOR_INTEL && gCPUStructure.Model >= CPU_MODEL_HASWELL &&
       (AsciiStrStr(gCPUStructure.BrandString, "Celeron") || AsciiStrStr(gCPUStructure.BrandString, "Pentium")) &&
       (AsciiOSVersionToUint64(Entry->OSVersion) >= AsciiOSVersionToUint64("10.8.5")) &&
       (AsciiOSVersionToUint64(Entry->OSVersion) < AsciiOSVersionToUint64("10.12")) &&
       (!StrStr(Entry->LoadOptions, L"-xcpm"))) {
        // add "-xcpm" argv if not present on Haswell+ Celeron/Pentium
        CHAR16 *tmpArgv = AddLoadOption(Entry->LoadOptions, L"-xcpm");
        FreePool(Entry->LoadOptions);
        Entry->LoadOptions = tmpArgv;
    }
    
    // add -xcpm on Ivy Bridge if set KernelXCPM and system version is 10.8.5 - 10.11.x
    if ((Entry->KernelAndKextPatches != NULL) && Entry->KernelAndKextPatches->KPKernelXCPM &&
        gCPUStructure.Model == CPU_MODEL_IVY_BRIDGE &&
        (AsciiOSVersionToUint64(Entry->OSVersion) >= AsciiOSVersionToUint64("10.8.5")) &&
        (AsciiOSVersionToUint64(Entry->OSVersion) < AsciiOSVersionToUint64("10.12")) &&
        (!StrStr(Entry->LoadOptions, L"-xcpm"))) {
      // add "-xcpm" argv if not present on Ivy Bridge
      CHAR16 *tmpArgv = AddLoadOption(Entry->LoadOptions, L"-xcpm");
      FreePool(Entry->LoadOptions);
      Entry->LoadOptions = tmpArgv;
    }
    

//    DBG("Set FakeCPUID: 0x%x\n", gSettings.FakeCPUID);
//    DBG("LoadKexts\n");
    // LoadKexts writes to DataHub, where large writes can prevent hibernate wake (happens when several kexts present in Clover's kexts dir)
    if (!DoHibernateWake) {
      LoadKexts(Entry);
    }

    // blocking boot.efi output if -v is not specified
    // note: this blocks output even if -v is specified in
    // /Library/Preferences/SystemConfiguration/com.apple.Boot.plist
    // which is wrong
    // apianti - only block console output if using graphics
    //           but don't block custom boot logo
    if ((Entry->LoadOptions != NULL) &&
        ((StrStr(Entry->LoadOptions, L"-v") != NULL) ||
         (StrStr(Entry->LoadOptions, L"-V") != NULL))) {
          Entry->Flags = OSFLAG_UNSET(Entry->Flags, OSFLAG_USEGRAPHICS);
        } else if (!Entry->LoadOptions) {
          CHAR16 *TempOptions = AddLoadOption(Entry->LoadOptions, L" ");
          FreePool(Entry->LoadOptions);
          Entry->LoadOptions = TempOptions;
        }
  }
  else if (OSTYPE_IS_WINDOWS(Entry->LoaderType)) {

    if (AudioIo) {
        AudioIo->StopPlayback(AudioIo);
    }

    DBG("Closing events for Windows\n");
    gBS->CloseEvent (OnReadyToBootEvent);
    gBS->CloseEvent (ExitBootServiceEvent);
    gBS->CloseEvent (mSimpleFileSystemChangeEvent);


    if (gEmuVariableControl != NULL) {
      gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
    }

    PatchACPI_OtherOS(L"Windows", FALSE);
    //PauseForKey(L"continue");

  }
  else if (OSTYPE_IS_LINUX(Entry->LoaderType) || (Entry->LoaderType == OSTYPE_LINEFI)) {

    DBG("Closing events for Linux\n");
    gBS->CloseEvent (OnReadyToBootEvent);
    gBS->CloseEvent (ExitBootServiceEvent);
    gBS->CloseEvent (mSimpleFileSystemChangeEvent);

    if (gEmuVariableControl != NULL) {
      gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
    }
    //FinalizeSmbios();
    PatchACPI_OtherOS(L"Linux", FALSE);
    //PauseForKey(L"continue");
  }

  if (gSettings.LastBootedVolume) {
    SetStartupDiskVolume(Entry->Volume, Entry->LoaderType == OSTYPE_OSX ? NULL : Entry->LoaderPath);
  } else if (gSettings.DefaultVolume != NULL) {
    // DefaultVolume specified in Config.plist or in Boot Option
    // we'll remove macOS Startup Disk vars which may be present if it is used
    // to reboot into another volume
    RemoveStartupDiskVolume();
  }
/*
  {
    //    UINT32                    machineSignature    = 0;
    EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE     *FadtPointer = NULL;
    EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs = NULL;

    //    DBG("---dump hibernations data---\n");
    FadtPointer = GetFadt();
    if (FadtPointer != NULL) {
      Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)(FadtPointer->FirmwareCtrl);

      DBG("  Firmware wake address=%08lx\n", Facs->FirmwareWakingVector);
      DBG("  Firmware wake 64 addr=%16llx\n",  Facs->XFirmwareWakingVector);
      DBG("  Hardware signature   =%08lx\n", Facs->HardwareSignature);
      DBG("  GlobalLock           =%08lx\n", Facs->GlobalLock);
      DBG("  Flags                =%08lx\n", Facs->Flags);
      DBG(" HS at offset 0x%08x\n", OFFSET_OF(EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE, HardwareSignature));
      //   machineSignature = Facs->HardwareSignature;
    }
  }
*/

//    DBG("BeginExternalScreen\n");
  BeginExternalScreen(OSFLAG_ISSET(Entry->Flags, OSFLAG_USEGRAPHICS), L"Booting OS");

  if (!OSTYPE_IS_WINDOWS(Entry->LoaderType)) {
    if (OSFLAG_ISSET(Entry->Flags, OSFLAG_USEGRAPHICS)) {
      // save orig OutputString and replace it with
      // null implementation
      ConOutOutputString = gST->ConOut->OutputString;
      gST->ConOut->OutputString = NullConOutOutputString;
    }
    
    // Initialize the boot screen
    if (EFI_ERROR(Status = InitBootScreen(Entry))) {
      if (Status != EFI_ABORTED) DBG("Failed to initialize custom boot screen: %r!\n", Status);
    }
    else if (EFI_ERROR(Status = LockBootScreen())) {
      DBG("Failed to lock custom boot screen: %r!\n", Status);
    }
  } // !OSTYPE_IS_WINDOWS

  if (OSTYPE_IS_OSX(Entry->LoaderType) ||
      OSTYPE_IS_OSX_RECOVERY(Entry->LoaderType) ||
      OSTYPE_IS_OSX_INSTALLER(Entry->LoaderType)) {

    if (DoHibernateWake) {
      DBG("Closing events for wake\n");
      gBS->CloseEvent (OnReadyToBootEvent);
      gBS->CloseEvent (ExitBootServiceEvent);
      gBS->CloseEvent (mSimpleFileSystemChangeEvent);
//      gBS->CloseEvent (mVirtualAddressChangeEvent);
      // When doing hibernate wake, save to DataHub only up to initial size of log
      SavePreBootLog = FALSE;
    } else {
      // delete boot-switch-vars if exists
      Status = gRT->SetVariable(L"boot-switch-vars", &gEfiAppleBootGuid,
                                EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                                0, NULL);
      DeleteNvramVariable(L"IOHibernateRTCVariables", &gEfiAppleBootGuid);
      DeleteNvramVariable(L"boot-image",              &gEfiAppleBootGuid);

    }
    SetupBooterLog(!DoHibernateWake);
  }


  
  DBG("Closing log\n");
  if (SavePreBootLog) {
    Status = SaveBooterLog(SelfRootDir, PREBOOT_LOG);
    if (EFI_ERROR(Status)) {
      /*Status = */SaveBooterLog(NULL, PREBOOT_LOG);
    }
  }

//  DBG("StartEFIImage\n");
//  StartEFIImage(Entry->DevicePath, Entry->LoadOptions,
//                Basename(Entry->LoaderPath), Basename(Entry->LoaderPath), NULL, NULL);

//  DBG("StartEFILoadedImage\n");
  StartEFILoadedImage(ImageHandle, Entry->LoadOptions,
                Basename(Entry->LoaderPath), Basename(Entry->LoaderPath), NULL);
  // Unlock boot screen
  if (EFI_ERROR(Status = UnlockBootScreen())) {
    DBG("Failed to unlock custom boot screen: %r!\n", Status);
  }
  if (OSFLAG_ISSET(Entry->Flags, OSFLAG_USEGRAPHICS)) {
    // return back orig OutputString
    gST->ConOut->OutputString = ConOutOutputString;
  }

//  PauseForKey(L"FinishExternalScreen");
  FinishExternalScreen();
//  PauseForKey(L"System started?!");
}
/*
// early 2006 Core Duo / Core Solo models
static UINT8 LegacyLoaderDevicePath1Data[] = {
    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
    0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
};
// mid-2006 Mac Pro (and probably other Core 2 models)
static UINT8 LegacyLoaderDevicePath2Data[] = {
    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
    0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
};
// mid-2007 MBP ("Santa Rosa" based models)
static UINT8 LegacyLoaderDevicePath3Data[] = {
    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
    0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
};
// early-2008 MBA
static UINT8 LegacyLoaderDevicePath4Data[] = {
    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
    0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
};
// late-2008 MB/MBP (NVidia chipset)
static UINT8 LegacyLoaderDevicePath5Data[] = {
    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
    0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
};
static EFI_DEVICE_PATH *LegacyLoaderList[] = {
    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
    NULL
};
*/
#define MAX_DISCOVERED_PATHS (16)
//#define PREBOOT_LOG L"EFI\\CLOVER\\misc\\preboot.log"

static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
{
    EFI_STATUS          Status = EFI_UNSUPPORTED;
    EG_IMAGE            *BootLogoImage;
//    UINTN               ErrorInStep = 0;
//    EFI_DEVICE_PATH     *DiscoveredPathList[MAX_DISCOVERED_PATHS];

    // Unload EmuVariable before booting legacy.
    // This is not needed in most cases, but it seems to interfere with legacy OS
    // booted on some UEFI bioses, such as Phoenix UEFI 2.0
    if (gEmuVariableControl != NULL) {
      gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
    }

    if (gSettings.LastBootedVolume) {
      SetStartupDiskVolume(Entry->Volume, NULL);
    } else if (gSettings.DefaultVolume != NULL) {
      // DefaultVolume specified in Config.plist:
      // we'll remove macOS Startup Disk vars which may be present if it is used
      // to reboot into another volume
      RemoveStartupDiskVolume();
    }

    egClearScreen(&DarkBackgroundPixel);
    BeginExternalScreen(TRUE, L"Booting Legacy OS");

    BootLogoImage = LoadOSIcon(Entry->Volume->LegacyOS->IconName, L"legacy", 128, TRUE, TRUE);
    if (BootLogoImage != NULL)
        BltImageAlpha(BootLogoImage,
                      (UGAWidth  - BootLogoImage->Width) >> 1,
                      (UGAHeight - BootLogoImage->Height) >> 1,
                      &StdBackgroundPixel, 16);

 //   if (StrCmp(gSettings.LegacyBoot, L"Apple") != 0) { // not Apple-style LegacyBoot
      //try my LegacyBoot
      switch (Entry->Volume->BootType) {
        case BOOTING_BY_CD:
          Status = bootElTorito(Entry->Volume);
          break;
        case BOOTING_BY_MBR:
          Status = bootMBR(Entry->Volume);
          break;
        case BOOTING_BY_PBR:
          if (StrCmp(gSettings.LegacyBoot, L"LegacyBiosDefault") == 0) {
            Status = bootLegacyBiosDefault(gSettings.LegacyBiosDefaultEntry);
          } else if (StrCmp(gSettings.LegacyBoot, L"PBRtest") == 0) {
            Status = bootPBRtest(Entry->Volume);
          } else if (StrCmp(gSettings.LegacyBoot, L"PBRsata") == 0) {
            Status = bootPBR(Entry->Volume, TRUE);
          } else {
            // default
            Status = bootPBR(Entry->Volume, FALSE);
          }
          break;
        default:
          break;
      }
      CheckError(Status, L"while LegacyBoot");
/*    } else { // Apple-style LegacyBoot
//      if (0 && Entry->Volume->IsMbrPartition && !Entry->Volume->HasBootCode)
//         ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);

      Status = ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
      if (!EFI_ERROR(Status)) {
        Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep, NULL);
      }
      if (Status == EFI_NOT_FOUND) {
        if (ErrorInStep == 1) {
          Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
        } else if (ErrorInStep == 3) {
          Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
                L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
        }
      }
    }
 */
    FinishExternalScreen();
}

//
// pre-boot tool functions
//

static VOID StartTool(IN LOADER_ENTRY *Entry)
{
  DBG("Start Tool: %s\n", Entry->LoaderPath);
  egClearScreen(&DarkBackgroundPixel);
	// assumes "Start <title>" as assigned below
	BeginExternalScreen(OSFLAG_ISSET(Entry->Flags, OSFLAG_USEGRAPHICS), Entry->me.Title + 6);
    StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
                  Basename(Entry->LoaderPath), NULL, NULL);
    FinishExternalScreen();
	//ReinitSelfLib();
}

//
// pre-boot driver functions
//

static VOID ScanDriverDir(IN CHAR16 *Path, OUT EFI_HANDLE **DriversToConnect, OUT UINTN *DriversToConnectNum)
{
  EFI_STATUS              Status;
  REFIT_DIR_ITER          DirIter;
  EFI_FILE_INFO           *DirEntry;
  CHAR16                  FileName[256];
  EFI_HANDLE              DriverHandle;
  EFI_DRIVER_BINDING_PROTOCOL  *DriverBinding;
  UINTN                   DriversArrSize;
  UINTN                   DriversArrNum;
  EFI_HANDLE              *DriversArr;
  INTN                    i;
  BOOLEAN                 Skip;
  UINT8                   AptioBlessed;
  STATIC CHAR16 CONST * CONST AptioNames[] = {
    L"AptioMemoryFix",
    L"AptioFix3Drv",
    L"AptioFix2Drv",
    L"AptioFixDrv",
    L"LowMemFix"
  };
  STATIC UINT8 CONST AptioIndices[] = {
    OFFSET_OF(DRIVERS_FLAGS, AptioMemFixLoaded),
    OFFSET_OF(DRIVERS_FLAGS, AptioFix3Loaded),
    OFFSET_OF(DRIVERS_FLAGS, AptioFix2Loaded),
    OFFSET_OF(DRIVERS_FLAGS, AptioFixLoaded),
    OFFSET_OF(DRIVERS_FLAGS, MemFixLoaded)
  };

  DriversArrSize = 0;
  DriversArrNum = 0;
  DriversArr = NULL;

//only one driver with highest priority will obtain status "Loaded"
  DirIterOpen(SelfRootDir, Path, &DirIter);
#define BOOLEAN_AT_INDEX(k) (*(BOOLEAN*)((UINTN)&gDriversFlags + AptioIndices[(k)]))
  for (i = 0; i != ARRAY_SIZE(AptioIndices); ++i)
    BOOLEAN_AT_INDEX(i) = FALSE;
  AptioBlessed = (UINT8) ARRAY_SIZE(AptioNames);
  while (DirIterNext(&DirIter, 2, L"*.efi", &DirEntry)) {
    for (i = 0; i != ARRAY_SIZE(AptioNames); ++i)
      if (StrStr(DirEntry->FileName, AptioNames[i]) != NULL)
        break;
    if (((UINT8) i) >= AptioBlessed)
      continue;
    AptioBlessed = (UINT8) i;
    if (!i)
      break;
  }
  DirIterClose(&DirIter);

  // look through contents of the directory
  DirIterOpen(SelfRootDir, Path, &DirIter);
  while (DirIterNext(&DirIter, 2, L"*.efi", &DirEntry)) {
    Skip = (DirEntry->FileName[0] == L'.');
    for (i=0; i<gSettings.BlackListCount; i++) {
      if (StrStr(DirEntry->FileName, gSettings.BlackList[i]) != NULL) {
        Skip = TRUE;   // skip this
        break;
      }
    }
    if (Skip) {
      continue;
    }

    // either AptioMem, AptioFix* or LowMemFix exclusively
    for (i = 0; i != ARRAY_SIZE(AptioNames); ++i)
      if (StrStr(DirEntry->FileName, AptioNames[i]) != NULL)
        break;
    if (i != ARRAY_SIZE(AptioNames)) {
      if (((UINT8) i) != AptioBlessed)
        continue;
      if (AptioBlessed < (UINT8) ARRAY_SIZE(AptioIndices))
        BOOLEAN_AT_INDEX(AptioBlessed) = TRUE;
      AptioBlessed = (UINT8) ARRAY_SIZE(AptioNames);
    }
#undef BOOLEAN_AT_INDEX

    UnicodeSPrint(FileName, 512, L"%s\\%s", Path, DirEntry->FileName);
    Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
                           L"", DirEntry->FileName, DirEntry->FileName, NULL, &DriverHandle);
    if (EFI_ERROR(Status)) {
      continue;
    }
    if (StrStr(FileName, L"EmuVariable") != NULL) {
      gDriversFlags.EmuVariableLoaded = TRUE;
    } else if (StrStr(FileName, L"Video") != NULL) {
      gDriversFlags.VideoLoaded = TRUE;
    } else if (StrStr(FileName, L"Partition") != NULL) {
      gDriversFlags.PartitionLoaded = TRUE;
    } else if (StrStr(FileName, L"HFS") != NULL) {
      gDriversFlags.HFSLoaded = TRUE;
    } else if (StriStr(FileName, L"apfs") != NULL) {
      gDriversFlags.APFSLoaded = TRUE;
    }
    if (DriverHandle != NULL && DriversToConnectNum != NULL && DriversToConnect != NULL) {
      // driver loaded - check for EFI_DRIVER_BINDING_PROTOCOL
      Status = gBS->HandleProtocol(DriverHandle, &gEfiDriverBindingProtocolGuid, (VOID **) &DriverBinding);
      if (!EFI_ERROR(Status) && DriverBinding != NULL) {
        DBG(" - driver needs connecting\n");
        // standard UEFI driver - we would reconnect after loading - add to array
        if (DriversArrSize == 0) {
          // new array
          DriversArrSize = 16;
          DriversArr = AllocateZeroPool(sizeof(EFI_HANDLE) * DriversArrSize);
        } else if (DriversArrNum + 1 == DriversArrSize) {
          // extend array
          DriversArr = ReallocatePool(DriversArrSize, DriversArrSize + 16, DriversArr);
          DriversArrSize += 16;
        }
        DriversArr[DriversArrNum] = DriverHandle;
 //       DBG(" driver %s included with Binding=%x\n", FileName, DriverBinding);
        DriversArrNum++;
        // we'll make array terminated
        DriversArr[DriversArrNum] = NULL;
      }
    }
  }
  Status = DirIterClose(&DirIter);
  if (Status != EFI_NOT_FOUND) {
    UnicodeSPrint(FileName, 512, L"while scanning the %s directory", Path);
    CheckError(Status, FileName);
  }

  if (DriversToConnectNum != NULL && DriversToConnect != NULL) {
    *DriversToConnectNum = DriversArrNum;
    *DriversToConnect = DriversArr;
  }
//release memory for BlackList
  for (i=0; i<gSettings.BlackListCount; i++) {
    if (gSettings.BlackList[i]) {
      FreePool(gSettings.BlackList[i]);
      gSettings.BlackList[i] = NULL;
    }
  }
}


/**
 * Some UEFI's (like HPQ EFI from HP notebooks) have DiskIo protocols
 * opened BY_DRIVER (by Partition driver in HP case) even when no file system
 * is produced from this DiskIo. This then blocks our FS drivers from connecting
 * and producing file systems.
 * To fix it: we will disconnect drivers that connected to DiskIo BY_DRIVER
 * if this is partition volume and if those drivers did not produce file system.
 */
VOID DisconnectInvalidDiskIoChildDrivers(VOID)
{
  EFI_STATUS                            Status;
  UINTN                                 HandleCount = 0;
  UINTN                                 Index;
  UINTN                                 OpenInfoIndex;
  EFI_HANDLE                            *Handles = NULL;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL       *Fs;
  EFI_BLOCK_IO_PROTOCOL                 *BlockIo;
  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY   *OpenInfo;
  UINTN                                 OpenInfoCount;
  BOOLEAN                               Found;

  DBG("Searching for invalid DiskIo BY_DRIVER connects:");

  //
  // Get all DiskIo handles
  //
  Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiDiskIoProtocolGuid, NULL, &HandleCount, &Handles);
  if (EFI_ERROR(Status) || HandleCount == 0) {
    DBG(" no DiskIo handles\n");
    return;
  }

  //
  // Check every DiskIo handle
  //
  Found = FALSE;
  for (Index = 0; Index < HandleCount; Index++) {
    //DBG("\n");
    //DBG(" - Handle %p:", Handles[Index]);
    //
    // If this is not partition - skip it.
    // This is then whole disk and DiskIo
    // should be opened here BY_DRIVER by Partition driver
    // to produce partition volumes.
    //
    Status = gBS->HandleProtocol (
                                  Handles[Index],
                                  &gEfiBlockIoProtocolGuid,
                                  (VOID **) &BlockIo
                                  );
    if (EFI_ERROR (Status)) {
      //DBG(" BlockIo: %r - skipping\n", Status);
      continue;
    }
    if (BlockIo->Media == NULL) {
      //DBG(" BlockIo: no media - skipping\n");
      continue;

    }
    if (!BlockIo->Media->LogicalPartition) {
      //DBG(" BlockIo: whole disk - skipping\n");
      continue;

    }
    //DBG(" BlockIo: partition");

    //
    // If SimpleFileSystem is already produced - skip it, this is ok
    //
    Status = gBS->HandleProtocol (
                                  Handles[Index],
                                  &gEfiSimpleFileSystemProtocolGuid,
                                  (VOID **) &Fs
                                  );
    if (Status == EFI_SUCCESS) {
      //DBG(" FS: ok - skipping\n");
      continue;
    }
    //DBG(" FS: no");

    //
    // If no SimpleFileSystem on this handle but DiskIo is opened BY_DRIVER
    // then disconnect this connection
    //
    Status = gBS->OpenProtocolInformation (
                                           Handles[Index],
                                           &gEfiDiskIoProtocolGuid,
                                           &OpenInfo,
                                           &OpenInfoCount
                                           );
    if (EFI_ERROR (Status)) {
      //DBG(" OpenInfo: no - skipping\n");
      continue;
    }
    //DBG(" OpenInfo: %d", OpenInfoCount);
    for (OpenInfoIndex = 0; OpenInfoIndex < OpenInfoCount; OpenInfoIndex++) {
      if ((OpenInfo[OpenInfoIndex].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) == EFI_OPEN_PROTOCOL_BY_DRIVER) {
        if (!Found) {
          DBG("\n");
        }
        Found = TRUE;
        Status = gBS->DisconnectController (Handles[Index], OpenInfo[OpenInfoIndex].AgentHandle, NULL);
        //DBG(" BY_DRIVER Agent: %p, Disconnect: %r", OpenInfo[OpenInfoIndex].AgentHandle, Status);
        DBG(" - Handle %p with DiskIo, is Partition, no Fs, BY_DRIVER Agent: %p, Disconnect: %r\n", Handles[Index], OpenInfo[OpenInfoIndex].AgentHandle, Status);
      }
    }
    FreePool (OpenInfo);
  }
  FreePool(Handles);

  if (!Found) {
    DBG(" not found, all ok\n");
  }
}

VOID DisconnectSomeDevices(VOID)
{
  EFI_STATUS              Status;
  UINTN                   HandleCount;
  UINTN                   Index, Index2;
  EFI_HANDLE              *Handles ;
  EFI_HANDLE              *ControllerHandles;
  UINTN                   ControllerHandleCount;
  EFI_BLOCK_IO_PROTOCOL   *BlockIo  = NULL;
//  EFI_DISK_IO_PROTOCOL    *DiskIo = NULL;
  EFI_PCI_IO_PROTOCOL     *PciIo  = NULL;
//  EFI_FILE_PROTOCOL       *RootFP = NULL;
//  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *VolumeFS = NULL;
  PCI_TYPE00              Pci;
  CHAR16                           *DriverName;
  EFI_COMPONENT_NAME_PROTOCOL      *CompName;

  if (gDriversFlags.PartitionLoaded) {
    DBG("Partition driver loaded: ");
    // get all BlockIo handles
    HandleCount = 0;
    Handles = NULL;

    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &HandleCount, &Handles);
    if (Status == EFI_SUCCESS) {
      for (Index = 0; Index < HandleCount; Index++) {
        Status = gBS->HandleProtocol(Handles[Index], &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
        if (EFI_ERROR(Status)) {
          continue;
        }
        if (BlockIo->Media->BlockSize == 2048) {
          // disconnect CD controller
          Status = gBS->DisconnectController(Handles[Index], NULL, NULL);
          DBG("CD disconnect %r", Status);
        }
      }
/*      for (Index = 0; Index < HandleCount; Index++) {
        Status = gBS->DisconnectController(Handles[Index], NULL, NULL);
      } */
      FreePool(Handles);
    }
    DBG("\n");
  }

  if ((gDriversFlags.HFSLoaded) || (gDriversFlags.APFSLoaded)) {
  if (gDriversFlags.HFSLoaded) {
    DBG("HFS+ driver loaded\n");
    }
    if (gDriversFlags.APFSLoaded) {
      DBG("APFS driver loaded\n");
    }

    // get all FileSystem handles
    ControllerHandleCount = 0;
    ControllerHandles = NULL;

    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &ControllerHandleCount, &ControllerHandles);
 /*   if (!EFI_ERROR (Status)) {
      for (Index2 = 0; Index2 < ControllerHandleCount; Index2++) {
        Status = gBS->DisconnectController(ControllerHandles[Index2],
                                           NULL, NULL);
        DBG("Driver [%d] disconnect %r\n", Index2, Status);
      }
    } */

    HandleCount = 0;
    Handles = NULL;

    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiComponentNameProtocolGuid, NULL, &HandleCount, &Handles);
    if (!EFI_ERROR (Status)) {
      for (Index = 0; Index < HandleCount; Index++) {
        Status = gBS->OpenProtocol(
                                   Handles[Index],
                                   &gEfiComponentNameProtocolGuid,
                                   (VOID**)&CompName,
                                   gImageHandle,
                                   NULL,
                                   EFI_OPEN_PROTOCOL_GET_PROTOCOL);

        if (EFI_ERROR(Status)) {
//          DBG("CompName %r\n", Status);
          continue;
        }
        Status = CompName->GetDriverName(CompName, "eng", &DriverName);
        if (EFI_ERROR(Status)) {
          continue;
        }
        if ((StriStr(DriverName, L"HFS")) || (StriStr(DriverName, L"apfs"))) {
          for (Index2 = 0; Index2 < ControllerHandleCount; Index2++) {
            Status = gBS->DisconnectController(ControllerHandles[Index2],
                                               Handles[Index], NULL);
//            DBG("Disconnect [%s] from %x: %r\n", DriverName, ControllerHandles[Index2], Status);
          }
        }
      }
      FreePool(Handles);
    }
//    DBG("\n");
    FreePool(ControllerHandles);
  }


  if (gDriversFlags.VideoLoaded) {
    DBG("Video driver loaded: ");
    // get all PciIo handles
    HandleCount = 0;
    Handles = NULL;
    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &Handles);
    if (Status == EFI_SUCCESS) {
      for (Index = 0; Index < HandleCount; Index++) {
        Status = gBS->HandleProtocol(Handles[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
        if (EFI_ERROR(Status)) {
          continue;
        }
        Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci);
        if (!EFI_ERROR (Status)) {
          if(IS_PCI_VGA(&Pci) == TRUE) {
            // disconnect VGA
            Status = gBS->DisconnectController(Handles[Index], NULL, NULL);
            DBG("disconnect %r", Status);
          }
        }
      }
      FreePool(Handles);
    }
    DBG("\n");
  }

  if (!gFirmwareClover) {
    DisconnectInvalidDiskIoChildDrivers();
  }
}


VOID PatchVideoBios(UINT8 *Edid)
{

  if (gSettings.PatchVBiosBytesCount > 0 && gSettings.PatchVBiosBytes != NULL) {
    VideoBiosPatchBytes(gSettings.PatchVBiosBytes, gSettings.PatchVBiosBytesCount);
  }

  if (gSettings.PatchVBios) {
    VideoBiosPatchNativeFromEdid(Edid);
  }
}


static VOID LoadDrivers(VOID)
{
  EFI_STATUS  Status;
  EFI_HANDLE  *DriversToConnect = NULL;
  UINTN       DriversToConnectNum = 0;
  UINT8       *Edid;
  UINTN       VarSize = 0;
  BOOLEAN     VBiosPatchNeeded;

  DbgHeader("LoadDrivers");

    // load drivers from /efi/drivers
#if defined(MDE_CPU_X64)
  if (gFirmwareClover) {
    if (FileExists(SelfRootDir, L"\\EFI\\CLOVER\\drivers\\BIOS")) {
      ScanDriverDir(L"\\EFI\\CLOVER\\drivers\\BIOS", &DriversToConnect, &DriversToConnectNum);
    } else {
      ScanDriverDir(L"\\EFI\\CLOVER\\drivers64", &DriversToConnect, &DriversToConnectNum);
    }
  } else
  if (FileExists(SelfRootDir, L"\\EFI\\CLOVER\\drivers\\UEFI")) {
    ScanDriverDir(L"\\EFI\\CLOVER\\drivers\\UEFI", &DriversToConnect, &DriversToConnectNum);
  } else {
    ScanDriverDir(L"\\EFI\\CLOVER\\drivers64UEFI", &DriversToConnect, &DriversToConnectNum);
  }
#else
  ScanDriverDir(L"\\EFI\\CLOVER\\drivers32", &DriversToConnect, &DriversToConnectNum);
#endif

  VBiosPatchNeeded = gSettings.PatchVBios || (gSettings.PatchVBiosBytesCount > 0 && gSettings.PatchVBiosBytes != NULL);
  if (VBiosPatchNeeded) {
    // check if it is already done in CloverEFI BiosVideo
    Status = gRT->GetVariable (
                               L"CloverVBiosPatchDone",
                               &gEfiGlobalVariableGuid,
                               NULL,
                               &VarSize,
                               NULL
                               );
    if (Status == EFI_BUFFER_TOO_SMALL) {
      // var exists - it's done - let's not do it again
      VBiosPatchNeeded = FALSE;
    }
  }

  if (((gSettings.CustomEDID != NULL) && gFirmwareClover) || (VBiosPatchNeeded && !gDriversFlags.VideoLoaded)) {
    // we have video bios patch - force video driver reconnect
    DBG("Video bios patch requested or CustomEDID - forcing video reconnect\n");
    gDriversFlags.VideoLoaded = TRUE;
    DriversToConnectNum++;
  }

  if (DriversToConnectNum > 0) {
    DBG("%d drivers needs connecting ...\n", DriversToConnectNum);
    // note: our platform driver protocol
    // will use DriversToConnect - do not release it
    RegisterDriversToHighestPriority(DriversToConnect);
    if (VBiosPatchNeeded) {
      if (gSettings.CustomEDID != NULL) {
        Edid = gSettings.CustomEDID;
      } else {
        Edid = getCurrentEdid();
      }
      DisconnectSomeDevices();
      PatchVideoBios(Edid);
      if (gSettings.CustomEDID == NULL) {
        FreePool(Edid);
      }
    } else {
      DisconnectSomeDevices();
    }
    BdsLibConnectAllDriversToAllControllers();

    // Boot speedup: remove temporary "BiosVideoBlockSwitchMode" RT var
    // to unlock mode switching in CsmVideo
    gRT->SetVariable(L"BiosVideoBlockSwitchMode", &gEfiGlobalVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, NULL);
  }
}


INTN FindDefaultEntry(VOID)
{
  INTN                Index = -1;
  REFIT_VOLUME        *Volume;
  LOADER_ENTRY        *Entry;
  BOOLEAN             SearchForLoader;

//  DBG("FindDefaultEntry ...\n");
  //DbgHeader("FindDefaultEntry");

  //
  // try to detect volume set by Startup Disk or previous Clover selection
  // with broken nvram this requires emulation to be installed.
  // enable emulation to determin efi-boot-device-data
  if (gEmuVariableControl != NULL) {
    gEmuVariableControl->InstallEmulation(gEmuVariableControl);
  }

  Index = FindStartupDiskVolume(&MainMenu);

  if (Index >= 0) {
    DBG("Boot redirected to Entry %d. '%s'\n", Index, MainMenu.Entries[Index]->Title);
    // we got boot-device-data, no need to keep emulating anymore
    if (gEmuVariableControl != NULL) {
        gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
    }
    return Index;
  }

  //
  // if not found, then try DefaultVolume from config.plist
  // if not null or empty, search volume that matches gSettings.DefaultVolume
  //
  if (gSettings.DefaultVolume != NULL) {

    // if not null or empty, also search for loader that matches gSettings.DefaultLoader
    SearchForLoader = (gSettings.DefaultLoader != NULL && gSettings.DefaultLoader[0] != L'\0');
/*
    if (SearchForLoader) {
      DBG("Searching for DefaultVolume '%s', DefaultLoader '%s' ...\n", gSettings.DefaultVolume, gSettings.DefaultLoader);
    } else {
      DBG("Searching for DefaultVolume '%s' ...\n", gSettings.DefaultVolume);
    }
*/
    for (Index = 0; ((Index < (INTN)MainMenu.EntryCount) && (MainMenu.Entries[Index]->Row == 0)); Index++) {

      Entry = (LOADER_ENTRY*)MainMenu.Entries[Index];
      if (!Entry->Volume) {
        continue;
      }

      Volume = Entry->Volume;
      if ((Volume->VolName == NULL || StrCmp(Volume->VolName, gSettings.DefaultVolume) != 0) &&
          !StrStr(Volume->DevicePathString, gSettings.DefaultVolume)) {
        continue;
      }

      if (SearchForLoader && (Entry->me.Tag != TAG_LOADER || !StriStr(Entry->LoaderPath, gSettings.DefaultLoader))) {
        continue;
      }

      DBG(" - found entry %d. '%s', Volume '%s', DevicePath '%s'\n", Index, Entry->me.Title, Volume->VolName, Entry->DevicePathString);
      // if first method failed and second succeeded - uninstall emulation
      if (gEmuVariableControl != NULL) {
        gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
      }
      return Index;
    }

  }

  DBG("Default boot entry not found\n");
 // if both methods to determine default boot entry have failed - uninstall emulation before GUI
 if (gEmuVariableControl != NULL) {
    gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
 }
  return -1;
}

VOID SetVariablesFromNvram()
{
  CHAR8  *tmpString;
  UINTN   Size = 0;
  UINTN   index = 0, index2, len, i;
  CHAR8  *arg = NULL;

//  DbgHeader("SetVariablesFromNvram");

  tmpString = GetNvramVariable(L"boot-args", &gEfiAppleBootGuid, NULL, &Size);
  if (tmpString && (Size <= 0x1000) && (Size > 0)) {
    DBG("found boot-args in NVRAM:%a, size=%d\n", tmpString, Size);
    // use and forget old one
//    DeleteNvramVariable(L"boot-args", &gEfiAppleBootGuid);
    Size = AsciiStrLen(tmpString); // some EFI implementations include '\0' in Size, and others don't, so update Size to string length
    arg = AllocatePool(Size+1);
    
/*    if (AsciiStrStr(tmpString, "nvda_drv=1")) { //found substring
      gSettings.NvidiaWeb = TRUE;
    } */
    //first we will find new args that is not present in main args
    index = 0;
    while ((index < Size) && (tmpString[index] != 0x0)) {
      ZeroMem(arg, Size+1);
      index2 = 0;
      if (tmpString[index] != '\"') {
 //       DBG("search space index=%d\n", index);
        while ((index < Size) && (tmpString[index] != 0x20) && (tmpString[index] != 0x0)) {
          arg[index2++] = tmpString[index++];
        }
        DBG("...found arg:%a\n", arg);
      } else {
        index++;
//        DBG("search quote index=%d\n", index);
        while ((index < Size) && (tmpString[index] != '\"') && (tmpString[index] != 0x0)) {
          arg[index2++] = tmpString[index++];
        }
        if (tmpString[index] == '\"') {
          index++;
        }
        DBG("...found quoted arg:\n", arg);
      }
      while (tmpString[index] == 0x20) {
        index++;
      }
      // For the moment only arg -s must be ignored
      if (AsciiStrCmp(arg, "-s") == 0) {
          DBG("...ignoring arg:%a\n", arg);
        continue;
      }
      if (!AsciiStrStr(gSettings.BootArgs, arg)) {
        //this arg is not present will add
        DBG("...adding arg:%a\n", arg);
        len = iStrLen(gSettings.BootArgs, 256);
        if (len + index2 > 256) {
          DBG("boot-args overflow... bytes=%d+%d\n", len, index2);
          break;
        }
        gSettings.BootArgs[len++] = 0x20;
        for (i = 0; i < index2; i++) {
          gSettings.BootArgs[len++] = arg[i];
        }
        gSettings.BootArgs[len++] = 0x20;
      }
    }
    FreePool(arg);
  }
  if (tmpString) {
    FreePool(tmpString);
  }
  
  tmpString = GetNvramVariable(L"nvda_drv", &gEfiAppleBootGuid, NULL, NULL);
  if (tmpString && AsciiStrCmp(tmpString, "1") == 0) {
    gSettings.NvidiaWeb = TRUE;
  }
  if (tmpString) {
    FreePool(tmpString);
  }

}

VOID ResetNvram () 
  {
    if (gFirmwareClover || gDriversFlags.EmuVariableLoaded) {
      //if (gEmuVariableControl != NULL) {
      //  gEmuVariableControl->InstallEmulation(gEmuVariableControl);
      //}
      ResetEmuNvram ();
      //if (gEmuVariableControl != NULL) {
      //  gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
      //}
    } else {
      ResetNativeNvram ();
    }
    // Attempt warm reboot
    gRS->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
    // Warm reboot may not be supported attempt cold reboot
    gRS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
    // Terminate the screen and just exit
    TerminateScreen();
}

VOID SetOEMPath(CHAR16 *ConfName)
  {
    if (ConfName == NULL) {
      OEMPath = L"EFI\\CLOVER";
    } else if (!gFirmwareClover &&
               FileExists(SelfRootDir,
                          PoolPrint(L"EFI\\CLOVER\\OEM\\%a\\UEFI\\%s.plist", gSettings.OEMBoard, ConfName))) {
      OEMPath = PoolPrint(L"EFI\\CLOVER\\OEM\\%a\\UEFI", gSettings.OEMBoard);
    } else if (FileExists(SelfRootDir, PoolPrint(L"EFI\\CLOVER\\OEM\\%a\\%s.plist", gSettings.OEMProduct, ConfName))) {
      OEMPath = PoolPrint(L"EFI\\CLOVER\\OEM\\%a", gSettings.OEMProduct);
    } else if (FileExists(SelfRootDir, PoolPrint(L"EFI\\CLOVER\\OEM\\%a\\%s.plist", gSettings.OEMBoard, ConfName))) {
      OEMPath = PoolPrint(L"EFI\\CLOVER\\OEM\\%a", gSettings.OEMBoard);
    } else {
      OEMPath = L"EFI\\CLOVER";
    }
  }

/*  S. Mitrofanov 08.06.2016
 *  APFS Container introduced new partitions structure
 *  Now we have, for example:
 * /dev/disk0 (internal, physical):
 *   #:                       TYPE NAME                    SIZE       IDENTIFIER
 *   0:      GUID_partition_scheme                        *240.1 GB   disk0
 *   1:                        EFI EFI                     209.7 MB   disk0s1
 *   2:                 Apple_APFS Container disk1         239.2 GB   disk0s2
 *   3:       Apple_KernelCoreDump                         655.4 MB   disk0s3
 *
 * /dev/disk1 (synthesized):
 *   #:                       TYPE NAME                    SIZE       IDENTIFIER
 *   0:      APFS Container Scheme -                      +239.2 GB   disk1
 *                                 Physical Store disk0s2
 *   1:                APFS Volume Macintosh SSD           170.8 GB   disk1s1
 *   2:                APFS Volume Preboot                 17.9 MB    disk1s2
 *   3:                APFS Volume Recovery                521.1 MB   disk1s3
 *   4:                APFS Volume VM                      1.1 GB     disk1s4
 */
UINTN    APFSUUIDBankCounter = 0;
UINT8   *APFSUUIDBank        = NULL;
//Vednor APFS device path signature
//BE74FCF7-0B7C-49F3-9147-01F4042E6842
EFI_GUID APFSSignature       = {0xBE74FCF7, 0x0B7C, 0x49F3, { 0x91, 0x47, 0x01, 0xf4, 0x04, 0x2E, 0x68, 0x42 }};

//Function for obtaining unique part id from APFS partition
//IN DevicePath
//Out: EFI_GUID 
//null if it is not APFS part
EFI_GUID *APFSPartitionUUIDExtract(
  IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
  )
{
  while (!IsDevicePathEndType(DevicePath) &&
         !(DevicePathType(DevicePath) == MEDIA_DEVICE_PATH && DevicePathSubType (DevicePath) == MEDIA_VENDOR_DP)) {
    DevicePath = NextDevicePathNode(DevicePath);
  }
  if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH && DevicePathSubType (DevicePath) == MEDIA_VENDOR_DP) {
    //Check that vendor-assigned GUID defines APFS Container Partition
    if (StriCmp(GuidLEToStr((EFI_GUID *)((UINT8 *)DevicePath+0x04)),GuidLEToStr(&APFSSignature)) == 0 ) {
      return (EFI_GUID *)((UINT8 *)DevicePath+0x14);
    }
  }         
  return NULL;
}

UINT8 *APFSContainer_Support(VOID) {
  /* 
   * S. Mtr 2017
   * APFS Container partition support
   * Gather System PartitionUniqueGUID
   * edit: 17.06.2017
   * Fiil UUIDBank only with APFS container UUIDs
   */
  UINTN                     VolumeIndex;
  REFIT_VOLUME             *Volume;
  EFI_GUID                 *TmpUUID    = NULL;

  //Fill APFSUUIDBank
  APFSUUIDBank = AllocateZeroPool(0x10*VolumesCount);
  for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
    Volume = Volumes[VolumeIndex];
    //Check that current volume - apfs partition
    if ((TmpUUID = APFSPartitionUUIDExtract(Volume->DevicePath)) != NULL){
      CopyMem(APFSUUIDBank+APFSUUIDBankCounter*0x10,(UINT8 *)TmpUUID,0x10);
      APFSUUIDBankCounter++;
    }                     
  }
  return APFSUUIDBank;
}

//System / Install / Recovery version filler
CHAR16 *SystemVersionPlist       = L"\\System\\Library\\CoreServices\\SystemVersion.plist";
CHAR16 *ServerVersionPlist       = L"\\System\\Library\\CoreServices\\ServerVersion.plist";
CHAR16 *InstallVersionPlist      = L"\\macOS Install Data\\Locked Files\\Boot Files\\SystemVersion.plist";
CHAR16 *RecoveryVersionPlist     = L"\\com.apple.recovery.boot\\SystemVersion.plist";
CHAR16  APFSSysPlistPath[86]     = L"\\00000000-0000-0000-0000-000000000000\\System\\Library\\CoreServices\\SystemVersion.plist";
CHAR16  APFSServerPlistPath[86]  = L"\\00000000-0000-0000-0000-000000000000\\System\\Library\\CoreServices\\ServerVersion.plist";
CHAR16  APFSInstallPlistPath[79] = L"\\00000000-0000-0000-0000-000000000000\\com.apple.installer\\SystemVersion.plist";
CHAR16  APFSRecPlistPath[58]     = L"\\00000000-0000-0000-0000-000000000000\\SystemVersion.plist";
  

VOID SystemVersionInit(VOID)
{
  //Plists iterators
  UINTN      SysIter            = 2;
  UINTN      InsIter            = 1;
  UINTN      RecIter            = 1;
  UINTN      k                  = 0;
  UINTN      i;

  // If scanloader starts multiple times, then we need to free systemplists, installplists, recoveryplists variables, also
  // refresh APFSUUIDBank
  if ((SystemPlists != NULL) || (InstallPlists != NULL) || (RecoveryPlists != NULL)) {
    if ((APFSUUIDBank != NULL) && (APFSSupport == TRUE)) {
      FreePool(APFSUUIDBank);
      //Reset APFSUUIDBank counter, we will re-enumerate it
      APFSUUIDBankCounter = 0;
      APFSUUIDBank = APFSContainer_Support();
      if (APFSUUIDBankCounter == 0) {
        APFSSupport = FALSE;
      }
    }
    if (SystemPlists != NULL) {
      k = 0;
      while (SystemPlists[k] != NULL) {
        SystemPlists[k] = NULL;
        k++;
      }
      FreePool(SystemPlists);
      SystemPlists = NULL;
    }
    if (InstallPlists != NULL) {
      k = 0;
      while (InstallPlists[k] != NULL) {
        InstallPlists[k] = NULL;
        k++;
      }
      FreePool(InstallPlists);
      InstallPlists = NULL;
    }
    if (RecoveryPlists != NULL) {
      k = 0;
      while (RecoveryPlists[k] != NULL) {
        RecoveryPlists[k] = NULL;
        k++;
      }
      FreePool(RecoveryPlists);
      RecoveryPlists = NULL;
    }
  }
  /************************************************************************/
  /*Allocate Memory for systemplists, installplists and recoveryplists********************/
  //Check apfs support
  if (APFSSupport == TRUE) {
    SystemPlists = AllocateZeroPool((2*APFSUUIDBankCounter+3)*sizeof(CHAR16 *));//array of pointers
    InstallPlists = AllocateZeroPool((APFSUUIDBankCounter+2)*sizeof(CHAR16 *));//array of pointers
    RecoveryPlists = AllocateZeroPool((APFSUUIDBankCounter+2)*sizeof(CHAR16 *));//array of pointers
  } else {
    SystemPlists = AllocateZeroPool(sizeof(CHAR16 *)*3);
    InstallPlists = AllocateZeroPool(sizeof(CHAR16 *)*2);
    RecoveryPlists = AllocateZeroPool(sizeof(CHAR16 *)*2);
  }
  /* Fill it with standard paths*******************************************/
  SystemPlists[0] = SystemVersionPlist;
  SystemPlists[1] = ServerVersionPlist;
  SystemPlists[2] = NULL;
  InstallPlists[0] = InstallVersionPlist;
  InstallPlists[1] = NULL;
  RecoveryPlists[0] = RecoveryVersionPlist;
  RecoveryPlists[1] = NULL;
  /************************************************************************/
  //Fill Plists 
  for (i = 0; i < APFSUUIDBankCounter+1; i++) {
    //Store UUID from bank
    CHAR16 *CurrentUUID = GuidLEToStr((EFI_GUID *)((UINT8 *)APFSUUIDBank+i*0x10));
    //Init temp string with system/install/recovery APFS path
    CHAR16 *TmpSysPlistPath = AllocateZeroPool(86*sizeof(CHAR16));
    CHAR16 *TmpServerPlistPath = AllocateZeroPool(86*sizeof(CHAR16));
    CHAR16 *TmpInsPlistPath = AllocateZeroPool(79*sizeof(CHAR16));
    CHAR16 *TmpRecPlistPath = AllocateZeroPool(58*sizeof(CHAR16));
    StrnCpy(TmpSysPlistPath, APFSSysPlistPath, 85);
    StrnCpy(TmpServerPlistPath, APFSServerPlistPath, 85);
    StrnCpy(TmpInsPlistPath, APFSInstallPlistPath, 78);
    StrnCpy(TmpRecPlistPath, APFSRecPlistPath, 57);
    StrnCpy(TmpSysPlistPath+1, CurrentUUID, 36);
    StrnCpy(TmpServerPlistPath+1, CurrentUUID, 36);
    StrnCpy(TmpInsPlistPath+1, CurrentUUID, 36);
    StrnCpy(TmpRecPlistPath+1, CurrentUUID, 36);
    //Fill SystemPlists/InstallPlists/RecoveryPlists arrays
    SystemPlists[SysIter] = TmpSysPlistPath;
    SystemPlists[SysIter+1] = TmpServerPlistPath;
    SystemPlists[SysIter+2] = NULL;
    InstallPlists[InsIter] = TmpInsPlistPath;
    InstallPlists[InsIter+1] = NULL;
    RecoveryPlists[RecIter] = TmpRecPlistPath;
    RecoveryPlists[RecIter+1] = NULL;
    SysIter+=2;
    InsIter++;
    RecIter++;
  }
}

//
// main entry point
//
EFI_STATUS
EFIAPI
RefitMain (IN EFI_HANDLE           ImageHandle,
           IN EFI_SYSTEM_TABLE     *SystemTable)
{
  EFI_STATUS Status;
  BOOLEAN           MainLoopRunning = TRUE;
  BOOLEAN           ReinitDesktop = TRUE;
  BOOLEAN           AfterTool = FALSE;
  REFIT_MENU_ENTRY  *ChosenEntry = NULL;
  REFIT_MENU_ENTRY  *DefaultEntry = NULL;
  REFIT_MENU_ENTRY  *OptionEntry = NULL;
  INTN              DefaultIndex;
  UINTN             MenuExit;
  UINTN             Size, i;
	//UINT64            TscDiv;
	//UINT64            TscRemainder = 0;
  LOADER_ENTRY      *LoaderEntry;
  CHAR16            *ConfName = NULL;
  TagPtr            smbiosTags = NULL;
  TagPtr            UniteTag = NULL;
  BOOLEAN           UniteConfigs = FALSE;
  EFI_TIME          Now;
  BOOLEAN           HaveDefaultVolume;
  CHAR16            *FirstMessage;

  // CHAR16            *InputBuffer; //, *Y;
  //  EFI_INPUT_KEY Key;

  // Init assets dir: misc
  /*Status = */ //egMkDir(SelfRootDir,  L"EFI\\CLOVER\\misc");
  //Should apply to: "ACPI/origin/" too

  // get TSC freq and init MemLog if needed
  gCPUStructure.TSCCalibr = GetMemLogTscTicksPerSecond(); //ticks for 1second
  //GlobalConfig.TextOnly = TRUE;

  // bootstrap
  gST       = SystemTable;
  gImageHandle  = ImageHandle;
  gBS       = SystemTable->BootServices;
  gRS       = SystemTable->RuntimeServices;
  /*Status = */EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
  
  ConsoleInHandle = SystemTable->ConsoleInHandle;
  

  gRS->GetTime(&Now, NULL);

  // firmware detection
  gFirmwareClover = StrCmp(gST->FirmwareVendor, L"CLOVER") == 0;
  if (!gFirmwareRevision) {
    gFirmwareRevision = PoolPrint(L"%d", gST->FirmwareRevision);
  }
  InitializeConsoleSim();
  InitBooterLog();
  ZeroMem((VOID*)&gGraphics[0], sizeof(GFX_PROPERTIES) * 4);
  ZeroMem((VOID*)&gAudios[0], sizeof(HDA_PROPERTIES) * 4);

  DBG("\n");
  if (Now.TimeZone < -1440 || Now.TimeZone > 1440) {
    MsgLog("Now is %d.%d.%d,  %d:%d:%d (GMT)\n",
           Now.Day, Now.Month, Now.Year, Now.Hour, Now.Minute, Now.Second);
  } else {
    MsgLog("Now is %d.%d.%d,  %d:%d:%d (GMT+%d)\n",
      Now.Day, Now.Month, Now.Year, Now.Hour, Now.Minute, Now.Second, GlobalConfig.Timezone);
  }
  //MsgLog("Starting Clover rev %s on %s EFI\n", FIRMWARE_REVISION, gST->FirmwareVendor);
  MsgLog("Starting %a on %s EFI\n", REVISION_STR, gST->FirmwareVendor);

  #ifdef BUILDINFOS_STR
    DBG("Build with: [%a]\n", BUILDINFOS_STR);
  #endif // BUILDINFOS_STR

  Status = InitRefitLib(gImageHandle);
  if (EFI_ERROR(Status))
    return Status;
  //dumping SETTING structure
  // if you change something in Platform.h, please uncomment and test that all offsets
  // are natural aligned i.e. pointers are 8 bytes aligned
  /*
  DBG("Settings offsets:\n");
  DBG(" OEMProduct:     %x\n",    OFFSET_OF(SETTINGS_DATA, OEMProduct));
  DBG(" DefaultVolume:  %x\n",    OFFSET_OF(SETTINGS_DATA, DefaultVolume));
  DBG(" DefaultLoader:  %x\n",    OFFSET_OF(SETTINGS_DATA, DefaultLoader));
  DBG(" ResetAddr:      %x\n",    OFFSET_OF(SETTINGS_DATA, ResetAddr));
  DBG(" FixDsdt:        %x\n",    OFFSET_OF(SETTINGS_DATA, FixDsdt));
  DBG(" FakeATI:        %x\n",    OFFSET_OF(SETTINGS_DATA, FakeATI));
  DBG(" PatchVBiosBytes:%x\n",    OFFSET_OF(SETTINGS_DATA, PatchVBiosBytes));
  DBG(" VRAM:           %x\n",    OFFSET_OF(SETTINGS_DATA, VRAM));
  DBG(" SecureBootWhiteListCount: %x\n",    OFFSET_OF(SETTINGS_DATA, SecureBootWhiteListCount));
  DBG(" LegacyBoot:     %x\n",    OFFSET_OF(SETTINGS_DATA, LegacyBoot));
  DBG(" HVHideStrings:  %x\n",    OFFSET_OF(SETTINGS_DATA, HVHideStrings));
  DBG(" PointerSpeed:   %x\n",    OFFSET_OF(SETTINGS_DATA, PointerSpeed));
  DBG(" RtMLB:          %x\n",    OFFSET_OF(SETTINGS_DATA, RtMLB));
  DBG(" ConfigName:     %x\n",    OFFSET_OF(SETTINGS_DATA, ConfigName));
  DBG(" PointerSpeed:   %x\n",    OFFSET_OF(SETTINGS_DATA, PointerSpeed));
  DBG(" PatchDsdtNum:   %x\n",    OFFSET_OF(SETTINGS_DATA, PatchDsdtNum));
  DBG(" LenToReplace:   %x\n",    OFFSET_OF(SETTINGS_DATA, LenToReplace));
  DBG(" ACPIDropTables: %x\n",    OFFSET_OF(SETTINGS_DATA, ACPIDropTables));
  DBG(" CustomEntries:  %x\n",    OFFSET_OF(SETTINGS_DATA, CustomEntries));
  DBG(" CustomTool:     %x\n",    OFFSET_OF(SETTINGS_DATA, CustomTool));
  DBG(" AddProperties:  %x\n",    OFFSET_OF(SETTINGS_DATA, AddProperties));
  DBG(" BlockKexts:     %x\n",    OFFSET_OF(SETTINGS_DATA, BlockKexts));
   */

  // disable EFI watchdog timer
  gBS->SetWatchdogTimer(0x0000, 0x0000, 0x0000, NULL);
  ZeroMem((VOID*)&gSettings, sizeof(SETTINGS_DATA));

  Status = InitializeUnicodeCollationProtocol();
  if (EFI_ERROR(Status)) {
    DBG("UnicodeCollation Status=%r\n", Status);
  }
  
  Status = gBS->HandleProtocol(ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **)&SimpleTextEx);
  if ( EFI_ERROR (Status) ) {
    SimpleTextEx = NULL;
  }
  DBG("SimpleTextEx Status=%r\n", Status);

  PrepatchSmbios();

//#ifdef REVISION_STR
//  DBG(REVISION_STR);
//#endif

  //replace / with _
  Size = iStrLen(gSettings.OEMProduct, 64);
  for (i=0; i<Size; i++) {
    if (gSettings.OEMProduct[i] == 0x2F) {
      gSettings.OEMProduct[i] = 0x5F;
    }
  }
  Size = iStrLen(gSettings.OEMBoard, 64);
  for (i=0; i<Size; i++) {
    if (gSettings.OEMBoard[i] == 0x2F) {
      gSettings.OEMBoard[i] = 0x5F;
    }
  }
  DBG("Running on: '%a' with board '%a'\n", gSettings.OEMProduct, gSettings.OEMBoard);

  GetCPUProperties();
  GetDevices();
  GetDefaultSettings();

  // LoadOptions Parsing
  DBG("Clover load options size = %d bytes\n", SelfLoadedImage->LoadOptionsSize);
  if ((SelfLoadedImage->LoadOptions != NULL) &&
      (SelfLoadedImage->LoadOptionsSize != 0)){
    if (*(UINT32*)SelfLoadedImage->LoadOptions == CLOVER_SIGN) {
      GetBootFromOption();
    } else {
      ParseLoadOptions(&ConfName, &gConfigDict[1]);
      if (ConfName) {
        if (StrLen(ConfName) == 0) {
          gConfigDict[1] = NULL;
          FreePool(ConfName);
          ConfName = NULL;
        } else {
          SetOEMPath(ConfName);
          Status = LoadUserSettings(SelfRootDir, ConfName, &gConfigDict[1]);
          DBG("%s\\%s.plist%s loaded with name from LoadOptions: %r\n",
              OEMPath, ConfName, EFI_ERROR(Status) ? L" not" : L"", Status);
          if (EFI_ERROR(Status)) {
            gConfigDict[1] = NULL;
            FreePool(ConfName);
            ConfName = NULL;
          }
        }
      }
    }
  }
  if (gConfigDict[1]) {
    UniteTag = GetProperty(gConfigDict[1], "Unite");
    if(UniteTag) {
      UniteConfigs =  (UniteTag->type == kTagTypeTrue) ||
                      ((UniteTag->type == kTagTypeString) &&
                      ((UniteTag->string[0] == 'y') || (UniteTag->string[0] == 'Y')));
      DBG("UniteConfigs = %s", UniteConfigs ? L"TRUE\n": L"FALSE\n" );
    }
  }
  if (!gConfigDict[1] || UniteConfigs) {
    SetOEMPath(L"config");
    Status = LoadUserSettings(SelfRootDir, L"config", &gConfigDict[0]);
      DBG("%s\\config.plist%s loaded: %r\n", OEMPath, EFI_ERROR(Status) ? L" not" : L"", Status);
  }
  UnicodeSPrint(gSettings.ConfigName, 64, L"%s%s%s",
/*  gSettings.ConfigName = PoolPrint(L"%s%s%s", */
                                   gConfigDict[0] ? L"config": L"",
                                   (gConfigDict[0] && gConfigDict[1]) ? L" + ": L"",
                                   !gConfigDict[1] ? L"": (ConfName ? ConfName : L"Load Options"));
  gSettings.MainConfigName = EfiStrDuplicate(gSettings.ConfigName);

  gSettings.PointerEnabled = TRUE;
  gSettings.PointerSpeed = 2;
  gSettings.DoubleClickTime = 500; //TODO - make it constant as nobody change it

#ifdef ENABLE_SECURE_BOOT
  InitializeSecureBoot();
#endif // ENABLE_SECURE_BOOT


  {
//    UINT32                    machineSignature    = 0;
    EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE     *FadtPointer = NULL;
    EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs = NULL;

//    DBG("---dump hibernations data---\n");
    FadtPointer = GetFadt();
    if (FadtPointer != NULL) {
      Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE*)(UINTN)(FadtPointer->FirmwareCtrl);
      /*
      DBG("  Firmware wake address=%08lx\n", Facs->FirmwareWakingVector);
      DBG("  Firmware wake 64 addr=%16llx\n",  Facs->XFirmwareWakingVector);
      DBG("  Hardware signature   =%08lx\n", Facs->HardwareSignature);
      DBG("  GlobalLock           =%08lx\n", Facs->GlobalLock);
      DBG("  Flags                =%08lx\n", Facs->Flags);
       */
      machineSignature = Facs->HardwareSignature;
    }
#if HIBERNATE_DUMP_DATA
//------------------------------------------------------
    DumpVariable(L"Boot0082", &gEfiGlobalVariableGuid, 8);
    DumpVariable(L"boot-switch-vars", &gEfiAppleBootGuid, -1);
    DumpVariable(L"boot-signature",   &gEfiAppleBootGuid, -1);
    DumpVariable(L"boot-image-key",   &gEfiAppleBootGuid, -1);
    DumpVariable(L"boot-image",       &gEfiAppleBootGuid, 0);
//-----------------------------------------------------------
 
#endif //
  }

#if 0
  //testing place
  {
    CHAR16 *aaa = L"12345";
    DBG(" string %s, size=%d, len=%d sizeof=%d\n", aaa, StrSize(aaa), StrLen(aaa), sizeof(aaa));
  }
#endif
  if (!GlobalConfig.FastBoot) {
    GetListOfThemes();
    GetListOfConfigs();
  }

  for (i=0; i<2; i++) {
    if (gConfigDict[i]) {
 /*     Status = */GetEarlyUserSettings(SelfRootDir, gConfigDict[i]);
 //     if (EFI_ERROR(Status)) {
 //       DBG("Error in Early settings%d: %r\n", i, Status);
 //     }
    }
  }

#ifdef ENABLE_SECURE_BOOT
  // Install secure boot shim
  if (EFI_ERROR(Status = InstallSecureBoot())) {
    PauseForKey(L"Secure boot failure!\n");
    return Status;
  }
#endif // ENABLE_SECURE_BOOT

  MainMenu.TimeoutSeconds = GlobalConfig.Timeout >= 0 ? GlobalConfig.Timeout : 0;

  //DBG("LoadDrivers() start\n");
  LoadDrivers();
  //DBG("LoadDrivers() end\n");

/*  if (!gFirmwareClover &&
      !gDriversFlags.EmuVariableLoaded) {
    GetSmcKeys(FALSE);  // later we can get here SMC information
  } */
  
  Status = gBS->LocateProtocol (&gEmuVariableControlProtocolGuid, NULL, (VOID**)&gEmuVariableControl);
  if (EFI_ERROR(Status)) {
    gEmuVariableControl = NULL;
  }
  if (gEmuVariableControl != NULL) {
    gEmuVariableControl->InstallEmulation(gEmuVariableControl);
  }

  DbgHeader("InitScreen");
	
  if (!GlobalConfig.FastBoot) {
    // init screen and dump video modes to log
    if (gDriversFlags.VideoLoaded) {
      InitScreen(FALSE);
    } else {
      InitScreen(!gFirmwareClover); // ? FALSE : TRUE);
    }
    //DBG("DBG: setup screen\n");
    SetupScreen();
  } else {
    InitScreen(FALSE);
  }
	
  //  DBG("DBG: ReinitSelfLib\n");
  //Now we have to reinit handles
  Status = ReinitSelfLib();
  if (EFI_ERROR(Status)){
    DebugLog(2, " %r", Status);
    PauseForKey(L"Error reinit refit\n");
#ifdef ENABLE_SECURE_BOOT
    UninstallSecureBoot();
#endif // ENABLE_SECURE_BOOT
    return Status;
  }
	
  //  DBG("DBG: messages\n");
  if (!GlobalConfig.NoEarlyProgress && !GlobalConfig.FastBoot  && GlobalConfig.Timeout>0) {
    FirstMessage = PoolPrint(L"   Welcome to Clover %s   ", gFirmwareRevision);
    DrawTextXY(FirstMessage, (UGAWidth >> 1), UGAHeight >> 1, X_IS_CENTER);
    FreePool(FirstMessage);
    FirstMessage = PoolPrint(L"... testing hardware ...");
    DrawTextXY(FirstMessage, (UGAWidth >> 1), (UGAHeight >> 1) + 20, X_IS_CENTER);
    FreePool(FirstMessage);
  }

//  DumpBiosMemoryMap();

  GuiEventsInitialize();

  if (!gSettings.EnabledCores) {
    gSettings.EnabledCores = gCPUStructure.Cores;
  }

  GetMacAddress();
  //DBG("ScanSPD() start\n");
  ScanSPD();
  //DBG("ScanSPD() end\n");

  SetPrivateVarProto();
//  GetDefaultSettings();
  GetAcpiTablesList();

  DBG("Calibrated TSC Frequency = %ld = %ldMHz\n", gCPUStructure.TSCCalibr, DivU64x32(gCPUStructure.TSCCalibr, Mega));
  if (gCPUStructure.TSCCalibr > 200000000ULL) {  //200MHz
    gCPUStructure.TSCFrequency = gCPUStructure.TSCCalibr;
  }

  gCPUStructure.CPUFrequency = gCPUStructure.TSCFrequency;
  gCPUStructure.FSBFrequency = DivU64x32(MultU64x32(gCPUStructure.CPUFrequency, 10),
                                         (gCPUStructure.MaxRatio == 0) ? 1 : gCPUStructure.MaxRatio);
  gCPUStructure.MaxSpeed = (UINT32)DivU64x32(gCPUStructure.TSCFrequency + (Mega >> 1), Mega);

  switch (gCPUStructure.Model) {
    case CPU_MODEL_PENTIUM_M:
    case CPU_MODEL_ATOM://  Atom
    case CPU_MODEL_DOTHAN:// Pentium M, Dothan, 90nm
    case CPU_MODEL_YONAH:// Core Duo/Solo, Pentium M DC
    case CPU_MODEL_MEROM:// Core Xeon, Core 2 Duo, 65nm, Mobile
    //case CPU_MODEL_CONROE:// Core Xeon, Core 2 Duo, 65nm, Desktop like Merom but not mobile
    case CPU_MODEL_CELERON:
    case CPU_MODEL_PENRYN:// Core 2 Duo/Extreme, Xeon, 45nm , Mobile
    case CPU_MODEL_NEHALEM:// Core i7 LGA1366, Xeon 5500, "Bloomfield", "Gainstown", 45nm
    case CPU_MODEL_FIELDS:// Core i7, i5 LGA1156, "Clarksfield", "Lynnfield", "Jasper", 45nm
    case CPU_MODEL_DALES:// Core i7, i5, Nehalem
    case CPU_MODEL_CLARKDALE:// Core i7, i5, i3 LGA1156, "Westmere", "Clarkdale", , 32nm
    case CPU_MODEL_WESTMERE:// Core i7 LGA1366, Six-core, "Westmere", "Gulftown", 32nm
    case CPU_MODEL_NEHALEM_EX:// Core i7, Nehalem-Ex Xeon, "Beckton"
    case CPU_MODEL_WESTMERE_EX:// Core i7, Nehalem-Ex Xeon, "Eagleton"
      gCPUStructure.ExternalClock = (UINT32)DivU64x32(gCPUStructure.FSBFrequency + kilo - 1, kilo);
      //DBG(" Read TSC ExternalClock: %d MHz\n", (INT32)(DivU64x32(gCPUStructure.ExternalClock, kilo)));
      break;
    default:
      //DBG(" Read TSC ExternalClock: %d MHz\n", (INT32)(DivU64x32(gCPUStructure.FSBFrequency, Mega)));
	  
      // for sandy bridge or newer
      // to match ExternalClock 25 MHz like real mac, divide FSBFrequency by 4
      gCPUStructure.ExternalClock = ((UINT32)DivU64x32(gCPUStructure.FSBFrequency + kilo - 1, kilo) + 3) / 4;
      //DBG(" Corrected TSC ExternalClock: %d MHz\n", (INT32)(DivU64x32(gCPUStructure.ExternalClock, kilo)));
      break;
  }

  if (!GlobalConfig.NoEarlyProgress && !GlobalConfig.FastBoot && GlobalConfig.Timeout>0) {
    FirstMessage = PoolPrint(L"... user settings ...");
 //   i = (UGAWidth - StrLen(FirstMessage) * GlobalConfig.CharWidth) >> 1;
    DrawTextXY(FirstMessage, (UGAWidth >> 1), (UGAHeight >> 1) + 20, X_IS_CENTER);
    FreePool(FirstMessage);
  }

  //Second step. Load config.plist into gSettings
  for (i=0; i<2; i++) {
    if (gConfigDict[i]) {
      Status = GetUserSettings(SelfRootDir, gConfigDict[i]);
      if (EFI_ERROR(Status)) {
 //       DBG("Error in Second part of settings%d: %r\n", i, Status);
      }
    }
  }
  

  if (gSettings.QEMU) {
//    UINT64 Msrflex = 0ULL;

    if (!gSettings.UserChange) {
      gSettings.BusSpeed = 200000;
    }
    gCPUStructure.MaxRatio = (UINT32)DivU64x32(gCPUStructure.TSCCalibr, gSettings.BusSpeed * kilo);
    DBG("Set MaxRatio for QEMU: %d\n", gCPUStructure.MaxRatio);
    gCPUStructure.MaxRatio *= 10;
    gCPUStructure.MinRatio = 60;
/*    AsmWriteMsr64(MSR_FLEX_RATIO, ((6ULL << 40) + //(1ULL << 16) +
                                   (gCPUStructure.MaxRatio << 8)));
    DBG("check if flex is RW\n");
    Msrflex = AsmReadMsr64(MSR_FLEX_RATIO); //0 == not Rw :(
    DBG("MSR_FLEX_RATIO = %lx\n", Msrflex);
 */
    gCPUStructure.FSBFrequency = DivU64x32(MultU64x32(gCPUStructure.CPUFrequency, 10),
                                           (gCPUStructure.MaxRatio == 0) ? 1 : gCPUStructure.MaxRatio);
    gCPUStructure.ExternalClock = (UINT32)DivU64x32(gCPUStructure.FSBFrequency + kilo - 1, kilo);
  }

  dropDSM = 0xFFFF; //by default we drop all OEM _DSM. They have no sense for us.
  if (defDSM) {
    dropDSM = gSettings.DropOEM_DSM;   //if set by user
  }
  // Load any extra SMBIOS information
  if (!EFI_ERROR(LoadUserSettings(SelfRootDir, L"smbios", &smbiosTags)) && (smbiosTags != NULL)) {
    TagPtr dictPointer = GetProperty(smbiosTags,"SMBIOS");
    if (dictPointer) {
      ParseSMBIOSSettings(dictPointer);
    } else {
      DBG("Invalid smbios.plist, not overriding config.plist!\n");
    }
  }
/*
  if (gFirmwareClover || gDriversFlags.EmuVariableLoaded) {
    if (GlobalConfig.StrictHibernate) {
      DBG(" Don't use StrictHibernate with emulated NVRAM!\n");
    }
    GlobalConfig.StrictHibernate = FALSE;    
  }
*/
  HaveDefaultVolume = gSettings.DefaultVolume != NULL;
  if (!gFirmwareClover &&
      !gDriversFlags.EmuVariableLoaded &&
      !HaveDefaultVolume &&
      GlobalConfig.Timeout == 0 && !ReadAllKeyStrokes()) {
// UEFI boot: get gEfiBootDeviceGuid from NVRAM.
// if present, ScanVolumes() will skip scanning other volumes
// in the first run.
// this speeds up loading of default macOS  volume.
     GetEfiBootDeviceFromNvram();
  }

  if (!GlobalConfig.NoEarlyProgress && !GlobalConfig.FastBoot && GlobalConfig.Timeout>0) {
    FirstMessage = PoolPrint(L"...  scan entries  ...");
    DrawTextXY(FirstMessage, (UGAWidth >> 1), (UGAHeight >> 1) + 20, X_IS_CENTER);
    FreePool(FirstMessage);
  }


  GetListOfDsdts(); //only after GetUserSettings
  GetListOfACPI(); //ssdt and other tables

  AfterTool = FALSE;
  gGuiIsReady = TRUE;
  do {
    MainMenu.EntryCount = 0;
    OptionMenu.EntryCount = 0;
    InitKextList();
    ScanVolumes();

    //Check apfs driver loaded state
    //Free APFSUUIDBank
    if (APFSUUIDBank != NULL) {
     //Free mem
     FreePool(APFSUUIDBank);
     //Reset counter
     APFSUUIDBankCounter=0;
    }
    /* APFS container support */
    //Fill APFSUUIDBank
    APFSUUIDBank = APFSContainer_Support();
    if (APFSUUIDBankCounter != 0) {
      APFSSupport = TRUE;
    } else {
      APFSSupport = FALSE;
    }
    //Fill systemversion plists path
    SystemVersionInit();

    // as soon as we have Volumes, find latest nvram.plist and copy it to RT vars
    if (!AfterTool) {
      if (gFirmwareClover || gDriversFlags.EmuVariableLoaded) {
        PutNvramPlistToRtVars();
      }
    }

    if (!GlobalConfig.FastBoot) {

      CHAR16 *TmpArgs;
      GetOutputs();
      if (gThemeNeedInit) {
        InitTheme(TRUE, &Now);
        gThemeNeedInit = FALSE;
      } else if (gThemeChanged) {
        DBG("change theme\n");
        InitTheme(FALSE, NULL);
        FreeMenu(&OptionMenu);
      }
      DBG("theme inited\n");
      gThemeChanged = FALSE;
      if (GlobalConfig.Theme) {
        DBG("Chosen theme %s\n", GlobalConfig.Theme);
      } else {
        DBG("Chosen embedded theme\n");
      }
//      DBG("initial boot-args=%a\n", gSettings.BootArgs);
      //now it is a time to set RtVariables
      SetVariablesFromNvram();
      
      TmpArgs = PoolPrint(L"%a ", gSettings.BootArgs);
      DBG("after NVRAM boot-args=%a\n", gSettings.BootArgs);
      gSettings.OptionsBits = EncodeOptions(TmpArgs);
//      DBG("initial OptionsBits %x\n", gSettings.OptionsBits);
      FreePool(TmpArgs);
      FillInputs(TRUE);

      // scan for loaders and tools, add then to the menu
      if (GlobalConfig.LegacyFirst){
        AddCustomLegacy();
        if (!GlobalConfig.NoLegacy) {
          ScanLegacy();
        }
      }
    }
    GetSmcKeys(TRUE);
    
    // Add custom entries
    AddCustomEntries();
    if (gSettings.DisableEntryScan) {
      DBG("Entry scan disabled\n");
    } else {
      ScanLoader();
    }

    if (!GlobalConfig.FastBoot) {

      if (!GlobalConfig.LegacyFirst) {
        AddCustomLegacy();
        if (!GlobalConfig.NoLegacy) {
          ScanLegacy();
        }
      }

      // fixed other menu entries
      if (!(GlobalConfig.DisableFlags & HIDEUI_FLAG_TOOLS)) {
        AddCustomTool();
        if (!gSettings.DisableToolScan) {
          ScanTool();
#ifdef ENABLE_SECURE_BOOT
          // Check for secure boot setup mode
          AddSecureBootTool();
#endif // ENABLE_SECURE_BOOT
        }
      }

      MenuEntryOptions.Image = BuiltinIcon(BUILTIN_ICON_FUNC_OPTIONS);
      if (gSettings.DisableCloverHotkeys)
        MenuEntryOptions.ShortcutLetter = 0x00;
      AddMenuEntry(&MainMenu, &MenuEntryOptions);
      MenuEntryAbout.Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
      if (gSettings.DisableCloverHotkeys)
        MenuEntryAbout.ShortcutLetter = 0x00;
      AddMenuEntry(&MainMenu, &MenuEntryAbout);


      if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_FUNCS) || MainMenu.EntryCount == 0) {
        if (gSettings.DisableCloverHotkeys)
          MenuEntryReset.ShortcutLetter = 0x00;
        MenuEntryReset.Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
        AddMenuEntry(&MainMenu, &MenuEntryReset);
        if (gSettings.DisableCloverHotkeys)
          MenuEntryShutdown.ShortcutLetter = 0x00;
        MenuEntryShutdown.Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
        AddMenuEntry(&MainMenu, &MenuEntryShutdown);
      }
// font already changed and this message very quirky, clear line here
      if (!GlobalConfig.NoEarlyProgress && !GlobalConfig.FastBoot && GlobalConfig.Timeout>0) {
        DrawTextXY(L"                          ", (UGAWidth >> 1), (UGAHeight >> 1) + 20, X_IS_CENTER);
      }
    }
    // wait for user ACK when there were errors
    FinishTextScreen(FALSE);
#if CHECK_SMC
    DumpSmcKeys();
#endif

    DefaultIndex = FindDefaultEntry();
    DBG("DefaultIndex=%d and MainMenu.EntryCount=%d\n", DefaultIndex, MainMenu.EntryCount);
    if ((DefaultIndex >= 0) && (DefaultIndex < (INTN)MainMenu.EntryCount)) {
      DefaultEntry = MainMenu.Entries[DefaultIndex];
    } else {
      DefaultEntry = NULL;
    }

    MainLoopRunning = TRUE;
    //    MainMenu.TimeoutSeconds = GlobalConfig.Timeout >= 0 ? GlobalConfig.Timeout : 0;
    if (DefaultEntry && (GlobalConfig.FastBoot ||
                         (gSettings.SkipHibernateTimeout &&
                          OSFLAG_ISSET(((LOADER_ENTRY *)DefaultEntry)->Flags, OSFLAG_HIBERNATED)))) {
      if (DefaultEntry->Tag == TAG_LOADER) {
        StartLoader((LOADER_ENTRY *)DefaultEntry);
      } else if (DefaultEntry->Tag == TAG_LEGACY){
        StartLegacy((LEGACY_ENTRY *)DefaultEntry);
      }
      GlobalConfig.FastBoot = FALSE; //Hmm... will never be here
    }
    MainAnime = GetAnime(&MainMenu);
//    DBG("MainAnime=%d\n", MainAnime);
    AfterTool = FALSE;
    gEvent = 0; //clear to cancel loop
    while (MainLoopRunning) {
      CHAR8 *LastChosenOS = NULL;
      if (GlobalConfig.Timeout == 0 && DefaultEntry != NULL && !ReadAllKeyStrokes()) {
        // go strait to DefaultVolume loading
        MenuExit = MENU_EXIT_TIMEOUT;
      } else {
        MainMenu.AnimeRun = MainAnime;
        MenuExit = RunMainMenu(&MainMenu, DefaultIndex, &ChosenEntry);
      }
      DBG("exit from MainMenu %d\n", MenuExit); //MENU_EXIT_ENTER=(1) MENU_EXIT_DETAILS=3
      // disable default boot - have sense only in the first run
      GlobalConfig.Timeout = -1;
      //remember OS before go to second row
      if (ChosenEntry->Row == 0) {
        LastChosenOS = ((LOADER_ENTRY *)ChosenEntry)->OSVersion;
      }

      if ((DefaultEntry != NULL) && (MenuExit == MENU_EXIT_TIMEOUT)) {
        if (DefaultEntry->Tag == TAG_LOADER) {
          StartLoader((LOADER_ENTRY *)DefaultEntry);
        } else if (DefaultEntry->Tag == TAG_LEGACY){
          StartLegacy((LEGACY_ENTRY *)DefaultEntry);
        }
        // if something goes wrong - break main loop to reinit volumes
        break;
      }

      if (MenuExit == MENU_EXIT_OPTIONS){
        gBootChanged = FALSE;
        OptionsMenu(&OptionEntry, LastChosenOS);
        if (gBootChanged) {
          AfterTool = TRUE;
          MainLoopRunning = FALSE;
          break;
        }
        continue;
      }

      if (MenuExit == MENU_EXIT_HELP){
        HelpRefit();
        continue;
      }

      // EjectVolume
      if (MenuExit == MENU_EXIT_EJECT){
        if ((ChosenEntry->Tag == TAG_LOADER) ||
            (ChosenEntry->Tag == TAG_LEGACY)) {
          Status = EjectVolume(((LOADER_ENTRY *)ChosenEntry)->Volume);
          if (!EFI_ERROR(Status)) {
            break; //main loop is broken so Reinit all
          }
        }
        continue;
      }

      // Hide toggle
      if (MenuExit == MENU_EXIT_HIDE_TOGGLE) {
        gSettings.ShowHiddenEntries = !gSettings.ShowHiddenEntries;
        AfterTool = TRUE;
        break;
      }

      // We don't allow exiting the main menu with the Escape key.
      if (MenuExit == MENU_EXIT_ESCAPE){
        break;   //refresh main menu
        //           continue;
      }

      switch (ChosenEntry->Tag) {

        case TAG_RESET:    // Restart
          if (MenuExit == MENU_EXIT_DETAILS) {
//            EFI_KEY_DATA KeyData;
//            ZeroMem(&KeyData, sizeof KeyData);
//            SimpleTextEx->ReadKeyStrokeEx (SimpleTextEx, &KeyData);
//            if ((KeyData.KeyState.KeyShiftState & (EFI_LEFT_CONTROL_PRESSED | EFI_RIGHT_CONTROL_PRESSED)) != 0) {
            //do clear cmos as for AMI BIOS
            // not sure for more robust method
            IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, 0x10);
            IoWrite8 (PCAT_RTC_DATA_REGISTER, 0x0);
            IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, 0x11);
            IoWrite8 (PCAT_RTC_DATA_REGISTER, 0x0);
//          }
          }
          // Attempt warm reboot
          gRS->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
          // Warm reboot may not be supported attempt cold reboot
          gRS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
          // Terminate the screen and just exit
          TerminateScreen();
          MainLoopRunning = FALSE;
          ReinitDesktop = FALSE;
          AfterTool = TRUE;
          break;

        case TAG_SHUTDOWN: // It is not Shut Down, it is Exit from Clover
          TerminateScreen();
          //         gRS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
          MainLoopRunning = FALSE;   // just in case we get this far
          ReinitDesktop = FALSE;
          AfterTool = TRUE;
          break;

        case TAG_OPTIONS:    // Options like KernelFlags, DSDTname etc.
          gBootChanged = FALSE;
          OptionsMenu(&OptionEntry, LastChosenOS);
          if (gBootChanged)
            AfterTool = TRUE;
          if (gBootChanged || gThemeChanged) // If theme has changed reinit the desktop
            MainLoopRunning = FALSE;
          break;

        case TAG_ABOUT:    // About rEFIt
          AboutRefit();
          break;
/* -- not passed here
        case TAG_HELP:
          HelpRefit();
          break;
*/
        case TAG_LOADER:   // Boot OS via .EFI loader
          SetBootCurrent(ChosenEntry);
          StartLoader((LOADER_ENTRY *)ChosenEntry);
          //if boot.efi failed we should somehow exit from the loop
          TerminateScreen();
          MainLoopRunning = FALSE;
          ReinitDesktop = FALSE;
          AfterTool = TRUE;
          break;

        case TAG_LEGACY:   // Boot legacy OS
          if (StrCmp(gST->FirmwareVendor, L"Phoenix Technologies Ltd.") == 0 &&
              gST->Hdr.Revision >> 16 == 2 && (gST->Hdr.Revision & ((1 << 16) - 1)) == 0){
            // Phoenix SecureCore Tiano 2.0 can't properly initiate LegacyBios protocol when called externally
            // which results in "Operating System not found" message coming from BIOS
            // in this case just quit Clover to enter BIOS again
            TerminateScreen();
            MainLoopRunning = FALSE;
            ReinitDesktop = FALSE;
            AfterTool = TRUE;
          } else {
            SetBootCurrent(ChosenEntry);
            StartLegacy((LEGACY_ENTRY *)ChosenEntry);
          }
          break;

        case TAG_TOOL:     // Start a EFI tool
          StartTool((LOADER_ENTRY *)ChosenEntry);
          TerminateScreen(); //does not happen
          //   return EFI_SUCCESS;
          //  BdsLibConnectAllDriversToAllControllers();
          //    PauseForKey(L"Returned from StartTool\n");
          MainLoopRunning = FALSE;
          AfterTool = TRUE;
          break;

#ifdef ENABLE_SECURE_BOOT
        case TAG_SECURE_BOOT: // Try to enable secure boot
          EnableSecureBoot();
          MainLoopRunning = FALSE;
          AfterTool = TRUE;
          break;

        case TAG_SECURE_BOOT_CONFIG: // Configure secure boot
          MainLoopRunning = !ConfigureSecureBoot();
          AfterTool = TRUE;
          break;
#endif // ENABLE_SECURE_BOOT

        case TAG_CLOVER:     // Clover options
          LoaderEntry = (LOADER_ENTRY *)ChosenEntry;
          if (LoaderEntry->LoadOptions != NULL) {
            // we are uninstalling in case user selected Clover Options and EmuVar is installed
            // because adding bios boot option requires access to real nvram
            //Slice: sure?
       /*     if (gEmuVariableControl != NULL) {
              gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
            }
        */
            if (StrStr(LoaderEntry->LoadOptions, L"BO-ADD") != NULL) {
              CHAR16 *Description;
              CHAR16 *VolName;
              CHAR16 *LoaderName;
              INTN EntryIndex, NameSize, Name2Size;
              LOADER_ENTRY *Entry;
              UINT8 *OptionalData;
              UINTN OptionalDataSize;
              UINTN BootNum;

              PrintBootOptions(FALSE);

              for (EntryIndex = 0; EntryIndex < (INTN)MainMenu.EntryCount; EntryIndex++) {
                if (MainMenu.Entries[EntryIndex]->Row != 0) {
                  continue;
                }

                Entry = (LOADER_ENTRY *)MainMenu.Entries[EntryIndex];
                VolName = Entry->Volume->VolName;
                if (VolName == NULL) {
                  VolName = L"";
                }
                NameSize = StrSize(VolName); //can't use StrSize with NULL! Stupid UEFI!!!
                Name2Size = 0;
                if (Entry->LoaderPath != NULL) {
                  LoaderName = Basename(Entry->LoaderPath);
                } else {
                  LoaderName = NULL;  //legacy boot
                }
                if (LoaderName != NULL) {
                  Name2Size = StrSize(LoaderName);
                }

                Description = PoolPrint(L"Clover start %s at %s", (LoaderName != NULL)?LoaderName:L"legacy", VolName);
                OptionalDataSize = NameSize + Name2Size + 4 + 2; //signature + VolNameSize
                OptionalData = AllocateZeroPool(OptionalDataSize);
                if (OptionalData == NULL) {
                  break;
                }
                CopyMem(OptionalData, "Clvr", 4); //signature = 0x72766c43
                CopyMem(OptionalData + 4, &NameSize, 2);
                CopyMem(OptionalData + 6, VolName, NameSize);
                if (Name2Size != 0) {
                  CopyMem(OptionalData + 6 + NameSize, LoaderName, Name2Size);
                }

                Status = AddBootOptionForFile (
                                      LoaderEntry->Volume->DeviceHandle,
                                      LoaderEntry->LoaderPath,
                                      TRUE,
                                      Description,
                                      OptionalData,
                                      OptionalDataSize,
                                      EntryIndex,
                                      (UINT16*)&BootNum
                                      );
                if (!EFI_ERROR(Status)) {
                  DBG("Entry %d assigned option %04x\n", EntryIndex, BootNum);
                  Entry->BootNum = BootNum;
                }
                FreePool(OptionalData);
                FreePool(Description);
              } //for (EntryIndex


              PrintBootOptions(FALSE);
            } else if (StrStr(LoaderEntry->LoadOptions, L"BO-REMOVE") != NULL) {
              PrintBootOptions(FALSE);
              Status = DeleteBootOptionForFile (LoaderEntry->Volume->DeviceHandle,
                                                LoaderEntry->LoaderPath
                                                );
              PrintBootOptions(FALSE);
            } else if (StrStr(LoaderEntry->LoadOptions, L"BO-PRINT") != NULL) {
              PrintBootOptions(TRUE);
            }

          }
          MainLoopRunning = FALSE;
          AfterTool = TRUE;
          break;

      } //switch
    } //MainLoopRunning
    UninitRefitLib();
    if (!AfterTool) {
      //   PauseForKey(L"After uninit");
      //reconnectAll
      if (!gFirmwareClover) {
        BdsLibConnectAllEfi();
      }
      else {
        DBG("ConnectAll after refresh menu\n");
        BdsLibConnectAllDriversToAllControllers();
      }
      //  ReinitRefitLib();
      //    PauseForKey(L"After ReinitRefitLib");
    }
    if (ReinitDesktop) {
      DBG("ReinitSelfLib after theme change\n");
      ReinitSelfLib();
    }
    //    PauseForKey(L"After ReinitSelfLib");
  } while (ReinitDesktop);

  // If we end up here, things have gone wrong. Try to reboot, and if that
  // fails, go into an endless loop.
  //Slice - NO!!! Return to EFI GUI
  //   gRS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
  //   EndlessIdleLoop();

#ifdef ENABLE_SECURE_BOOT
  UninstallSecureBoot();
#endif // ENABLE_SECURE_BOOT

  // Unload EmuVariable before returning to EFI GUI, as it should not be present when booting other Operating Systems.
  // This seems critical in some UEFI implementations, such as Phoenix UEFI 2.0
  if (gEmuVariableControl != NULL) {
    gEmuVariableControl->UninstallEmulation(gEmuVariableControl);
  }
  return EFI_SUCCESS;
}
