Step to UEFI (66) —– Decompress的使用

这里介绍一下解压缩 EFI_DECOMPRESS_PROTOCOL 的使用。

首先是 GetInfo 函数【参考1】。通过它我们能够获得压缩文件的一些基本信息,比如:解压后的大小,解压过程需要的临时内存空间的大小。

getinfo

之后就是具体的解压函数 Decompress 【参考2】

decompress

根据上面的信息,编写一个简单的测试程序,首先将压缩格式的文件读取到内存中,再使用 GetInfo 取得必要的信息,最后,根据必须要的信息创建内存 Buffer ,使用 Decompress 解压即可。

代码如下

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include <Protocol/Decompress.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	EFI_DECOMPRESS_PROTOCOL       *Decompress;
    VOID                          *ImageBuffer=NULL;
	UINT32                        ImageLength=0;
    UINT32                        DestinationSize;
    UINT8                         *Scratch;
    UINT32                        ScratchSize;
    VOID                          *DecompressedImageBuffer=NULL;
    EFI_STATUS                    Status;
	EFI_FILE_HANDLE   			  FileHandle;
	EFI_FILE_INFO     			  *FileInfo = NULL;  
	UINTN  				    	  ReadSize;  
	EFI_HANDLE       			 *HandleBuffer=NULL;
	
    Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid,
									NULL, (VOID**)&Decompress);

    if (EFI_ERROR (Status)) {
		Print(L"Can't find Decompress Protocol! \n");	
    } else {
			//Open the file given by the parameter
			Status = ShellOpenFileByName(
							Argv[1], 
							(SHELL_FILE_HANDLE *)&FileHandle,
                            EFI_FILE_MODE_READ, 
							0);

			if(Status != RETURN_SUCCESS) {
				Print(L"OpenFile failed!\n");
				return EFI_SUCCESS;
			}//if(Status != RETURN_SUCCESS) {			

			//Get file size	  
			FileInfo = ShellGetFileInfo((SHELL_FILE_HANDLE)FileHandle);	

			//Allocate a memory buffer
			HandleBuffer = AllocateZeroPool((UINTN) FileInfo->FileSize);
			if (HandleBuffer == NULL) {
				return (SHELL_OUT_OF_RESOURCES);   }

			ReadSize=(UINTN) FileInfo-> FileSize;
  
			//Load the whole file to the buffer
			Status = ShellReadFile(FileHandle,&ReadSize,HandleBuffer);
  
			//Close the source file
			ShellCloseFile(&FileHandle);
			
            Status = Decompress->GetInfo (
                                  Decompress,
                                  HandleBuffer,
                                  ReadSize,
                                  &DestinationSize,
                                  &ScratchSize
                                 );
            if (!EFI_ERROR (Status)) {
			  Print(L"[GetInfo] Destination Size %d\n",DestinationSize);					 
			  Print(L"[GetInfo] Scratch Size %d\n",ScratchSize);	
			  
              DecompressedImageBuffer = AllocateZeroPool (DestinationSize);
              if (DecompressedImageBuffer != NULL) {
                Scratch = AllocateZeroPool (ScratchSize);
                if (Scratch != NULL) {
                  Status = Decompress->Decompress(
                                        Decompress,
                                        HandleBuffer,	//Source
                                        ReadSize,	    //Source Size
                                        DecompressedImageBuffer,//Destination
                                        DestinationSize,	//DestinationSize
                                        Scratch,
                                        ScratchSize
                                       );
                  if (!EFI_ERROR (Status)) {
                    ImageBuffer = DecompressedImageBuffer;
                    ImageLength = DestinationSize;
			
					//Create a new file
					Status = ShellOpenFileByName(L"decomp.bmp", 
                               (SHELL_FILE_HANDLE *)&FileHandle,
                               EFI_FILE_MODE_READ |
							   EFI_FILE_MODE_WRITE|
							   EFI_FILE_MODE_CREATE, 
							   0);  
					if(Status != RETURN_SUCCESS) {
						Print(L"CreatFile failed [%r]!\n",Status);
						return EFI_SUCCESS;
					}	
					
					Status = ShellWriteFile(FileHandle,
						&DestinationSize,
						ImageBuffer
						);
			
					//Close the source file
					ShellCloseFile(&FileHandle);
                  } //if (!EFI_ERROR (Status)) {
				  else
					{
						Print(L"Decompress error [%r] \n",Status);	
					}
                  FreePool (Scratch);
                } // if (Scratch != NULL) {
              } //if (ImageBuffer != NULL) {
            } //if (!EFI_ERROR (Status)) {
 			
			else	{
				Print(L"Read compressed file error!\n",ScratchSize);	
			}
			
          } //if (EFI_ERROR (Status)) {
  
  return EFI_SUCCESS;
}

 

运行结果

comptest

完整程序下载

CompTest

特别注意:

1.本文测试使用的压缩文件是 UEFI 下面生成的,具体命令是

eficompress testc.bmp compressed.z

我尝试使用 BaseTools 里面的压缩工具,生成的文件格式会出现不兼容,无法正常解压的情况。

2.根据我自己的理解 Scratch Buffer 是解压过程中解压算法使用的内存区域,不同压缩文件对于这片区域的大小要求不同。

参考:

1. UEFI Spec 2.4 P883
2. UEFI Spec 2.4 P885
3. ShellPkg 中的 EfiDecompress 程序是非常好的参考例子。

发表评论

电子邮件地址不会被公开。 必填项已用*标注