2015-03-13 14:48:53 +08:00
/* utility.cpp
2016-02-02 09:08:08 +08:00
Copyright ( c ) 2016 , Nikolaj Schlej . All rights reserved .
2015-03-13 14:48:53 +08:00
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution . The full text of the license may be found at
http : //opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN " AS IS " BASIS ,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND , EITHER EXPRESS OR IMPLIED .
*/
2016-06-26 11:54:21 +08:00
2018-08-02 08:41:11 +08:00
# include <cstdio>
# include <cctype>
2018-08-02 08:54:45 +08:00
# include <cstring>
2018-08-02 08:41:11 +08:00
2015-04-02 16:04:37 +08:00
# include "treemodel.h"
2015-03-13 14:48:53 +08:00
# include "utility.h"
# include "ffs.h"
# include "Tiano/EfiTianoCompress.h"
# include "Tiano/EfiTianoDecompress.h"
# include "LZMA/LzmaCompress.h"
# include "LZMA/LzmaDecompress.h"
2016-07-09 14:31:08 +08:00
// Returns unique name string based for tree item
UString uniqueItemName ( const UModelIndex & index )
{
// Sanity check
if ( ! index . isValid ( ) )
2017-10-12 13:59:23 +08:00
return UString ( " Invalid_index " ) ;
2016-07-09 14:31:08 +08:00
// Get model from index
2016-07-09 16:01:41 +08:00
const TreeModel * model = ( const TreeModel * ) index . model ( ) ;
2016-07-09 14:31:08 +08:00
// Construct the name
UString itemName = model - > name ( index ) ;
UString itemText = model - > text ( index ) ;
// Default name
UString name = itemName ;
switch ( model - > type ( index ) ) {
case Types : : NvarEntry :
case Types : : VssEntry :
case Types : : FsysEntry :
case Types : : EvsaEntry :
case Types : : FlashMapEntry :
2016-07-09 16:01:41 +08:00
case Types : : File :
name = itemText . isEmpty ( ) ? itemName : itemName + ' _ ' + itemText ;
2016-07-09 14:31:08 +08:00
break ;
case Types : : Section : {
// Get parent file name
UModelIndex fileIndex = model - > findParentOfType ( index , Types : : File ) ;
UString fileText = model - > text ( fileIndex ) ;
2016-07-09 16:01:41 +08:00
name = fileText . isEmpty ( ) ? model - > name ( fileIndex ) : model - > name ( fileIndex ) + ' _ ' + fileText ;
2018-04-30 13:33:19 +08:00
// Special case of GUIDed sections
if ( model - > subtype ( index ) = = EFI_SECTION_GUID_DEFINED | | model - > subtype ( index ) = = EFI_SECTION_FREEFORM_SUBTYPE_GUID ) {
name = model - > name ( index ) + ' _ ' + name ;
}
} break ;
2016-07-09 14:31:08 +08:00
}
2018-04-30 13:33:19 +08:00
// Populate subtypeString
2016-07-09 16:01:41 +08:00
UString subtypeString = itemSubtypeToUString ( model - > type ( index ) , model - > subtype ( index ) ) ;
2018-04-30 13:33:19 +08:00
// Create final name
2016-07-09 16:01:41 +08:00
name = itemTypeToUString ( model - > type ( index ) )
+ ( subtypeString . length ( ) ? ( ' _ ' + subtypeString ) : UString ( ) )
+ ' _ ' + name ;
2019-01-20 18:23:28 +08:00
// Replace some symbols with underscores for compatibility
const char table [ ] = {
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , // ASCII control characters, banned in Windows, hard to work with in *nix
' / ' , // Banned in *nix and Windows
' < ' , ' > ' , ' : ' , ' \" ' , ' \\ ' , ' | ' , ' ? ' , ' * ' , // Banned in Windows
' ' // Provides better readability
} ;
int nameLength = name . length ( ) ; // Note: Qt uses int for whatever reason.
for ( int i = 0 ; i < nameLength ; i + + ) {
for ( size_t j = 0 ; j < sizeof ( table ) ; j + + ) {
if ( name [ i ] = = table [ j ] ) {
name [ i ] = ' _ ' ;
}
}
}
2016-07-09 14:31:08 +08:00
return name ;
}
2015-03-13 14:48:53 +08:00
// Returns text representation of error code
2018-07-13 03:56:51 +08:00
UString errorCodeToUString ( USTATUS errorCode )
2015-03-13 14:48:53 +08:00
{
switch ( errorCode ) {
2016-06-26 11:54:21 +08:00
case U_SUCCESS : return UString ( " Success " ) ;
case U_NOT_IMPLEMENTED : return UString ( " Not implemented " ) ;
case U_INVALID_PARAMETER : return UString ( " Function called with invalid parameter " ) ;
case U_BUFFER_TOO_SMALL : return UString ( " Buffer too small " ) ;
case U_OUT_OF_RESOURCES : return UString ( " Out of resources " ) ;
case U_OUT_OF_MEMORY : return UString ( " Out of memory " ) ;
case U_FILE_OPEN : return UString ( " File can't be opened " ) ;
case U_FILE_READ : return UString ( " File can't be read " ) ;
case U_FILE_WRITE : return UString ( " File can't be written " ) ;
case U_ITEM_NOT_FOUND : return UString ( " Item not found " ) ;
case U_UNKNOWN_ITEM_TYPE : return UString ( " Unknown item type " ) ;
case U_INVALID_FLASH_DESCRIPTOR : return UString ( " Invalid flash descriptor " ) ;
case U_INVALID_REGION : return UString ( " Invalid region " ) ;
case U_EMPTY_REGION : return UString ( " Empty region " ) ;
case U_BIOS_REGION_NOT_FOUND : return UString ( " BIOS region not found " ) ;
case U_VOLUMES_NOT_FOUND : return UString ( " UEFI volumes not found " ) ;
case U_INVALID_VOLUME : return UString ( " Invalid UEFI volume " ) ;
case U_VOLUME_REVISION_NOT_SUPPORTED : return UString ( " Volume revision not supported " ) ;
//case U_VOLUME_GROW_FAILED: return UString("Volume grow failed");
case U_UNKNOWN_FFS : return UString ( " Unknown file system " ) ;
case U_INVALID_FILE : return UString ( " Invalid file " ) ;
case U_INVALID_SECTION : return UString ( " Invalid section " ) ;
case U_UNKNOWN_SECTION : return UString ( " Unknown section " ) ;
case U_STANDARD_COMPRESSION_FAILED : return UString ( " Standard compression failed " ) ;
case U_CUSTOMIZED_COMPRESSION_FAILED : return UString ( " Customized compression failed " ) ;
case U_STANDARD_DECOMPRESSION_FAILED : return UString ( " Standard decompression failed " ) ;
case U_CUSTOMIZED_DECOMPRESSION_FAILED : return UString ( " Customized decompression failed " ) ;
case U_UNKNOWN_COMPRESSION_TYPE : return UString ( " Unknown compression type " ) ;
case U_UNKNOWN_EXTRACT_MODE : return UString ( " Unknown extract mode " ) ;
case U_UNKNOWN_REPLACE_MODE : return UString ( " Unknown replace mode " ) ;
//case U_UNKNOWN_INSERT_MODE: return UString("Unknown insert mode");
case U_UNKNOWN_IMAGE_TYPE : return UString ( " Unknown executable image type " ) ;
case U_UNKNOWN_PE_OPTIONAL_HEADER_TYPE : return UString ( " Unknown PE optional header type " ) ;
case U_UNKNOWN_RELOCATION_TYPE : return UString ( " Unknown relocation type " ) ;
//case U_GENERIC_CALL_NOT_SUPPORTED: return UString("Generic call not supported");
//case U_VOLUME_BASE_NOT_FOUND: return UString("Volume base address not found");
//case U_PEI_CORE_ENTRY_POINT_NOT_FOUND: return UString("PEI core entry point not found");
case U_COMPLEX_BLOCK_MAP : return UString ( " Block map structure too complex for correct analysis " ) ;
case U_DIR_ALREADY_EXIST : return UString ( " Directory already exists " ) ;
case U_DIR_CREATE : return UString ( " Directory can't be created " ) ;
2016-07-09 14:31:08 +08:00
case U_DIR_CHANGE : return UString ( " Change directory failed " ) ;
2016-06-26 11:54:21 +08:00
//case U_UNKNOWN_PATCH_TYPE: return UString("Unknown patch type");
//case U_PATCH_OFFSET_OUT_OF_BOUNDS: return UString("Patch offset out of bounds");
//case U_INVALID_SYMBOL: return UString("Invalid symbol");
//case U_NOTHING_TO_PATCH: return UString("Nothing to patch");
case U_DEPEX_PARSE_FAILED : return UString ( " Dependency expression parsing failed " ) ;
case U_TRUNCATED_IMAGE : return UString ( " Image is truncated " ) ;
case U_INVALID_CAPSULE : return UString ( " Invalid capsule " ) ;
case U_STORES_NOT_FOUND : return UString ( " Stores not found " ) ;
2019-07-25 01:30:59 +08:00
case U_INVALID_STORE_SIZE : return UString ( " Invalid store size " ) ;
2016-06-26 11:54:21 +08:00
default : return usprintf ( " Unknown error %02X " , errorCode ) ;
2015-03-13 14:48:53 +08:00
}
}
// Compression routines
2019-01-04 03:53:31 +08:00
USTATUS decompress ( const UByteArray & compressedData , const UINT8 compressionType , UINT8 & algorithm , UINT32 & dictionarySize , UByteArray & decompressedData , UByteArray & efiDecompressedData )
2015-03-13 14:48:53 +08:00
{
const UINT8 * data ;
2019-07-25 01:30:59 +08:00
UINT32 dataSize ;
2015-03-13 14:48:53 +08:00
UINT8 * decompressed ;
2016-02-02 09:08:08 +08:00
UINT8 * efiDecompressed ;
2019-07-25 01:30:59 +08:00
UINT32 decompressedSize = 0 ;
2015-03-13 14:48:53 +08:00
UINT8 * scratch ;
2019-07-25 01:30:59 +08:00
UINT32 scratchSize = 0 ;
2015-03-13 14:48:53 +08:00
const EFI_TIANO_HEADER * header ;
2019-01-04 03:53:31 +08:00
// For all but LZMA dictionary size is 0
dictionarySize = 0 ;
2016-10-28 00:31:15 +08:00
switch ( compressionType )
2015-03-13 14:48:53 +08:00
{
case EFI_NOT_COMPRESSED :
decompressedData = compressedData ;
algorithm = COMPRESSION_ALGORITHM_NONE ;
2016-06-26 11:54:21 +08:00
return U_SUCCESS ;
2016-02-02 09:08:08 +08:00
case EFI_STANDARD_COMPRESSION : {
2015-03-13 14:48:53 +08:00
// Set default algorithm to unknown
algorithm = COMPRESSION_ALGORITHM_UNKNOWN ;
// Get buffer sizes
data = ( UINT8 * ) compressedData . data ( ) ;
dataSize = compressedData . size ( ) ;
// Check header to be valid
header = ( const EFI_TIANO_HEADER * ) data ;
if ( header - > CompSize + sizeof ( EFI_TIANO_HEADER ) ! = dataSize )
2016-06-26 11:54:21 +08:00
return U_STANDARD_DECOMPRESSION_FAILED ;
2015-03-13 14:48:53 +08:00
// Get info function is the same for both algorithms
2016-06-26 11:54:21 +08:00
if ( U_SUCCESS ! = EfiTianoGetInfo ( data , dataSize , & decompressedSize , & scratchSize ) )
return U_STANDARD_DECOMPRESSION_FAILED ;
2015-03-13 14:48:53 +08:00
// Allocate memory
2016-02-02 09:08:08 +08:00
decompressed = ( UINT8 * ) malloc ( decompressedSize ) ;
efiDecompressed = ( UINT8 * ) malloc ( decompressedSize ) ;
scratch = ( UINT8 * ) malloc ( scratchSize ) ;
if ( ! decompressed | | ! efiDecompressed | | ! scratch ) {
2016-10-10 14:05:04 +08:00
free ( decompressed ) ;
free ( efiDecompressed ) ;
free ( scratch ) ;
2016-06-26 11:54:21 +08:00
return U_STANDARD_DECOMPRESSION_FAILED ;
2015-12-30 06:39:43 +08:00
}
2015-03-13 14:48:53 +08:00
2016-02-02 09:08:08 +08:00
// Decompress section data using both algorithms
2016-06-26 11:54:21 +08:00
USTATUS result = U_SUCCESS ;
2016-02-02 09:08:08 +08:00
// Try Tiano
2016-06-26 11:54:21 +08:00
USTATUS TianoResult = TianoDecompress ( data , dataSize , decompressed , decompressedSize , scratch , scratchSize ) ;
2016-02-02 09:08:08 +08:00
// Try EFI 1.1
2016-06-26 11:54:21 +08:00
USTATUS EfiResult = EfiDecompress ( data , dataSize , efiDecompressed , decompressedSize , scratch , scratchSize ) ;
2015-03-13 14:48:53 +08:00
2018-05-08 23:42:16 +08:00
if ( decompressedSize > INT32_MAX ) {
2019-01-04 03:53:31 +08:00
result = U_STANDARD_DECOMPRESSION_FAILED ;
2018-05-08 23:42:16 +08:00
}
2019-01-04 03:53:31 +08:00
else if ( EfiResult = = U_SUCCESS & & TianoResult = = U_SUCCESS ) { // Both decompressions are OK
2016-02-02 09:08:08 +08:00
algorithm = COMPRESSION_ALGORITHM_UNDECIDED ;
2018-05-08 23:42:16 +08:00
decompressedData = UByteArray ( ( const char * ) decompressed , ( int ) decompressedSize ) ;
efiDecompressedData = UByteArray ( ( const char * ) efiDecompressed , ( int ) decompressedSize ) ;
2016-02-02 09:08:08 +08:00
}
2016-06-26 11:54:21 +08:00
else if ( TianoResult = = U_SUCCESS ) { // Only Tiano is OK
2016-02-02 09:08:08 +08:00
algorithm = COMPRESSION_ALGORITHM_TIANO ;
2018-05-08 23:42:16 +08:00
decompressedData = UByteArray ( ( const char * ) decompressed , ( int ) decompressedSize ) ;
2016-02-02 09:08:08 +08:00
}
2016-06-26 11:54:21 +08:00
else if ( EfiResult = = U_SUCCESS ) { // Only EFI 1.1 is OK
2016-02-02 09:08:08 +08:00
algorithm = COMPRESSION_ALGORITHM_EFI11 ;
2018-05-08 23:42:16 +08:00
decompressedData = UByteArray ( ( const char * ) efiDecompressed , ( int ) decompressedSize ) ;
2016-02-02 09:08:08 +08:00
}
else { // Both decompressions failed
2016-06-26 11:54:21 +08:00
result = U_STANDARD_DECOMPRESSION_FAILED ;
2015-03-13 14:48:53 +08:00
}
2016-02-02 09:08:08 +08:00
free ( decompressed ) ;
free ( efiDecompressed ) ;
free ( scratch ) ;
return result ;
}
2015-03-13 14:48:53 +08:00
case EFI_CUSTOMIZED_COMPRESSION :
// Set default algorithm to unknown
algorithm = COMPRESSION_ALGORITHM_UNKNOWN ;
// Get buffer sizes
data = ( const UINT8 * ) compressedData . constData ( ) ;
dataSize = compressedData . size ( ) ;
2019-11-28 00:46:16 +08:00
// Get info as normal LZMA section
if ( U_SUCCESS ! = LzmaGetInfo ( data , dataSize , & decompressedSize ) ) {
// Get info as Intel legacy LZMA section
2015-03-13 14:48:53 +08:00
data + = sizeof ( UINT32 ) ;
2016-06-26 11:54:21 +08:00
if ( U_SUCCESS ! = LzmaGetInfo ( data , dataSize , & decompressedSize ) ) {
return U_CUSTOMIZED_DECOMPRESSION_FAILED ;
2015-03-13 14:48:53 +08:00
}
else {
2019-11-28 00:46:16 +08:00
algorithm = COMPRESSION_ALGORITHM_LZMA_INTEL_LEGACY ;
2015-03-13 14:48:53 +08:00
}
}
else {
algorithm = COMPRESSION_ALGORITHM_LZMA ;
}
2019-11-28 00:46:16 +08:00
// Allocate memory
decompressed = ( UINT8 * ) malloc ( decompressedSize ) ;
if ( ! decompressed ) {
return U_OUT_OF_MEMORY ;
}
// Decompress section data
if ( U_SUCCESS ! = LzmaDecompress ( data , dataSize , decompressed ) ) {
return U_CUSTOMIZED_DECOMPRESSION_FAILED ;
}
if ( decompressedSize > INT32_MAX ) {
free ( decompressed ) ;
return U_CUSTOMIZED_DECOMPRESSION_FAILED ;
}
dictionarySize = readUnaligned ( ( UINT32 * ) ( data + 1 ) ) ; // LZMA dictionary size is stored in bytes 1-4 of LZMA properties header
decompressedData = UByteArray ( ( const char * ) decompressed , ( int ) decompressedSize ) ;
2016-02-02 09:08:08 +08:00
free ( decompressed ) ;
2016-06-26 11:54:21 +08:00
return U_SUCCESS ;
2015-03-13 14:48:53 +08:00
default :
algorithm = COMPRESSION_ALGORITHM_UNKNOWN ;
2016-06-26 11:54:21 +08:00
return U_UNKNOWN_COMPRESSION_TYPE ;
2015-03-13 14:48:53 +08:00
}
}
2017-12-11 09:56:00 +08:00
// 8bit sum calculation routine
UINT8 calculateSum8 ( const UINT8 * buffer , UINT32 bufferSize )
2015-07-07 21:57:41 +08:00
{
if ( ! buffer )
return 0 ;
UINT8 counter = 0 ;
while ( bufferSize - - )
counter + = buffer [ bufferSize ] ;
2017-12-11 09:56:00 +08:00
return counter ;
}
// 8bit checksum calculation routine
UINT8 calculateChecksum8 ( const UINT8 * buffer , UINT32 bufferSize )
{
if ( ! buffer )
return 0 ;
2018-05-08 23:42:16 +08:00
return ( UINT8 ) ( 0x100U - calculateSum8 ( buffer , bufferSize ) ) ;
2015-07-07 21:57:41 +08:00
}
// 16bit checksum calculation routine
UINT16 calculateChecksum16 ( const UINT16 * buffer , UINT32 bufferSize )
{
if ( ! buffer )
return 0 ;
UINT16 counter = 0 ;
UINT32 index = 0 ;
bufferSize / = sizeof ( UINT16 ) ;
for ( ; index < bufferSize ; index + + ) {
counter = ( UINT16 ) ( counter + buffer [ index ] ) ;
}
return ( UINT16 ) ( 0x10000 - counter ) ;
2016-10-10 14:05:04 +08:00
}
2019-07-25 01:30:59 +08:00
// 32bit checksum calculation routine
UINT32 calculateChecksum32 ( const UINT32 * buffer , UINT32 bufferSize )
{
if ( ! buffer )
return 0 ;
UINT32 counter = 0 ;
UINT32 index = 0 ;
bufferSize / = sizeof ( UINT32 ) ;
for ( ; index < bufferSize ; index + + ) {
counter = ( UINT32 ) ( counter + buffer [ index ] ) ;
}
return ( UINT32 ) ( 0x100000000ULL - counter ) ;
}
2018-04-30 13:33:19 +08:00
// Get padding type for a given padding
2016-10-10 14:05:04 +08:00
UINT8 getPaddingType ( const UByteArray & padding )
{
if ( padding . count ( ' \x00 ' ) = = padding . size ( ) )
return Subtypes : : ZeroPadding ;
if ( padding . count ( ' \xFF ' ) = = padding . size ( ) )
return Subtypes : : OnePadding ;
return Subtypes : : DataPadding ;
}
2018-08-02 08:41:11 +08:00
2018-11-11 21:33:13 +08:00
static inline int char2hex ( char c )
{
2018-08-02 08:41:11 +08:00
if ( c > = ' 0 ' & & c < = ' 9 ' )
return c - ' 0 ' ;
if ( c > = ' A ' & & c < = ' F ' )
2018-10-10 22:20:00 +08:00
return c - ' A ' + 10 ;
2018-08-02 08:41:11 +08:00
if ( c = = ' . ' )
return - 2 ;
return - 1 ;
}
INTN findPattern ( const UINT8 * pattern , const UINT8 * patternMask , UINTN patternSize ,
2018-11-11 21:33:13 +08:00
const UINT8 * data , UINTN dataSize , UINTN dataOff )
{
2018-08-02 08:41:11 +08:00
if ( patternSize = = 0 | | dataSize = = 0 | | dataOff > = dataSize | | dataSize - dataOff < patternSize )
return - 1 ;
while ( dataOff + patternSize < dataSize ) {
BOOLEAN matches = TRUE ;
for ( UINTN i = 0 ; i < patternSize ; i + + ) {
if ( ( data [ dataOff + i ] & patternMask [ i ] ) ! = pattern [ i ] ) {
matches = FALSE ;
break ;
}
}
if ( matches )
return static_cast < INTN > ( dataOff ) ;
dataOff + + ;
}
return - 1 ;
}
2018-11-11 21:33:13 +08:00
BOOLEAN makePattern ( const CHAR8 * textPattern , std : : vector < UINT8 > & pattern , std : : vector < UINT8 > & patternMask )
{
2018-08-02 08:41:11 +08:00
UINTN len = std : : strlen ( textPattern ) ;
if ( len = = 0 | | len % 2 ! = 0 )
return FALSE ;
len / = 2 ;
pattern . resize ( len ) ;
patternMask . resize ( len ) ;
for ( UINTN i = 0 ; i < len ; i + + ) {
int v1 = char2hex ( std : : toupper ( textPattern [ i * 2 ] ) ) ;
int v2 = char2hex ( std : : toupper ( textPattern [ i * 2 + 1 ] ) ) ;
if ( v1 = = - 1 | | v2 = = - 1 )
return FALSE ;
if ( v1 ! = - 2 ) {
patternMask [ i ] = 0xF0 ;
pattern [ i ] = static_cast < UINT8 > ( v1 ) < < 4 ;
}
if ( v2 ! = - 2 ) {
patternMask [ i ] | = 0x0F ;
pattern [ i ] | = static_cast < UINT8 > ( v2 ) ;
}
}
return TRUE ;
}
2018-11-11 21:33:13 +08:00
USTATUS gzipDecompress ( const UByteArray & input , UByteArray & output )
{
output . clear ( ) ;
if ( input . size ( ) = = 0 )
return U_SUCCESS ;
z_stream stream ;
stream . next_in = ( z_const Bytef * ) input . data ( ) ;
stream . avail_in = input . size ( ) ;
stream . zalloc = Z_NULL ;
stream . zfree = Z_NULL ;
stream . opaque = Z_NULL ;
// 15 for the maximum history buffer, 16 for gzip only input.
int ret = inflateInit2 ( & stream , 15U | 16U ) ;
if ( ret ! = Z_OK )
2019-01-04 03:53:31 +08:00
return U_GZIP_DECOMPRESSION_FAILED ;
2018-11-11 21:33:13 +08:00
while ( ret = = Z_OK ) {
Bytef out [ 4096 ] ;
stream . next_out = out ;
stream . avail_out = sizeof ( out ) ;
ret = inflate ( & stream , Z_NO_FLUSH ) ;
if ( ( ret = = Z_OK | | ret = = Z_STREAM_END ) & & stream . avail_out ! = sizeof ( out ) )
output + = UByteArray ( ( char * ) out , sizeof ( out ) - stream . avail_out ) ;
}
inflateEnd ( & stream ) ;
2019-01-04 03:53:31 +08:00
return ret = = Z_STREAM_END ? U_SUCCESS : U_GZIP_DECOMPRESSION_FAILED ;
2018-11-11 21:33:13 +08:00
}