text: /*
   * libeg/image.c
   * Image handling functions
   *
   * Copyright (c) 2006 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 "libegint.h"
  #include "lodepng.h"
  
  #define MAX_FILE_SIZE (1024*1024*1024)
  
  #ifndef DEBUG_ALL
  #define DEBUG_IMG 1
  #else
  #define DEBUG_IMG DEBUG_ALL
  #endif
  
  #if DEBUG_IMG == 0
  #define DBG(...)
  #else
  #define DBG(...) DebugLog(DEBUG_IMG, __VA_ARGS__)
  #endif
  
  //
  // Basic file operations should be separated into separate file
  //
  EFI_STATUS egLoadFile(IN EFI_FILE_HANDLE BaseDir, IN CONST CHAR16 *FileName,
                        OUT UINT8 **FileData, OUT UINTN *FileDataLength)
  {
    EFI_STATUS          Status = EFI_NOT_FOUND;
    EFI_FILE_HANDLE     FileHandle = 0;
    EFI_FILE_INFO       *FileInfo;
    UINT64              ReadSize;
    UINTN               BufferSize;
    UINT8               *Buffer;
  
    if (!BaseDir) {
      goto Error;
    }
  
    Status = BaseDir->Open(BaseDir, &FileHandle, (CHAR16*)FileName, EFI_FILE_MODE_READ, 0); // const missing in EFI_FILE_HANDLE->Open
    if (EFI_ERROR(Status) || !FileHandle) {
      goto Error;
    }
  
    FileInfo = EfiLibFileInfo(FileHandle);
    if (FileInfo == NULL) {
      FileHandle->Close(FileHandle);
      goto Error;
    }
    ReadSize = FileInfo->FileSize;
    if (ReadSize > MAX_FILE_SIZE)
      ReadSize = MAX_FILE_SIZE;
    FreePool(FileInfo);
  
    BufferSize = (UINTN)ReadSize;   // was limited to 1 GB above, so this is safe
    Buffer = (UINT8 *) AllocatePool (BufferSize);
    if (Buffer == NULL) {
      FileHandle->Close(FileHandle);
      Status = EFI_OUT_OF_RESOURCES;
      goto Error;
    }
  
    Status = FileHandle->Read(FileHandle, &BufferSize, Buffer);
    FileHandle->Close(FileHandle);
    if (EFI_ERROR(Status)) {
      FreePool(Buffer);
      goto Error;
    }
  
    if(FileData) {
      *FileData = Buffer;
    }
    if (FileDataLength) {
      *FileDataLength = BufferSize;
    }
    return Status;
  Error:
    if (FileData) {
      *FileData = NULL;
    }
    if (FileDataLength) {
      *FileDataLength = 0;
    }
    return Status;
  }
  //Slice - this is gEfiPartTypeSystemPartGuid
  //static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
  //there is assumed only one ESP partition. What if there are two HDD gpt formatted?
  EFI_STATUS egFindESP(OUT EFI_FILE_HANDLE *RootDir)
  {
      EFI_STATUS          Status;
      UINTN               HandleCount = 0;
      EFI_HANDLE          *Handles;
  
      Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiPartTypeSystemPartGuid, NULL, &HandleCount, &Handles);
      if (!EFI_ERROR(Status) && HandleCount > 0) {
          *RootDir = EfiLibOpenRoot(Handles[0]);
          if (*RootDir == NULL)
              Status = EFI_NOT_FOUND;
          FreePool(Handles);
      }
      return Status;
  }
  //if (NULL, ...) then save to EFI partition
  EFI_STATUS egSaveFile(IN EFI_FILE_HANDLE BaseDir OPTIONAL, IN CONST CHAR16 *FileName,
                        IN CONST VOID *FileData, IN UINTN FileDataLength)
  {
    EFI_STATUS          Status;
    EFI_FILE_HANDLE     FileHandle;
    UINTN               BufferSize;
    BOOLEAN             CreateNew = TRUE;
    CONST CHAR16              *p = FileName + StrLen(FileName);
    CHAR16              DirName[256];
    UINTN               dirNameLen;
  
    if (BaseDir == NULL) {
      Status = egFindESP(&BaseDir);
      if (EFI_ERROR(Status)) {
        DBG("no ESP %s\n", strerror(Status));
        return Status;
      }
    }
      
    // syscl - make directory if not exist
    while (*p != L'\\' && p >= FileName) {
      // find the first '\\' traverse from the end to head of FileName
      p -= 1;
    }
    dirNameLen = p - FileName;
    StrnCpy(DirName, FileName, dirNameLen);
    Status = BaseDir->Open(BaseDir, &FileHandle, DirName,
                             EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, EFI_FILE_DIRECTORY);
      
    if (EFI_ERROR(Status)) {
        // make dir
  //    DBG("no dir %s\n", strerror(Status));
        Status = BaseDir->Open(BaseDir, &FileHandle, DirName,
                                 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY);
  //    DBG("cant make dir %s\n", strerror(Status));
    }
    // end of folder checking
  
    // Delete existing file if it exists
    Status = BaseDir->Open(BaseDir, &FileHandle, FileName,
                           EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
    if (!EFI_ERROR(Status)) {
      Status = FileHandle->Delete(FileHandle);
      if (Status == EFI_WARN_DELETE_FAILURE) {
        //This is READ_ONLY file system
        CreateNew = FALSE; // will write into existing file
  //      DBG("RO FS %s\n", strerror(Status));
      }
    }
  
    if (CreateNew) {
      // Write new file
      Status = BaseDir->Open(BaseDir, &FileHandle, FileName,
                             EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
      if (EFI_ERROR(Status)) {
  //      DBG("no write %s\n", strerror(Status));
        return Status;
      }
    } else {
      //to write into existing file we must sure it size larger then our data
      EFI_FILE_INFO *Info = EfiLibFileInfo(FileHandle);
      if (Info) {
        if (Info->FileSize < FileDataLength) {
  //        DBG("no old file %s\n", strerror(Status));
          return EFI_NOT_FOUND;
        }
        FreePool(Info);
      }
    }
  
    if (!FileHandle) {
  //    DBG("no FileHandle %s\n", strerror(Status));
      return EFI_DEVICE_ERROR;
    }
  
    BufferSize = FileDataLength;
    Status = FileHandle->Write(FileHandle, &BufferSize, (VOID*)FileData); // CONST missing in EFI_FILE_HANDLE->write
    FileHandle->Close(FileHandle);
  //  DBG("not written %s\n", strerror(Status));
    return Status;
  }
  
  
  EFI_STATUS egMkDir(IN EFI_FILE_HANDLE BaseDir OPTIONAL, IN CHAR16 *DirName)
  {
    EFI_STATUS          Status;
    EFI_FILE_HANDLE     FileHandle;
  
    //DBG("Looking up dir assets (%ls):", DirName);
  
    if (BaseDir == NULL) {
      Status = egFindESP(&BaseDir);
      if (EFI_ERROR(Status)) {
        //DBG(" %s\n", strerror(Status));
        return Status;
      }
    }
  
    Status = BaseDir->Open(BaseDir, &FileHandle, DirName,
                           EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, EFI_FILE_DIRECTORY);
  
    if (EFI_ERROR(Status)) {
      // Write new dir
      //DBG("%s, attempt to create one:", strerror(Status));
      Status = BaseDir->Open(BaseDir, &FileHandle, DirName,
                             EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY);
    }
  
    //DBG(" %s\n", strerror(Status));
    return Status;
  }
  
  
  
  /* EOF */
  