mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-14 10:05:43 +01:00
270 lines
8.5 KiB
C++
270 lines
8.5 KiB
C++
/*
|
|
* 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 <Platform.h> // Only use angled for Platform, else, xcode project won't compile
|
|
#include <Efi.h>
|
|
|
|
#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(const EFI_FILE* BaseDir, IN CONST CHAR16 *FileName,
|
|
OUT UINT8 **FileData, OUT UINTN *FileDataLength)
|
|
{
|
|
EFI_STATUS Status = EFI_NOT_FOUND;
|
|
EFI_FILE* 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 const EFI_FILE*->Open
|
|
if (EFI_ERROR(Status) || !FileHandle) {
|
|
goto Error;
|
|
}
|
|
|
|
FileInfo = EfiLibFileInfo(FileHandle);
|
|
if (FileInfo == NULL) {
|
|
FileHandle->Close(FileHandle);
|
|
Status = EFI_NOT_READY;
|
|
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;
|
|
}
|
|
// be 100% sure that we don't return EFI_SUCCESS with *FileData == NULL
|
|
if ( !EFI_ERROR(Status) && *FileData == NULL ) {
|
|
log_technical_bug("%s : EFI_ERROR(Status) && ConfigPtr", __PRETTY_FUNCTION__);
|
|
return EFI_COMPROMISED_DATA;
|
|
}
|
|
return Status;
|
|
Error:
|
|
if (FileData) {
|
|
*FileData = NULL;
|
|
}
|
|
if (FileDataLength) {
|
|
*FileDataLength = 0;
|
|
}
|
|
// be 100% sure that we don't return EFI_SUCCESS with *FileData == NULL
|
|
if ( !EFI_ERROR(Status) && *FileData == NULL ) {
|
|
log_technical_bug("%s : EFI_ERROR(Status) && ConfigPtr", __PRETTY_FUNCTION__);
|
|
return EFI_COMPROMISED_DATA;
|
|
}
|
|
return Status;
|
|
}
|
|
//Slice - this is gEfiPartTypeSystemPartGuid
|
|
//constexpr const 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** 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(const EFI_FILE* BaseDir OPTIONAL, IN CONST CHAR16 *FileName,
|
|
IN CONST void *FileData, IN UINTN FileDataLength)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE* FileHandle;
|
|
UINTN BufferSize;
|
|
XBool CreateNew = true;
|
|
CONST CHAR16 *p = FileName + StrLen(FileName);
|
|
CHAR16 DirName[256];
|
|
UINTN dirNameLen;
|
|
EFI_FILE* espDir;
|
|
|
|
if (BaseDir == NULL) {
|
|
Status = egFindESP(&espDir);
|
|
if (EFI_ERROR(Status)) {
|
|
DBG("no ESP %s\n", efiStrError(Status));
|
|
return Status;
|
|
}
|
|
BaseDir = espDir;
|
|
}
|
|
|
|
// 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;
|
|
StrnCpyS(DirName, 256, FileName, dirNameLen);
|
|
DirName[dirNameLen] = L'\0';
|
|
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", efiStrError(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", efiStrError(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 (Slice - ???)
|
|
// DBG("RO FS %s\n", efiStrError(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", efiStrError(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", efiStrError(Status));
|
|
// return EFI_NOT_FOUND;
|
|
// }
|
|
// FreePool(Info);
|
|
// }
|
|
Status = FileHandle->Delete(FileHandle); //don't write into existing file. Delete it!
|
|
}
|
|
|
|
if (!FileHandle) {
|
|
// DBG("no FileHandle %s\n", efiStrError(Status));
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
BufferSize = FileDataLength;
|
|
Status = FileHandle->Write(FileHandle, &BufferSize, (void*)FileData); // CONST missing const EFI_FILE*->write
|
|
FileHandle->Close(FileHandle);
|
|
// DBG("not written %s\n", efiStrError(Status));
|
|
return Status;
|
|
}
|
|
|
|
|
|
EFI_STATUS egMkDir(const EFI_FILE* BaseDir OPTIONAL, const CHAR16 *DirName)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE* FileHandle;
|
|
|
|
//DBG("Looking up dir assets (%ls):", DirName);
|
|
|
|
if (BaseDir == NULL) {
|
|
EFI_FILE* espDir;
|
|
Status = egFindESP(&espDir);
|
|
if (EFI_ERROR(Status)) {
|
|
//DBG(" %s\n", efiStrError(Status));
|
|
return Status;
|
|
}
|
|
BaseDir = espDir;
|
|
}
|
|
|
|
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:", efiStrError(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", efiStrError(Status));
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|