text: // UEFI From Scratch Tutorials - ThatOSDev ( 2021 )
  // https://github.com/ThatOSDev/UEFI-Tuts
  
  #ifndef EFILIBS_H
  #define EFILIBS_H
  
               // ARGB             
  #define ORANGE 0xffffa500
  #define CYAN   0xff00ffff
  #define RED    0xffff0000
  #define GREEN  0xff00ff00
  #define BLUE   0xff0000ff
  #define GRAY   0xff888888
  #define WHITE  0xffffffff
  #define BLACK  0xff000000
  
  EFI_HANDLE ImageHandle;
  EFI_SYSTEM_TABLE* SystemTable;
  EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL GraphicsColor;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
  void* OSBuffer_Handle;
  
  void SetTextPosition(UINT32 Col, UINT32 Row)
  {
      // Sets the Column and Row of the text screen cursor position.
      SystemTable->ConOut->SetCursorPosition(SystemTable->ConOut, Col, Row);
  }
  
  void ResetScreen()
  {
      // This resets the whole console ( A.K.A. your display screen ) interface.
      SystemTable->ConOut->Reset(SystemTable->ConOut, 1);
  }
  
  void ClearScreen()
  {
      // This clears the screen buffer, but does not reset it.
      SystemTable->ConOut->ClearScreen(SystemTable->ConOut);
  }
  
  void SetColor(UINTN Attribute)
  {
      // We set the color for the text output.
      SystemTable->ConOut->SetAttribute(SystemTable->ConOut, Attribute);
  }
  
  void Print(CHAR16* str)
  {
      // Text Output  ( A.K.A. ConOut is Console Out )
      SystemTable->ConOut->OutputString(SystemTable->ConOut, str);
  }
  
  void HitAnyKey()
  {
      // This clears the keyboard buffer.
      SystemTable->ConIn->Reset(SystemTable->ConIn, 1);
  
      // We setup the struct to take keyboard input.
      EFI_INPUT_KEY Key;
  
      // In a while loop to see if the keyboard has a key stroke in the buffer.
      while((SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &Key)) == EFI_NOT_READY);
  }
  
  void ResetKeyboard()
  {
      SystemTable->ConIn->Reset(SystemTable->ConIn, 1);
  }
  
  EFI_INPUT_KEY CheckKeystroke;
  BOOLEAN GetKey(CHAR16 key)
  {
      if(CheckKeystroke.UnicodeChar == key)
      {
          return 1;
      } else {
          return 0;
      }
  }
  
  EFI_STATUS CheckKey()
  {
      return SystemTable->ConIn->ReadKeyStroke(SystemTable->ConIn, &CheckKeystroke);
  }
  
  // This sets the color of the pixels ( Graphics Color )
  void SetGraphicsColor(UINT32 color)
  {
      EFI_GRAPHICS_OUTPUT_BLT_PIXEL GColor;
      GColor.Reserved = color >> 24;
      GColor.Red      = color >> 16;
      GColor.Green    = color >> 8;
      GColor.Blue     = color;
      GraphicsColor = GColor;
  }
  
  // This positions the pixel in the row and column ( X and Y )
  void SetPixel(UINT32 xPos, UINT32 yPos)
  {
      // TODO : Add in a choice instead of defaulting to EfiBltVideoFill.
      gop->Blt(gop, &GraphicsColor, EfiBltVideoFill, 0, 0, xPos, yPos, 1, 1, 0);
  }
  
  // This creates a filled box of pixels.
  // NOTE : Call SetGraphicsColor prior to using this function.
  void CreateFilledBox(UINT32 xPos, UINT32 yPos, UINT32 w, UINT32 h)
  {
      // TODO : Add in a choice instead of defaulting to EfiBltVideoFill.
      gop->Blt(gop, &GraphicsColor, EfiBltVideoFill, 0, 0, xPos, yPos, w, h, 0);
  }
  
  typedef struct GRAPHICSBUFFER
  {
      void*  BaseAddress;
      UINT64 BufferSize;
      UINT32 ScreenWidth;
      UINT32 ScreenHeight;
      UINT32 PixelsPerScanLine;
  } GRAPHICSBUFFER;
  
  GRAPHICSBUFFER         gBuffer;
  
  // This creates a filled box of pixels using the GRAPHICSBUFFER.
  // NOTE : Call SetGraphicsColor prior to using this function.
  void CreateBufferFilledBox(UINT32 xPos, UINT32 yPos, UINT32 w, UINT32 h)
  {
      // This functions puts a color filled box on the screen
      UINT32 BBP = 4;
      if(xPos < 0){xPos = 0;}
      if(yPos < 0){yPos = 0;}
      if(w < 1){w = 1;}
      if(h < 1){h = 1;}
      UINT32 x;
      UINT32 y      = yPos;
      UINT32 width  = (xPos + w) * BBP;
      UINT32 height = yPos + h;
  
      for(y = yPos; y <= height; y++)
      {
          for(x = xPos * BBP; x <= width; x+=BBP)
          {
              *(UINT32*)(x + (y * gBuffer.PixelsPerScanLine * BBP) + gBuffer.BaseAddress) = *(UINT32*)&GraphicsColor;
          }
      }
  }
  
  void Delay(UINTN d)
  {
      // The Stall function is set is microseconds. So you have to convert
      // from microseconds ( µs ) to milliseconds ( ms ). EXAMPLE : 1ms = 1000µs.
      // The microsecond is the smallest unit of time measurement the EFI provides.
      SystemTable->BootServices->Stall(d * 1000);
  }
  
  void Delay1()
  {
      // The Stall function is set as microseconds. We stall 1 microsecond.
      SystemTable->BootServices->Stall(1);
  }
  
  void COLD_REBOOT()
  {
      // Hardware Reboot
      SystemTable->RuntimeServices->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, 0);
  }
  
  void WARM_REBOOT()
  {
      // Software reboot
      SystemTable->RuntimeServices->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, 0);
  }
  
  void SHUTDOWN()
  {
      // Shuts off the computer
      // NOTE : This does not work in VirtualBox.
      // WORKS in QEMU !!!
      SystemTable->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, 0);
  }
  
  GRAPHICSBUFFER* InitializeGOP()
  {
      // We initialize the Graphics Output Protocol.
      // This is used instead of the VGA interface.
      SetColor(EFI_BROWN);
      Print(L"\r\n\r\nLoading Graphics Output Protocol ... ");
      EFI_STATUS Status = SystemTable->BootServices->LocateProtocol(&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, 0, (void**)&gop);
      if(Status != EFI_SUCCESS)
      {
          return NULL;
      }
  	
      gBuffer.BaseAddress        = (void*)gop->Mode->FrameBufferBase;
      gBuffer.BufferSize         = gop->Mode->FrameBufferSize;
      gBuffer.ScreenWidth        = gop->Mode->Info->HorizontalResolution;
      gBuffer.ScreenHeight       = gop->Mode->Info->VerticalResolution;
      gBuffer.PixelsPerScanLine  = gop->Mode->Info->PixelsPerScanLine;
  
      return &gBuffer;
  }
  
  void InitializeFILESYSTEM()
  {
      EFI_STATUS Status;
      // To load a file, you must have a file system. EFI takes advantage of the FAT32 file system.
      SetColor(EFI_BROWN);
      Print(L"LoadedImage ... ");
      EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
      Status = SystemTable->BootServices->HandleProtocol(ImageHandle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, (void**)&LoadedImage);
      SetColor(EFI_CYAN);
      Print(CheckStandardEFIError(Status));
      
      SetColor(EFI_BROWN);
      Print(L"DevicePath ... ");
      EFI_DEVICE_PATH_PROTOCOL *DevicePath;
      Status = SystemTable->BootServices->HandleProtocol(LoadedImage->DeviceHandle, &EFI_DEVICE_PATH_PROTOCOL_GUID, (void**)&DevicePath);
      SetColor(EFI_CYAN);
      Print(CheckStandardEFIError(Status));
      
      SetColor(EFI_BROWN);
      Print(L"Volume ... ");
      Status = SystemTable->BootServices->HandleProtocol(LoadedImage->DeviceHandle, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, (void**)&Volume);
      SetColor(EFI_CYAN);
      Print(CheckStandardEFIError(Status));
  }
  
  EFI_FILE_PROTOCOL* openFile(CHAR16* FileName)
  {
      // This opens a file from the EFI FAT32 file system volume.
      // It loads from root, so you must supply full path if the file is not in the root.
      // Example : "somefolder//myfile"  <--- Notice the double forward slash.
      EFI_STATUS Status;
      SetColor(EFI_BROWN);
      Print(L"RootFS ... ");
      EFI_FILE_PROTOCOL* RootFS;
      Status = Volume->OpenVolume(Volume, &RootFS);
      SetColor(EFI_CYAN);
      Print(CheckStandardEFIError(Status));
      
      SetColor(EFI_BROWN);
      Print(L"Opening File ... ");
      EFI_FILE_PROTOCOL* FileHandle = NULL;
      Status = RootFS->Open(RootFS, &FileHandle, FileName, 0x0000000000000001, 0);
      SetColor(EFI_CYAN);
      Print(CheckStandardEFIError(Status));
      
      return FileHandle;
  }
  
  void closeFile(EFI_FILE_PROTOCOL* FileHandle)
  {
      // This closes the file.
      EFI_STATUS Status;
      SetColor(EFI_BROWN);
      Print(L"Closing File ... ");
      Status = FileHandle->Close(FileHandle);
      SetColor(EFI_CYAN);
      Print(CheckStandardEFIError(Status));
  }
  
  void readFile(CHAR16* FileName)
  {
      // We create the buffer, allocate memory for it, then read
      // the rile into the buffer. After which, we close the file.
  	// Currently we are using a fixed size. Eventually we will fix that.
  	// Currently we have a fixed Buffer Handle as well. Eventually we will fixe that.
      EFI_FILE_PROTOCOL* mytextfile = openFile(FileName);
      if(mytextfile != NULL)
      {
          SetColor(EFI_BROWN);
          Print(L"AllocatingPool ... ");
          EFI_STATUS Status = SystemTable->BootServices->AllocatePool(EfiLoaderData, 0x00100000, (void**)&OSBuffer_Handle);
          SetColor(EFI_CYAN);
          Print(CheckStandardEFIError(Status));
      
          UINT64 fileSize = 0x00100000;
          
          SetColor(EFI_BROWN);
          Print(L"Reading File ... ");
          Status = mytextfile->Read(mytextfile, &fileSize, OSBuffer_Handle);
          SetColor(EFI_CYAN);
          Print(CheckStandardEFIError(Status));
  
          closeFile(mytextfile);
      }
  }
  
  typedef struct PIXELPOSITIONS
  {
      UINT32 PixelxPos;
      UINT32 PixelyPos;
  } PIXELPOSITIONS;
  
  PIXELPOSITIONS* pixelpos;
  
  #endif // EFILIBS_H