pax_global_header00006660000000000000000000000064136363110000014503gustar00rootroot0000000000000052 comment=d9642c53e7b873fcce858ec2bea577f099b966d5 UEFITool-0.28.0/000077500000000000000000000000001363631100000131605ustar00rootroot00000000000000UEFITool-0.28.0/.appveyor.yml000066400000000000000000000010011363631100000156160ustar00rootroot00000000000000image: Visual Studio 2015 environment: matrix: - MSYS2_ARCH: i686 MSYSTEM: MINGW32 clone_depth: 10 build_script: - cd %APPVEYOR_BUILD_FOLDER% - set PATH=C:\msys64\mingw32\bin;C:\msys64\usr\bin;%PATH% - bash ./unixbuild.sh deploy: provider: GitHub force_update: true auth_token: secure: zSJnpSnrKY1NO5RPVBaD/uq7UPyc+GW7ecjPFqEMsLjtnd6H+iNfROdoeuxJgt5T artifact: Binaries prerelease: true on: appveyor_repo_tag: true artifacts: - path: dist\**\*.zip name: Binaries UEFITool-0.28.0/.gitattributes000066400000000000000000000007431363631100000160570ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain UEFITool-0.28.0/.gitignore000066400000000000000000000056641363631100000151630ustar00rootroot00000000000000################# ## Qt ################# moc_*.* ui_*.* ################# ## Qt Creator ################# *.pro.user.* ################# ## Eclipse ################# *.pydevproject .project .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ build/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml *.pubxml # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line #packages/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf ############# ## Windows detritus ############# # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac crap .DS_Store ############# ## Python ############# *.py[co] # Packages *.egg *.egg-info dist/ build/ eggs/ parts/ var/ sdist/ develop-eggs/ .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg ############# ## qmake / make ############# *.o Makefile UEFIExtract/UEFIExtract UEFIFind/UEFIFind UEFIPatch/UEFIPatch UEFIReplace/UEFIReplace UEFITool .qmake.stash UEFITool.app/ uefitool_plugin_import.cpp DerivedData *.xcodeproj UEFITool-0.28.0/.travis.yml000066400000000000000000000023461363631100000152760ustar00rootroot00000000000000language: cpp matrix: include: - os: osx osx_image: xcode9.2 compiler: clang script: - ./unixbuild.sh deploy: provider: releases skip_cleanup: true file: "dist/*.zip" file_glob: true api_key: secure: "WjYd93lVLKHULBpUXS/WtGrkdXyAwxHOUnLJotyDmQipAQP5Ox7Kj12JwkSJGEmVOEdcbIQJyi0QxPjn1UYbYsAt6Op8zrjnYLS4G4fMdBtcxprWzid85uTW7oAAIFs7ygMVhpzxRKpu70yNb683vbThqNmaOu6RyG9aJOLtPAg=" on: tags: true - os: linux dist: trusty compiler: clang before_install: - sudo apt-get update -qq - sudo apt-get install -qq qt5-default qt5-qmake qtbase5-dev-tools cmake script: - ./unixbuild.sh - os: linux dist: trusty compiler: gcc before_install: - sudo apt-get update -qq - sudo apt-get install -qq qt5-default qt5-qmake qtbase5-dev-tools cmake script: - ./unixbuild.sh deploy: provider: releases skip_cleanup: true file: "dist/*.zip" file_glob: true api_key: secure: "WjYd93lVLKHULBpUXS/WtGrkdXyAwxHOUnLJotyDmQipAQP5Ox7Kj12JwkSJGEmVOEdcbIQJyi0QxPjn1UYbYsAt6Op8zrjnYLS4G4fMdBtcxprWzid85uTW7oAAIFs7ygMVhpzxRKpu70yNb683vbThqNmaOu6RyG9aJOLtPAg=" on: tags: true UEFITool-0.28.0/LICENSE.md000066400000000000000000000024211363631100000145630ustar00rootroot00000000000000Copyright (c) 2015, Nikolaj Schlej 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. 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 HOLDER 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. UEFITool-0.28.0/LZMA/000077500000000000000000000000001363631100000137235ustar00rootroot00000000000000UEFITool-0.28.0/LZMA/LzmaCompress.c000066400000000000000000000051431363631100000165110ustar00rootroot00000000000000/* LZMA Compress Implementation Copyright (c) 2012, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include "LzmaCompress.h" #include "SDK/C/7zVersion.h" #include "SDK/C/LzmaEnc.h" #include #define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) static void * AllocForLzma(void *p, size_t size) { (void)p; return malloc(size); } static void FreeForLzma(void *p, void *address) { (void)p; free(address); } static ISzAlloc SzAllocForLzma = { &AllocForLzma, &FreeForLzma }; SRes OnProgress(void *p, UInt64 inSize, UInt64 outSize) { (void)p; (void)inSize; (void)outSize; return SZ_OK; } static ICompressProgress g_ProgressCallback = { &OnProgress }; STATIC UINT64 EFIAPI RShiftU64 ( UINT64 Operand, UINT32 Count ) { return Operand >> Count; } VOID SetEncodedSizeOfBuf( UINT64 EncodedSize, UINT8* EncodedData ) { INT32 Index; EncodedData[LZMA_PROPS_SIZE] = EncodedSize & 0xFF; for (Index = LZMA_PROPS_SIZE + 1; Index <= LZMA_PROPS_SIZE + 7; Index++) { EncodedSize = RShiftU64(EncodedSize, 8); EncodedData[Index] = EncodedSize & 0xFF; } } EFI_STATUS EFIAPI LzmaCompress ( CONST UINT8 *Source, UINT32 SourceSize, UINT8 *Destination, UINT32 *DestinationSize, UINT32 DictionarySize ) { SRes LzmaResult; CLzmaEncProps props; SizeT propsSize = LZMA_PROPS_SIZE; SizeT destLen = SourceSize + SourceSize / 3 + 128; if (*DestinationSize < (UINT32)destLen) { *DestinationSize = (UINT32)destLen; return EFI_BUFFER_TOO_SMALL; } LzmaEncProps_Init(&props); props.dictSize = DictionarySize; props.level = 9; props.fb = 273; LzmaResult = LzmaEncode( (Byte*)((UINT8*)Destination + LZMA_HEADER_SIZE), &destLen, Source, (SizeT)SourceSize, &props, (UINT8*)Destination, &propsSize, props.writeEndMark, &g_ProgressCallback, &SzAllocForLzma, &SzAllocForLzma); *DestinationSize = (UINT32)(destLen + LZMA_HEADER_SIZE); SetEncodedSizeOfBuf(SourceSize, Destination); if (LzmaResult == SZ_OK) { return EFI_SUCCESS; } else { return EFI_INVALID_PARAMETER; } } UEFITool-0.28.0/LZMA/LzmaCompress.h000066400000000000000000000020011363631100000165040ustar00rootroot00000000000000/* LZMA Compress Header Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef LZMACOMPRESS_H #define LZMACOMPRESS_H #include "SDK/C/Types.h" #include "../basetypes.h" #ifdef __cplusplus extern "C" { #endif #define DEFAULT_LZMA_DICTIONARY_SIZE 0x800000 #define _LZMA_SIZE_OPT EFI_STATUS EFIAPI LzmaCompress ( const UINT8 *Source, UINT32 SourceSize, UINT8 *Destination, UINT32 *DestinationSize, UINT32 DictionarySize ); #ifdef __cplusplus } #endif #endif // LZMACOMPRESS_H UEFITool-0.28.0/LZMA/LzmaDecompress.c000066400000000000000000000114521363631100000170220ustar00rootroot00000000000000/* LZMA Decompress Implementation Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include "LzmaDecompress.h" #include "SDK/C/Types.h" #include "SDK/C/7zVersion.h" #include UINT64 EFIAPI LShiftU64 ( UINT64 Operand, UINT32 Count ) { return Operand << Count; } static void * AllocForLzma(void *p, size_t size) { (void)p; return malloc(size); } static void FreeForLzma(void *p, void *address) { (void)p; free(address); } static ISzAlloc SzAllocForLzma = { &AllocForLzma, &FreeForLzma }; /* Get the size of the uncompressed buffer by parsing EncodeData header. @param EncodedData Pointer to the compressed data. @return The size of the uncompressed buffer. */ UINT64 GetDecodedSizeOfBuf ( UINT8 *EncodedData ) { UINT64 DecodedSize; INT32 Index; // Parse header DecodedSize = 0; for (Index = LZMA_PROPS_SIZE + 7; Index >= LZMA_PROPS_SIZE; Index--) DecodedSize = LShiftU64(DecodedSize, 8) + EncodedData[Index]; return DecodedSize; } // // LZMA functions and data as defined local LzmaDecompressLibInternal.h // /* Given a Lzma compressed source buffer, this function retrieves the size of the uncompressed buffer and the size of the scratch buffer required to decompress the compressed source buffer. Retrieves the size of the uncompressed buffer and the temporary scratch buffer required to decompress the buffer specified by Source and SourceSize. The size of the uncompressed buffer is returned DestinationSize, the size of the scratch buffer is returned ScratchSize, and RETURN_SUCCESS is returned. This function does not have scratch buffer available to perform a thorough checking of the validity of the source data. It just retrieves the "Original Size" field from the LZMA_HEADER_SIZE beginning bytes of the source data and output it as DestinationSize. And ScratchSize is specific to the decompression implementation. If SourceSize is less than LZMA_HEADER_SIZE, then ASSERT(). @param Source The source buffer containing the compressed data. @param SourceSize The size, bytes, of the source buffer. @param DestinationSize A pointer to the size, bytes, of the uncompressed buffer that will be generated when the compressed buffer specified by Source and SourceSize is decompressed. @retval EFI_SUCCESS The size of the uncompressed data was returned DestinationSize and the size of the scratch buffer was returned ScratchSize. */ EFI_STATUS EFIAPI LzmaGetInfo ( CONST VOID *Source, UINT32 SourceSize, UINT32 *DestinationSize ) { UINT64 DecodedSize; ASSERT(SourceSize >= LZMA_HEADER_SIZE); (void)SourceSize; DecodedSize = GetDecodedSizeOfBuf((UINT8*)Source); if (DecodedSize <= UINT32_MAX) { *DestinationSize = (UINT32)DecodedSize; return ERR_SUCCESS; } else { return ERR_INVALID_PARAMETER; } } /* Decompresses a Lzma compressed source buffer. Extracts decompressed data to its original form. If the compressed source data specified by Source is successfully decompressed into Destination, then RETURN_SUCCESS is returned. If the compressed source data specified by Source is not a valid compressed data format, then RETURN_INVALID_PARAMETER is returned. @param Source The source buffer containing the compressed data. @param SourceSize The size of source buffer. @param Destination The destination buffer to store the decompressed data @retval EFI_SUCCESS Decompression completed successfully, and the uncompressed buffer is returned Destination. @retval EFI_INVALID_PARAMETER The source buffer specified by Source is corrupted (not a valid compressed format). */ EFI_STATUS EFIAPI LzmaDecompress ( CONST VOID *Source, UINT32 SourceSize, VOID *Destination ) { SRes LzmaResult; ELzmaStatus Status; SizeT DecodedBufSize; SizeT EncodedDataSize; DecodedBufSize = (SizeT)GetDecodedSizeOfBuf((UINT8*)Source); EncodedDataSize = (SizeT)(SourceSize - LZMA_HEADER_SIZE); LzmaResult = LzmaDecode( (Byte*)Destination, &DecodedBufSize, (Byte*)((UINT8*)Source + LZMA_HEADER_SIZE), &EncodedDataSize, (CONST Byte*) Source, LZMA_PROPS_SIZE, LZMA_FINISH_END, &Status, &SzAllocForLzma ); if (LzmaResult == SZ_OK) { return ERR_SUCCESS; } else { return ERR_INVALID_PARAMETER; } } UEFITool-0.28.0/LZMA/LzmaDecompress.h000066400000000000000000000066231363631100000170330ustar00rootroot00000000000000/* LZMA Decompress Header Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef LZMADECOMPRESS_H #define LZMADECOMPRESS_H #include "../basetypes.h" #include "SDK/C/LzmaDec.h" #ifdef __cplusplus extern "C" { #endif #define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) /* Given a Lzma compressed source buffer, this function retrieves the size of the uncompressed buffer and the size of the scratch buffer required to decompress the compressed source buffer. Retrieves the size of the uncompressed buffer and the temporary scratch buffer required to decompress the buffer specified by Source and SourceSize. The size of the uncompressed buffer is returned DestinationSize, the size of the scratch buffer is returned ScratchSize, and RETURN_SUCCESS is returned. This function does not have scratch buffer available to perform a thorough checking of the validity of the source data. It just retrieves the "Original Size" field from the LZMA_HEADER_SIZE beginning bytes of the source data and output it as DestinationSize. And ScratchSize is specific to the decompression implementation. If SourceSize is less than LZMA_HEADER_SIZE, then ASSERT(). @param Source The source buffer containing the compressed data. @param SourceSize The size, bytes, of the source buffer. @param DestinationSize A pointer to the size, bytes, of the uncompressed buffer that will be generated when the compressed buffer specified by Source and SourceSize is decompressed. @retval EFI_SUCCESS The size of the uncompressed data was returned DestinationSize and the size of the scratch buffer was returned ScratchSize. */ EFI_STATUS EFIAPI LzmaGetInfo ( CONST VOID *Source, UINT32 SourceSize, UINT32 *DestinationSize ); /* Decompresses a Lzma compressed source buffer. Extracts decompressed data to its original form. If the compressed source data specified by Source is successfully decompressed into Destination, then RETURN_SUCCESS is returned. If the compressed source data specified by Source is not a valid compressed data format, then RETURN_INVALID_PARAMETER is returned. @param Source The source buffer containing the compressed data. @param SourceSize The size of source buffer. @param Destination The destination buffer to store the decompressed data @retval EFI_SUCCESS Decompression completed successfully, and the uncompressed buffer is returned Destination. @retval EFI_INVALID_PARAMETER The source buffer specified by Source is corrupted (not a valid compressed format). */ EFI_STATUS EFIAPI LzmaDecompress ( CONST VOID *Source, UINT32 SourceSize, VOID *Destination ); #ifdef __cplusplus } #endif #endif // LZMADECOMPRESS_H UEFITool-0.28.0/LZMA/SDK/000077500000000000000000000000001363631100000143445ustar00rootroot00000000000000UEFITool-0.28.0/LZMA/SDK/C/000077500000000000000000000000001363631100000145265ustar00rootroot00000000000000UEFITool-0.28.0/LZMA/SDK/C/7zVersion.h000066400000000000000000000003761363631100000166130ustar00rootroot00000000000000#define MY_VER_MAJOR 9 #define MY_VER_MINOR 20 #define MY_VER_BUILD 0 #define MY_VERSION "9.20" #define MY_DATE "2010-11-18" #define MY_COPYRIGHT ": Igor Pavlov : Public domain" #define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " : " MY_DATE UEFITool-0.28.0/LZMA/SDK/C/Bra.h000066400000000000000000000035531363631100000154110ustar00rootroot00000000000000/* Bra.h -- Branch converters for executables 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __BRA_H #define __BRA_H #include "Types.h" #ifdef __cplusplus extern "C" { #endif /* These functions convert relative addresses to absolute addresses in CALL instructions to increase the compression ratio. In: data - data buffer size - size of data ip - current virtual Instruction Pinter (IP) value state - state variable for x86 converter encoding - 0 (for decoding), 1 (for encoding) Out: state - state variable for x86 converter Returns: The number of processed bytes. If you call these functions with multiple calls, you must start next call with first byte after block of processed bytes. Type Endian Alignment LookAhead x86 little 1 4 ARMT little 2 2 ARM little 4 0 PPC big 4 0 SPARC big 4 0 IA64 little 16 0 size must be >= Alignment + LookAhead, if it's not last block. If (size < Alignment + LookAhead), converter returns 0. Example: UInt32 ip = 0; for () { ; size must be >= Alignment + LookAhead, if it's not last block SizeT processed = Convert(data, size, ip, 1); data += processed; size -= processed; ip += processed; } */ #define x86_Convert_Init(state) { state = 0; } SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); #ifdef __cplusplus } #endif #endif UEFITool-0.28.0/LZMA/SDK/C/Bra86.c000066400000000000000000000042101363631100000155510ustar00rootroot00000000000000/* Bra86.c -- Converter for x86 code (BCJ) 2008-10-04 : Igor Pavlov : Public domain */ #include "Bra.h" #define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) { SizeT bufferPos = 0, prevPosT; UInt32 prevMask = *state & 0x7; if (size < 5) return 0; ip += 5; prevPosT = (SizeT)0 - 1; for (;;) { Byte *p = data + bufferPos; Byte *limit = data + size - 4; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; bufferPos = (SizeT)(p - data); if (p >= limit) break; prevPosT = bufferPos - prevPosT; if (prevPosT > 3) prevMask = 0; else { prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; if (prevMask != 0) { Byte b = p[4 - kMaskToBitNumber[prevMask]]; if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) { prevPosT = bufferPos; prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; continue; } } } prevPosT = bufferPos; if (Test86MSByte(p[4])) { UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); UInt32 dest; for (;;) { Byte b; int index; if (encoding) dest = (ip + (UInt32)bufferPos) + src; else dest = src - (ip + (UInt32)bufferPos); if (prevMask == 0) break; index = kMaskToBitNumber[prevMask] * 8; b = (Byte)(dest >> (24 - index)); if (!Test86MSByte(b)) break; src = dest ^ ((1 << (32 - index)) - 1); } p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); p[3] = (Byte)(dest >> 16); p[2] = (Byte)(dest >> 8); p[1] = (Byte)dest; bufferPos += 5; } else { prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; } } prevPosT = bufferPos - prevPosT; *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); return bufferPos; } UEFITool-0.28.0/LZMA/SDK/C/CpuArch.h000066400000000000000000000073021363631100000162260ustar00rootroot00000000000000/* CpuArch.h -- CPU specific code 2010-10-26: Igor Pavlov : Public domain */ #ifndef __CPU_ARCH_H #define __CPU_ARCH_H #include "Types.h" EXTERN_C_BEGIN /* MY_CPU_LE means that CPU is LITTLE ENDIAN. If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. */ #if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) #define MY_CPU_AMD64 #endif #if defined(MY_CPU_AMD64) || defined(_M_IA64) #define MY_CPU_64BIT #endif #if defined(_M_IX86) || defined(__i386__) #define MY_CPU_X86 #endif #if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) #define MY_CPU_X86_OR_AMD64 #endif #if defined(MY_CPU_X86) || defined(_M_ARM) #define MY_CPU_32BIT #endif #if defined(_WIN32) && defined(_M_ARM) #define MY_CPU_ARM_LE #endif #if defined(_WIN32) && defined(_M_IA64) #define MY_CPU_IA64_LE #endif #if defined(MY_CPU_X86_OR_AMD64) #define MY_CPU_LE_UNALIGN #endif #if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) #define MY_CPU_LE #endif #if defined(__BIG_ENDIAN__) #define MY_CPU_BE #endif #if defined(MY_CPU_LE) && defined(MY_CPU_BE) Stop_Compiling_Bad_Endian #endif #ifdef MY_CPU_LE_UNALIGN #define GetUi16(p) (*(const UInt16 *)(p)) #define GetUi32(p) (*(const UInt32 *)(p)) #define GetUi64(p) (*(const UInt64 *)(p)) #define SetUi16(p, d) *(UInt16 *)(p) = (d); #define SetUi32(p, d) *(UInt32 *)(p) = (d); #define SetUi64(p, d) *(UInt64 *)(p) = (d); #else #define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) #define GetUi32(p) ( \ ((const Byte *)(p))[0] | \ ((UInt32)((const Byte *)(p))[1] << 8) | \ ((UInt32)((const Byte *)(p))[2] << 16) | \ ((UInt32)((const Byte *)(p))[3] << 24)) #define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) #define SetUi16(p, d) { UInt32 _x_ = (d); \ ((Byte *)(p))[0] = (Byte)_x_; \ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } #define SetUi32(p, d) { UInt32 _x_ = (d); \ ((Byte *)(p))[0] = (Byte)_x_; \ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } #define SetUi64(p, d) { UInt64 _x64_ = (d); \ SetUi32(p, (UInt32)_x64_); \ SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } #endif #if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) #pragma intrinsic(_byteswap_ulong) #pragma intrinsic(_byteswap_uint64) #define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) #define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) #else #define GetBe32(p) ( \ ((UInt32)((const Byte *)(p))[0] << 24) | \ ((UInt32)((const Byte *)(p))[1] << 16) | \ ((UInt32)((const Byte *)(p))[2] << 8) | \ ((const Byte *)(p))[3] ) #define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) #endif #define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) #ifdef MY_CPU_X86_OR_AMD64 typedef struct { UInt32 maxFunc; UInt32 vendor[3]; UInt32 ver; UInt32 b; UInt32 c; UInt32 d; } Cx86cpuid; enum { CPU_FIRM_INTEL, CPU_FIRM_AMD, CPU_FIRM_VIA }; Bool x86cpuid_CheckAndRead(Cx86cpuid *p); int x86cpuid_GetFirm(const Cx86cpuid *p); #define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) #define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) #define x86cpuid_GetStepping(p) ((p)->ver & 0xF) Bool CPU_Is_InOrder(); Bool CPU_Is_Aes_Supported(); #endif EXTERN_C_END #endif UEFITool-0.28.0/LZMA/SDK/C/LzFind.c000066400000000000000000000521021363631100000160600ustar00rootroot00000000000000/* LzFind.c -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain */ #include #include "LzFind.h" #include "LzHash.h" #define kEmptyHashValue 0 #define kMaxValForNormalize ((UInt32)0xFFFFFFFF) #define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ #define kNormalizeMask (~(kNormalizeStepMin - 1)) #define kMaxHistorySize ((UInt32)3 << 30) #define kStartMaxLen 3 static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) { if (!p->directInput) { alloc->Free(alloc, p->bufferBase); p->bufferBase = 0; } } /* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) { UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; if (p->directInput) { p->blockSize = blockSize; return 1; } if (p->bufferBase == 0 || p->blockSize != blockSize) { LzInWindow_Free(p, alloc); p->blockSize = blockSize; p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); } return (p->bufferBase != 0); } Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) { p->posLimit -= subValue; p->pos -= subValue; p->streamPos -= subValue; } static void MatchFinder_ReadBlock(CMatchFinder *p) { if (p->streamEndWasReached || p->result != SZ_OK) return; if (p->directInput) { UInt32 curSize = 0xFFFFFFFF - p->streamPos; if (curSize > p->directInputRem) curSize = (UInt32)p->directInputRem; p->directInputRem -= curSize; p->streamPos += curSize; if (p->directInputRem == 0) p->streamEndWasReached = 1; return; } for (;;) { Byte *dest = p->buffer + (p->streamPos - p->pos); size_t size = (p->bufferBase + p->blockSize - dest); if (size == 0) return; p->result = p->stream->Read(p->stream, dest, &size); if (p->result != SZ_OK) return; if (size == 0) { p->streamEndWasReached = 1; return; } p->streamPos += (UInt32)size; if (p->streamPos - p->pos > p->keepSizeAfter) return; } } void MatchFinder_MoveBlock(CMatchFinder *p) { memmove(p->bufferBase, p->buffer - p->keepSizeBefore, (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); p->buffer = p->bufferBase + p->keepSizeBefore; } int MatchFinder_NeedMove(CMatchFinder *p) { if (p->directInput) return 0; /* if (p->streamEndWasReached) return 0; */ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); } void MatchFinder_ReadIfRequired(CMatchFinder *p) { if (p->streamEndWasReached) return; if (p->keepSizeAfter >= p->streamPos - p->pos) MatchFinder_ReadBlock(p); } static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) { if (MatchFinder_NeedMove(p)) MatchFinder_MoveBlock(p); MatchFinder_ReadBlock(p); } static void MatchFinder_SetDefaultSettings(CMatchFinder *p) { p->cutValue = 32; p->btMode = 1; p->numHashBytes = 4; p->bigHash = 0; } #define kCrcPoly 0xEDB88320 void MatchFinder_Construct(CMatchFinder *p) { UInt32 i; p->bufferBase = 0; p->directInput = 0; p->hash = 0; MatchFinder_SetDefaultSettings(p); for (i = 0; i < 256; i++) { UInt32 r = i; int j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); p->crc[i] = r; } } static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hash); p->hash = 0; } void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) { MatchFinder_FreeThisClassMemory(p, alloc); LzInWindow_Free(p, alloc); } static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) { size_t sizeInBytes = (size_t)num * sizeof(CLzRef); if (sizeInBytes / sizeof(CLzRef) != num) return 0; return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); } int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) { UInt32 sizeReserv; if (historySize > kMaxHistorySize) { MatchFinder_Free(p, alloc); return 0; } sizeReserv = historySize >> 1; if (historySize > ((UInt32)2 << 30)) sizeReserv = historySize >> 2; sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); p->keepSizeBefore = historySize + keepAddBufferBefore + 1; p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ if (LzInWindow_Create(p, sizeReserv, alloc)) { UInt32 newCyclicBufferSize = historySize + 1; UInt32 hs; p->matchMaxLen = matchMaxLen; { p->fixedHashSize = 0; if (p->numHashBytes == 2) hs = (1 << 16) - 1; else { hs = historySize - 1; hs |= (hs >> 1); hs |= (hs >> 2); hs |= (hs >> 4); hs |= (hs >> 8); hs >>= 1; hs |= 0xFFFF; /* don't change it! It's required for Deflate */ if (hs > (1 << 24)) { if (p->numHashBytes == 3) hs = (1 << 24) - 1; else hs >>= 1; } } p->hashMask = hs; hs++; if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; hs += p->fixedHashSize; } { UInt32 prevSize = p->hashSizeSum + p->numSons; UInt32 newSize; p->historySize = historySize; p->hashSizeSum = hs; p->cyclicBufferSize = newCyclicBufferSize; p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); newSize = p->hashSizeSum + p->numSons; if (p->hash != 0 && prevSize == newSize) return 1; MatchFinder_FreeThisClassMemory(p, alloc); p->hash = AllocRefs(newSize, alloc); if (p->hash != 0) { p->son = p->hash + p->hashSizeSum; return 1; } } } MatchFinder_Free(p, alloc); return 0; } static void MatchFinder_SetLimits(CMatchFinder *p) { UInt32 limit = kMaxValForNormalize - p->pos; UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; if (limit2 < limit) limit = limit2; limit2 = p->streamPos - p->pos; if (limit2 <= p->keepSizeAfter) { if (limit2 > 0) limit2 = 1; } else limit2 -= p->keepSizeAfter; if (limit2 < limit) limit = limit2; { UInt32 lenLimit = p->streamPos - p->pos; if (lenLimit > p->matchMaxLen) lenLimit = p->matchMaxLen; p->lenLimit = lenLimit; } p->posLimit = p->pos + limit; } void MatchFinder_Init(CMatchFinder *p) { UInt32 i; for (i = 0; i < p->hashSizeSum; i++) p->hash[i] = kEmptyHashValue; p->cyclicBufferPos = 0; p->buffer = p->bufferBase; p->pos = p->streamPos = p->cyclicBufferSize; p->result = SZ_OK; p->streamEndWasReached = 0; MatchFinder_ReadBlock(p); MatchFinder_SetLimits(p); } static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) { return (p->pos - p->historySize - 1) & kNormalizeMask; } void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) { UInt32 i; for (i = 0; i < numItems; i++) { UInt32 value = items[i]; if (value <= subValue) value = kEmptyHashValue; else value -= subValue; items[i] = value; } } static void MatchFinder_Normalize(CMatchFinder *p) { UInt32 subValue = MatchFinder_GetSubValue(p); MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); MatchFinder_ReduceOffsets(p, subValue); } static void MatchFinder_CheckLimits(CMatchFinder *p) { if (p->pos == kMaxValForNormalize) MatchFinder_Normalize(p); if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) MatchFinder_CheckAndMoveAndRead(p); if (p->cyclicBufferPos == p->cyclicBufferSize) p->cyclicBufferPos = 0; MatchFinder_SetLimits(p); } static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, UInt32 *distances, UInt32 maxLen) { son[_cyclicBufferPos] = curMatch; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) return distances; { const Byte *pb = cur - delta; curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; if (pb[maxLen] == cur[maxLen] && *pb == *cur) { UInt32 len = 0; while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) return distances; } } } } } UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, UInt32 *distances, UInt32 maxLen) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return distances; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return distances; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { while (++len != lenLimit) if (pb[len] != cur[len]) break; { if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } #define MOVE_POS \ ++p->cyclicBufferPos; \ p->buffer++; \ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); #define MOVE_POS_RET MOVE_POS return offset; static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } #define GET_MATCHES_HEADER2(minLen, ret_op) \ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ cur = p->buffer; #define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) #define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) #define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue #define GET_MATCHES_FOOTER(offset, maxLen) \ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ distances + offset, maxLen) - distances); MOVE_POS_RET; #define SKIP_FOOTER \ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 1) } UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 2) } static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, delta2, maxLen, offset; GET_MATCHES_HEADER(3) HASH3_CALC; delta2 = p->pos - p->hash[hash2Value]; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; maxLen = 2; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[0] = maxLen; distances[1] = delta2 - 1; offset = 2; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances + offset, maxLen) - (distances)); MOVE_POS_RET } UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances, 2) - (distances)); MOVE_POS_RET } static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value; SKIP_HEADER(3) HASH3_CALC; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->pos; p->hash[kFix4HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinder_Init; vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; if (!p->btMode) { vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; } else if (p->numHashBytes == 2) { vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; } else if (p->numHashBytes == 3) { vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; } else { vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; } }UEFITool-0.28.0/LZMA/SDK/C/LzFind.h000066400000000000000000000063321363631100000160710ustar00rootroot00000000000000/* LzFind.h -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_H #define __LZ_FIND_H #include "Types.h" #ifdef __cplusplus extern "C" { #endif typedef UInt32 CLzRef; typedef struct _CMatchFinder { Byte *buffer; UInt32 pos; UInt32 posLimit; UInt32 streamPos; UInt32 lenLimit; UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ UInt32 matchMaxLen; CLzRef *hash; CLzRef *son; UInt32 hashMask; UInt32 cutValue; Byte *bufferBase; ISeqInStream *stream; int streamEndWasReached; UInt32 blockSize; UInt32 keepSizeBefore; UInt32 keepSizeAfter; UInt32 numHashBytes; int directInput; size_t directInputRem; int btMode; int bigHash; UInt32 historySize; UInt32 fixedHashSize; UInt32 hashSizeSum; UInt32 numSons; SRes result; UInt32 crc[256]; } CMatchFinder; #define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) #define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) int MatchFinder_NeedMove(CMatchFinder *p); Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); void MatchFinder_MoveBlock(CMatchFinder *p); void MatchFinder_ReadIfRequired(CMatchFinder *p); void MatchFinder_Construct(CMatchFinder *p); /* Conditions: historySize <= 3 GB keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB */ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *distances, UInt32 maxLen); /* Conditions: Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. Mf_GetPointerToCurrentPos_Func's result must be used only before any other function */ typedef void (*Mf_Init_Func)(void *object); typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); typedef void (*Mf_Skip_Func)(void *object, UInt32); typedef struct _IMatchFinder { Mf_Init_Func Init; Mf_GetIndexByte_Func GetIndexByte; Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; Mf_GetMatches_Func GetMatches; Mf_Skip_Func Skip; } IMatchFinder; void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); void MatchFinder_Init(CMatchFinder *p); UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); #ifdef __cplusplus } #endif #endif UEFITool-0.28.0/LZMA/SDK/C/LzHash.h000066400000000000000000000036541363631100000161000ustar00rootroot00000000000000/* LzHash.h -- HASH functions for LZ algorithms 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __LZ_HASH_H #define __LZ_HASH_H #define kHash2Size (1 << 10) #define kHash3Size (1 << 16) #define kHash4Size (1 << 20) #define kFix3HashSize (kHash2Size) #define kFix4HashSize (kHash2Size + kHash3Size) #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) #define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); #define HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } #define HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } #define HASH5_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ hash4Value &= (kHash4Size - 1); } /* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ #define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; #define MT_HASH2_CALC \ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); #define MT_HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } #define MT_HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } #endif UEFITool-0.28.0/LZMA/SDK/C/LzmaDec.c000066400000000000000000000675531363631100000162310ustar00rootroot00000000000000/* LzmaDec.c -- LZMA Decoder 2009-09-20 : Igor Pavlov : Public domain*/ #include "LzmaDec.h" #include #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_INIT_SIZE 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); #define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } #define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) #define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } /* #define _LZMA_SIZE_OPT */ #ifdef _LZMA_SIZE_OPT #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ i -= 0x40; } #endif #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; #define UPDATE_1_CHECK range -= bound; code -= bound; #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define LenChoice 0 #define LenChoice2 (LenChoice + 1) #define LenLow (LenChoice2 + 1) #define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) #define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) #define kNumStates 12 #define kNumLitStates 7 #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define kNumPosSlotBits 6 #define kNumLenToPosStates 4 #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define IsMatch 0 #define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) #define IsRep0Long (IsRepG2 + kNumStates) #define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) #define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) #define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) #define LenCoder (Align + kAlignTableSize) #define RepLenCoder (LenCoder + kNumLenProbs) #define Literal (RepLenCoder + kNumLenProbs) #define LZMA_BASE_SIZE 1846 #define LZMA_LIT_SIZE 768 #define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) #if Literal != LZMA_BASE_SIZE StopCompilingDueBUG #endif #define LZMA_DIC_MIN (1 << 12) /* First LZMA-symbol is always decoded. And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is with last normalization Out: Result: SZ_OK - OK SZ_ERROR_DATA - Error p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished = kMatchSpecLenStart + 1 : Flush marker = kMatchSpecLenStart + 2 : State Init Marker */ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { CLzmaProb *probs = p->probs; unsigned state = p->state; UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; unsigned lc = p->prop.lc; Byte *dic = p->dic; SizeT dicBufSize = p->dicBufSize; SizeT dicPos = p->dicPos; UInt32 processedPos = p->processedPos; UInt32 checkDicSize = p->checkDicSize; unsigned len = 0; const Byte *buf = p->buf; UInt32 range = p->range; UInt32 code = p->code; do { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = processedPos & pbMask; prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { unsigned symbol; UPDATE_0(prob); prob = probs + Literal; if (checkDicSize != 0 || processedPos != 0) prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); if (state < kNumLitStates) { state -= (state < 4) ? state : 3; symbol = 1; do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; unsigned offs = 0x100; state -= (state < 10) ? 3 : 6; symbol = 1; do { unsigned bit; CLzmaProb *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) } while (symbol < 0x100); } dic[dicPos++] = (Byte)symbol; processedPos++; continue; } else { UPDATE_1(prob); prob = probs + IsRep + state; IF_BIT_0(prob) { UPDATE_0(prob); state += kNumStates; prob = probs + LenCoder; } else { UPDATE_1(prob); if (checkDicSize == 0 && processedPos == 0) return SZ_ERROR_DATA; prob = probs + IsRepG0 + state; IF_BIT_0(prob) { UPDATE_0(prob); prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { UPDATE_0(prob); dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; processedPos++; state = state < kNumLitStates ? 9 : 11; continue; } UPDATE_1(prob); } else { UInt32 distance; UPDATE_1(prob); prob = probs + IsRepG1 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep1; } else { UPDATE_1(prob); prob = probs + IsRepG2 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep2; } else { UPDATE_1(prob); distance = rep3; rep3 = rep2; } rep2 = rep1; } rep1 = rep0; rep0 = distance; } state = state < kNumLitStates ? 8 : 11; prob = probs + RepLenCoder; } { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = (1 << kLenNumMidBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = (1 << kLenNumHighBits); } } TREE_DECODE(probLen, limit, len); len += offset; } if (state >= kNumStates) { UInt32 distance; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_6_DECODE(prob, distance); if (distance >= kStartPosModelIndex) { unsigned posSlot = (unsigned)distance; int numDirectBits = (int)(((distance >> 1) - 1)); distance = (2 | (distance & 1)); if (posSlot < kEndPosModelIndex) { distance <<= numDirectBits; prob = probs + SpecPos + distance - posSlot - 1; { UInt32 mask = 1; unsigned i = 1; do { GET_BIT2(prob + i, i, ;, distance |= mask); mask <<= 1; } while (--numDirectBits != 0); } } else { numDirectBits -= kNumAlignBits; do { NORMALIZE range >>= 1; { UInt32 t; code -= range; t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ distance = (distance << 1) + (t + 1); code += range & t; } /* distance <<= 1; if (code >= range) { code -= range; distance |= 1; } */ } while (--numDirectBits != 0); prob = probs + Align; distance <<= kNumAlignBits; { unsigned i = 1; GET_BIT2(prob + i, i, ;, distance |= 1); GET_BIT2(prob + i, i, ;, distance |= 2); GET_BIT2(prob + i, i, ;, distance |= 4); GET_BIT2(prob + i, i, ;, distance |= 8); } if (distance == (UInt32)0xFFFFFFFF) { len += kMatchSpecLenStart; state -= kNumStates; break; } } } rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance + 1; if (checkDicSize == 0) { if (distance >= processedPos) return SZ_ERROR_DATA; } else if (distance >= checkDicSize) return SZ_ERROR_DATA; state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; if (limit == dicPos) return SZ_ERROR_DATA; { SizeT rem = limit - dicPos; unsigned curLen = ((rem < len) ? (unsigned)rem : len); SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); processedPos += curLen; len -= curLen; if (pos + curLen <= dicBufSize) { Byte *dest = dic + dicPos; ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; const Byte *lim = dest + curLen; dicPos += curLen; do *(dest) = (Byte)*(dest + src); while (++dest != lim); } else { do { dic[dicPos++] = dic[pos]; if (++pos == dicBufSize) pos = 0; } while (--curLen != 0); } } } } while (dicPos < limit && buf < bufLimit); NORMALIZE; p->buf = buf; p->range = range; p->code = code; p->remainLen = len; p->dicPos = dicPos; p->processedPos = processedPos; p->reps[0] = rep0; p->reps[1] = rep1; p->reps[2] = rep2; p->reps[3] = rep3; p->state = state; return SZ_OK; } static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) { if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) { Byte *dic = p->dic; SizeT dicPos = p->dicPos; SizeT dicBufSize = p->dicBufSize; unsigned len = p->remainLen; UInt32 rep0 = p->reps[0]; if (limit - dicPos < len) len = (unsigned)(limit - dicPos); if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) p->checkDicSize = p->prop.dicSize; p->processedPos += len; p->remainLen -= len; while (len-- != 0) { dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; } p->dicPos = dicPos; } } static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { do { SizeT limit2 = limit; if (p->checkDicSize == 0) { UInt32 rem = p->prop.dicSize - p->processedPos; if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; } RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); if (p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; LzmaDec_WriteRem(p, limit); } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); if (p->remainLen > kMatchSpecLenStart) { p->remainLen = kMatchSpecLenStart; } return 0; } typedef enum { DUMMY_ERROR, /* unexpected end of input stream */ DUMMY_LIT, DUMMY_MATCH, DUMMY_REP } ELzmaDummy; static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) { UInt32 range = p->range; UInt32 code = p->code; const Byte *bufLimit = buf + inSize; CLzmaProb *probs = p->probs; unsigned state = p->state; ELzmaDummy res; { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) prob += (LZMA_LIT_SIZE * ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); if (state < kNumLitStates) { unsigned symbol = 1; do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; unsigned offs = 0x100; unsigned symbol = 1; do { unsigned bit; CLzmaProb *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) } while (symbol < 0x100); } res = DUMMY_LIT; } else { unsigned len; UPDATE_1_CHECK; prob = probs + IsRep + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; state = 0; prob = probs + LenCoder; res = DUMMY_MATCH; } else { UPDATE_1_CHECK; res = DUMMY_REP; prob = probs + IsRepG0 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; NORMALIZE_CHECK; return DUMMY_REP; } else { UPDATE_1_CHECK; } } else { UPDATE_1_CHECK; prob = probs + IsRepG1 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; prob = probs + IsRepG2 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; } } } state = kNumStates; prob = probs + RepLenCoder; } { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenChoice2; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = 1 << kLenNumMidBits; } else { UPDATE_1_CHECK; probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = 1 << kLenNumHighBits; } } TREE_DECODE_CHECK(probLen, limit, len); len += offset; } if (state < 4) { unsigned posSlot; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { int numDirectBits = ((posSlot >> 1) - 1); /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ if (posSlot < kEndPosModelIndex) { prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; } else { numDirectBits -= kNumAlignBits; do { NORMALIZE_CHECK range >>= 1; code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ } while (--numDirectBits != 0); prob = probs + Align; numDirectBits = kNumAlignBits; } { unsigned i = 1; do { GET_BIT_CHECK(prob + i, i); } while (--numDirectBits != 0); } } } } } NORMALIZE_CHECK; return res; } static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) { p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); p->range = 0xFFFFFFFF; p->needFlush = 0; } void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) { p->needFlush = 1; p->remainLen = 0; p->tempBufSize = 0; if (initDic) { p->processedPos = 0; p->checkDicSize = 0; p->needInitState = 1; } if (initState) p->needInitState = 1; } void LzmaDec_Init(CLzmaDec *p) { p->dicPos = 0; LzmaDec_InitDicAndState(p, True, True); } static void LzmaDec_InitStateReal(CLzmaDec *p) { UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); UInt32 i; CLzmaProb *probs = p->probs; for (i = 0; i < numProbs; i++) probs[i] = kBitModelTotal >> 1; p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; p->state = 0; p->needInitState = 0; } SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; (*srcLen) = 0; LzmaDec_WriteRem(p, dicLimit); *status = LZMA_STATUS_NOT_SPECIFIED; while (p->remainLen != kMatchSpecLenStart) { int checkEndMarkNow; if (p->needFlush != 0) { for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; if (p->tempBufSize < RC_INIT_SIZE) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (p->tempBuf[0] != 0) return SZ_ERROR_DATA; LzmaDec_InitRc(p, p->tempBuf); p->tempBufSize = 0; } checkEndMarkNow = 0; if (p->dicPos >= dicLimit) { if (p->remainLen == 0 && p->code == 0) { *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; return SZ_OK; } if (finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } if (p->remainLen != 0) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } checkEndMarkNow = 1; } if (p->needInitState) LzmaDec_InitStateReal(p); if (p->tempBufSize == 0) { SizeT processed; const Byte *bufLimit; if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, src, inSize); if (dummyRes == DUMMY_ERROR) { memcpy(p->tempBuf, src, inSize); p->tempBufSize = (unsigned)inSize; (*srcLen) += inSize; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } bufLimit = src; } else bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; p->buf = src; if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) return SZ_ERROR_DATA; processed = (SizeT)(p->buf - src); (*srcLen) += processed; src += processed; inSize -= processed; } else { unsigned rem = p->tempBufSize, lookAhead = 0; while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) p->tempBuf[rem++] = src[lookAhead++]; p->tempBufSize = rem; if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); if (dummyRes == DUMMY_ERROR) { (*srcLen) += lookAhead; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } } p->buf = p->tempBuf; if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) return SZ_ERROR_DATA; lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); (*srcLen) += lookAhead; src += lookAhead; inSize -= lookAhead; p->tempBufSize = 0; } } if (p->code == 0) *status = LZMA_STATUS_FINISHED_WITH_MARK; return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; } SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen; SizeT inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { SizeT inSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; SRes res; if (p->dicPos == p->dicBufSize) p->dicPos = 0; dicPos = p->dicPos; if (outSize > p->dicBufSize - dicPos) { outSizeCur = p->dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); src += inSizeCur; inSize -= inSizeCur; *srcLen += inSizeCur; outSizeCur = p->dicPos - dicPos; memcpy(dest, p->dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if (res != 0) return res; if (outSizeCur == 0 || outSize == 0) return SZ_OK; } } void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->probs); p->probs = 0; } static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->dic); p->dic = 0; } void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) { LzmaDec_FreeProbs(p, alloc); LzmaDec_FreeDict(p, alloc); } SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) { UInt32 dicSize; Byte d; if (size < LZMA_PROPS_SIZE) return SZ_ERROR_UNSUPPORTED; else dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); if (dicSize < LZMA_DIC_MIN) dicSize = LZMA_DIC_MIN; p->dicSize = dicSize; d = data[0]; if (d >= (9 * 5 * 5)) return SZ_ERROR_UNSUPPORTED; p->lc = d % 9; d /= 9; p->pb = d / 5; p->lp = d % 5; return SZ_OK; } static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); if (p->probs == 0 || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); p->numProbs = numProbs; if (p->probs == 0) return SZ_ERROR_MEM; } return SZ_OK; } SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) { CLzmaProps propNew; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); p->prop = propNew; return SZ_OK; } SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) { CLzmaProps propNew; SizeT dicBufSize; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); dicBufSize = propNew.dicSize; if (p->dic == 0 || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); if (p->dic == 0) { LzmaDec_FreeProbs(p, alloc); return SZ_ERROR_MEM; } } p->dicBufSize = dicBufSize; p->prop = propNew; return SZ_OK; } SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) { CLzmaDec p; SRes res; SizeT inSize = *srcLen; SizeT outSize = *destLen; *srcLen = *destLen = 0; if (inSize < RC_INIT_SIZE) return SZ_ERROR_INPUT_EOF; LzmaDec_Construct(&p); res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); if (res != 0) return res; p.dic = dest; p.dicBufSize = outSize; LzmaDec_Init(&p); *srcLen = inSize; res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) res = SZ_ERROR_INPUT_EOF; (*destLen) = p.dicPos; LzmaDec_FreeProbs(&p, alloc); return res; }UEFITool-0.28.0/LZMA/SDK/C/LzmaDec.h000066400000000000000000000153211363631100000162200ustar00rootroot00000000000000/* LzmaDec.h -- LZMA Decoder 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H #include "Types.h" #ifdef __cplusplus extern "C" { #endif /* #define _LZMA_PROB32 */ /* _LZMA_PROB32 can increase the speed on some CPUs, but memory usage for CLzmaDec::probs will be doubled in that case */ #ifdef _LZMA_PROB32 #define CLzmaProb UInt32 #else #define CLzmaProb UInt16 #endif /* ---------- LZMA Properties ---------- */ #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaProps { unsigned lc, lp, pb; UInt32 dicSize; } CLzmaProps; /* LzmaProps_Decode - decodes properties Returns: SZ_OK SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); /* ---------- LZMA Decoder state ---------- */ /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ #define LZMA_REQUIRED_INPUT_MAX 20 typedef struct { CLzmaProps prop; CLzmaProb *probs; Byte *dic; const Byte *buf; UInt32 range, code; SizeT dicPos; SizeT dicBufSize; UInt32 processedPos; UInt32 checkDicSize; unsigned state; UInt32 reps[4]; unsigned remainLen; int needFlush; int needInitState; UInt32 numProbs; unsigned tempBufSize; Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; #define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } void LzmaDec_Init(CLzmaDec *p); /* There are two types of LZMA streams: 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ typedef enum { LZMA_FINISH_ANY, /* finish at any point */ LZMA_FINISH_END /* block must be finished at the end */ } ELzmaFinishMode; /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! You must use LZMA_FINISH_END, when you know that current output buffer covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, and output value of destLen will be less than output buffer size limit. You can check status result also. You can use multiple checks to test data integrity after full decompression: 1) Check Result and "status" variable. 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. You must use correct finish mode in that case. */ typedef enum { LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ } ELzmaStatus; /* ELzmaStatus is used only as output value for function call */ /* ---------- Interfaces ---------- */ /* There are 3 levels of interfaces: 1) Dictionary Interface 2) Buffer Interface 3) One Call Interface You can select any of these interfaces, but don't mix functions from different groups for same object. */ /* There are two variants to allocate state for Dictionary Interface: 1) LzmaDec_Allocate / LzmaDec_Free 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs You can use variant 2, if you set dictionary buffer manually. For Buffer Interface you must always use variant 1. LzmaDec_Allocate* can return: SZ_OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); /* ---------- Dictionary Interface ---------- */ /* You can use it, if you want to eliminate the overhead for data copying from dictionary to some other external buffer. You must work with CLzmaDec variables directly in this interface. STEPS: LzmaDec_Constr() LzmaDec_Allocate() for (each new stream) { LzmaDec_Init() while (it needs more decompression) { LzmaDec_DecodeToDic() use data from CLzmaDec::dic and update CLzmaDec::dicPos } } LzmaDec_Free() */ /* LzmaDec_DecodeToDic The decoding to internal dictionary buffer (CLzmaDec::dic). You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! finishMode: It has meaning only if the decoding reaches output limit (dicLimit). LZMA_FINISH_ANY - Decode just dicLimit bytes. LZMA_FINISH_END - Stream must be finished after dicLimit. Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_NEEDS_MORE_INPUT LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error */ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- Buffer Interface ---------- */ /* It's zlib-like interface. See LzmaDec_DecodeToDic description for information about STEPS and return results, but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need to work with CLzmaDec variables manually. finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). */ SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- One Call Interface ---------- */ /* LzmaDecode finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). */ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); #ifdef __cplusplus } #endif #endif UEFITool-0.28.0/LZMA/SDK/C/LzmaEnc.c000066400000000000000000002076271363631100000162410ustar00rootroot00000000000000/* LzmaEnc.c -- LZMA Encoder 2010-04-16 : Igor Pavlov : Public domain*/ #include /* #define SHOW_STAT */ /* #define SHOW_STAT2 */ #if defined(SHOW_STAT) || defined(SHOW_STAT2) #include #endif #include "LzmaEnc.h" #include "LzFind.h" #ifndef _7ZIP_ST #include "LzFindMt.h" #endif #ifdef SHOW_STAT static int ttt = 0; #endif #define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) #define kBlockSize (9 << 10) #define kUnpackBlockSize (1 << 18) #define kMatchArraySize (1 << 21) #define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) #define kNumMaxDirectBits (31) #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define kProbInitValue (kBitModelTotal >> 1) #define kNumMoveReducingBits 4 #define kNumBitPriceShiftBits 4 #define kBitPrice (1 << kNumBitPriceShiftBits) void LzmaEncProps_Init(CLzmaEncProps *p) { p->level = 5; p->dictSize = p->mc = 0; p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->writeEndMark = 0; } void LzmaEncProps_Normalize(CLzmaEncProps *p) { int level = p->level; if (level < 0) level = 5; p->level = level; if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); if (p->lc < 0) p->lc = 3; if (p->lp < 0) p->lp = 0; if (p->pb < 0) p->pb = 2; if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); if (p->numHashBytes < 0) p->numHashBytes = 4; if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); if (p->numThreads < 0) p->numThreads = #ifndef _7ZIP_ST ((p->btMode && p->algo) ? 2 : 1); #else 1; #endif } UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) { CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); return props.dictSize; } /* #define LZMA_LOG_BSR */ /* Define it for Intel's CPU */ #ifdef LZMA_LOG_BSR #define kDicLogSizeMaxCompress 30 #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } UInt32 GetPosSlot1(UInt32 pos) { UInt32 res; BSR2_RET(pos, res); return res; } #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } #else #define kNumLogBits (9 + (int)sizeof(size_t) / 2) #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) void LzmaEnc_FastPosInit(Byte *g_FastPos) { int c = 2, slotFast; g_FastPos[0] = 0; g_FastPos[1] = 1; for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) { UInt32 k = (1 << ((slotFast >> 1) - 1)); UInt32 j; for (j = 0; j < k; j++, c++) g_FastPos[c] = (Byte)slotFast; } } #define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ res = p->g_FastPos[pos >> i] + (i * 2); } /* #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ p->g_FastPos[pos >> 6] + 12 : \ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } */ #define GetPosSlot1(pos) p->g_FastPos[pos] #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } #endif #define LZMA_NUM_REPS 4 typedef unsigned CState; typedef struct { UInt32 price; CState state; int prev1IsChar; int prev2; UInt32 posPrev2; UInt32 backPrev2; UInt32 posPrev; UInt32 backPrev; UInt32 backs[LZMA_NUM_REPS]; } COptimal; #define kNumOpts (1 << 12) #define kNumLenToPosStates 4 #define kNumPosSlotBits 6 #define kDicLogSizeM0 #define kDicLogSizeMax 32 #define kDistTableSizeMax (kDicLogSizeMax * 2) #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kAlignMask (kAlignTableSize - 1) #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #ifdef _LZMA_PROB32 #define CLzmaProb UInt32 #else #define CLzmaProb UInt16 #endif #define LZMA_PB_MAX 4 #define LZMA_LC_MAX 8 #define LZMA_LP_MAX 4 #define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define LZMA_MATCH_LEN_MIN 2 #define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) #define kNumStates 12 typedef struct { CLzmaProb choice; CLzmaProb choice2; CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; CLzmaProb high[kLenNumHighSymbols]; } CLenEnc; typedef struct { CLenEnc p; UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; UInt32 tableSize; UInt32 counters[LZMA_NUM_PB_STATES_MAX]; } CLenPriceEnc; typedef struct { UInt32 range; Byte cache; UInt64 low; UInt64 cacheSize; Byte *buf; Byte *bufLim; Byte *bufBase; ISeqOutStream *outStream; UInt64 processed; SRes res; } CRangeEnc; typedef struct { CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; UInt32 reps[LZMA_NUM_REPS]; UInt32 state; } CSaveState; typedef struct { IMatchFinder matchFinder; void *matchFinderObj; #ifndef _7ZIP_ST Bool mtMode; CMatchFinderMt matchFinderMt; #endif CMatchFinder matchFinderBase; #ifndef _7ZIP_ST Byte pad[128]; #endif UInt32 optimumEndIndex; UInt32 optimumCurrentIndex; UInt32 longestMatchLength; UInt32 numPairs; UInt32 numAvail; COptimal opt[kNumOpts]; #ifndef LZMA_LOG_BSR Byte g_FastPos[1 << kNumLogBits]; #endif UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; UInt32 numFastBytes; UInt32 additionalOffset; UInt32 reps[LZMA_NUM_REPS]; UInt32 state; UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; UInt32 alignPrices[kAlignTableSize]; UInt32 alignPriceCount; UInt32 distTableSize; unsigned lc, lp, pb; unsigned lpMask, pbMask; CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; unsigned lclp; Bool fastMode; CRangeEnc rc; Bool writeEndMark; UInt64 nowPos64; UInt32 matchPriceCount; Bool finished; Bool multiThread; SRes result; UInt32 dictSize; UInt32 matchFinderCycles; int needInit; CSaveState saveState; } CLzmaEnc; void LzmaEnc_SaveState(CLzmaEncHandle pp) { CLzmaEnc *p = (CLzmaEnc *)pp; CSaveState *dest = &p->saveState; int i; dest->lenEnc = p->lenEnc; dest->repLenEnc = p->repLenEnc; dest->state = p->state; for (i = 0; i < kNumStates; i++) { memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); } for (i = 0; i < kNumLenToPosStates; i++) memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); } void LzmaEnc_RestoreState(CLzmaEncHandle pp) { CLzmaEnc *dest = (CLzmaEnc *)pp; const CSaveState *p = &dest->saveState; int i; dest->lenEnc = p->lenEnc; dest->repLenEnc = p->repLenEnc; dest->state = p->state; for (i = 0; i < kNumStates; i++) { memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); } for (i = 0; i < kNumLenToPosStates; i++) memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); } SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) { CLzmaEnc *p = (CLzmaEnc *)pp; CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || props.dictSize > ((UInt32)1 << kDicLogSizeMaxCompress) || props.dictSize > ((UInt32)1 << 30)) return SZ_ERROR_PARAM; p->dictSize = props.dictSize; p->matchFinderCycles = props.mc; { unsigned fb = props.fb; if (fb < 5) fb = 5; if (fb > LZMA_MATCH_LEN_MAX) fb = LZMA_MATCH_LEN_MAX; p->numFastBytes = fb; } p->lc = props.lc; p->lp = props.lp; p->pb = props.pb; p->fastMode = (props.algo == 0); p->matchFinderBase.btMode = props.btMode; { UInt32 numHashBytes = 4; if (props.btMode) { if (props.numHashBytes < 2) numHashBytes = 2; else if (props.numHashBytes < 4) numHashBytes = props.numHashBytes; } p->matchFinderBase.numHashBytes = numHashBytes; } p->matchFinderBase.cutValue = props.mc; p->writeEndMark = props.writeEndMark; #ifndef _7ZIP_ST /* if (newMultiThread != _multiThread) { ReleaseMatchFinder(); _multiThread = newMultiThread; } */ p->multiThread = (props.numThreads > 1); #endif return SZ_OK; } static const int kLiteralNextStates[kNumStates] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 }; static const int kMatchNextStates[kNumStates] = { 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 }; static const int kRepNextStates[kNumStates] = { 8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11 }; static const int kShortRepNextStates[kNumStates] = { 9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11 }; #define IsCharState(s) ((s) < 7) #define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) #define kInfinityPrice (1 << 30) static void RangeEnc_Construct(CRangeEnc *p) { p->outStream = 0; p->bufBase = 0; } #define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) #define RC_BUF_SIZE (1 << 16) static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) { if (p->bufBase == 0) { p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); if (p->bufBase == 0) return 0; p->bufLim = p->bufBase + RC_BUF_SIZE; } return 1; } static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->bufBase); p->bufBase = 0; } static void RangeEnc_Init(CRangeEnc *p) { /* Stream.Init(); */ p->low = 0; p->range = 0xFFFFFFFF; p->cacheSize = 1; p->cache = 0; p->buf = p->bufBase; p->processed = 0; p->res = SZ_OK; } static void RangeEnc_FlushStream(CRangeEnc *p) { size_t num; if (p->res != SZ_OK) return; num = p->buf - p->bufBase; if (num != p->outStream->Write(p->outStream, p->bufBase, num)) p->res = SZ_ERROR_WRITE; p->processed += num; p->buf = p->bufBase; } static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) { if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) { Byte temp = p->cache; do { Byte *buf = p->buf; *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); p->buf = buf; if (buf == p->bufLim) RangeEnc_FlushStream(p); temp = 0xFF; } while (--p->cacheSize != 0); p->cache = (Byte)((UInt32)p->low >> 24); } p->cacheSize++; p->low = (UInt32)p->low << 8; } static void RangeEnc_FlushData(CRangeEnc *p) { int i; for (i = 0; i < 5; i++) RangeEnc_ShiftLow(p); } static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) { do { p->range >>= 1; p->low += p->range & (0 - ((value >> --numBits) & 1)); if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } while (numBits != 0); } static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) { UInt32 ttt = *prob; UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; if (symbol == 0) { p->range = newBound; ttt += (kBitModelTotal - ttt) >> kNumMoveBits; } else { p->low += newBound; p->range -= newBound; ttt -= ttt >> kNumMoveBits; } *prob = (CLzmaProb)ttt; if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) { symbol |= 0x100; do { RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); } static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) { UInt32 offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); } void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) { UInt32 i; for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) { const int kCyclesBits = kNumBitPriceShiftBits; UInt32 w = i; UInt32 bitCount = 0; int j; for (j = 0; j < kCyclesBits; j++) { w = w * w; bitCount <<= 1; while (w >= ((UInt32)1 << 16)) { w >>= 1; bitCount++; } } ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); } } #define GET_PRICE(prob, symbol) \ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICEa(prob, symbol) \ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] #define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; symbol |= 0x100; do { price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); return price; } static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) { UInt32 price = 0; UInt32 offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); return price; } static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) { UInt32 m = 1; int i; for (i = numBitLevels; i != 0;) { UInt32 bit; i--; bit = (symbol >> i) & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; } } static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) { UInt32 m = 1; int i; for (i = 0; i < numBitLevels; i++) { UInt32 bit = symbol & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; symbol >>= 1; } } static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; symbol |= (1 << numBitLevels); while (symbol != 1) { price += GET_PRICEa(probs[symbol >> 1], symbol & 1); symbol >>= 1; } return price; } static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; UInt32 m = 1; int i; for (i = numBitLevels; i != 0; i--) { UInt32 bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) | bit; } return price; } static void LenEnc_Init(CLenEnc *p) { unsigned i; p->choice = p->choice2 = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) p->low[i] = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) p->mid[i] = kProbInitValue; for (i = 0; i < kLenNumHighSymbols; i++) p->high[i] = kProbInitValue; } static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) { if (symbol < kLenNumLowSymbols) { RangeEnc_EncodeBit(rc, &p->choice, 0); RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); } else { RangeEnc_EncodeBit(rc, &p->choice, 1); if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) { RangeEnc_EncodeBit(rc, &p->choice2, 0); RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); } else { RangeEnc_EncodeBit(rc, &p->choice2, 1); RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); } } } static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) { UInt32 a0 = GET_PRICE_0a(p->choice); UInt32 a1 = GET_PRICE_1a(p->choice); UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); UInt32 i = 0; for (i = 0; i < kLenNumLowSymbols; i++) { if (i >= numSymbols) return; prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); } for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) { if (i >= numSymbols) return; prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); } for (; i < numSymbols; i++) prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); } static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) { LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); p->counters[posState] = p->tableSize; } static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) { UInt32 posState; for (posState = 0; posState < numPosStates; posState++) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) { LenEnc_Encode(&p->p, rc, symbol, posState); if (updatePrice) if (--p->counters[posState] == 0) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void MovePos(CLzmaEnc *p, UInt32 num) { #ifdef SHOW_STAT ttt += num; printf("\n MovePos %d", num); #endif if (num != 0) { p->additionalOffset += num; p->matchFinder.Skip(p->matchFinderObj, num); } } static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) { UInt32 lenRes = 0, numPairs; p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); #ifdef SHOW_STAT printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ttt++; { UInt32 i; for (i = 0; i < numPairs; i += 2) printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); } #endif if (numPairs > 0) { lenRes = p->matches[numPairs - 2]; if (lenRes == p->numFastBytes) { const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; UInt32 distance = p->matches[numPairs - 1] + 1; UInt32 numAvail = p->numAvail; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; { const Byte *pby2 = pby - distance; for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); } } } p->additionalOffset++; *numDistancePairsRes = numPairs; return lenRes; } #define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; #define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; #define IsShortRep(p) ((p)->backPrev == 0) static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) { return GET_PRICE_0(p->isRepG0[state]) + GET_PRICE_0(p->isRep0Long[state][posState]); } static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) { UInt32 price; if (repIndex == 0) { price = GET_PRICE_0(p->isRepG0[state]); price += GET_PRICE_1(p->isRep0Long[state][posState]); } else { price = GET_PRICE_1(p->isRepG0[state]); if (repIndex == 1) price += GET_PRICE_0(p->isRepG1[state]); else { price += GET_PRICE_1(p->isRepG1[state]); price += GET_PRICE(p->isRepG2[state], repIndex - 2); } } return price; } static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) { return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + GetPureRepPrice(p, repIndex, state, posState); } static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) { UInt32 posMem = p->opt[cur].posPrev; UInt32 backMem = p->opt[cur].backPrev; p->optimumEndIndex = cur; do { if (p->opt[cur].prev1IsChar) { MakeAsChar(&p->opt[posMem]) p->opt[posMem].posPrev = posMem - 1; if (p->opt[cur].prev2) { p->opt[posMem - 1].prev1IsChar = False; p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; } } { UInt32 posPrev = posMem; UInt32 backCur = backMem; backMem = p->opt[posPrev].backPrev; posMem = p->opt[posPrev].posPrev; p->opt[posPrev].backPrev = backCur; p->opt[posPrev].posPrev = cur; cur = posPrev; } } while (cur != 0); *backRes = p->opt[0].backPrev; p->optimumCurrentIndex = p->opt[0].posPrev; return p->optimumCurrentIndex; } #define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; UInt32 matchPrice, repMatchPrice, normalMatchPrice; UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; UInt32 *matches; const Byte *data; Byte curByte, matchByte; if (p->optimumEndIndex != p->optimumCurrentIndex) { const COptimal *opt = &p->opt[p->optimumCurrentIndex]; UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; *backRes = opt->backPrev; p->optimumCurrentIndex = opt->posPrev; return lenRes; } p->optimumCurrentIndex = p->optimumEndIndex = 0; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; if (numAvail < 2) { *backRes = (UInt32)(-1); return 1; } if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; repMaxIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 lenTest; const Byte *data2; reps[i] = p->reps[i]; data2 = data - (reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) { repLens[i] = 0; continue; } for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); repLens[i] = lenTest; if (lenTest > repLens[repMaxIndex]) repMaxIndex = i; } if (repLens[repMaxIndex] >= p->numFastBytes) { UInt32 lenRes; *backRes = repMaxIndex; lenRes = repLens[repMaxIndex]; MovePos(p, lenRes - 1); return lenRes; } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } curByte = *data; matchByte = *(data - (reps[0] + 1)); if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) { *backRes = (UInt32)-1; return 1; } p->opt[0].state = (CState)p->state; posState = (position & p->pbMask); { const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + (!IsCharState(p->state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } MakeAsChar(&p->opt[1]); matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); if (matchByte == curByte) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); if (shortRepPrice < p->opt[1].price) { p->opt[1].price = shortRepPrice; MakeAsShortRep(&p->opt[1]); } } lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); if (lenEnd < 2) { *backRes = p->opt[1].backPrev; return 1; } p->opt[1].posPrev = 0; for (i = 0; i < LZMA_NUM_REPS; i++) p->opt[0].backs[i] = reps[i]; len = lenEnd; do p->opt[len--].price = kInfinityPrice; while (len >= 2); for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 repLen = repLens[i]; UInt32 price; if (repLen < 2) continue; price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); do { UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; COptimal *opt = &p->opt[repLen]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = i; opt->prev1IsChar = False; } } while (--repLen >= 2); } normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); if (len <= mainLen) { UInt32 offs = 0; while (len > matches[offs]) offs += 2; for (;; len++) { COptimal *opt; UInt32 distance = matches[offs + 1]; UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; UInt32 lenToPosState = GetLenToPosState(len); if (distance < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][distance]; else { UInt32 slot; GetPosSlot2(distance, slot); curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; } opt = &p->opt[len]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = distance + LZMA_NUM_REPS; opt->prev1IsChar = False; } if (len == matches[offs]) { offs += 2; if (offs == numPairs) break; } } } cur = 0; #ifdef SHOW_STAT2 if (position >= 0) { unsigned i; printf("\n pos = %4X", position); for (i = cur; i <= lenEnd; i++) printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); } #endif for (;;) { UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; Bool nextIsChar; Byte curByte, matchByte; const Byte *data; COptimal *curOpt; COptimal *nextOpt; cur++; if (cur == lenEnd) return Backward(p, backRes, cur); newLen = ReadMatchDistances(p, &numPairs); if (newLen >= p->numFastBytes) { p->numPairs = numPairs; p->longestMatchLength = newLen; return Backward(p, backRes, cur); } position++; curOpt = &p->opt[cur]; posPrev = curOpt->posPrev; if (curOpt->prev1IsChar) { posPrev--; if (curOpt->prev2) { state = p->opt[curOpt->posPrev2].state; if (curOpt->backPrev2 < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } else state = p->opt[posPrev].state; state = kLiteralNextStates[state]; } else state = p->opt[posPrev].state; if (posPrev == cur - 1) { if (IsShortRep(curOpt)) state = kShortRepNextStates[state]; else state = kLiteralNextStates[state]; } else { UInt32 pos; const COptimal *prevOpt; if (curOpt->prev1IsChar && curOpt->prev2) { posPrev = curOpt->posPrev2; pos = curOpt->backPrev2; state = kRepNextStates[state]; } else { pos = curOpt->backPrev; if (pos < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } prevOpt = &p->opt[posPrev]; if (pos < LZMA_NUM_REPS) { UInt32 i; reps[0] = prevOpt->backs[pos]; for (i = 1; i <= pos; i++) reps[i] = prevOpt->backs[i - 1]; for (; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i]; } else { UInt32 i; reps[0] = (pos - LZMA_NUM_REPS); for (i = 1; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i - 1]; } } curOpt->state = (CState)state; curOpt->backs[0] = reps[0]; curOpt->backs[1] = reps[1]; curOpt->backs[2] = reps[2]; curOpt->backs[3] = reps[3]; curPrice = curOpt->price; nextIsChar = False; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; curByte = *data; matchByte = *(data - (reps[0] + 1)); posState = (position & p->pbMask); curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); { const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); curAnd1Price += (!IsCharState(state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } nextOpt = &p->opt[cur + 1]; if (curAnd1Price < nextOpt->price) { nextOpt->price = curAnd1Price; nextOpt->posPrev = cur; MakeAsChar(nextOpt); nextIsChar = True; } matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); if (shortRepPrice <= nextOpt->price) { nextOpt->price = shortRepPrice; nextOpt->posPrev = cur; MakeAsShortRep(nextOpt); nextIsChar = True; } } numAvailFull = p->numAvail; { UInt32 temp = kNumOpts - 1 - cur; if (temp < numAvailFull) numAvailFull = temp; } if (numAvailFull < 2) continue; numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); if (!nextIsChar && matchByte != curByte) /* speed optimization */ { /* try Literal + rep0 */ UInt32 temp; UInt32 lenTest2; const Byte *data2 = data - (reps[0] + 1); UInt32 limit = p->numFastBytes + 1; if (limit > numAvailFull) limit = numAvailFull; for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); lenTest2 = temp - 1; if (lenTest2 >= 2) { UInt32 state2 = kLiteralNextStates[state]; UInt32 posStateNext = (position + 1) & p->pbMask; UInt32 nextRepMatchPrice = curAnd1Price + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 curAndLenPrice; COptimal *opt; UInt32 offset = cur + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = False; } } } } startLen = 2; /* speed optimization */ { UInt32 repIndex; for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) { UInt32 lenTest; UInt32 lenTestTemp; UInt32 price; const Byte *data2 = data - (reps[repIndex] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); while (lenEnd < cur + lenTest) p->opt[++lenEnd].price = kInfinityPrice; lenTestTemp = lenTest; price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); do { UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; COptimal *opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = repIndex; opt->prev1IsChar = False; } } while (--lenTest >= 2); lenTest = lenTestTemp; if (repIndex == 0) startLen = lenTest + 1; /* if (_maxMode) */ { UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { UInt32 state2 = kRepNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = price + p->repLenEnc.prices[posState][lenTest - 2] + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (position + lenTest + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 curAndLenPrice; COptimal *opt; UInt32 offset = cur + lenTest + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = True; opt->posPrev2 = cur; opt->backPrev2 = repIndex; } } } } } } /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ if (newLen > numAvail) { newLen = numAvail; for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); matches[numPairs] = newLen; numPairs += 2; } if (newLen >= startLen) { UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); UInt32 offs, curBack, posSlot; UInt32 lenTest; while (lenEnd < cur + newLen) p->opt[++lenEnd].price = kInfinityPrice; offs = 0; while (startLen > matches[offs]) offs += 2; curBack = matches[offs + 1]; GetPosSlot2(curBack, posSlot); for (lenTest = /*2*/ startLen;; lenTest++) { UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; UInt32 lenToPosState = GetLenToPosState(lenTest); COptimal *opt; if (curBack < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; else curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = curBack + LZMA_NUM_REPS; opt->prev1IsChar = False; } if (/*_maxMode && */lenTest == matches[offs]) { /* Try Match + Literal + Rep0 */ const Byte *data2 = data - (curBack + 1); UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { UInt32 state2 = kMatchNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = curAndLenPrice + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (posStateNext + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 offset = cur + lenTest + 1 + lenTest2; UInt32 curAndLenPrice; COptimal *opt; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = True; opt->posPrev2 = cur; opt->backPrev2 = curBack + LZMA_NUM_REPS; } } } offs += 2; if (offs == numPairs) break; curBack = matches[offs + 1]; if (curBack >= kNumFullDistances) GetPosSlot2(curBack, posSlot); } } } } } #define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) { UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; const Byte *data; const UInt32 *matches; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; *backRes = (UInt32)-1; if (numAvail < 2) return 1; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; repLen = repIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len; const Byte *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (len = 2; len < numAvail && data[len] == data2[len]; len++); if (len >= p->numFastBytes) { *backRes = i; MovePos(p, len - 1); return len; } if (len > repLen) { repIndex = i; repLen = len; } } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } mainDist = 0; /* for GCC */ if (mainLen >= 2) { mainDist = matches[numPairs - 1]; while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) { if (!ChangePair(matches[numPairs - 3], mainDist)) break; numPairs -= 2; mainLen = matches[numPairs - 2]; mainDist = matches[numPairs - 1]; } if (mainLen == 2 && mainDist >= 0x80) mainLen = 1; } if (repLen >= 2 && ( (repLen + 1 >= mainLen) || (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) { *backRes = repIndex; MovePos(p, repLen - 1); return repLen; } if (mainLen < 2 || numAvail <= 2) return 1; p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); if (p->longestMatchLength >= 2) { UInt32 newDistance = matches[p->numPairs - 1]; if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || (p->longestMatchLength > mainLen + 1) || (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) return 1; } data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len, limit; const Byte *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; limit = mainLen - 1; for (len = 2; len < limit && data[len] == data2[len]; len++); if (len >= limit) return 1; } *backRes = mainDist + LZMA_NUM_REPS; MovePos(p, mainLen - 2); return mainLen; } static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) { UInt32 len; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; len = LZMA_MATCH_LEN_MIN; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); } static SRes CheckErrors(CLzmaEnc *p) { if (p->result != SZ_OK) return p->result; if (p->rc.res != SZ_OK) p->result = SZ_ERROR_WRITE; if (p->matchFinderBase.result != SZ_OK) p->result = SZ_ERROR_READ; if (p->result != SZ_OK) p->finished = True; return p->result; } static SRes Flush(CLzmaEnc *p, UInt32 nowPos) { /* ReleaseMFStream(); */ p->finished = True; if (p->writeEndMark) WriteEndMarker(p, nowPos & p->pbMask); RangeEnc_FlushData(&p->rc); RangeEnc_FlushStream(&p->rc); return CheckErrors(p); } static void FillAlignPrices(CLzmaEnc *p) { UInt32 i; for (i = 0; i < kAlignTableSize; i++) p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); p->alignPriceCount = 0; } static void FillDistancesPrices(CLzmaEnc *p) { UInt32 tempPrices[kNumFullDistances]; UInt32 i, lenToPosState; for (i = kStartPosModelIndex; i < kNumFullDistances; i++) { UInt32 posSlot = GetPosSlot1(i); UInt32 footerBits = ((posSlot >> 1) - 1); UInt32 base = ((2 | (posSlot & 1)) << footerBits); tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); } for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) { UInt32 posSlot; const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; for (posSlot = 0; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); { UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; UInt32 i; for (i = 0; i < kStartPosModelIndex; i++) distancesPrices[i] = posSlotPrices[i]; for (; i < kNumFullDistances; i++) distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; } } p->matchPriceCount = 0; } void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); MatchFinder_Construct(&p->matchFinderBase); #ifndef _7ZIP_ST MatchFinderMt_Construct(&p->matchFinderMt); p->matchFinderMt.MatchFinder = &p->matchFinderBase; #endif { CLzmaEncProps props; LzmaEncProps_Init(&props); LzmaEnc_SetProps(p, &props); } #ifndef LZMA_LOG_BSR LzmaEnc_FastPosInit(p->g_FastPos); #endif LzmaEnc_InitPriceTables(p->ProbPrices); p->litProbs = 0; p->saveState.litProbs = 0; } CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) { void *p; p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); if (p != 0) LzmaEnc_Construct((CLzmaEnc *)p); return p; } void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->litProbs); alloc->Free(alloc, p->saveState.litProbs); p->litProbs = 0; p->saveState.litProbs = 0; } void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) { #ifndef _7ZIP_ST MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); #endif MatchFinder_Free(&p->matchFinderBase, allocBig); LzmaEnc_FreeLits(p, alloc); RangeEnc_Free(&p->rc, alloc); } void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) { LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); alloc->Free(alloc, p); } static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) { UInt32 nowPos32, startPos32; if (p->needInit) { p->matchFinder.Init(p->matchFinderObj); p->needInit = 0; } if (p->finished) return p->result; RINOK(CheckErrors(p)); nowPos32 = (UInt32)p->nowPos64; startPos32 = nowPos32; if (p->nowPos64 == 0) { UInt32 numPairs; Byte curByte; if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) return Flush(p, nowPos32); ReadMatchDistances(p, &numPairs); RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); p->state = kLiteralNextStates[p->state]; curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); LitEnc_Encode(&p->rc, p->litProbs, curByte); p->additionalOffset--; nowPos32++; } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) for (;;) { UInt32 pos, len, posState; if (p->fastMode) len = GetOptimumFast(p, &pos); else len = GetOptimum(p, nowPos32, &pos); #ifdef SHOW_STAT2 printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); #endif posState = nowPos32 & p->pbMask; if (len == 1 && pos == (UInt32)-1) { Byte curByte; CLzmaProb *probs; const Byte *data; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; curByte = *data; probs = LIT_PROBS(nowPos32, *(data - 1)); if (IsCharState(p->state)) LitEnc_Encode(&p->rc, probs, curByte); else LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); p->state = kLiteralNextStates[p->state]; } else { RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); if (pos < LZMA_NUM_REPS) { RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); if (pos == 0) { RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); } else { UInt32 distance = p->reps[pos]; RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); if (pos == 1) RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); else { RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); if (pos == 3) p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; } p->reps[1] = p->reps[0]; p->reps[0] = distance; } if (len == 1) p->state = kShortRepNextStates[p->state]; else { LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); p->state = kRepNextStates[p->state]; } } else { UInt32 posSlot; RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); pos -= LZMA_NUM_REPS; GetPosSlot(pos, posSlot); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { UInt32 footerBits = ((posSlot >> 1) - 1); UInt32 base = ((2 | (posSlot & 1)) << footerBits); UInt32 posReduced = pos - base; if (posSlot < kEndPosModelIndex) RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); else { RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); p->alignPriceCount++; } } p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; p->reps[1] = p->reps[0]; p->reps[0] = pos; p->matchPriceCount++; } } p->additionalOffset -= len; nowPos32 += len; if (p->additionalOffset == 0) { UInt32 processed; if (!p->fastMode) { if (p->matchPriceCount >= (1 << 7)) FillDistancesPrices(p); if (p->alignPriceCount >= kAlignTableSize) FillAlignPrices(p); } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) break; processed = nowPos32 - startPos32; if (useLimits) { if (processed + kNumOpts + 300 >= maxUnpackSize || RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) break; } else if (processed >= (1 << 15)) { p->nowPos64 += nowPos32 - startPos32; return CheckErrors(p); } } } p->nowPos64 += nowPos32 - startPos32; return Flush(p, nowPos32); } #define kBigHashDicLimit ((UInt32)1 << 24) static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 beforeSize = kNumOpts; if (!RangeEnc_Alloc(&p->rc, alloc)) return SZ_ERROR_MEM; #ifndef _7ZIP_ST { Bool btMode = (p->matchFinderBase.btMode != 0); p->mtMode = (p->multiThread && !p->fastMode && btMode); } #endif { unsigned lclp = p->lc + p->lp; if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) { LzmaEnc_FreeLits(p, alloc); p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); if (p->litProbs == 0 || p->saveState.litProbs == 0) { LzmaEnc_FreeLits(p, alloc); return SZ_ERROR_MEM; } p->lclp = lclp; } } p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); if (beforeSize + p->dictSize < keepWindowSize) beforeSize = keepWindowSize - p->dictSize; #ifndef _7ZIP_ST if (p->mtMode) { RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); p->matchFinderObj = &p->matchFinderMt; MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); } else #endif { if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) return SZ_ERROR_MEM; p->matchFinderObj = &p->matchFinderBase; MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); } return SZ_OK; } void LzmaEnc_Init(CLzmaEnc *p) { UInt32 i; p->state = 0; for (i = 0; i < LZMA_NUM_REPS; i++) p->reps[i] = 0; RangeEnc_Init(&p->rc); for (i = 0; i < kNumStates; i++) { UInt32 j; for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) { p->isMatch[i][j] = kProbInitValue; p->isRep0Long[i][j] = kProbInitValue; } p->isRep[i] = kProbInitValue; p->isRepG0[i] = kProbInitValue; p->isRepG1[i] = kProbInitValue; p->isRepG2[i] = kProbInitValue; } { UInt32 num = 0x300 << (p->lp + p->lc); for (i = 0; i < num; i++) p->litProbs[i] = kProbInitValue; } { for (i = 0; i < kNumLenToPosStates; i++) { CLzmaProb *probs = p->posSlotEncoder[i]; UInt32 j; for (j = 0; j < (1 << kNumPosSlotBits); j++) probs[j] = kProbInitValue; } } { for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) p->posEncoders[i] = kProbInitValue; } LenEnc_Init(&p->lenEnc.p); LenEnc_Init(&p->repLenEnc.p); for (i = 0; i < (1 << kNumAlignBits); i++) p->posAlignEncoder[i] = kProbInitValue; p->optimumEndIndex = 0; p->optimumCurrentIndex = 0; p->additionalOffset = 0; p->pbMask = (1 << p->pb) - 1; p->lpMask = (1 << p->lp) - 1; } void LzmaEnc_InitPrices(CLzmaEnc *p) { if (!p->fastMode) { FillDistancesPrices(p); FillAlignPrices(p); } p->lenEnc.tableSize = p->repLenEnc.tableSize = p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); } static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 i; for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) if (p->dictSize <= ((UInt32)1 << i)) break; p->distTableSize = i * 2; p->finished = False; p->result = SZ_OK; RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); LzmaEnc_Init(p); LzmaEnc_InitPrices(p); p->nowPos64 = 0; return SZ_OK; } static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; p->needInit = 1; p->rc.outStream = outStream; return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); } SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) { p->matchFinderBase.directInput = 1; p->matchFinderBase.bufferBase = (Byte *)src; p->matchFinderBase.directInputRem = srcLen; } SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; LzmaEnc_SetInputBuf(p, src, srcLen); p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } void LzmaEnc_Finish(CLzmaEncHandle pp) { #ifndef _7ZIP_ST CLzmaEnc *p = (CLzmaEnc *)pp; if (p->mtMode) MatchFinderMt_ReleaseStream(&p->matchFinderMt); #else (void)pp; #endif } typedef struct { ISeqOutStream funcTable; Byte *data; SizeT rem; Bool overflow; } CSeqOutStreamBuf; static size_t MyWrite(void *pp, const void *data, size_t size) { CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; if (p->rem < size) { size = p->rem; p->overflow = True; } memcpy(p->data, data, size); p->rem -= size; p->data += size; return size; } UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); } const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; } SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) { CLzmaEnc *p = (CLzmaEnc *)pp; UInt64 nowPos64; SRes res; CSeqOutStreamBuf outStream; outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = False; p->finished = False; p->result = SZ_OK; if (reInit) LzmaEnc_Init(p); LzmaEnc_InitPrices(p); nowPos64 = p->nowPos64; RangeEnc_Init(&p->rc); p->rc.outStream = &outStream.funcTable; res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); *unpackSize = (UInt32)(p->nowPos64 - nowPos64); *destLen -= outStream.rem; if (outStream.overflow) return SZ_ERROR_OUTPUT_EOF; return res; } static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) { SRes res = SZ_OK; #ifndef _7ZIP_ST Byte allocaDummy[0x300]; int i = 0; for (i = 0; i < 16; i++) allocaDummy[i] = (Byte)i; #endif for (;;) { res = LzmaEnc_CodeOneBlock(p, False, 0, 0); if (res != SZ_OK || p->finished != 0) break; if (progress != 0) { res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); if (res != SZ_OK) { res = SZ_ERROR_PROGRESS; break; } } } LzmaEnc_Finish(p); return res; } SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); } SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) { CLzmaEnc *p = (CLzmaEnc *)pp; int i; UInt32 dictSize = p->dictSize; if (*size < LZMA_PROPS_SIZE) return SZ_ERROR_PARAM; *size = LZMA_PROPS_SIZE; props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); for (i = 11; i <= 30; i++) { if (dictSize <= ((UInt32)2 << i)) { dictSize = (2 << i); break; } if (dictSize <= ((UInt32)3 << i)) { dictSize = (3 << i); break; } } for (i = 0; i < 4; i++) props[1 + i] = (Byte)(dictSize >> (8 * i)); return SZ_OK; } SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { SRes res; CLzmaEnc *p = (CLzmaEnc *)pp; CSeqOutStreamBuf outStream; LzmaEnc_SetInputBuf(p, src, srcLen); outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = writeEndMark; p->rc.outStream = &outStream.funcTable; res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); if (res == SZ_OK) res = LzmaEnc_Encode2(p, progress); *destLen -= outStream.rem; if (outStream.overflow) return SZ_ERROR_OUTPUT_EOF; return res; } SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); SRes res; if (p == 0) return SZ_ERROR_MEM; res = LzmaEnc_SetProps(p, props); if (res == SZ_OK) { res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); if (res == SZ_OK) res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, writeEndMark, progress, alloc, allocBig); } LzmaEnc_Destroy(p, alloc, allocBig); return res; } UEFITool-0.28.0/LZMA/SDK/C/LzmaEnc.h000066400000000000000000000055311363631100000162340ustar00rootroot00000000000000/* LzmaEnc.h -- LZMA Encoder 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __LZMA_ENC_H #define __LZMA_ENC_H #include "Types.h" #ifdef __cplusplus extern "C" { #endif #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaEncProps { int level; /* 0 <= level <= 9 */ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version (1 << 12) <= dictSize <= (1 << 30) for 64-bit version default = (1 << 24) */ int lc; /* 0 <= lc <= 8, default = 3 */ int lp; /* 0 <= lp <= 4, default = 0 */ int pb; /* 0 <= pb <= 4, default = 2 */ int algo; /* 0 - fast, 1 - normal, default = 1 */ int fb; /* 5 <= fb <= 273, default = 32 */ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ int numHashBytes; /* 2, 3 or 4, default = 4 */ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ int numThreads; /* 1 or 2, default = 2 */ } CLzmaEncProps; void LzmaEncProps_Init(CLzmaEncProps *p); void LzmaEncProps_Normalize(CLzmaEncProps *p); UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); /* ---------- CLzmaEncHandle Interface ---------- */ /* LzmaEnc_* functions can return the following exit codes: Returns: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater in props SZ_ERROR_WRITE - Write callback error. SZ_ERROR_PROGRESS - some break from progress callback SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ typedef void * CLzmaEncHandle; CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); /* ---------- One Call Interface ---------- */ /* LzmaEncode Return code: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater SZ_ERROR_OUTPUT_EOF - output buffer overflow SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); #ifdef __cplusplus } #endif #endif UEFITool-0.28.0/LZMA/SDK/C/Types.h000066400000000000000000000125141363631100000160060ustar00rootroot00000000000000/* Types.h -- Basic types 2010-10-09 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H #include "../../UefiLzma.h" #include #ifdef _WIN32 #include #endif #ifndef EXTERN_C_BEGIN #ifdef __cplusplus #define EXTERN_C_BEGIN extern "C" { #define EXTERN_C_END } #else #define EXTERN_C_BEGIN #define EXTERN_C_END #endif #endif EXTERN_C_BEGIN #define SZ_OK 0 #define SZ_ERROR_DATA 1 #define SZ_ERROR_MEM 2 #define SZ_ERROR_CRC 3 #define SZ_ERROR_UNSUPPORTED 4 #define SZ_ERROR_PARAM 5 #define SZ_ERROR_INPUT_EOF 6 #define SZ_ERROR_OUTPUT_EOF 7 #define SZ_ERROR_READ 8 #define SZ_ERROR_WRITE 9 #define SZ_ERROR_PROGRESS 10 #define SZ_ERROR_FAIL 11 #define SZ_ERROR_THREAD 12 #define SZ_ERROR_ARCHIVE 16 #define SZ_ERROR_NO_ARCHIVE 17 typedef int SRes; #ifdef _WIN32 typedef DWORD WRes; #else typedef int WRes; #endif #ifndef RINOK #define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } #endif typedef unsigned char Byte; typedef short Int16; typedef unsigned short UInt16; #ifdef _LZMA_UINT32_IS_ULONG typedef long Int32; typedef unsigned long UInt32; #else typedef int Int32; typedef unsigned int UInt32; #endif #ifdef _SZ_NO_INT_64 /* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. NOTES: Some code will work incorrectly in that case! */ typedef long Int64; typedef unsigned long UInt64; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 Int64; typedef unsigned __int64 UInt64; #define UINT64_CONST(n) n #else typedef long long int Int64; typedef unsigned long long int UInt64; #define UINT64_CONST(n) n ## ULL #endif #endif #ifdef _LZMA_NO_SYSTEM_SIZE_T typedef UInt32 SizeT; #else typedef size_t SizeT; #endif typedef int Bool; #define True 1 #define False 0 #ifdef _WIN32 #define MY_STD_CALL __stdcall #else #define MY_STD_CALL #endif #ifdef _MSC_VER #if _MSC_VER >= 1300 #define MY_NO_INLINE __declspec(noinline) #else #define MY_NO_INLINE #endif #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall #else #define MY_CDECL #define MY_FAST_CALL #endif /* The following interfaces use first parameter as pointer to structure */ typedef struct { Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ } IByteIn; typedef struct { void (*Write)(void *p, Byte b); } IByteOut; typedef struct { SRes (*Read)(void *p, void *buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) < input(*size)) is allowed */ } ISeqInStream; /* it can return SZ_ERROR_INPUT_EOF */ SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); typedef struct { size_t (*Write)(void *p, const void *buf, size_t size); /* Returns: result - the number of actually written bytes. (result < size) means error */ } ISeqOutStream; typedef enum { SZ_SEEK_SET = 0, SZ_SEEK_CUR = 1, SZ_SEEK_END = 2 } ESzSeek; typedef struct { SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); } ISeekInStream; typedef struct { SRes (*Look)(void *p, const void **buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) > input(*size)) is not allowed (output(*size) < input(*size)) is allowed */ SRes (*Skip)(void *p, size_t offset); /* offset must be <= output(*size) of Look */ SRes (*Read)(void *p, void *buf, size_t *size); /* reads directly (without buffer). It's same as ISeqInStream::Read */ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); } ILookInStream; SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); /* reads via ILookInStream::Read */ SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); #define LookToRead_BUF_SIZE (1 << 14) typedef struct { ILookInStream s; ISeekInStream *realStream; size_t pos; size_t size; Byte buf[LookToRead_BUF_SIZE]; } CLookToRead; void LookToRead_CreateVTable(CLookToRead *p, int lookahead); void LookToRead_Init(CLookToRead *p); typedef struct { ISeqInStream s; ILookInStream *realStream; } CSecToLook; void SecToLook_CreateVTable(CSecToLook *p); typedef struct { ISeqInStream s; ILookInStream *realStream; } CSecToRead; void SecToRead_CreateVTable(CSecToRead *p); typedef struct { SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); /* Returns: result. (result != SZ_OK) means break. Value (UInt64)(Int64)-1 for size means unknown value. */ } ICompressProgress; typedef struct { void *(*Alloc)(void *p, size_t size); void (*Free)(void *p, void *address); /* address can be 0 */ } ISzAlloc; #define IAlloc_Alloc(p, size) (p)->Alloc((p), size) #define IAlloc_Free(p, a) (p)->Free((p), a) #ifdef _WIN32 #define CHAR_PATH_SEPARATOR '\\' #define WCHAR_PATH_SEPARATOR L'\\' #define STRING_PATH_SEPARATOR "\\" #define WSTRING_PATH_SEPARATOR L"\\" #else #define CHAR_PATH_SEPARATOR '/' #define WCHAR_PATH_SEPARATOR L'/' #define STRING_PATH_SEPARATOR "/" #define WSTRING_PATH_SEPARATOR L"/" #endif EXTERN_C_END #endif UEFITool-0.28.0/LZMA/UefiLzma.h000066400000000000000000000013171363631100000156120ustar00rootroot00000000000000/* LZMA UEFI header file Copyright (c) 2009, Intel Corporation. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef __UEFILZMA_H__ #define __UEFILZMA_H__ #include "../basetypes.h" #ifdef _WIN32 #undef _WIN32 #endif #ifdef _WIN64 #undef _WIN64 #endif #define _LZMA_SIZE_OPT #define _7ZIP_ST #endif // __UEFILZMA_H__ UEFITool-0.28.0/LZMA/x86Convert.h000066400000000000000000000000261363631100000160600ustar00rootroot00000000000000#include "SDK/C/Bra.h"UEFITool-0.28.0/README.rst000066400000000000000000000124251363631100000146530ustar00rootroot00000000000000UEFITool ======== .. image:: https://raw.githubusercontent.com/LongSoft/UEFITool/master/uefitool.ico .. image:: https://scan.coverity.com/projects/1812/badge.svg?flat=1 :target: https://scan.coverity.com/projects/1812/ | | UEFITool is a cross-platform C++/Qt program for parsing, extracting and modifying UEFI firmware images. | It supports parsing of full BIOS images starting with the flash descriptor or any binary files containing UEFI volumes. | Original development was started `here `_ at MDL forums as a cross-platform analog to `PhoenixTool `_'s structure mode with some additional features, but the program's engine was proven to be usefull for another projects like `UEFIPatch `_, `UBU `_ and `OZMTool `_. Installation ------------ | You can either use `pre-built binaries for Windows and OSX `_ or build a binary yourself. | To build a binary you need a C++ compiler and an instance of Qt4/Qt5 library for it. | Install both of them, get the sources, generate makefiles using qmake (*qmake UEFITool.pro*) and use your make command on that generated files (i.e. *nmake release*, *make release* and so on). Usage ----- | The program can be started directly without any arguments or supplied with a single argument - a path to the UEFI image file to open after start. | | The program window is divided into three panels: **Structure**, **Information** and **Messages**. | Structure of the image is represented as a tree of elements with different names, types and subtypes. If you select an element, **Information** panel will show the available information about the selected element based on it's type and contents. | **Messages** panel show all messages from the engine, including structure warnings and search results. Most of messages can be double-clicked to select the element that causes the message. | | You can open a menu on each tree element to see what operations are possible for the selected element. This can include various types of **Extract**, **Insert** and **Replace** operations, as well as **Remove** and **Rebuild**. | **Extract** has two variants: **Extract as is** and **Extract body**. The difference is that **Extract as is** extracts the element with it's header (GUID, size, attributes and other structure-related information are located there), and **Extract body** extracts the element data only. | **Replace** has the same two variants as **Extract** with the same meaning. | **Insert** has the three different variants: **Insert before**, **Insert after** and **Insert into**, which is only available for UEFI volumes and encapsulation sections. | **Remove** marks an element for removal on image reconstuction. | **Rebuild** marks an element for rebuilding on image reconstruction. Normally, all elements that aren't marked for rebuild won't be changed at all and if you need to correct some structure error (i.e. invalid data checksum) you must mark an element for rebuild manually. If you change an element all it's parents up to the tree root will be marked for rebuild automatically. If UEFI volume is marked for rebuild all uncompressed PEI files in it will also be marked for rebuild because they must be rebased in the reconstructed image to maintain the executable-in-place constraint. | | There is also a search function available from the *File* menu, you can search all tree elements for a specified hexadecimal pattern (spaces are not counted, dot symbol (.) is used as placeholder for a single hex digit), a specified GUID (rules are the same as for hex except for spaces) and a specified text (either Unicode or ASCII, case sensitive or not). Search results will be added into **Messages** panel, if anything is found. | | After you've finished the modifications, you need to initiate image reconstruction using *Save image file* command from the *File* menu. If anything goes wrong on the reconstruction, an error will pop up, otherwise the program will prompt if you need to open the reconstructed file. Don't rush it, because reconstruction process can also generate some usefull messages, which will be lost if you open the reconstructed file immediatelly. Known issues ------------ * Some images has non-standard calculation of base address of TE images, so the program can rebase them incorrectly after modifications. Will be solved ASAP. * Some images may not work after modification because of no FIT table support implemented yet. It's on my high priority features list, so I hope it will be corrected soon. * The program is meant to work with BIOS images, not some vendor-specific BIOS update files, that is why some of that update file either can\t be opened at all or return errors on reconstruction. If someone wants to write an unpacker for such crappy files - I will be glad to use it. * AMI-specific features like NCBs, ROM_AREA structure and other things like that can't be implemented by me because of the NDA I have. UEFITool-0.28.0/Tiano/000077500000000000000000000000001363631100000142325ustar00rootroot00000000000000UEFITool-0.28.0/Tiano/EfiTianoCompress.c000066400000000000000000000662121363631100000176170ustar00rootroot00000000000000/** @file Compression routine. The compression algorithm is a mixture of LZ77 and Huffman coding. LZ77 transforms the source data into a sequence of Original Characters and Pointers to repeated strings. This sequence is further divided into Blocks and Huffman codings are applied to each Block. Copyright (c) 2014, Nikolaj Schlej Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
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. **/ #include "EfiTianoCompress.h" // // Macro Definitions // #undef UINT8_MAX typedef INT16 NODE; #define UINT8_MAX 0xff #define UINT8_BIT 8 #define THRESHOLD 3 #define INIT_CRC 0 #define WNDBIT 13 #define WNDSIZ (1U << WNDBIT) #define MAXMATCH 256 #define PERC_FLAG 0x8000U #define CODE_BIT 16 #define NIL 0 #define MAX_HASH_VAL (3 * WNDSIZ + (WNDSIZ / 512 + 1) * UINT8_MAX) #define HASH(p, c) ((p) + ((c) << (WNDBIT - 9)) + WNDSIZ * 2) #define CRCPOLY 0xA001 #define UPDATE_CRC(c) mCrc = mCrcTable[(mCrc ^ (c)) & 0xFF] ^ (mCrc >> UINT8_BIT) // // C: the Char&Len Set; P: the Position Set; T: the exTra Set // #define NC (UINT8_MAX + MAXMATCH + 2 - THRESHOLD) #define CBIT 9 #define NP (WNDBIT + 1) //#define PBIT 4 UINT8 gPBIT = 4; #define NT (CODE_BIT + 3) #define TBIT 5 #if NT > NP #define NPT NT #else #define NPT NP #endif // // Function Prototypes // STATIC VOID PutDword( IN UINT32 Data ); STATIC EFI_STATUS AllocateMemory (VOID); STATIC VOID FreeMemory (VOID); STATIC VOID InitSlide (VOID); STATIC NODE Child ( IN NODE q, IN UINT8 c ); STATIC VOID MakeChild ( IN NODE q, IN UINT8 c, IN NODE r ); STATIC VOID Split ( IN NODE Old ); STATIC VOID InsertNode (VOID); STATIC VOID DeleteNode (VOID); STATIC VOID GetNextMatch (VOID); STATIC EFI_STATUS Encode (VOID); STATIC VOID CountTFreq (VOID); STATIC VOID WritePTLen ( IN INT32 n, IN INT32 nbit, IN INT32 Special ); STATIC VOID WriteCLen (VOID); STATIC VOID EncodeC ( IN INT32 c ); STATIC VOID EncodeP ( IN UINT32 p ); STATIC VOID SendBlock (VOID); STATIC VOID Output ( IN UINT32 c, IN UINT32 p ); STATIC VOID HufEncodeStart (VOID); STATIC VOID HufEncodeEnd (VOID); STATIC VOID MakeCrcTable (VOID); STATIC VOID PutBits ( IN INT32 n, IN UINT32 x ); STATIC INT32 FreadCrc ( OUT UINT8 *p, IN INT32 n ); STATIC VOID InitPutBits (VOID); STATIC VOID CountLen ( IN INT32 i ); STATIC VOID MakeLen ( IN INT32 Root ); STATIC VOID DownHeap ( IN INT32 i ); STATIC VOID MakeCode ( IN INT32 n, IN UINT8 Len[], OUT UINT16 Code[] ); STATIC INT32 MakeTree ( IN INT32 NParm, IN UINT16 FreqParm[], OUT UINT8 LenParm[], OUT UINT16 CodeParm[] ); // // Global Variables // STATIC UINT8 *mSrc, *mDst, *mSrcUpperLimit, *mDstUpperLimit; STATIC UINT8 *mLevel, *mText, *mChildCount, *mBuf, mCLen[NC], mPTLen[NPT], *mLen; STATIC INT16 mHeap[NC + 1]; STATIC INT32 mRemainder, mMatchLen, mBitCount, mHeapSize, mN; STATIC UINT32 mBufSiz = 0, mOutputPos, mOutputMask, mSubBitBuf, mCrc; STATIC UINT32 mCompSize, mOrigSize; STATIC UINT16 *mFreq, *mSortPtr, mLenCnt[17], mLeft[2 * NC - 1], mRight[2 * NC - 1], mCrcTable[UINT8_MAX + 1], mCFreq[2 * NC - 1],mCCode[NC], mPFreq[2 * NP - 1], mPTCode[NPT], mTFreq[2 * NT - 1]; STATIC NODE mPos, mMatchPos, mAvail, *mPosition, *mParent, *mPrev, *mNext = NULL; // // functions // EFI_STATUS EfiCompress ( IN CONST VOID *SrcBuffer, IN UINT32 SrcSize, IN VOID *DstBuffer, IN OUT UINT32 *DstSize ) /*++ Routine Description: The main compression routine. Arguments: SrcBuffer - The buffer storing the source data SrcSize - The size of source data DstBuffer - The buffer to store the compressed data DstSize - On input, the size of DstBuffer; On output, the size of the actual compressed data. Returns: EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. In this case, DstSize contains the size needed. EFI_SUCCESS - Compression is successful. --*/ { EFI_STATUS Status = EFI_SUCCESS; // // Initializations // mBufSiz = 0; mBuf = NULL; mText = NULL; mLevel = NULL; mChildCount = NULL; mPosition = NULL; mParent = NULL; mPrev = NULL; mNext = NULL; gPBIT = 4; mSrc = (UINT8*)SrcBuffer; mSrcUpperLimit = mSrc + SrcSize; mDst = DstBuffer; mDstUpperLimit = mDst + *DstSize; PutDword(0L); PutDword(0L); MakeCrcTable (); mOrigSize = mCompSize = 0; mCrc = INIT_CRC; // // Compress it // Status = Encode(); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } // // Null terminate the compressed data // if (mDst < mDstUpperLimit) { *mDst++ = 0; } // // Fill in compressed size and original size // mDst = DstBuffer; PutDword(mCompSize+1); PutDword(mOrigSize); // // Return // if (mCompSize + 1 + 8 > *DstSize) { *DstSize = mCompSize + 1 + 8; return EFI_BUFFER_TOO_SMALL; } else { *DstSize = mCompSize + 1 + 8; return EFI_SUCCESS; } } EFI_STATUS TianoCompress( IN CONST VOID *SrcBuffer, IN UINT32 SrcSize, IN VOID *DstBuffer, IN OUT UINT32 *DstSize ) /*++ Routine Description: The main compression routine. Arguments: SrcBuffer - The buffer storing the source data SrcSize - The size of source data DstBuffer - The buffer to store the compressed data DstSize - On input, the size of DstBuffer; On output, the size of the actual compressed data. Returns: EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. In this case, DstSize contains the size needed. EFI_SUCCESS - Compression is successful. --*/ { EFI_STATUS Status = EFI_SUCCESS; // // Initializations // mBufSiz = 0; mBuf = NULL; mText = NULL; mLevel = NULL; mChildCount = NULL; mPosition = NULL; mParent = NULL; mPrev = NULL; mNext = NULL; gPBIT = 5; mSrc = (UINT8*)SrcBuffer; mSrcUpperLimit = mSrc + SrcSize; mDst = DstBuffer; mDstUpperLimit = mDst + *DstSize; PutDword(0L); PutDword(0L); MakeCrcTable(); mOrigSize = mCompSize = 0; mCrc = INIT_CRC; // // Compress it // Status = Encode(); if (EFI_ERROR(Status)) { return EFI_OUT_OF_RESOURCES; } // // Null terminate the compressed data // if (mDst < mDstUpperLimit) { *mDst++ = 0; } // // Fill in compressed size and original size // mDst = DstBuffer; PutDword(mCompSize + 1); PutDword(mOrigSize); // // Return // if (mCompSize + 1 + 8 > *DstSize) { *DstSize = mCompSize + 1 + 8; return EFI_BUFFER_TOO_SMALL; } else { *DstSize = mCompSize + 1 + 8; return EFI_SUCCESS; } } STATIC VOID PutDword( IN UINT32 Data ) /*++ Routine Description: Put a dword to output stream Arguments: Data - the dword to put Returns: (VOID) --*/ { if (mDst < mDstUpperLimit) { *mDst++ = (UINT8)(((UINT8)(Data )) & 0xff); } if (mDst < mDstUpperLimit) { *mDst++ = (UINT8)(((UINT8)(Data >> 0x08)) & 0xff); } if (mDst < mDstUpperLimit) { *mDst++ = (UINT8)(((UINT8)(Data >> 0x10)) & 0xff); } if (mDst < mDstUpperLimit) { *mDst++ = (UINT8)(((UINT8)(Data >> 0x18)) & 0xff); } } STATIC EFI_STATUS AllocateMemory () /*++ Routine Description: Allocate memory spaces for data structures used in compression process Argements: (VOID) Returns: EFI_SUCCESS - Memory is allocated successfully EFI_OUT_OF_RESOURCES - Allocation fails --*/ { UINT32 i; mText = malloc (WNDSIZ * 2 + MAXMATCH); if (!mText) return EFI_OUT_OF_RESOURCES; for (i = 0 ; i < WNDSIZ * 2 + MAXMATCH; i ++) { mText[i] = 0; } mLevel = malloc((WNDSIZ + UINT8_MAX + 1) * sizeof(*mLevel)); if (!mLevel) return EFI_OUT_OF_RESOURCES; mChildCount = malloc((WNDSIZ + UINT8_MAX + 1) * sizeof(*mChildCount)); if (!mChildCount) return EFI_OUT_OF_RESOURCES; mPosition = malloc((WNDSIZ + UINT8_MAX + 1) * sizeof(*mPosition)); if (!mPosition) return EFI_OUT_OF_RESOURCES; mParent = malloc(WNDSIZ * 2 * sizeof(*mParent)); if (!mParent) return EFI_OUT_OF_RESOURCES; mPrev = malloc(WNDSIZ * 2 * sizeof(*mPrev)); if (!mPrev) return EFI_OUT_OF_RESOURCES; mNext = malloc((MAX_HASH_VAL + 1) * sizeof(*mNext)); if (!mNext) return EFI_OUT_OF_RESOURCES; mBufSiz = 16 * 1024U; while ((mBuf = malloc(mBufSiz)) == NULL) { mBufSiz = (mBufSiz / 10U) * 9U; if (mBufSiz < 4 * 1024U) { return EFI_OUT_OF_RESOURCES; } } mBuf[0] = 0; return EFI_SUCCESS; } VOID FreeMemory () /*++ Routine Description: Called when compression is completed to free memory previously allocated. Arguments: (VOID) Returns: (VOID) --*/ { if (mText) { free (mText); } if (mLevel) { free (mLevel); } if (mChildCount) { free (mChildCount); } if (mPosition) { free (mPosition); } if (mParent) { free (mParent); } if (mPrev) { free (mPrev); } if (mNext) { free (mNext); } if (mBuf) { free (mBuf); } return; } STATIC VOID InitSlide () /*++ Routine Description: Initialize String Info Log data structures Arguments: (VOID) Returns: (VOID) --*/ { NODE i; for (i = WNDSIZ; i <= (NODE)(WNDSIZ + UINT8_MAX); i++) { mLevel[i] = 1; mPosition[i] = NIL; /* sentinel */ } for (i = WNDSIZ; i < (NODE)(WNDSIZ * 2); i++) { mParent[i] = NIL; } mAvail = 1; for (i = 1; i < (NODE)(WNDSIZ - 1); i++) { mNext[i] = (NODE)(i + 1); } mNext[WNDSIZ - 1] = NIL; for (i = WNDSIZ * 2; i <= (NODE)MAX_HASH_VAL; i++) { mNext[i] = NIL; } } STATIC NODE Child ( IN NODE q, IN UINT8 c ) /*++ Routine Description: Find child node given the parent node and the edge character Arguments: q - the parent node c - the edge character Returns: The child node (NIL if not found) --*/ { NODE r; r = mNext[HASH(q, c)]; mParent[NIL] = q; /* sentinel */ while (mParent[r] != q) { r = mNext[r]; } return r; } STATIC VOID MakeChild ( IN NODE q, IN UINT8 c, IN NODE r ) /*++ Routine Description: Create a new child for a given parent node. Arguments: q - the parent node c - the edge character r - the child node Returns: (VOID) --*/ { NODE h, t; h = (NODE)HASH(q, c); t = mNext[h]; mNext[h] = r; mNext[r] = t; mPrev[t] = r; mPrev[r] = h; mParent[r] = q; mChildCount[q]++; } STATIC VOID Split ( NODE Old ) /*++ Routine Description: Split a node. Arguments: Old - the node to split Returns: (VOID) --*/ { NODE New, t; New = mAvail; mAvail = mNext[New]; mChildCount[New] = 0; t = mPrev[Old]; mPrev[New] = t; mNext[t] = New; t = mNext[Old]; mNext[New] = t; mPrev[t] = New; mParent[New] = mParent[Old]; mLevel[New] = (UINT8)mMatchLen; mPosition[New] = mPos; MakeChild(New, mText[mMatchPos + mMatchLen], Old); MakeChild(New, mText[mPos + mMatchLen], mPos); } STATIC VOID InsertNode () /*++ Routine Description: Insert string info for current position into the String Info Log Arguments: (VOID) Returns: (VOID) --*/ { NODE q, r, j, t; UINT8 c, *t1, *t2; if (mMatchLen >= 4) { // // We have just got a long match, the target tree // can be located by MatchPos + 1. Travese the tree // from bottom up to get to a proper starting point. // The usage of PERC_FLAG ensures proper node deletion // in DeleteNode() later. // mMatchLen--; r = (INT16)((mMatchPos + 1) | WNDSIZ); while ((q = mParent[r]) == NIL) { r = mNext[r]; } while (mLevel[q] >= mMatchLen) { r = q; q = mParent[q]; } t = q; while (mPosition[t] < 0) { mPosition[t] = mPos; t = mParent[t]; } if (t < (NODE)WNDSIZ) { mPosition[t] = (NODE)(mPos | PERC_FLAG); } } else { // // Locate the target tree // q = (INT16)(mText[mPos] + WNDSIZ); c = mText[mPos + 1]; if ((r = Child(q, c)) == NIL) { MakeChild(q, c, mPos); mMatchLen = 1; return; } mMatchLen = 2; } // // Traverse down the tree to find a match. // Update Position value along the route. // Node split or creation is involved. // for ( ; ; ) { if (r >= (NODE)WNDSIZ) { j = MAXMATCH; mMatchPos = r; } else { j = mLevel[r]; mMatchPos = (NODE)(mPosition[r] & ~PERC_FLAG); } if (mMatchPos >= mPos) { mMatchPos -= WNDSIZ; } t1 = &mText[mPos + mMatchLen]; t2 = &mText[mMatchPos + mMatchLen]; while (mMatchLen < j) { if (*t1 != *t2) { Split(r); return; } mMatchLen++; t1++; t2++; } if (mMatchLen >= MAXMATCH) { break; } mPosition[r] = mPos; q = r; if ((r = Child(q, *t1)) == NIL) { MakeChild(q, *t1, mPos); return; } mMatchLen++; } t = mPrev[r]; mPrev[mPos] = t; mNext[t] = mPos; t = mNext[r]; mNext[mPos] = t; mPrev[t] = mPos; mParent[mPos] = q; mParent[r] = NIL; // // Special usage of 'next' // mNext[r] = mPos; } STATIC VOID DeleteNode () /*++ Routine Description: Delete outdated string info. (The Usage of PERC_FLAG ensures a clean deletion) Arguments: (VOID) Returns: (VOID) --*/ { NODE q, r, s, t, u; if (mParent[mPos] == NIL) { return; } r = mPrev[mPos]; s = mNext[mPos]; mNext[r] = s; mPrev[s] = r; r = mParent[mPos]; mParent[mPos] = NIL; if (r >= (NODE)WNDSIZ || --mChildCount[r] > 1) { return; } t = (NODE)(mPosition[r] & ~PERC_FLAG); if (t >= mPos) { t -= WNDSIZ; } s = t; q = mParent[r]; while ((u = mPosition[q]) & PERC_FLAG) { u &= ~PERC_FLAG; if (u >= mPos) { u -= WNDSIZ; } if (u > s) { s = u; } mPosition[q] = (INT16)(s | WNDSIZ); q = mParent[q]; } if (q < (NODE)WNDSIZ) { if (u >= mPos) { u -= WNDSIZ; } if (u > s) { s = u; } mPosition[q] = (INT16)(s | WNDSIZ | PERC_FLAG); } s = Child(r, mText[t + mLevel[r]]); t = mPrev[s]; u = mNext[s]; mNext[t] = u; mPrev[u] = t; t = mPrev[r]; mNext[t] = s; mPrev[s] = t; t = mNext[r]; mPrev[t] = s; mNext[s] = t; mParent[s] = mParent[r]; mParent[r] = NIL; mNext[r] = mAvail; mAvail = r; } STATIC VOID GetNextMatch () /*++ Routine Description: Advance the current position (read in new data if needed). Delete outdated string info. Find a match string for current position. Arguments: (VOID) Returns: (VOID) --*/ { INT32 n; mRemainder--; if (++mPos == WNDSIZ * 2) { memmove(&mText[0], &mText[WNDSIZ], WNDSIZ + MAXMATCH); n = FreadCrc(&mText[WNDSIZ + MAXMATCH], WNDSIZ); mRemainder += n; mPos = WNDSIZ; } DeleteNode(); InsertNode(); } STATIC EFI_STATUS Encode () /*++ Routine Description: The main controlling routine for compression process. Arguments: (VOID) Returns: EFI_SUCCESS - The compression is successful EFI_OUT_0F_RESOURCES - Not enough memory for compression process --*/ { EFI_STATUS Status; INT32 LastMatchLen; NODE LastMatchPos; Status = AllocateMemory(); if (EFI_ERROR(Status)) { FreeMemory(); return Status; } InitSlide(); HufEncodeStart(); mRemainder = FreadCrc(&mText[WNDSIZ], WNDSIZ + MAXMATCH); mMatchLen = 0; mPos = WNDSIZ; InsertNode(); if (mMatchLen > mRemainder) { mMatchLen = mRemainder; } while (mRemainder > 0) { LastMatchLen = mMatchLen; LastMatchPos = mMatchPos; GetNextMatch(); if (mMatchLen > mRemainder) { mMatchLen = mRemainder; } if (mMatchLen > LastMatchLen || LastMatchLen < THRESHOLD) { // // Not enough benefits are gained by outputting a pointer, // so just output the original character // Output(mText[mPos - 1], 0); } else { // // Outputting a pointer is beneficial enough, do it. // Output(LastMatchLen + (UINT8_MAX + 1 - THRESHOLD), (mPos - LastMatchPos - 2) & (WNDSIZ - 1)); while (--LastMatchLen > 0) { GetNextMatch(); } if (mMatchLen > mRemainder) { mMatchLen = mRemainder; } } } HufEncodeEnd(); FreeMemory(); return EFI_SUCCESS; } STATIC VOID CountTFreq () /*++ Routine Description: Count the frequencies for the Extra Set Arguments: (VOID) Returns: (VOID) --*/ { INT32 i, k, n, Count; for (i = 0; i < NT; i++) { mTFreq[i] = 0; } n = NC; while (n > 0 && mCLen[n - 1] == 0) { n--; } i = 0; while (i < n) { k = mCLen[i++]; if (k == 0) { Count = 1; while (i < n && mCLen[i] == 0) { i++; Count++; } if (Count <= 2) { mTFreq[0] = (UINT16)(mTFreq[0] + Count); } else if (Count <= 18) { mTFreq[1]++; } else if (Count == 19) { mTFreq[0]++; mTFreq[1]++; } else { mTFreq[2]++; } } else { mTFreq[k + 2]++; } } } STATIC VOID WritePTLen ( IN INT32 n, IN INT32 nbit, IN INT32 Special ) /*++ Routine Description: Outputs the code length array for the Extra Set or the Position Set. Arguments: n - the number of symbols nbit - the number of bits needed to represent 'n' Special - the special symbol that needs to be take care of Returns: (VOID) --*/ { INT32 i, k; while (n > 0 && mPTLen[n - 1] == 0) { n--; } PutBits(nbit, n); i = 0; while (i < n) { k = mPTLen[i++]; if (k <= 6) { PutBits(3, k); } else { PutBits(k - 3, (1U << (k - 3)) - 2); } if (i == Special) { while (i < 6 && mPTLen[i] == 0) { i++; } PutBits(2, (i - 3) & 3); } } } STATIC VOID WriteCLen () /*++ Routine Description: Outputs the code length array for Char&Length Set Arguments: (VOID) Returns: (VOID) --*/ { INT32 i, k, n, Count; n = NC; while (n > 0 && mCLen[n - 1] == 0) { n--; } PutBits(CBIT, n); i = 0; while (i < n) { k = mCLen[i++]; if (k == 0) { Count = 1; while (i < n && mCLen[i] == 0) { i++; Count++; } if (Count <= 2) { for (k = 0; k < Count; k++) { PutBits(mPTLen[0], mPTCode[0]); } } else if (Count <= 18) { PutBits(mPTLen[1], mPTCode[1]); PutBits(4, Count - 3); } else if (Count == 19) { PutBits(mPTLen[0], mPTCode[0]); PutBits(mPTLen[1], mPTCode[1]); PutBits(4, 15); } else { PutBits(mPTLen[2], mPTCode[2]); PutBits(CBIT, Count - 20); } } else { PutBits(mPTLen[k + 2], mPTCode[k + 2]); } } } STATIC VOID EncodeC ( IN INT32 c ) { PutBits(mCLen[c], mCCode[c]); } STATIC VOID EncodeP ( IN UINT32 p ) { UINT32 c, q; c = 0; q = p; while (q) { q >>= 1; c++; } PutBits(mPTLen[c], mPTCode[c]); if (c > 1) { PutBits(c - 1, p & (0xFFFFU >> (17 - c))); } } STATIC VOID SendBlock () /*++ Routine Description: Huffman code the block and output it. Argument: (VOID) Returns: (VOID) --*/ { UINT32 i, k, Flags, Root, Pos, Size; Flags = 0; Root = MakeTree(NC, mCFreq, mCLen, mCCode); Size = mCFreq[Root]; PutBits(16, Size); if (Root >= NC) { CountTFreq(); Root = MakeTree(NT, mTFreq, mPTLen, mPTCode); if (Root >= NT) { WritePTLen(NT, TBIT, 3); } else { PutBits(TBIT, 0); PutBits(TBIT, Root); } WriteCLen(); } else { PutBits(TBIT, 0); PutBits(TBIT, 0); PutBits(CBIT, 0); PutBits(CBIT, Root); } Root = MakeTree(NP, mPFreq, mPTLen, mPTCode); if (Root >= NP) { WritePTLen(NP, gPBIT, -1); } else { PutBits(gPBIT, 0); PutBits(gPBIT, Root); } Pos = 0; for (i = 0; i < Size; i++) { if (i % UINT8_BIT == 0) { Flags = mBuf[Pos++]; } else { Flags <<= 1; } if (Flags & (1U << (UINT8_BIT - 1))) { EncodeC(mBuf[Pos++] + (1U << UINT8_BIT)); k = mBuf[Pos++] << UINT8_BIT; k += mBuf[Pos++]; EncodeP(k); } else { EncodeC(mBuf[Pos++]); } } for (i = 0; i < NC; i++) { mCFreq[i] = 0; } for (i = 0; i < NP; i++) { mPFreq[i] = 0; } } STATIC VOID Output ( IN UINT32 c, IN UINT32 p ) /*++ Routine Description: Outputs an Original Character or a Pointer Arguments: c - The original character or the 'String Length' element of a Pointer p - The 'Position' field of a Pointer Returns: (VOID) --*/ { STATIC UINT32 CPos; if ((mOutputMask >>= 1) == 0) { mOutputMask = 1U << (UINT8_BIT - 1); if (mOutputPos >= mBufSiz - 3 * UINT8_BIT) { SendBlock(); mOutputPos = 0; } CPos = mOutputPos++; mBuf[CPos] = 0; } mBuf[mOutputPos++] = (UINT8) c; mCFreq[c]++; if (c >= (1U << UINT8_BIT)) { mBuf[CPos] |= mOutputMask; mBuf[mOutputPos++] = (UINT8)(p >> UINT8_BIT); mBuf[mOutputPos++] = (UINT8) p; c = 0; while (p) { p >>= 1; c++; } mPFreq[c]++; } } STATIC VOID HufEncodeStart () { INT32 i; for (i = 0; i < NC; i++) { mCFreq[i] = 0; } for (i = 0; i < NP; i++) { mPFreq[i] = 0; } mOutputPos = mOutputMask = 0; InitPutBits(); return; } STATIC VOID HufEncodeEnd () { SendBlock(); // // Flush remaining bits // PutBits(UINT8_BIT - 1, 0); return; } STATIC VOID MakeCrcTable () { UINT32 i, j, r; for (i = 0; i <= UINT8_MAX; i++) { r = i; for (j = 0; j < UINT8_BIT; j++) { if (r & 1) { r = (r >> 1) ^ CRCPOLY; } else { r >>= 1; } } mCrcTable[i] = (UINT16)r; } } STATIC VOID PutBits ( IN INT32 n, IN UINT32 x ) /*++ Routine Description: Outputs rightmost n bits of x Argments: n - the rightmost n bits of the data is used x - the data Returns: (VOID) --*/ { UINT8 Temp; if (n < mBitCount) { mSubBitBuf |= x << (mBitCount -= n); } else { Temp = (UINT8)(mSubBitBuf | (x >> (n -= mBitCount))); if (mDst < mDstUpperLimit) { *mDst++ = Temp; } mCompSize++; if (n < UINT8_BIT) { mSubBitBuf = x << (mBitCount = UINT8_BIT - n); } else { Temp = (UINT8)(x >> (n - UINT8_BIT)); if (mDst < mDstUpperLimit) { *mDst++ = Temp; } mCompSize++; mSubBitBuf = x << (mBitCount = 2 * UINT8_BIT - n); } } } STATIC INT32 FreadCrc ( OUT UINT8 *p, IN INT32 n ) /*++ Routine Description: Read in source data Arguments: p - the buffer to hold the data n - number of bytes to read Returns: number of bytes actually read --*/ { INT32 i; for (i = 0; mSrc < mSrcUpperLimit && i < n; i++) { *p++ = *mSrc++; } n = i; p -= n; mOrigSize += n; while (--i >= 0) { UPDATE_CRC(*p++); } return n; } STATIC VOID InitPutBits () { mBitCount = UINT8_BIT; mSubBitBuf = 0; } STATIC VOID CountLen ( IN INT32 i ) /*++ Routine Description: Count the number of each code length for a Huffman tree. Arguments: i - the top node Returns: (VOID) --*/ { STATIC INT32 Depth = 0; if (i < mN) { mLenCnt[(Depth < 16) ? Depth : 16]++; } else { Depth++; CountLen(mLeft [i]); CountLen(mRight[i]); Depth--; } } STATIC VOID MakeLen ( IN INT32 Root ) /*++ Routine Description: Create code length array for a Huffman tree Arguments: Root - the root of the tree --*/ { INT32 i, k; UINT32 Cum; for (i = 0; i <= 16; i++) { mLenCnt[i] = 0; } CountLen(Root); // // Adjust the length count array so that // no code will be generated longer than its designated length // Cum = 0; for (i = 16; i > 0; i--) { Cum += mLenCnt[i] << (16 - i); } while (Cum != (1U << 16)) { mLenCnt[16]--; for (i = 15; i > 0; i--) { if (mLenCnt[i] != 0) { mLenCnt[i]--; mLenCnt[i+1] += 2; break; } } Cum--; } for (i = 16; i > 0; i--) { k = mLenCnt[i]; while (--k >= 0) { mLen[*mSortPtr++] = (UINT8)i; } } } STATIC VOID DownHeap ( IN INT32 i ) { INT32 j, k; // // priority queue: send i-th entry down heap // k = mHeap[i]; while ((j = 2 * i) <= mHeapSize) { if (j < mHeapSize && mFreq[mHeap[j]] > mFreq[mHeap[j + 1]]) { j++; } if (mFreq[k] <= mFreq[mHeap[j]]) { break; } mHeap[i] = mHeap[j]; i = j; } mHeap[i] = (INT16)k; } STATIC VOID MakeCode ( IN INT32 n, IN UINT8 Len[], OUT UINT16 Code[] ) /*++ Routine Description: Assign code to each symbol based on the code length array Arguments: n - number of symbols Len - the code length array Code - stores codes for each symbol Returns: (VOID) --*/ { INT32 i; UINT16 Start[18]; Start[1] = 0; for (i = 1; i <= 16; i++) { Start[i + 1] = (UINT16)((Start[i] + mLenCnt[i]) << 1); } for (i = 0; i < n; i++) { Code[i] = Start[Len[i]]++; } } STATIC INT32 MakeTree ( IN INT32 NParm, IN UINT16 FreqParm[], OUT UINT8 LenParm[], OUT UINT16 CodeParm[] ) /*++ Routine Description: Generates Huffman codes given a frequency distribution of symbols Arguments: NParm - number of symbols FreqParm - frequency of each symbol LenParm - code length for each symbol CodeParm - code for each symbol Returns: Root of the Huffman tree. --*/ { INT32 i, j, k, Avail; // // make tree, calculate len[], return root // mN = NParm; mFreq = FreqParm; mLen = LenParm; Avail = mN; mHeapSize = 0; mHeap[1] = 0; for (i = 0; i < mN; i++) { mLen[i] = 0; if (mFreq[i]) { mHeap[++mHeapSize] = (INT16)i; } } if (mHeapSize < 2) { CodeParm[mHeap[1]] = 0; return mHeap[1]; } for (i = mHeapSize / 2; i >= 1; i--) { // // make priority queue // DownHeap(i); } mSortPtr = CodeParm; do { i = mHeap[1]; if (i < mN) { *mSortPtr++ = (UINT16)i; } mHeap[1] = mHeap[mHeapSize--]; DownHeap(1); j = mHeap[1]; if (j < mN) { *mSortPtr++ = (UINT16)j; } k = Avail++; mFreq[k] = (UINT16)(mFreq[i] + mFreq[j]); mHeap[1] = (INT16)k; DownHeap(1); mLeft[k] = (UINT16)i; mRight[k] = (UINT16)j; } while (mHeapSize > 1); mSortPtr = CodeParm; MakeLen(k); MakeCode(NParm, LenParm, CodeParm); // // return root // return k; } UEFITool-0.28.0/Tiano/EfiTianoCompress.h000066400000000000000000000054331363631100000176220ustar00rootroot00000000000000/* EfiTianoCompress.h Copyright (c) 2014, Nikolaj Schlej. All rights reserved.
Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.
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. Module Name: TianoCompress.h Abstract: Header file for compression routine. */ #ifndef EFITIANOCOMPRESS_H #define EFITIANOCOMPRESS_H #include #include #include "../basetypes.h" #ifdef __cplusplus extern "C" { #endif /*++ Routine Description: Tiano compression routine. Arguments: SrcBuffer - The buffer storing the source data SrcSize - The size of source data DstBuffer - The buffer to store the compressed data DstSize - On input, the size of DstBuffer; On output, the size of the actual compressed data. Returns: EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. this case, DstSize contains the size needed. EFI_SUCCESS - Compression is successful. EFI_OUT_OF_RESOURCES - No resource to complete function. EFI_INVALID_PARAMETER - Parameter supplied is wrong. --*/ EFI_STATUS TianoCompress( CONST VOID *SrcBuffer, UINT32 SrcSize, VOID *DstBuffer, UINT32 *DstSize ) ; EFI_STATUS TianoCompressLegacy( CONST VOID *SrcBuffer, UINT32 SrcSize, VOID *DstBuffer, UINT32 *DstSize ) ; /*++ Routine Description: EFI 1.1 compression routine. Arguments: SrcBuffer - The buffer storing the source data SrcSize - The size of source data DstBuffer - The buffer to store the compressed data DstSize - On input, the size of DstBuffer; On output, the size of the actual compressed data. Returns: EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. this case, DstSize contains the size needed. EFI_SUCCESS - Compression is successful. EFI_OUT_OF_RESOURCES - No resource to complete function. EFI_INVALID_PARAMETER - Parameter supplied is wrong. --*/ EFI_STATUS EfiCompress( CONST VOID *SrcBuffer, UINT32 SrcSize, VOID *DstBuffer, UINT32 *DstSize ) ; EFI_STATUS EfiCompressLegacy( CONST VOID *SrcBuffer, UINT32 SrcSize, VOID *DstBuffer, UINT32 *DstSize ) ; #ifdef __cplusplus } #endif #endif // EFITIANOCOMPRESS_H UEFITool-0.28.0/Tiano/EfiTianoCompressLegacy.c000066400000000000000000000762141363631100000207470ustar00rootroot00000000000000/* EFI11/Tiano Compress Implementation Copyright (c) 2015, Nikolaj Schlej Copyright (c) 2006 - 2008, Intel Corporation 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. Module Name: EfiTianoCompressLegacy.c Abstract: Compression routine. The compression algorithm is a mixture of LZ77 and Huffman coding. LZ77 transforms the source data into a sequence of Original Characters and Pointers to repeated strings. This sequence is further divided into Blocks and Huffman codings are applied to each Block. Notes: This legacy compression code can produce incorrect output, but has better compression ratio as newer releases */ #include "EfiTianoCompress.h" // // Macro Definitions // #undef UINT8_MAX typedef INT32 NODE; #define UINT8_MAX 0xff #define UINT8_BIT 8 #define THRESHOLD 3 #define INIT_CRC 0 #define WNDBIT 19 #define WNDSIZ (1U << WNDBIT) #define MAXMATCH 256 #define BLKSIZ (1U << 14) // 16 * 1024U #define PERC_FLAG 0x80000000U #define CODE_BIT 16 #define NIL 0 #define MAX_HASH_VAL (3 * WNDSIZ + (WNDSIZ / 512 + 1) * UINT8_MAX) #define HASH(p, c) ((p) + ((c) << (WNDBIT - 9)) + WNDSIZ * 2) #define CRCPOLY 0xA001 #define UPDATE_CRC(c) mCrc = mCrcTable[(mCrc ^ (c)) & 0xFF] ^ (mCrc >> UINT8_BIT) // // C: the Char&Len Set; P: the Position Set; T: the exTra Set // #define NC (UINT8_MAX + MAXMATCH + 2 - THRESHOLD) #define CBIT 9 #define NP (WNDBIT + 1) //#define PBIT 5 #define NT (CODE_BIT + 3) #define TBIT 5 #if NT > NP #define NPT NT #else #define NPT NP #endif // // Function Prototypes // STATIC VOID PutDword( UINT32 Data ); STATIC INT32 AllocateMemory ( VOID ); STATIC VOID FreeMemory ( VOID ); STATIC VOID InitSlide ( VOID ); STATIC NODE Child ( NODE NodeQ, UINT8 CharC ); STATIC VOID MakeChild ( NODE NodeQ, UINT8 CharC, NODE NodeR ); STATIC VOID Split ( NODE Old ); STATIC VOID InsertNode ( VOID ); STATIC VOID DeleteNode ( VOID ); STATIC VOID GetNextMatch ( VOID ); STATIC INT32 Encode ( VOID ); STATIC VOID CountTFreq ( VOID ); STATIC VOID WritePTLen ( INT32 Number, INT32 nbit, INT32 Special ); STATIC VOID WriteCLen ( VOID ); STATIC VOID EncodeC ( INT32 Value ); STATIC VOID EncodeP ( UINT32 Value ); STATIC VOID SendBlock ( VOID ); STATIC VOID Output ( UINT32 c, UINT32 p ); STATIC VOID HufEncodeStart ( VOID ); STATIC VOID HufEncodeEnd ( VOID ); STATIC VOID MakeCrcTable ( VOID ); STATIC VOID PutBits ( INT32 Number, UINT32 Value ); STATIC INT32 FreadCrc ( UINT8 *Pointer, INT32 Number ); STATIC VOID InitPutBits ( VOID ); STATIC VOID CountLen ( INT32 Index ); STATIC VOID MakeLen ( INT32 Root ); STATIC VOID DownHeap ( INT32 Index ); STATIC VOID MakeCode ( INT32 Number, UINT8 Len[ ], UINT16 Code[] ); STATIC INT32 MakeTree ( INT32 NParm, UINT16 FreqParm[], UINT8 LenParm[ ], UINT16 CodeParm[] ); // // Global Variables // STATIC UINT8 *mSrc, *mDst, *mSrcUpperLimit, *mDstUpperLimit; STATIC UINT8 *mLevel, *mText, *mChildCount, *mBuf, mCLen[NC], mPTLen[NPT], *mLen; STATIC INT16 mHeap[NC + 1]; STATIC INT32 mRemainder, mMatchLen, mBitCount, mHeapSize, mN; STATIC UINT32 mBufSiz = 0, mOutputPos, mOutputMask, mSubBitBuf, mCrc; STATIC UINT32 mCompSize, mOrigSize; STATIC UINT16 *mFreq, *mSortPtr, mLenCnt[17], mLeft[2 * NC - 1], mRight[2 * NC - 1], mCrcTable[UINT8_MAX + 1], mCFreq[2 * NC - 1], mCCode[NC], mPFreq[2 * NP - 1], mPTCode[NPT], mTFreq[2 * NT - 1]; STATIC UINT8 mPbit; STATIC NODE mPos, mMatchPos, mAvail, *mPosition, *mParent, *mPrev, *mNext = NULL; // // functions // EFI_STATUS EfiCompressLegacy( CONST VOID *SrcBuffer, UINT32 SrcSize, VOID *DstBuffer, UINT32 *DstSize ) /*++ Routine Description: The internal implementation of [Efi/Tiano]Compress(). Arguments: SrcBuffer - The buffer storing the source data SrcSize - The size of source data DstBuffer - The buffer to store the compressed data DstSize - On input, the size of DstBuffer; On output, the size of the actual compressed data. Returns: EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. this case, DstSize contains the size needed. EFI_SUCCESS - Compression is successful. EFI_OUT_OF_RESOURCES - No resource to complete function. EFI_INVALID_PARAMETER - Parameter supplied is wrong. --*/ { INT32 Status; // // Initializations // mPbit = 4; mBufSiz = 0; mBuf = NULL; mText = NULL; mLevel = NULL; mChildCount = NULL; mPosition = NULL; mParent = NULL; mPrev = NULL; mNext = NULL; mSrc = (UINT8*) SrcBuffer; mSrcUpperLimit = mSrc + SrcSize; mDst = DstBuffer; mDstUpperLimit = mDst + *DstSize; PutDword(0L); PutDword(0L); MakeCrcTable(); mOrigSize = mCompSize = 0; mCrc = INIT_CRC; // // Compress it // Status = Encode(); if (Status) { return EFI_OUT_OF_RESOURCES; } // // Null terminate the compressed data // if (mDst < mDstUpperLimit) { *mDst++ = 0; } // // Fill compressed size and original size // mDst = DstBuffer; PutDword(mCompSize + 1); PutDword(mOrigSize); // // Return // if (mCompSize + 1 + 8 > *DstSize) { *DstSize = mCompSize + 1 + 8; return EFI_BUFFER_TOO_SMALL; } else { *DstSize = mCompSize + 1 + 8; return EFI_SUCCESS; } } EFI_STATUS TianoCompressLegacy ( CONST VOID *SrcBuffer, UINT32 SrcSize, VOID *DstBuffer, UINT32 *DstSize ) /*++ Routine Description: The internal implementation of [Efi/Tiano]Compress(). Arguments: SrcBuffer - The buffer storing the source data SrcSize - The size of source data DstBuffer - The buffer to store the compressed data DstSize - On input, the size of DstBuffer; On output, the size of the actual compressed data. Version - The version of de/compression algorithm. Version 1 for UEFI 2.0 de/compression algorithm. Version 2 for Tiano de/compression algorithm. Returns: EFI_BUFFER_TOO_SMALL - The DstBuffer is too small. this case, DstSize contains the size needed. EFI_SUCCESS - Compression is successful. EFI_OUT_OF_RESOURCES - No resource to complete function. EFI_INVALID_PARAMETER - Parameter supplied is wrong. --*/ { INT32 Status; // // Initializations // mPbit = 5; mBufSiz = 0; mBuf = NULL; mText = NULL; mLevel = NULL; mChildCount = NULL; mPosition = NULL; mParent = NULL; mPrev = NULL; mNext = NULL; mSrc = (UINT8*) SrcBuffer; mSrcUpperLimit = mSrc + SrcSize; mDst = DstBuffer; mDstUpperLimit = mDst +*DstSize; PutDword (0L); PutDword (0L); MakeCrcTable (); mOrigSize = mCompSize = 0; mCrc = INIT_CRC; // // Compress it // Status = Encode (); if (Status) { return EFI_OUT_OF_RESOURCES; } // // Null terminate the compressed data // if (mDst < mDstUpperLimit) { *mDst++ = 0; } // // Fill compressed size and original size // mDst = DstBuffer; PutDword (mCompSize + 1); PutDword (mOrigSize); // // Return // if (mCompSize + 1 + 8 > *DstSize) { *DstSize = mCompSize + 1 + 8; return EFI_BUFFER_TOO_SMALL; } else { *DstSize = mCompSize + 1 + 8; return EFI_SUCCESS; } } STATIC VOID PutDword ( UINT32 Data ) /*++ Routine Description: Put a DWORD to output stream Arguments: Data - the DWORD to put Returns: (VOID) --*/ { if (mDst < mDstUpperLimit) { *mDst++ = (UINT8) (((UINT8) (Data)) & 0xff); } if (mDst < mDstUpperLimit) { *mDst++ = (UINT8) (((UINT8) (Data >> 0x08)) & 0xff); } if (mDst < mDstUpperLimit) { *mDst++ = (UINT8) (((UINT8) (Data >> 0x10)) & 0xff); } if (mDst < mDstUpperLimit) { *mDst++ = (UINT8) (((UINT8) (Data >> 0x18)) & 0xff); } } STATIC INT32 AllocateMemory ( VOID ) /*++ Routine Description: Allocate memory spaces for data structures used compression process Arguments: VOID Returns: EFI_SUCCESS - Memory is allocated successfully EFI_OUT_OF_RESOURCES - Allocation fails --*/ { UINT32 Index; mText = malloc (WNDSIZ * 2 + MAXMATCH); if (!mText) return EFI_OUT_OF_RESOURCES; for (Index = 0; Index < WNDSIZ * 2 + MAXMATCH; Index++) { mText[Index] = 0; } mLevel = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mLevel)); if (!mLevel) return EFI_OUT_OF_RESOURCES; mChildCount = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mChildCount)); if (!mChildCount) return EFI_OUT_OF_RESOURCES; mPosition = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mPosition)); if (!mPosition) return EFI_OUT_OF_RESOURCES; mParent = malloc (WNDSIZ * 2 * sizeof (*mParent)); if (!mParent) return EFI_OUT_OF_RESOURCES; mPrev = malloc (WNDSIZ * 2 * sizeof (*mPrev)); if (!mPrev) return EFI_OUT_OF_RESOURCES; mNext = malloc ((MAX_HASH_VAL + 1) * sizeof (*mNext)); if (!mNext) return EFI_OUT_OF_RESOURCES; mBufSiz = BLKSIZ; mBuf = malloc (mBufSiz); while (mBuf == NULL) { mBufSiz = (mBufSiz / 10U) * 9U; if (mBufSiz < 4 * 1024U) { return EFI_OUT_OF_RESOURCES; } mBuf = malloc (mBufSiz); } mBuf[0] = 0; return EFI_SUCCESS; } VOID FreeMemory ( VOID ) /*++ Routine Description: Called when compression is completed to free memory previously allocated. Arguments: (VOID) Returns: (VOID) --*/ { if (mText != NULL) { free (mText); } if (mLevel != NULL) { free (mLevel); } if (mChildCount != NULL) { free (mChildCount); } if (mPosition != NULL) { free (mPosition); } if (mParent != NULL) { free (mParent); } if (mPrev != NULL) { free (mPrev); } if (mNext != NULL) { free (mNext); } if (mBuf != NULL) { free (mBuf); } return ; } STATIC VOID InitSlide ( VOID ) /*++ Routine Description: Initialize String Info Log data structures Arguments: (VOID) Returns: (VOID) --*/ { NODE Index; for (Index = (NODE) WNDSIZ; Index <= (NODE) WNDSIZ + UINT8_MAX; Index++) { mLevel[Index] = 1; mPosition[Index] = NIL; // sentinel } for (Index = (NODE) WNDSIZ; Index < (NODE) WNDSIZ * 2; Index++) { mParent[Index] = NIL; } mAvail = 1; for (Index = 1; Index < (NODE) WNDSIZ - 1; Index++) { mNext[Index] = (NODE) (Index + 1); } mNext[WNDSIZ - 1] = NIL; for (Index = (NODE) WNDSIZ * 2; Index <= (NODE) MAX_HASH_VAL; Index++) { mNext[Index] = NIL; } } STATIC NODE Child ( NODE NodeQ, UINT8 CharC ) /*++ Routine Description: Find child node given the parent node and the edge character Arguments: NodeQ - the parent node CharC - the edge character Returns: The child node (NIL if not found) --*/ { NODE NodeR; NodeR = mNext[HASH (NodeQ, CharC)]; // // sentinel // mParent[NIL] = NodeQ; while (mParent[NodeR] != NodeQ) { NodeR = mNext[NodeR]; } return NodeR; } STATIC VOID MakeChild ( NODE Parent, UINT8 CharC, NODE Child ) /*++ Routine Description: Create a new child for a given parent node. Arguments: Parent - the parent node CharC - the edge character Child - the child node Returns: (VOID) --*/ { NODE Node1; NODE Node2; Node1 = (NODE) HASH (Parent, CharC); Node2 = mNext[Node1]; mNext[Node1] = Child; mNext[Child] = Node2; mPrev[Node2] = Child; mPrev[Child] = Node1; mParent[Child] = Parent; mChildCount[Parent]++; } STATIC VOID Split ( NODE Old ) /*++ Routine Description: Split a node. Arguments: Old - the node to split Returns: (VOID) --*/ { NODE New; NODE TempNode; New = mAvail; mAvail = mNext[New]; mChildCount[New] = 0; TempNode = mPrev[Old]; mPrev[New] = TempNode; mNext[TempNode] = New; TempNode = mNext[Old]; mNext[New] = TempNode; mPrev[TempNode] = New; mParent[New] = mParent[Old]; mLevel[New] = (UINT8) mMatchLen; mPosition[New] = mPos; MakeChild (New, mText[mMatchPos + mMatchLen], Old); MakeChild (New, mText[mPos + mMatchLen], mPos); } STATIC VOID InsertNode ( VOID ) /*++ Routine Description: Insert string info for current position into the String Info Log Arguments: (VOID) Returns: (VOID) --*/ { NODE NodeQ; NODE NodeR; NODE Index2; NODE NodeT; UINT8 CharC; UINT8 *t1; UINT8 *t2; if (mMatchLen >= 4) { // // We have just got a long match, the target tree // can be located by MatchPos + 1. Traverse the tree // from bottom up to get to a proper starting point. // The usage of PERC_FLAG ensures proper node deletion // DeleteNode() later. // mMatchLen--; NodeR = (NODE) ((mMatchPos + 1) | WNDSIZ); NodeQ = mParent[NodeR]; while (NodeQ == NIL) { NodeR = mNext[NodeR]; NodeQ = mParent[NodeR]; } while (mLevel[NodeQ] >= mMatchLen) { NodeR = NodeQ; NodeQ = mParent[NodeQ]; } NodeT = NodeQ; while (mPosition[NodeT] < 0) { mPosition[NodeT] = mPos; NodeT = mParent[NodeT]; } if (NodeT < (NODE) WNDSIZ) { mPosition[NodeT] = (NODE) (mPos | (UINT32) PERC_FLAG); } } else { // // Locate the target tree // NodeQ = (NODE) (mText[mPos] + WNDSIZ); CharC = mText[mPos + 1]; NodeR = Child (NodeQ, CharC); if (NodeR == NIL) { MakeChild (NodeQ, CharC, mPos); mMatchLen = 1; return ; } mMatchLen = 2; } // // Traverse down the tree to find a match. // Update Position value along the route. // Node split or creation is involved. // for (;;) { if (NodeR >= (NODE) WNDSIZ) { Index2 = MAXMATCH; mMatchPos = NodeR; } else { Index2 = mLevel[NodeR]; mMatchPos = (NODE) (mPosition[NodeR] & (UINT32)~PERC_FLAG); } if (mMatchPos >= mPos) { mMatchPos -= WNDSIZ; } t1 = &mText[mPos + mMatchLen]; t2 = &mText[mMatchPos + mMatchLen]; while (mMatchLen < Index2) { if (*t1 != *t2) { Split (NodeR); return ; } mMatchLen++; t1++; t2++; } if (mMatchLen >= MAXMATCH) { break; } mPosition[NodeR] = mPos; NodeQ = NodeR; NodeR = Child (NodeQ, *t1); if (NodeR == NIL) { MakeChild (NodeQ, *t1, mPos); return ; } mMatchLen++; } NodeT = mPrev[NodeR]; mPrev[mPos] = NodeT; mNext[NodeT] = mPos; NodeT = mNext[NodeR]; mNext[mPos] = NodeT; mPrev[NodeT] = mPos; mParent[mPos] = NodeQ; mParent[NodeR] = NIL; // // Special usage of 'next' // mNext[NodeR] = mPos; } STATIC VOID DeleteNode ( VOID ) /*++ Routine Description: Delete outdated string info. (The Usage of PERC_FLAG ensures a clean deletion) Arguments: (VOID) Returns: (VOID) --*/ { NODE NodeQ; NODE NodeR; NODE NodeS; NODE NodeT; NODE NodeU; if (mParent[mPos] == NIL) { return ; } NodeR = mPrev[mPos]; NodeS = mNext[mPos]; mNext[NodeR] = NodeS; mPrev[NodeS] = NodeR; NodeR = mParent[mPos]; mParent[mPos] = NIL; if (NodeR >= (NODE) WNDSIZ) { return ; } mChildCount[NodeR]--; if (mChildCount[NodeR] > 1) { return ; } NodeT = (NODE) (mPosition[NodeR] & (UINT32)~PERC_FLAG); if (NodeT >= mPos) { NodeT -= WNDSIZ; } NodeS = NodeT; NodeQ = mParent[NodeR]; NodeU = mPosition[NodeQ]; while (NodeU & (UINT32) PERC_FLAG) { NodeU &= (UINT32)~PERC_FLAG; if (NodeU >= mPos) { NodeU -= WNDSIZ; } if (NodeU > NodeS) { NodeS = NodeU; } mPosition[NodeQ] = (NODE) (NodeS | WNDSIZ); NodeQ = mParent[NodeQ]; NodeU = mPosition[NodeQ]; } if (NodeQ < (NODE) WNDSIZ) { if (NodeU >= mPos) { NodeU -= WNDSIZ; } if (NodeU > NodeS) { NodeS = NodeU; } mPosition[NodeQ] = (NODE) (NodeS | WNDSIZ | (UINT32) PERC_FLAG); } NodeS = Child (NodeR, mText[NodeT + mLevel[NodeR]]); NodeT = mPrev[NodeS]; NodeU = mNext[NodeS]; mNext[NodeT] = NodeU; mPrev[NodeU] = NodeT; NodeT = mPrev[NodeR]; mNext[NodeT] = NodeS; mPrev[NodeS] = NodeT; NodeT = mNext[NodeR]; mPrev[NodeT] = NodeS; mNext[NodeS] = NodeT; mParent[NodeS] = mParent[NodeR]; mParent[NodeR] = NIL; mNext[NodeR] = mAvail; mAvail = NodeR; } STATIC VOID GetNextMatch ( VOID ) /*++ Routine Description: Advance the current position (read new data if needed). Delete outdated string info. Find a match string for current position. Arguments: (VOID) Returns: (VOID) --*/ { INT32 Number; mRemainder--; mPos++; if (mPos == WNDSIZ * 2) { memmove (&mText[0], &mText[WNDSIZ], WNDSIZ + MAXMATCH); Number = FreadCrc (&mText[WNDSIZ + MAXMATCH], WNDSIZ); mRemainder += Number; mPos = WNDSIZ; } DeleteNode (); InsertNode (); } STATIC INT32 Encode ( VOID ) /*++ Routine Description: The mac controlling routine for compression process. Arguments: (VOID) Returns: EFI_SUCCESS - The compression is successful EFI_OUT_0F_RESOURCES - Not enough memory for compression process --*/ { INT32 Status; INT32 LastMatchLen; NODE LastMatchPos; Status = AllocateMemory (); if (Status) { FreeMemory (); return Status; } InitSlide (); HufEncodeStart (); mRemainder = FreadCrc (&mText[WNDSIZ], WNDSIZ + MAXMATCH); mMatchLen = 0; mPos = WNDSIZ; InsertNode (); if (mMatchLen > mRemainder) { mMatchLen = mRemainder; } while (mRemainder > 0) { LastMatchLen = mMatchLen; LastMatchPos = mMatchPos; GetNextMatch (); if (mMatchLen > mRemainder) { mMatchLen = mRemainder; } if (mMatchLen > LastMatchLen || LastMatchLen < THRESHOLD) { // // Not enough benefits are gained by outputting a pointer, // so just output the original character // Output (mText[mPos - 1], 0); } else { if (LastMatchLen == THRESHOLD) { if (((mPos - LastMatchPos - 2) & (WNDSIZ - 1)) > (1U << 11)) { Output (mText[mPos - 1], 0); continue; } } // // Outputting a pointer is beneficial enough, do it. // Output ( LastMatchLen + (UINT8_MAX + 1 - THRESHOLD), (mPos - LastMatchPos - 2) & (WNDSIZ - 1) ); LastMatchLen--; while (LastMatchLen > 0) { GetNextMatch (); LastMatchLen--; } if (mMatchLen > mRemainder) { mMatchLen = mRemainder; } } } HufEncodeEnd (); FreeMemory (); return EFI_SUCCESS; } STATIC VOID CountTFreq ( VOID ) /*++ Routine Description: Count the frequencies for the Extra Set Arguments: (VOID) Returns: (VOID) --*/ { INT32 Index; INT32 Index3; INT32 Number; INT32 Count; for (Index = 0; Index < NT; Index++) { mTFreq[Index] = 0; } Number = NC; while (Number > 0 && mCLen[Number - 1] == 0) { Number--; } Index = 0; while (Index < Number) { Index3 = mCLen[Index++]; if (Index3 == 0) { Count = 1; while (Index < Number && mCLen[Index] == 0) { Index++; Count++; } if (Count <= 2) { mTFreq[0] = (UINT16) (mTFreq[0] + Count); } else if (Count <= 18) { mTFreq[1]++; } else if (Count == 19) { mTFreq[0]++; mTFreq[1]++; } else { mTFreq[2]++; } } else { mTFreq[Index3 + 2]++; } } } STATIC VOID WritePTLen ( INT32 Number, INT32 nbit, INT32 Special ) /*++ Routine Description: Outputs the code length array for the Extra Set or the Position Set. Arguments: Number - the number of symbols nbit - the number of bits needed to represent 'n' Special - the special symbol that needs to be take care of Returns: (VOID) --*/ { INT32 Index; INT32 Index3; while (Number > 0 && mPTLen[Number - 1] == 0) { Number--; } PutBits (nbit, Number); Index = 0; while (Index < Number) { Index3 = mPTLen[Index++]; if (Index3 <= 6) { PutBits (3, Index3); } else { PutBits (Index3 - 3, (1U << (Index3 - 3)) - 2); } if (Index == Special) { while (Index < 6 && mPTLen[Index] == 0) { Index++; } PutBits (2, (Index - 3) & 3); } } } STATIC VOID WriteCLen ( VOID ) /*++ Routine Description: Outputs the code length array for Char&Length Set Arguments: (VOID) Returns: (VOID) --*/ { INT32 Index; INT32 Index3; INT32 Number; INT32 Count; Number = NC; while (Number > 0 && mCLen[Number - 1] == 0) { Number--; } PutBits (CBIT, Number); Index = 0; while (Index < Number) { Index3 = mCLen[Index++]; if (Index3 == 0) { Count = 1; while (Index < Number && mCLen[Index] == 0) { Index++; Count++; } if (Count <= 2) { for (Index3 = 0; Index3 < Count; Index3++) { PutBits (mPTLen[0], mPTCode[0]); } } else if (Count <= 18) { PutBits (mPTLen[1], mPTCode[1]); PutBits (4, Count - 3); } else if (Count == 19) { PutBits (mPTLen[0], mPTCode[0]); PutBits (mPTLen[1], mPTCode[1]); PutBits (4, 15); } else { PutBits (mPTLen[2], mPTCode[2]); PutBits (CBIT, Count - 20); } } else { PutBits (mPTLen[Index3 + 2], mPTCode[Index3 + 2]); } } } STATIC VOID EncodeC ( INT32 Value ) { PutBits (mCLen[Value], mCCode[Value]); } STATIC VOID EncodeP ( UINT32 Value ) { UINT32 Index; UINT32 NodeQ; Index = 0; NodeQ = Value; while (NodeQ) { NodeQ >>= 1; Index++; } PutBits (mPTLen[Index], mPTCode[Index]); if (Index > 1) { PutBits (Index - 1, Value & (0xFFFFFFFFU >> (32 - Index + 1))); } } STATIC VOID SendBlock ( VOID ) /*++ Routine Description: Huffman code the block and output it. Arguments: (VOID) Returns: (VOID) --*/ { UINT32 Index; UINT32 Index2; UINT32 Index3; UINT32 Flags; UINT32 Root; UINT32 Pos; UINT32 Size; Flags = 0; Root = MakeTree (NC, mCFreq, mCLen, mCCode); Size = mCFreq[Root]; PutBits (16, Size); if (Root >= NC) { CountTFreq (); Root = MakeTree (NT, mTFreq, mPTLen, mPTCode); if (Root >= NT) { WritePTLen (NT, TBIT, 3); } else { PutBits (TBIT, 0); PutBits (TBIT, Root); } WriteCLen (); } else { PutBits (TBIT, 0); PutBits (TBIT, 0); PutBits (CBIT, 0); PutBits (CBIT, Root); } Root = MakeTree (NP, mPFreq, mPTLen, mPTCode); if (Root >= NP) { WritePTLen (NP, mPbit, -1); } else { PutBits (mPbit, 0); PutBits (mPbit, Root); } Pos = 0; for (Index = 0; Index < Size; Index++) { if (Index % UINT8_BIT == 0) { Flags = mBuf[Pos++]; } else { Flags <<= 1; } if (Flags & (1U << (UINT8_BIT - 1))) { EncodeC (mBuf[Pos++] + (1U << UINT8_BIT)); Index3 = mBuf[Pos++]; for (Index2 = 0; Index2 < 3; Index2++) { Index3 <<= UINT8_BIT; Index3 += mBuf[Pos++]; } EncodeP (Index3); } else { EncodeC (mBuf[Pos++]); } } for (Index = 0; Index < NC; Index++) { mCFreq[Index] = 0; } for (Index = 0; Index < NP; Index++) { mPFreq[Index] = 0; } } STATIC VOID Output ( UINT32 CharC, UINT32 Pos ) /*++ Routine Description: Outputs an Original Character or a Pointer Arguments: CharC - The original character or the 'String Length' element of a Pointer Pos - The 'Position' field of a Pointer Returns: (VOID) --*/ { STATIC UINT32 CPos; if ((mOutputMask >>= 1) == 0) { mOutputMask = 1U << (UINT8_BIT - 1); // // Check the buffer overflow per outputting UINT8_BIT symbols // which is an Original Character or a Pointer. The biggest // symbol is a Pointer which occupies 5 bytes. // if (mOutputPos >= mBufSiz - 5 * UINT8_BIT) { SendBlock (); mOutputPos = 0; } CPos = mOutputPos++; mBuf[CPos] = 0; } mBuf[mOutputPos++] = (UINT8) CharC; mCFreq[CharC]++; if (CharC >= (1U << UINT8_BIT)) { mBuf[CPos] |= mOutputMask; mBuf[mOutputPos++] = (UINT8) (Pos >> 24); mBuf[mOutputPos++] = (UINT8) (Pos >> 16); mBuf[mOutputPos++] = (UINT8) (Pos >> (UINT8_BIT)); mBuf[mOutputPos++] = (UINT8) Pos; CharC = 0; while (Pos) { Pos >>= 1; CharC++; } mPFreq[CharC]++; } } STATIC VOID HufEncodeStart ( VOID ) { INT32 Index; for (Index = 0; Index < NC; Index++) { mCFreq[Index] = 0; } for (Index = 0; Index < NP; Index++) { mPFreq[Index] = 0; } mOutputPos = mOutputMask = 0; InitPutBits (); return ; } STATIC VOID HufEncodeEnd ( VOID ) { SendBlock (); // // Flush remaining bits // PutBits (UINT8_BIT - 1, 0); return ; } STATIC VOID MakeCrcTable ( VOID ) { UINT32 Index; UINT32 Index2; UINT32 Temp; for (Index = 0; Index <= UINT8_MAX; Index++) { Temp = Index; for (Index2 = 0; Index2 < UINT8_BIT; Index2++) { if (Temp & 1) { Temp = (Temp >> 1) ^ CRCPOLY; } else { Temp >>= 1; } } mCrcTable[Index] = (UINT16) Temp; } } STATIC VOID PutBits ( INT32 Number, UINT32 Value ) /*++ Routine Description: Outputs rightmost n bits of x Arguments: Number - the rightmost n bits of the data is used x - the data Returns: (VOID) --*/ { UINT8 Temp; while (Number >= mBitCount) { // // Number -= mBitCount should never equal to 32 // Temp = (UINT8) (mSubBitBuf | (Value >> (Number -= mBitCount))); if (mDst < mDstUpperLimit) { *mDst++ = Temp; } mCompSize++; mSubBitBuf = 0; mBitCount = UINT8_BIT; } mSubBitBuf |= Value << (mBitCount -= Number); } STATIC INT32 FreadCrc ( UINT8 *Pointer, INT32 Number ) /*++ Routine Description: Read source data Arguments: Pointer - the buffer to hold the data Number - number of bytes to read Returns: number of bytes actually read --*/ { INT32 Index; for (Index = 0; mSrc < mSrcUpperLimit && Index < Number; Index++) { *Pointer++ = *mSrc++; } Number = Index; Pointer -= Number; mOrigSize += Number; Index--; while (Index >= 0) { UPDATE_CRC (*Pointer++); Index--; } return Number; } STATIC VOID InitPutBits ( VOID ) { mBitCount = UINT8_BIT; mSubBitBuf = 0; } STATIC VOID CountLen ( INT32 Index ) /*++ Routine Description: Count the number of each code length for a Huffman tree. Arguments: Index - the top node Returns: (VOID) --*/ { STATIC INT32 Depth = 0; if (Index < mN) { mLenCnt[(Depth < 16) ? Depth : 16]++; } else { Depth++; CountLen (mLeft[Index]); CountLen (mRight[Index]); Depth--; } } STATIC VOID MakeLen ( INT32 Root ) /*++ Routine Description: Create code length array for a Huffman tree Arguments: Root - the root of the tree Returns: VOID --*/ { INT32 Index; INT32 Index3; UINT32 Cum; for (Index = 0; Index <= 16; Index++) { mLenCnt[Index] = 0; } CountLen (Root); // // Adjust the length count array so that // no code will be generated longer than its designated length // Cum = 0; for (Index = 16; Index > 0; Index--) { Cum += mLenCnt[Index] << (16 - Index); } while (Cum != (1U << 16)) { mLenCnt[16]--; for (Index = 15; Index > 0; Index--) { if (mLenCnt[Index] != 0) { mLenCnt[Index]--; mLenCnt[Index + 1] += 2; break; } } Cum--; } for (Index = 16; Index > 0; Index--) { Index3 = mLenCnt[Index]; Index3--; while (Index3 >= 0) { mLen[*mSortPtr++] = (UINT8) Index; Index3--; } } } STATIC VOID DownHeap ( INT32 Index ) { INT32 Index2; INT32 Index3; // // priority queue: send Index-th entry down heap // Index3 = mHeap[Index]; Index2 = 2 * Index; while (Index2 <= mHeapSize) { if (Index2 < mHeapSize && mFreq[mHeap[Index2]] > mFreq[mHeap[Index2 + 1]]) { Index2++; } if (mFreq[Index3] <= mFreq[mHeap[Index2]]) { break; } mHeap[Index] = mHeap[Index2]; Index = Index2; Index2 = 2 * Index; } mHeap[Index] = (INT16) Index3; } STATIC VOID MakeCode ( INT32 Number, UINT8 Len[ ], UINT16 Code[] ) /*++ Routine Description: Assign code to each symbol based on the code length array Arguments: Number - number of symbols Len - the code length array Code - stores codes for each symbol Returns: (VOID) --*/ { INT32 Index; UINT16 Start[18]; Start[1] = 0; for (Index = 1; Index <= 16; Index++) { Start[Index + 1] = (UINT16) ((Start[Index] + mLenCnt[Index]) << 1); } for (Index = 0; Index < Number; Index++) { Code[Index] = Start[Len[Index]]++; } } STATIC INT32 MakeTree ( INT32 NParm, UINT16 FreqParm[], UINT8 LenParm[ ], UINT16 CodeParm[] ) /*++ Routine Description: Generates Huffman codes given a frequency distribution of symbols Arguments: NParm - number of symbols FreqParm - frequency of each symbol LenParm - code length for each symbol CodeParm - code for each symbol Returns: Root of the Huffman tree. --*/ { INT32 Index; INT32 Index2; INT32 Index3; INT32 Avail; // // make tree, calculate len[], return root // mN = NParm; mFreq = FreqParm; mLen = LenParm; Avail = mN; mHeapSize = 0; mHeap[1] = 0; for (Index = 0; Index < mN; Index++) { mLen[Index] = 0; if (mFreq[Index]) { mHeapSize++; mHeap[mHeapSize] = (INT16) Index; } } if (mHeapSize < 2) { CodeParm[mHeap[1]] = 0; return mHeap[1]; } for (Index = mHeapSize / 2; Index >= 1; Index--) { // // make priority queue // DownHeap (Index); } mSortPtr = CodeParm; do { Index = mHeap[1]; if (Index < mN) { *mSortPtr++ = (UINT16) Index; } mHeap[1] = mHeap[mHeapSize--]; DownHeap (1); Index2 = mHeap[1]; if (Index2 < mN) { *mSortPtr++ = (UINT16) Index2; } Index3 = Avail++; mFreq[Index3] = (UINT16) (mFreq[Index] + mFreq[Index2]); mHeap[1] = (INT16) Index3; DownHeap (1); mLeft[Index3] = (UINT16) Index; mRight[Index3] = (UINT16) Index2; } while (mHeapSize > 1); mSortPtr = CodeParm; MakeLen (Index3); MakeCode (NParm, LenParm, CodeParm); // // return root // return Index3; } UEFITool-0.28.0/Tiano/EfiTianoDecompress.c000066400000000000000000000525341363631100000201320ustar00rootroot00000000000000/*++ EfiTianoDecompress.c Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.
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. Module Name: Decompress.c Abstract: Decompressor. Algorithm Ported from OPSD code (Decomp.asm) --*/ #include "EfiTianoDecompress.h" // // Decompression algorithm begins here // #define BITBUFSIZ 32 #define MAXMATCH 256 #define THRESHOLD 3 #define CODE_BIT 16 #ifndef UINT8_MAX #define UINT8_MAX 0xff #endif #define BAD_TABLE - 1 // // C: Char&Len Set; P: Position Set; T: exTra Set // #define NC (0xff + MAXMATCH + 2 - THRESHOLD) #define CBIT 9 #define MAXPBIT 5 #define TBIT 5 #define MAXNP ((1U << MAXPBIT) - 1) #define NT (CODE_BIT + 3) #if NT > MAXNP #define NPT NT #else #define NPT MAXNP #endif typedef struct { UINT8 *mSrcBase; // Starting address of compressed data UINT8 *mDstBase; // Starting address of decompressed data UINT32 mOutBuf; UINT32 mInBuf; UINT16 mBitCount; UINT32 mBitBuf; UINT32 mSubBitBuf; UINT16 mBlockSize; UINT32 mCompSize; UINT32 mOrigSize; UINT16 mBadTableFlag; UINT16 mLeft[2 * NC - 1]; UINT16 mRight[2 * NC - 1]; UINT8 mCLen[NC]; UINT8 mPTLen[NPT]; UINT16 mCTable[4096]; UINT16 mPTTable[256]; // // The length of the field 'Position Set Code Length Array Size' in Block Header. // For EFI 1.1 de/compression algorithm, mPBit = 4 // For Tiano de/compression algorithm, mPBit = 5 // UINT8 mPBit; } SCRATCH_DATA; STATIC VOID FillBuf( IN SCRATCH_DATA *Sd, IN UINT16 NumOfBits ) /*++ Routine Description: Shift mBitBuf NumOfBits left. Read in NumOfBits of bits from source. Arguments: Sd - The global scratch data NumOfBits - The number of bits to shift and read. Returns: (VOID) --*/ { Sd->mBitBuf = (UINT32) (((UINT64)Sd->mBitBuf) << NumOfBits); while (NumOfBits > Sd->mBitCount) { Sd->mBitBuf |= (UINT32) (((UINT64)Sd->mSubBitBuf) << (NumOfBits = (UINT16) (NumOfBits - Sd->mBitCount))); if (Sd->mCompSize > 0) { // // Get 1 byte into SubBitBuf // Sd->mCompSize--; Sd->mSubBitBuf = 0; Sd->mSubBitBuf = Sd->mSrcBase[Sd->mInBuf++]; Sd->mBitCount = 8; } else { // // No more bits from the source, just pad zero bit. // Sd->mSubBitBuf = 0; Sd->mBitCount = 8; } } Sd->mBitCount = (UINT16)(Sd->mBitCount - NumOfBits); Sd->mBitBuf |= Sd->mSubBitBuf >> Sd->mBitCount; } STATIC UINT32 GetBits( IN SCRATCH_DATA *Sd, IN UINT16 NumOfBits ) /*++ Routine Description: Get NumOfBits of bits out from mBitBuf. Fill mBitBuf with subsequent NumOfBits of bits from source. Returns NumOfBits of bits that are popped out. Arguments: Sd - The global scratch data. NumOfBits - The number of bits to pop and read. Returns: The bits that are popped out. --*/ { UINT32 OutBits; OutBits = (UINT32)(Sd->mBitBuf >> (BITBUFSIZ - NumOfBits)); FillBuf(Sd, NumOfBits); return OutBits; } STATIC UINT16 MakeTable( IN SCRATCH_DATA *Sd, IN UINT16 NumOfChar, IN UINT8 *BitLen, IN UINT16 TableBits, OUT UINT16 *Table ) /*++ Routine Description: Creates Huffman Code mapping table according to code length array. Arguments: Sd - The global scratch data NumOfChar - Number of symbols in the symbol set BitLen - Code length array TableBits - The width of the mapping table Table - The table Returns: 0 - OK. BAD_TABLE - The table is corrupted. --*/ { UINT16 Count[17]; UINT16 Weight[17]; UINT16 Start[18]; UINT16 *Pointer; UINT16 Index3; UINT16 Index; UINT16 Len; UINT16 Char; UINT16 JuBits; UINT16 Avail; UINT16 NextCode; UINT16 Mask; // // TableBits should not be greater than 16. // if (TableBits >= (sizeof(Count) / sizeof(UINT16))) { return (UINT16)BAD_TABLE; } // // Initialize Count array starting from Index 0, as there is a possibility of Count array being uninitialized. // for (Index = 0; Index <= 16; Index++) { Count[Index] = 0; } for (Index = 0; Index < NumOfChar; Index++) { // // Count array index should not be greater than or equal to its size. // if (BitLen[Index] < (sizeof(Count) / sizeof(UINT16))) { Count[BitLen[Index]]++; } else { return (UINT16)BAD_TABLE; } } Start[0] = 0; Start[1] = 0; for (Index = 1; Index <= 16; Index++) { Start[Index + 1] = (UINT16)(Start[Index] + (Count[Index] << (16 - Index))); } if (Start[17] != 0) { /*(1U << 16)*/ return (UINT16)BAD_TABLE; } JuBits = (UINT16)(16 - TableBits); for (Index = 1; Index <= TableBits; Index++) { Start[Index] >>= JuBits; Weight[Index] = (UINT16)(1U << (TableBits - Index)); } while (Index <= 16) { Weight[Index] = (UINT16)(1U << (16 - Index)); Index++; } Index = (UINT16)(Start[TableBits + 1] >> JuBits); if (Index != 0) { Index3 = (UINT16)(1U << TableBits); while (Index != Index3) { Table[Index++] = 0; } } Avail = NumOfChar; Mask = (UINT16)(1U << (15 - TableBits)); for (Char = 0; Char < NumOfChar; Char++) { Len = BitLen[Char]; if (Len == 0 || Len >= 17) { continue; } NextCode = (UINT16)(Start[Len] + Weight[Len]); if (Len <= TableBits) { for (Index = Start[Len]; Index < NextCode; Index++) { // Check to prevent possible heap corruption if (Index >= (UINT16)(1U << TableBits)) return (UINT16)BAD_TABLE; Table[Index] = Char; } } else { Index3 = Start[Len]; Pointer = &Table[Index3 >> JuBits]; Index = (UINT16)(Len - TableBits); while (Index != 0) { // // Avail should be lesser than size of mRight and mLeft to prevent buffer overflow. // if ((*Pointer == 0) && (Avail < sizeof(Sd->mRight) / sizeof(UINT16)) && (Avail < sizeof(Sd->mLeft) / sizeof(UINT16))) { Sd->mRight[Avail] = Sd->mLeft[Avail] = 0; *Pointer = Avail++; } // // *Pointer should be lesser than size of mRight and mLeft to prevent buffer overflow. // if ((Index3 & Mask) && (*Pointer < (sizeof(Sd->mRight) / sizeof(UINT16)))) { Pointer = &Sd->mRight[*Pointer]; } else if (*Pointer < (sizeof(Sd->mLeft) / sizeof(UINT16))) { Pointer = &Sd->mLeft[*Pointer]; } Index3 <<= 1; Index--; } *Pointer = Char; } Start[Len] = NextCode; } // // Succeeds // return 0; } STATIC UINT32 DecodeP( IN SCRATCH_DATA *Sd ) /*++ Routine Description: Decodes a position value. Arguments: Sd - the global scratch data Returns: The position value decoded. --*/ { UINT16 Val; UINT32 Mask; UINT32 Pos; Val = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; if (Val >= MAXNP) { Mask = 1U << (BITBUFSIZ - 1 - 8); do { if (Sd->mBitBuf & Mask) { Val = Sd->mRight[Val]; } else { Val = Sd->mLeft[Val]; } Mask >>= 1; } while (Val >= MAXNP); } // // Advance what we have read // FillBuf(Sd, Sd->mPTLen[Val]); Pos = Val; if (Val > 1) { Pos = (UINT32)((1U << (Val - 1)) + GetBits(Sd, (UINT16)(Val - 1))); } return Pos; } STATIC UINT16 ReadPTLen( IN SCRATCH_DATA *Sd, IN UINT16 nn, IN UINT16 nbit, IN UINT16 Special ) /*++ Routine Description: Reads code lengths for the Extra Set or the Position Set Arguments: Sd - The global scratch data nn - Number of symbols nbit - Number of bits needed to represent nn Special - The special symbol that needs to be taken care of Returns: 0 - OK. BAD_TABLE - Table is corrupted. --*/ { UINT16 Number; UINT16 CharC; UINT16 Index; UINT32 Mask; Number = (UINT16)GetBits(Sd, nbit); if ((Number > sizeof(Sd->mPTLen)) || (nn > sizeof(Sd->mPTLen))) { // // Fail if Number or nn is greater than size of mPTLen // return (UINT16)BAD_TABLE; } if (Number == 0) { CharC = (UINT16)GetBits(Sd, nbit); for (Index = 0; Index < 256; Index++) { Sd->mPTTable[Index] = CharC; } for (Index = 0; Index < nn; Index++) { Sd->mPTLen[Index] = 0; } return 0; } Index = 0; while (Index < Number) { CharC = (UINT16)(Sd->mBitBuf >> (BITBUFSIZ - 3)); if (CharC == 7) { Mask = 1U << (BITBUFSIZ - 1 - 3); while (Mask & Sd->mBitBuf) { Mask >>= 1; CharC += 1; } } FillBuf(Sd, (UINT16)((CharC < 7) ? 3 : CharC - 3)); Sd->mPTLen[Index++] = (UINT8)CharC; if (Index == Special) { CharC = (UINT16)GetBits(Sd, 2); while ((INT16)(--CharC) >= 0) { if (Index >= sizeof(Sd->mPTLen)) { // // Fail if Index is greater than or equal to mPTLen // return (UINT16)BAD_TABLE; } Sd->mPTLen[Index++] = 0; } } } while (Index < nn) { Sd->mPTLen[Index++] = 0; } return MakeTable(Sd, nn, Sd->mPTLen, 8, Sd->mPTTable); } STATIC VOID ReadCLen( SCRATCH_DATA *Sd ) /*++ Routine Description: Reads code lengths for Char&Len Set. Arguments: Sd - the global scratch data Returns: (VOID) --*/ { UINT16 Number; UINT16 CharC; UINT16 Index; UINT32 Mask; Number = (UINT16)GetBits(Sd, CBIT); if (Number == 0) { CharC = (UINT16)GetBits(Sd, CBIT); for (Index = 0; Index < NC; Index++) { Sd->mCLen[Index] = 0; } for (Index = 0; Index < 4096; Index++) { Sd->mCTable[Index] = CharC; } return; } Index = 0; while (Index < Number) { CharC = Sd->mPTTable[Sd->mBitBuf >> (BITBUFSIZ - 8)]; if (CharC >= NT) { Mask = 1U << (BITBUFSIZ - 1 - 8); do { if (Mask & Sd->mBitBuf) { CharC = Sd->mRight[CharC]; } else { CharC = Sd->mLeft[CharC]; } Mask >>= 1; } while (CharC >= NT); } // // Advance what we have read // FillBuf(Sd, Sd->mPTLen[CharC]); if (CharC <= 2) { if (CharC == 0) { CharC = 1; } else if (CharC == 1) { CharC = (UINT16)(GetBits(Sd, 4) + 3); } else if (CharC == 2) { CharC = (UINT16)(GetBits(Sd, CBIT) + 20); } while ((INT16)(--CharC) >= 0) { Sd->mCLen[Index++] = 0; } } else { Sd->mCLen[Index++] = (UINT8)(CharC - 2); } } while (Index < NC) { Sd->mCLen[Index++] = 0; } MakeTable(Sd, NC, Sd->mCLen, 12, Sd->mCTable); return; } STATIC UINT16 DecodeC( SCRATCH_DATA *Sd ) /*++ Routine Description: Decode a character/length value. Arguments: Sd - The global scratch data. Returns: The value decoded. --*/ { UINT16 Index2; UINT32 Mask; if (Sd->mBlockSize == 0) { // // Starting a new block // Sd->mBlockSize = (UINT16)GetBits(Sd, 16); Sd->mBadTableFlag = ReadPTLen(Sd, NT, TBIT, 3); if (Sd->mBadTableFlag != 0) { return 0; } ReadCLen(Sd); Sd->mBadTableFlag = ReadPTLen(Sd, MAXNP, Sd->mPBit, (UINT16)(-1)); if (Sd->mBadTableFlag != 0) { return 0; } } Sd->mBlockSize--; Index2 = Sd->mCTable[Sd->mBitBuf >> (BITBUFSIZ - 12)]; if (Index2 >= NC) { Mask = 1U << (BITBUFSIZ - 1 - 12); do { if (Sd->mBitBuf & Mask) { Index2 = Sd->mRight[Index2]; } else { Index2 = Sd->mLeft[Index2]; } Mask >>= 1; } while (Index2 >= NC); } // // Advance what we have read // FillBuf(Sd, Sd->mCLen[Index2]); return Index2; } STATIC VOID Decode( SCRATCH_DATA *Sd ) /*++ Routine Description: Decode the source data and put the resulting data into the destination buffer. Arguments: Sd - The global scratch data Returns: (VOID) --*/ { UINT16 BytesRemain; UINT32 DataIdx; UINT16 CharC; BytesRemain = (UINT16)(-1); DataIdx = 0; for (;;) { CharC = DecodeC(Sd); if (Sd->mBadTableFlag != 0) { return; } if (CharC < 256) { // // Process an Original character // if (Sd->mOutBuf >= Sd->mOrigSize) { return; } else { Sd->mDstBase[Sd->mOutBuf++] = (UINT8)CharC; } } else { // // Process a Pointer // CharC = (UINT16)(CharC - (UINT8_MAX + 1 - THRESHOLD)); BytesRemain = CharC; DataIdx = Sd->mOutBuf - DecodeP(Sd) - 1; // Check to prevent possible heap corruption if (DataIdx >= Sd->mOrigSize - BytesRemain) { Sd->mBadTableFlag = 1; return; } BytesRemain--; while ((INT16)(BytesRemain) >= 0) { Sd->mDstBase[Sd->mOutBuf++] = Sd->mDstBase[DataIdx++]; if (Sd->mOutBuf >= Sd->mOrigSize) { return; } BytesRemain--; } } } } EFI_STATUS GetInfo( IN const VOID *Source, IN UINT32 SrcSize, OUT UINT32 *DstSize, OUT UINT32 *ScratchSize ) /*++ Routine Description: The internal implementation of *_DECOMPRESS_PROTOCOL.GetInfo(). Arguments: Source - The source buffer containing the compressed data. SrcSize - The size of source buffer DstSize - The size of destination buffer. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successull retrieved. EFI_INVALID_PARAMETER - The source data is corrupted --*/ { const UINT8 *Src; *ScratchSize = sizeof(SCRATCH_DATA); Src = Source; if (SrcSize < 8) { return EFI_INVALID_PARAMETER; } *DstSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); return EFI_SUCCESS; } EFI_STATUS Decompress( IN const VOID *Source, IN UINT32 SrcSize, IN OUT VOID *Destination, IN UINT32 DstSize, IN OUT VOID *Scratch, IN UINT32 ScratchSize, IN UINT8 Version ) /*++ Routine Description: The internal implementation of *_DECOMPRESS_PROTOCOL.Decompress(). Arguments: Source - The source buffer containing the compressed data. SrcSize - The size of source buffer Destination - The destination buffer to store the decompressed data DstSize - The size of destination buffer. Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. ScratchSize - The size of scratch buffer. Version - The version of de/compression algorithm. Version 1 for EFI 1.1 de/compression algorithm. Version 2 for Tiano de/compression algorithm. Returns: EFI_SUCCESS - Decompression is successful EFI_INVALID_PARAMETER - The source data is corrupted --*/ { UINT32 Index; UINT32 CompSize; UINT32 OrigSize; EFI_STATUS Status; SCRATCH_DATA *Sd; const UINT8 *Src; UINT8 *Dst; Status = EFI_SUCCESS; Src = Source; Dst = Destination; if (ScratchSize < sizeof(SCRATCH_DATA)) { return EFI_INVALID_PARAMETER; } Sd = (SCRATCH_DATA *)Scratch; if (SrcSize < 8) { return EFI_INVALID_PARAMETER; } CompSize = Src[0] + (Src[1] << 8) + (Src[2] << 16) + (Src[3] << 24); OrigSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); // // If compressed file size is 0, return // if (OrigSize == 0) { return Status; } if (SrcSize < CompSize + 8) { return EFI_INVALID_PARAMETER; } if (DstSize != OrigSize) { return EFI_INVALID_PARAMETER; } Src = Src + 8; for (Index = 0; Index < sizeof(SCRATCH_DATA); Index++) { ((UINT8 *)Sd)[Index] = 0; } // // The length of the field 'Position Set Code Length Array Size' in Block Header. // For EFI 1.1 de/compression algorithm(Version 1), mPBit = 4 // For Tiano de/compression algorithm(Version 2), mPBit = 5 // switch (Version) { case 1: Sd->mPBit = 4; break; case 2: Sd->mPBit = 5; break; default: // // Currently, only have 2 versions // return EFI_INVALID_PARAMETER; } Sd->mSrcBase = (UINT8*)Src; Sd->mDstBase = Dst; Sd->mCompSize = CompSize; Sd->mOrigSize = OrigSize; // // Fill the first BITBUFSIZ bits // FillBuf(Sd, BITBUFSIZ); // // Decompress it // Decode(Sd); if (Sd->mBadTableFlag != 0) { // // Something wrong with the source // Status = EFI_INVALID_PARAMETER; } return Status; } EFI_STATUS EFIAPI EfiTianoGetInfo( IN const VOID *Source, IN UINT32 SrcSize, OUT UINT32 *DstSize, OUT UINT32 *ScratchSize ) /*++ Routine Description: The implementation of EFI_DECOMPRESS_PROTOCOL.GetInfo(). Arguments: This - The protocol instance pointer Source - The source buffer containing the compressed data. SrcSize - The size of source buffer DstSize - The size of destination buffer. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successful retrieved. EFI_INVALID_PARAMETER - The source data is corrupted --*/ { return GetInfo( Source, SrcSize, DstSize, ScratchSize ); } EFI_STATUS EFIAPI EfiDecompress( IN const VOID *Source, IN UINT32 SrcSize, IN OUT VOID *Destination, IN UINT32 DstSize, IN OUT VOID *Scratch, IN UINT32 ScratchSize ) /*++ Routine Description: The implementation of EFI_DECOMPRESS_PROTOCOL.Decompress(). Arguments: This - The protocol instance pointer Source - The source buffer containing the compressed data. SrcSize - The size of source buffer Destination - The destination buffer to store the decompressed data DstSize - The size of destination buffer. Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - Decompression is successful EFI_INVALID_PARAMETER - The source data is corrupted --*/ { // // For EFI 1.1 de/compression algorithm, the version is 1. // return Decompress( Source, SrcSize, Destination, DstSize, Scratch, ScratchSize, 1 ); } EFI_STATUS EFIAPI TianoDecompress( IN const VOID *Source, IN UINT32 SrcSize, IN OUT VOID *Destination, IN UINT32 DstSize, IN OUT VOID *Scratch, IN UINT32 ScratchSize ) /*++ Routine Description: The implementation of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). Arguments: This - The protocol instance pointer Source - The source buffer containing the compressed data. SrcSize - The size of source buffer Destination - The destination buffer to store the decompressed data DstSize - The size of destination buffer. Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - Decompression is successful EFI_INVALID_PARAMETER - The source data is corrupted --*/ { // // For Tiano de/compression algorithm, the version is 2. // return Decompress( Source, SrcSize, Destination, DstSize, Scratch, ScratchSize, 2 ); } UEFITool-0.28.0/Tiano/EfiTianoDecompress.h000066400000000000000000000070361363631100000201340ustar00rootroot00000000000000/* EfiTianoDecompress.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.
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. Module Name: Decompress.h Abstract: Header file for decompression routine. Providing both EFI and Tiano decompress algorithms. --*/ #ifndef EFITIANODECOMPRESS_H #define EFITIANODECOMPRESS_H #include #include #include "../basetypes.h" #ifdef __cplusplus extern "C" { #endif typedef struct EFI_TIANO_HEADER_ { UINT32 CompSize; UINT32 OrigSize; } EFI_TIANO_HEADER; EFI_STATUS EFIAPI EfiTianoGetInfo( const VOID *Source, UINT32 SrcSize, UINT32 *DstSize, UINT32 *ScratchSize ) /*++ Routine Description: The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.GetInfo(). Arguments: Source - The source buffer containing the compressed data. SrcSize - The size of source buffer DstSize - The size of destination buffer. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - The size of destination buffer and the size of scratch buffer are successfully retrieved. EFI_INVALID_PARAMETER - The source data is corrupted --*/ ; EFI_STATUS EFIAPI EfiDecompress( const VOID *Source, UINT32 SrcSize, VOID *Destination, UINT32 DstSize, VOID *Scratch, UINT32 ScratchSize ); /*++ Routine Description: The implementation is same as that of EFI_DECOMPRESS_PROTOCOL.Decompress(). Arguments: Source - The source buffer containing the compressed data. SrcSize - The size of source buffer Destination - The destination buffer to store the decompressed data DstSize - The size of destination buffer. Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - Decompression is successful EFI_INVALID_PARAMETER - The source data is corrupted --*/ ; EFI_STATUS EFIAPI TianoDecompress( const VOID *Source, UINT32 SrcSize, VOID *Destination, UINT32 DstSize, VOID *Scratch, UINT32 ScratchSize ) /*++ Routine Description: The implementation is same as that of EFI_TIANO_DECOMPRESS_PROTOCOL.Decompress(). Arguments: Source - The source buffer containing the compressed data. SrcSize - The size of source buffer Destination - The destination buffer to store the decompressed data DstSize - The size of destination buffer. Scratch - The buffer used internally by the decompress routine. This buffer is needed to store intermediate data. ScratchSize - The size of scratch buffer. Returns: EFI_SUCCESS - Decompression is successful EFI_INVALID_PARAMETER - The source data is corrupted --*/ ; #ifdef __cplusplus } #endif #endif // EFITIANODECOMPRESS_H UEFITool-0.28.0/UEFIPatch/000077500000000000000000000000001363631100000146705ustar00rootroot00000000000000UEFITool-0.28.0/UEFIPatch/patches-misc.txt000066400000000000000000000031561363631100000200160ustar00rootroot00000000000000# Patch string format # FileGuid SectionType PatchType:FindPatternOrOffset:ReplacePattern # Please ensure that the latest symbol in patch string is space # Possible section types: # PE32 image 10 # Position-independent code 11 # TE Image 12 # DXE Dependency 13 # Version information 14 # User interface string 15 # 16-bit code 16 # Guided freeform 18 # Raw data 19 # PEI Dependency 1B # SMM Dependency 1C # Please do not try another section types, it can make the resulting image broken # Possible patch types: # P - pattern-based, first parameter is a pattern to find, second - a pattern to replace # O - offset-based, first parameter is hexadecimal offset, second - a pattern to replace # Patterns can have . as "any possible value" symbol #---------------------------------------------------------------------------------- # Set IA32_FEATURE_CONTROL.SGX_LC #---------------------------------------------------------------------------------- # SiInit | Gemini Lake 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:B93A0000000F320D00000400:B93A0000000F320D00000600 #---------------------------------------------------------------------------------- # Broken EFI_SIMPLE_POINTER_PROTOCOL implementation patches # Fixes incorrect pointer position update/click condition on Z87 chips #---------------------------------------------------------------------------------- # UHCD | ASUS Z87-Pro 580DD900-385D-11D7-883A-00500473D4EB 10 P:807A3200745C807A4000:807A32007506807A4000 UEFITool-0.28.0/UEFIPatch/patches.txt000066400000000000000000000054431363631100000170660ustar00rootroot00000000000000# Patch string format # FileGuid SectionType PatchType:FindPatternOrOffset:ReplacePattern # Please ensure that the latest symbol in patch string is space # Possible section types: # PE32 image 10 # Position-independent code 11 # TE Image 12 # DXE Dependency 13 # Version information 14 # User interface string 15 # 16-bit code 16 # Guided freeform 18 # Raw data 19 # PEI Dependency 1B # SMM Dependency 1C # Please do not try another section types, it can make the resulting image broken # Possible patch types: # P - pattern-based, first parameter is a pattern to find, second - a pattern to replace # O - offset-based, first parameter is hexadecimal offset, second - a pattern to replace # Patterns can have . as "any possible value" symbol #---------------------------------------------------------------------------------- # OSX CPU Power Management patches # Remove lock from MSR 0xE2 register #---------------------------------------------------------------------------------- # PowerMgmtDxe | Haswell F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 # PowerMgmtDxe | Haswell-E F7731B4C-58A2-4DF4-8980-5645D39ECE58 10 P:0FBA6C24380F:0FBA7424380F # PowerManagement | Sandy Bridge with ME 8.xx, Ivy Bridge 8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:75080FBAE80F89442430:EB080FBAE80F89442430 # PowerManagement | New SB-E/IB-E 8C783970-F02A-4A4D-AF09-8797A51EEC8D 10 P:0FBA6C24380F:0FBA7424380F # CpuPei | Sandy Bridge with ME 7.xx, old SB-E/IB-E 2BB5AFA9-FF33-417B-8497-CB773C2B93BF 10 P:800018EB050D0080:000018EB050D0000 # PpmInitialize | Broadwell-E 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:0FBA6C24400F:0FBA7424400F # SiInit | Skylake 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:75080D00800000:EB080D00800000 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:75080D00800000:EB080D00800000 # SiInit | Kaby Lake 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 10 P:81E10080000033C1:9090909090909090 299D6F8B-2EC9-4E40-9EC6-DDAA7EBF5FD9 12 P:81E10080000033C1:9090909090909090 # PpmInitialize | Skylake-X 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:742CB9E2000000:752CB9E2000000 # CpuInitPei | Skylake-X 01359D99-9446-456D-ADA4-50A711C03ADA 12 P:BE0080000023CE0B:BE0000000023CE0B # PpmInitialize | 0xE2 & 0x1AA unlock for C422 Chipsets & Some X299 Chipsets 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:4889442430....0FBAE8:4889442430....0FBAE0 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:4889442450....0FBAE8:4889442450....0FBAE0 # PpmInitialize | 0xE2 & 0x1AA unlock for C620 Chipsets 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:480BC2BA00200000:480BC2BA00000000 3FFCAE95-23CF-4967-94F5-16352F68E43B 10 P:480BC2BA00800000:480BC2BA00000000UEFITool-0.28.0/UEFIPatch/uefipatch.cpp000066400000000000000000000155631363631100000173560ustar00rootroot00000000000000/* uefipatch.cpp Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #include "uefipatch.h" UEFIPatch::UEFIPatch(QObject *parent) : QObject(parent) { ffsEngine = new FfsEngine(this); model = ffsEngine->treeModel(); } UEFIPatch::~UEFIPatch() { delete ffsEngine; } UINT8 UEFIPatch::patchFromFile(const QString & path, const QString & patches, const QString & outputPath) { QFileInfo patchInfo = QFileInfo(patches); if (!patchInfo.exists()) return ERR_INVALID_FILE; QFile file; file.setFileName(patches); if (!file.open(QFile::ReadOnly | QFile::Text)) return ERR_INVALID_FILE; QFileInfo fileInfo = QFileInfo(path); if (!fileInfo.exists()) return ERR_FILE_OPEN; QFile inputFile; inputFile.setFileName(path); if (!inputFile.open(QFile::ReadOnly)) return ERR_FILE_READ; QByteArray buffer = inputFile.readAll(); inputFile.close(); UINT8 result = ffsEngine->parseImageFile(buffer); if (result) return result; UINT8 counter = 0; while (!file.atEnd()) { QByteArray line = file.readLine().trimmed(); // Use sharp sign as commentary if (line.count() == 0 || line[0] == '#') continue; QList list = line.split(' '); if (list.count() < 3) continue; QUuid uuid = QUuid(list.at(0)); QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID)); bool converted; UINT8 sectionType = (UINT8)list.at(1).toUShort(&converted, 16); if (!converted) return ERR_INVALID_PARAMETER; QVector patches; for (int i = 2; i < list.count(); i++) { QList patchList = list.at(i).split(':'); PatchData patch; patch.type = *(UINT8*)patchList.at(0).constData(); if (patch.type == PATCH_TYPE_PATTERN) { patch.offset = 0xFFFFFFFF; patch.hexFindPattern = patchList.at(1); patch.hexReplacePattern = patchList.at(2); patches.append(patch); } else if (patch.type == PATCH_TYPE_OFFSET) { patch.offset = patchList.at(1).toUInt(NULL, 16); patch.hexReplacePattern = patchList.at(2); patches.append(patch); } else { // Ignore unknown patch type continue; } } result = patchFile(model->index(0, 0), guid, sectionType, patches); if (result && result != ERR_NOTHING_TO_PATCH) return result; counter++; } QByteArray reconstructed; result = ffsEngine->reconstructImageFile(reconstructed); if (result) return result; if (reconstructed == buffer) return ERR_NOTHING_TO_PATCH; QFile outputFile; outputFile.setFileName(outputPath); if (!outputFile.open(QFile::WriteOnly)) return ERR_FILE_WRITE; outputFile.resize(0); outputFile.write(reconstructed); outputFile.close(); return ERR_SUCCESS; } UINT8 UEFIPatch::patchFromArg(const QString & path, const QString & patch, const QString & outputPath) { QFileInfo fileInfo = QFileInfo(path); if (!fileInfo.exists()) return ERR_FILE_OPEN; QFile inputFile; inputFile.setFileName(path); if (!inputFile.open(QFile::ReadOnly)) return ERR_FILE_READ; QByteArray buffer = inputFile.readAll(); inputFile.close(); UINT8 result = ffsEngine->parseImageFile(buffer); if (result) return result; if (patch != NULL && !patch.isEmpty()) { QByteArray line = patch.toUtf8(); QList list = line.split(' '); if (list.count() < 3) return ERR_INVALID_PARAMETER; QUuid uuid = QUuid(list.at(0)); QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID)); bool converted; UINT8 sectionType = (UINT8)list.at(1).toUShort(&converted, 16); if (!converted) return ERR_INVALID_PARAMETER; QVector patches; for (int i = 2; i < list.count(); i++) { QList patchList = list.at(i).split(':'); PatchData patch; patch.type = *(UINT8*)patchList.at(0).constData(); if (patch.type == PATCH_TYPE_PATTERN) { patch.offset = 0xFFFFFFFF; patch.hexFindPattern = patchList.at(1); patch.hexReplacePattern = patchList.at(2); patches.append(patch); } else if (patch.type == PATCH_TYPE_OFFSET) { patch.offset = patchList.at(1).toUInt(NULL, 16); patch.hexReplacePattern = patchList.at(2); patches.append(patch); } else { // Ignore unknown patch type continue; } } result = patchFile(model->index(0, 0), guid, sectionType, patches); if (result && result != ERR_NOTHING_TO_PATCH) return result; } QByteArray reconstructed; result = ffsEngine->reconstructImageFile(reconstructed); if (result) return result; if (reconstructed == buffer) return ERR_NOTHING_TO_PATCH; QFile outputFile; outputFile.setFileName(outputPath); if (!outputFile.open(QFile::WriteOnly)) return ERR_FILE_WRITE; outputFile.resize(0); outputFile.write(reconstructed); outputFile.close(); return ERR_SUCCESS; } UINT8 UEFIPatch::patchFile(const QModelIndex & index, const QByteArray & fileGuid, const UINT8 sectionType, const QVector & patches) { if (!model || !index.isValid()) return ERR_INVALID_PARAMETER; if (model->type(index) == Types::Section && model->subtype(index) == sectionType) { QModelIndex fileIndex = model->findParentOfType(index, Types::File); if (model->type(fileIndex) == Types::File && model->header(fileIndex).left(sizeof(EFI_GUID)) == fileGuid) { return ffsEngine->patch(index, patches); } } if (model->rowCount(index) > 0) { for (int i = 0; i < model->rowCount(index); i++) { UINT8 result = patchFile(index.child(i, 0), fileGuid, sectionType, patches); if (!result) break; else if (result != ERR_NOTHING_TO_PATCH) return result; } } return ERR_NOTHING_TO_PATCH; } UEFITool-0.28.0/UEFIPatch/uefipatch.h000066400000000000000000000023431363631100000170130ustar00rootroot00000000000000/* uefipatch.h Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __UEFIPATCH_H__ #define __UEFIPATCH_H__ #include #include #include #include #include #include #include "../basetypes.h" #include "../ffs.h" #include "../ffsengine.h" class UEFIPatch : public QObject { Q_OBJECT public: explicit UEFIPatch(QObject *parent = 0); ~UEFIPatch(); UINT8 patchFromFile(const QString & path, const QString & patches, const QString & outputPath); UINT8 patchFromArg(const QString & path, const QString & patch, const QString & outputPath); private: UINT8 patchFile(const QModelIndex & index, const QByteArray & fileGuid, const UINT8 sectionType, const QVector & patches); FfsEngine* ffsEngine; TreeModel* model; }; #endif UEFITool-0.28.0/UEFIPatch/uefipatch.pro000066400000000000000000000016211363631100000173620ustar00rootroot00000000000000QT += core QT -= gui TARGET = UEFIPatch TEMPLATE = app CONFIG += console CONFIG -= app_bundle DEFINES += _CONSOLE SOURCES += uefipatch_main.cpp \ uefipatch.cpp \ ../types.cpp \ ../descriptor.cpp \ ../ffs.cpp \ ../ffsengine.cpp \ ../peimage.cpp \ ../treeitem.cpp \ ../treemodel.cpp \ ../LZMA/LzmaCompress.c \ ../LZMA/LzmaDecompress.c \ ../LZMA/SDK/C/LzFind.c \ ../LZMA/SDK/C/LzmaDec.c \ ../LZMA/SDK/C/LzmaEnc.c \ ../LZMA/SDK/C/Bra86.c \ ../Tiano/EfiTianoDecompress.c \ ../Tiano/EfiTianoCompress.c \ ../Tiano/EfiTianoCompressLegacy.c HEADERS += uefipatch.h \ ../basetypes.h \ ../descriptor.h \ ../gbe.h \ ../me.h \ ../ffs.h \ ../peimage.h \ ../types.h \ ../ffsengine.h \ ../treeitem.h \ ../treemodel.h \ ../version.h \ ../LZMA/LzmaCompress.h \ ../LZMA/LzmaDecompress.h \ ../LZMA/x86Convert.h \ ../Tiano/EfiTianoDecompress.h \ ../Tiano/EfiTianoCompress.h UEFITool-0.28.0/UEFIPatch/uefipatch_main.cpp000066400000000000000000000070701363631100000203540ustar00rootroot00000000000000/* uefipatch_main.cpp Copyright (c) 2018, LongSoft. All rights reserved. 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. */ #include #include #include #include #include #include "../version.h" #include "uefipatch.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setOrganizationName("LongSoft"); a.setOrganizationDomain("longsoft.me"); a.setApplicationName("UEFIPatch"); UEFIPatch w; UINT8 result = ERR_SUCCESS; const QStringList &args = a.arguments(); UINT32 argumentsCount = args.length(); if (argumentsCount < 2) { std::cout << "UEFIPatch " PROGRAM_VERSION " - UEFI image file patching utility" << std::endl << std::endl << "Usage: UEFIPatch image_file [patches.txt] [-o output]" << std::endl << "Usage: UEFIPatch image_file [-p \"Guid SectionType Patch\"] [-o output]" << std::endl << std::endl << "Patches will be read from patches.txt file by default\n"; return ERR_SUCCESS; } QString inputPath = a.arguments().at(1); QString patches = "patches.txt"; QString outputPath = inputPath + ".patched"; int patchFrom = PATCH_FROM_FILE; for (UINT32 i = 2; i < argumentsCount; i++) { if ((args.at(i) == "-p" || args.at(i) == "--patch") && i + 1 < argumentsCount) { patchFrom = PATCH_FROM_ARG; patches = args.at(i+1); i++; } else if ((args.at(i) == "-o" || args.at(i) == "--output") && i + 1 < argumentsCount) { outputPath = args.at(i+1); i++; } else if (patches == "patches.txt") { patches = args.at(i); } else { result = ERR_INVALID_PARAMETER; } } if (result == ERR_SUCCESS) { if (patchFrom == PATCH_FROM_FILE) { result = w.patchFromFile(inputPath, patches, outputPath); } else if (patchFrom == PATCH_FROM_ARG) { result = w.patchFromArg(inputPath, patches, outputPath); } } switch (result) { case ERR_SUCCESS: std::cout << "Image patched" << std::endl; break; case ERR_INVALID_PARAMETER: std::cout << "Function called with invalid parameter" << std::endl; break; case ERR_NOTHING_TO_PATCH: std::cout << "No patches can be applied to input file" << std::endl; break; case ERR_UNKNOWN_PATCH_TYPE: std::cout << "Unknown patch type" << std::endl; break; case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: std::cout << "Patch offset out of bounds" << std::endl; break; case ERR_INVALID_SYMBOL: std::cout << "Pattern format mismatch" << std::endl; break; case ERR_INVALID_FILE: std::cout << patches.toStdString() << " file not found or can't be read" << std::endl; break; case ERR_FILE_OPEN: std::cout << "Input file not found" << std::endl; break; case ERR_FILE_READ: std::cout << "Input file can't be read" << std::endl; break; case ERR_FILE_WRITE: std::cout << "Output file can't be written" << std::endl; break; default: std::cout << "Error " << result << std::endl; } return result; } UEFITool-0.28.0/UEFIReplace/000077500000000000000000000000001363631100000152045ustar00rootroot00000000000000UEFITool-0.28.0/UEFIReplace/uefireplace.cpp000066400000000000000000000075041363631100000202020ustar00rootroot00000000000000/* uefireplace.cpp Copyright (c) 2017, mxxxc. All rights reserved. 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. */ #include "uefireplace.h" UEFIReplace::UEFIReplace(QObject *parent) : QObject(parent) { ffsEngine = new FfsEngine(this); model = ffsEngine->treeModel(); } UEFIReplace::~UEFIReplace() { delete ffsEngine; } UINT8 UEFIReplace::replace(const QString & inPath, const QByteArray & guid, const UINT8 sectionType, const QString & contentPath, const QString & outPath, bool replaceAsIs, bool replaceOnce) { QFileInfo fileInfo = QFileInfo(inPath); if (!fileInfo.exists()) return ERR_FILE_OPEN; fileInfo = QFileInfo(contentPath); if (!fileInfo.exists()) return ERR_FILE_OPEN; QFile inputFile; inputFile.setFileName(inPath); if (!inputFile.open(QFile::ReadOnly)) return ERR_FILE_READ; QByteArray buffer = inputFile.readAll(); inputFile.close(); UINT8 result = ffsEngine->parseImageFile(buffer); if (result) return result; QFile contentFile; contentFile.setFileName(contentPath); if (!contentFile.open(QFile::ReadOnly)) return ERR_FILE_READ; QByteArray contents = contentFile.readAll(); contentFile.close(); result = replaceInFile(model->index(0, 0), guid, sectionType, contents, replaceAsIs ? REPLACE_MODE_AS_IS : REPLACE_MODE_BODY, replaceOnce); if (result) return result; QByteArray reconstructed; result = ffsEngine->reconstructImageFile(reconstructed); if (result) return result; if (reconstructed == buffer) return ERR_NOTHING_TO_PATCH; QFile outputFile; outputFile.setFileName(outPath); if (!outputFile.open(QFile::WriteOnly)) return ERR_FILE_WRITE; outputFile.resize(0); outputFile.write(reconstructed); outputFile.close(); return ERR_SUCCESS; } UINT8 UEFIReplace::replaceInFile(const QModelIndex & index, const QByteArray & guid, const UINT8 sectionType, const QByteArray & newData, const UINT8 mode, bool replaceOnce) { if (!model || !index.isValid()) return ERR_INVALID_PARAMETER; bool patched = false; if (model->subtype(index) == sectionType) { QModelIndex fileIndex = index; if (model->type(index) != Types::File) fileIndex = model->findParentOfType(index, Types::File); QByteArray fileGuid = model->header(fileIndex).left(sizeof(EFI_GUID)); bool guidMatch = fileGuid == guid; if (!guidMatch && sectionType == EFI_SECTION_FREEFORM_SUBTYPE_GUID) { QByteArray subGuid = model->header(index).mid(sizeof(UINT32), sizeof(EFI_GUID)); guidMatch = subGuid == guid; } if (guidMatch && model->action(index) != Actions::Replace) { UINT8 result = ffsEngine->replace(index, newData, mode); if (replaceOnce || (result != ERR_SUCCESS && result != ERR_NOTHING_TO_PATCH)) return result; patched = result == ERR_SUCCESS; } } if (model->rowCount(index) > 0) { for (int i = 0; i < model->rowCount(index); i++) { UINT8 result = replaceInFile(index.child(i, 0), guid, sectionType, newData, mode, replaceOnce); if (result == ERR_SUCCESS) { patched = true; if (replaceOnce) break; } else if (result != ERR_NOTHING_TO_PATCH) { return result; } } } return patched ? ERR_SUCCESS : ERR_NOTHING_TO_PATCH; } UEFITool-0.28.0/UEFIReplace/uefireplace.h000066400000000000000000000023631363631100000176450ustar00rootroot00000000000000/* uefireplace.h Copyright (c) 2017, mxxxc. All rights reserved. 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. */ #ifndef __UEFIREPLACE_H__ #define __UEFIREPLACE_H__ #include #include #include #include #include #include #include "../basetypes.h" #include "../ffs.h" #include "../ffsengine.h" class UEFIReplace : public QObject { Q_OBJECT public: explicit UEFIReplace(QObject *parent = 0); ~UEFIReplace(); UINT8 replace(const QString & inPath, const QByteArray & guid, const UINT8 sectionType, const QString & contentPath, const QString & outPath, bool replaceAsIs, bool replaceOnce); private: UINT8 replaceInFile(const QModelIndex & index, const QByteArray & guid, const UINT8 sectionType, const QByteArray & contents, const UINT8 mode, bool replaceOnce); FfsEngine* ffsEngine; TreeModel* model; }; #endif UEFITool-0.28.0/UEFIReplace/uefireplace.pro000066400000000000000000000016261363631100000202170ustar00rootroot00000000000000QT += core QT -= gui TARGET = UEFIReplace TEMPLATE = app CONFIG += console CONFIG -= app_bundle DEFINES += _CONSOLE SOURCES += uefireplace_main.cpp \ uefireplace.cpp \ ../types.cpp \ ../descriptor.cpp \ ../ffs.cpp \ ../ffsengine.cpp \ ../peimage.cpp \ ../treeitem.cpp \ ../treemodel.cpp \ ../LZMA/LzmaCompress.c \ ../LZMA/LzmaDecompress.c \ ../LZMA/SDK/C/LzFind.c \ ../LZMA/SDK/C/LzmaDec.c \ ../LZMA/SDK/C/LzmaEnc.c \ ../LZMA/SDK/C/Bra86.c \ ../Tiano/EfiTianoDecompress.c \ ../Tiano/EfiTianoCompress.c \ ../Tiano/EfiTianoCompressLegacy.c HEADERS += uefireplace.h \ ../basetypes.h \ ../descriptor.h \ ../gbe.h \ ../me.h \ ../ffs.h \ ../peimage.h \ ../types.h \ ../ffsengine.h \ ../treeitem.h \ ../treemodel.h \ ../version.h \ ../LZMA/LzmaCompress.h \ ../LZMA/LzmaDecompress.h \ ../LZMA/x86Convert.h \ ../Tiano/EfiTianoDecompress.h \ ../Tiano/EfiTianoCompress.hUEFITool-0.28.0/UEFIReplace/uefireplace_main.cpp000066400000000000000000000064321363631100000212050ustar00rootroot00000000000000/* uefireplace_main.cpp Copyright (c) 2017, mxxxc. All rights reserved. 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. */ #include #include #include #include #include "../version.h" #include "uefireplace.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); a.setOrganizationName("LongSoft"); a.setOrganizationDomain("longsoft.me"); a.setApplicationName("UEFIReplace"); UEFIReplace r; UINT8 result = ERR_SUCCESS; QStringList args = a.arguments(); if (args.length() < 5) { std::cout << "UEFIReplace " PROGRAM_VERSION " - UEFI image file replacement utility" << std::endl << std::endl << "Usage: UEFIReplace image_file guid section_type contents_file [-o output] [-all] [-asis]" << std::endl; return ERR_SUCCESS; } QUuid uuid = QUuid(args.at(2)); QByteArray guid = QByteArray::fromRawData((const char*)&uuid.data1, sizeof(EFI_GUID)); bool converted; UINT8 sectionType = (UINT8)args.at(3).toUShort(&converted, 16); if (!converted) { result = ERR_INVALID_PARAMETER; } else { QString output = args.at(1) + ".patched"; bool replaceOnce = true; bool replaceAsIs = false; for (int i = 5, sz = args.size(); i < sz; i++) { if ((args.at(i) == "-o" || args.at(i) == "--output") && i + 1 < sz) { output = args.at(i+1); i++; } else if (args.at(i) == "-all") { replaceOnce = false; } else if (args.at(i) == "-asis") { replaceAsIs = true; } else { result = ERR_INVALID_PARAMETER; } } if (result == ERR_SUCCESS) { result = r.replace(args.at(1), guid, sectionType, args.at(4), output, replaceAsIs, replaceOnce); } } switch (result) { case ERR_SUCCESS: std::cout << "File replaced" << std::endl; break; case ERR_INVALID_PARAMETER: std::cout << "Function called with invalid parameter" << std::endl; break; case ERR_INVALID_FILE: std::cout << "Invalid/corrupted file specified" << std::endl; break; case ERR_INVALID_SECTION: std::cout << "Invalid/corrupted section specified" << std::endl; break; case ERR_NOTHING_TO_PATCH: std::cout << "No replacements can be applied to input file" << std::endl; break; case ERR_NOT_IMPLEMENTED: std::cout << "Can't replace body of this section type" << std::endl; break; case ERR_FILE_OPEN: std::cout << "Input file not found" << std::endl; break; case ERR_FILE_READ: std::cout << "Input file can't be read" << std::endl; break; case ERR_FILE_WRITE: std::cout << "Output file can't be written" << std::endl; break; default: std::cout << "Error " << result << std::endl; } return result; } UEFITool-0.28.0/basetypes.h000066400000000000000000000116161363631100000153350ustar00rootroot00000000000000/* basetypes.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __BASETYPES_H__ #define __BASETYPES_H__ #include #include #include typedef uint8_t BOOLEAN; typedef int8_t INT8; typedef uint8_t UINT8; typedef int16_t INT16; typedef uint16_t UINT16; typedef int32_t INT32; typedef uint32_t UINT32; typedef int64_t INT64; typedef uint64_t UINT64; typedef char CHAR8; typedef uint16_t CHAR16; typedef size_t UINTN; #define CONST const #define VOID void #define STATIC static #ifndef TRUE #define TRUE ((BOOLEAN)(1==1)) #endif #ifndef FALSE #define FALSE ((BOOLEAN)(0==1)) #endif #define ERR_SUCCESS 0 #define ERR_INVALID_PARAMETER 1 #define ERR_BUFFER_TOO_SMALL 2 #define ERR_OUT_OF_RESOURCES 3 #define ERR_OUT_OF_MEMORY 4 #define ERR_FILE_OPEN 5 #define ERR_FILE_READ 6 #define ERR_FILE_WRITE 7 #define ERR_ITEM_NOT_FOUND 8 #define ERR_UNKNOWN_ITEM_TYPE 9 #define ERR_INVALID_FLASH_DESCRIPTOR 10 #define ERR_INVALID_REGION 11 #define ERR_EMPTY_REGION 12 #define ERR_BIOS_REGION_NOT_FOUND 13 #define ERR_VOLUMES_NOT_FOUND 14 #define ERR_INVALID_VOLUME 15 #define ERR_VOLUME_REVISION_NOT_SUPPORTED 16 #define ERR_VOLUME_GROW_FAILED 17 #define ERR_UNKNOWN_FFS 18 #define ERR_INVALID_FILE 19 #define ERR_INVALID_SECTION 20 #define ERR_UNKNOWN_SECTION 21 #define ERR_STANDARD_COMPRESSION_FAILED 22 #define ERR_CUSTOMIZED_COMPRESSION_FAILED 23 #define ERR_STANDARD_DECOMPRESSION_FAILED 24 #define ERR_CUSTOMIZED_DECOMPRESSION_FAILED 25 #define ERR_UNKNOWN_COMPRESSION_ALGORITHM 26 #define ERR_UNKNOWN_EXTRACT_MODE 27 #define ERR_UNKNOWN_INSERT_MODE 28 #define ERR_UNKNOWN_IMAGE_TYPE 29 #define ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE 30 #define ERR_UNKNOWN_RELOCATION_TYPE 31 #define ERR_GENERIC_CALL_NOT_SUPPORTED 32 #define ERR_VOLUME_BASE_NOT_FOUND 33 #define ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND 34 #define ERR_COMPLEX_BLOCK_MAP 35 #define ERR_DIR_ALREADY_EXIST 36 #define ERR_DIR_CREATE 37 #define ERR_UNKNOWN_PATCH_TYPE 38 #define ERR_PATCH_OFFSET_OUT_OF_BOUNDS 39 #define ERR_INVALID_SYMBOL 40 #define ERR_NOTHING_TO_PATCH 41 #define ERR_DEPEX_PARSE_FAILED 42 #define ERR_TRUNCATED_IMAGE 43 #define ERR_BAD_RELOCATION_ENTRY 44 #define ERR_NOT_IMPLEMENTED 0xFF // UDK porting definitions #define IN #define OUT #define EFIAPI #define EFI_STATUS UINTN #define EFI_SUCCESS ERR_SUCCESS #define EFI_INVALID_PARAMETER ERR_INVALID_PARAMETER #define EFI_OUT_OF_RESOURCES ERR_OUT_OF_RESOURCES #define EFI_BUFFER_TOO_SMALL ERR_BUFFER_TOO_SMALL #define EFI_ERROR(X) (X) // Compression algorithms #define COMPRESSION_ALGORITHM_UNKNOWN 0 #define COMPRESSION_ALGORITHM_NONE 1 #define COMPRESSION_ALGORITHM_EFI11 2 #define COMPRESSION_ALGORITHM_TIANO 3 #define COMPRESSION_ALGORITHM_LZMA 4 #define COMPRESSION_ALGORITHM_IMLZMA 5 // Item create modes #define CREATE_MODE_APPEND 0 #define CREATE_MODE_PREPEND 1 #define CREATE_MODE_BEFORE 2 #define CREATE_MODE_AFTER 3 // Item extract modes #define EXTRACT_MODE_AS_IS 0 #define EXTRACT_MODE_BODY 1 // Item replace modes #define REPLACE_MODE_AS_IS 0 #define REPLACE_MODE_BODY 1 // Item patch modes #define PATCH_MODE_HEADER 0 #define PATCH_MODE_BODY 1 // Patch from #define PATCH_FROM_FILE 0 #define PATCH_FROM_ARG 1 // Patch types #define PATCH_TYPE_OFFSET 'O' #define PATCH_TYPE_PATTERN 'P' // Erase polarity types #define ERASE_POLARITY_FALSE 0 #define ERASE_POLARITY_TRUE 1 #define ERASE_POLARITY_UNKNOWN 0xFF // Search modes #define SEARCH_MODE_HEADER 1 #define SEARCH_MODE_BODY 2 #define SEARCH_MODE_ALL 3 // EFI GUID typedef struct _EFI_GUID { UINT8 Data[16]; } EFI_GUID; #define ALIGN4(Value) (((Value)+3) & ~3) #define ALIGN8(Value) (((Value)+7) & ~7) #include #define ASSERT(x) assert(x) //Hexarg macros #define hexarg(X) arg(QString("%1").arg((X),0,16).toUpper()) #define hexarg2(X, Y) arg(QString("%1").arg((X),(Y),16,QChar('0')).toUpper()) #endif UEFITool-0.28.0/descriptor.cpp000066400000000000000000000023241363631100000160430ustar00rootroot00000000000000/* descriptor.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include #include "descriptor.h" // Calculate address of data structure addressed by descriptor address format // 8 bit base or limit const UINT8* calculateAddress8(const UINT8* baseAddress, const UINT8 baseOrLimit) { return baseAddress + baseOrLimit * 0x10; } // 16 bit base or limit const UINT8* calculateAddress16(const UINT8* baseAddress, const UINT16 baseOrLimit) { return baseAddress + baseOrLimit * 0x1000; } // Calculate offset of region using its base UINT32 calculateRegionOffset(const UINT16 base) { return base * 0x1000; } //Calculate size of region using its base and limit UINT32 calculateRegionSize(const UINT16 base, const UINT16 limit) { if (limit) return (limit + 1 - base) * 0x1000; return 0; }UEFITool-0.28.0/descriptor.h000066400000000000000000000200061363631100000155050ustar00rootroot00000000000000/* descriptor.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef __DESCRIPTOR_H__ #define __DESCRIPTOR_H__ #include #include "basetypes.h" // Make sure we use right packing rules #pragma pack(push, 1) // Flash descriptor header typedef struct _FLASH_DESCRIPTOR_HEADER { UINT8 FfVector[16]; // Must be 16 0xFFs UINT32 Signature; // 0x0FF0A55A } FLASH_DESCRIPTOR_HEADER; // Flash descriptor signature #define FLASH_DESCRIPTOR_SIGNATURE 0x0FF0A55A // Descriptor region size #define FLASH_DESCRIPTOR_SIZE 0x1000 // Descriptor version was reserved in older firmware #define FLASH_DESCRIPTOR_VERSION_INVALID 0xFFFFFFFF // The only known version found in Coffee Lake #define FLASH_DESCRIPTOR_VERSION_MAJOR 1 #define FLASH_DESCRIPTOR_VERSION_MINOR 0 // Descriptor version present in Coffee Lake and newer typedef struct _FLASH_DESCRIPTOR_VERSION { UINT32 Reserved : 14; UINT32 Minor : 7; UINT32 Major : 11; } FLASH_DESCRIPTOR_VERSION; // Descriptor map // Base fields are storing bits [11:4] of actual base addresses, all other bits are 0 typedef struct _FLASH_DESCRIPTOR_MAP { // FLMAP0 UINT32 ComponentBase : 8; UINT32 NumberOfFlashChips : 2; // Zero-based number of flash chips installed on board UINT32 : 6; UINT32 RegionBase : 8; UINT32 NumberOfRegions : 3; // Reserved in v2 descriptor UINT32 : 5; // FLMAP 1 UINT32 MasterBase : 8; UINT32 NumberOfMasters : 2; // Zero-based number of flash masters UINT32 : 6; UINT32 PchStrapsBase : 8; UINT32 NumberOfPchStraps : 8; // One-based number of UINT32s to read as PCH straps, min=0, max=255 (1 Kb) // FLMAP 2 UINT32 ProcStrapsBase : 8; UINT32 NumberOfProcStraps : 8; // One-based number of UINT32s to read as processor straps, min=0, max=255 (1 Kb) UINT32: 16; // FLMAP 3 UINT32 DescriptorVersion; // Reserved prior to Coffee Lake } FLASH_DESCRIPTOR_MAP; #define FLASH_DESCRIPTOR_MAX_BASE 0xE0 // Component section // Flash parameters DWORD structure typedef struct _FLASH_PARAMETERS { UINT8 FirstChipDensity : 4; UINT8 SecondChipDensity : 4; UINT8 : 8; UINT8 : 1; UINT8 ReadClockFrequency : 3; // Hardcoded value of 20 Mhz (000b) in v1 descriptors and 17 Mhz (110b) in v2 ones UINT8 FastReadEnabled : 1; UINT8 FastReadFrequency : 3; UINT8 FlashWriteFrequency : 3; UINT8 FlashReadStatusFrequency : 3; UINT8 DualOutputFastReadSupported : 1; UINT8 : 1; } FLASH_PARAMETERS; // Flash densities #define FLASH_DENSITY_512KB 0x00 #define FLASH_DENSITY_1MB 0x01 #define FLASH_DENSITY_2MB 0x02 #define FLASH_DENSITY_4MB 0x03 #define FLASH_DENSITY_8MB 0x04 #define FLASH_DENSITY_16MB 0x05 #define FLASH_DENSITY_32MB 0x06 #define FLASH_DENSITY_64MB 0x07 #define FLASH_DENSITY_UNUSED 0x0F // Flash frequencies #define FLASH_FREQUENCY_20MHZ 0x00 #define FLASH_FREQUENCY_33MHZ 0x01 #define FLASH_FREQUENCY_48MHZ 0x02 #define FLASH_FREQUENCY_50MHZ_30MHZ 0x04 #define FLASH_FREQUENCY_17MHZ 0x06 // Component section structure typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION { FLASH_PARAMETERS FlashParameters; UINT8 InvalidInstruction0; // Instructions for SPI chip, that must not be executed, like FLASH ERASE UINT8 InvalidInstruction1; // UINT8 InvalidInstruction2; // UINT8 InvalidInstruction3; // } FLASH_DESCRIPTOR_COMPONENT_SECTION; // Component section structure typedef struct _FLASH_DESCRIPTOR_COMPONENT_SECTION_V2 { FLASH_PARAMETERS FlashParameters; UINT8 InvalidInstruction0; // Instructions for SPI chip, that must not be executed, like FLASH ERASE UINT8 InvalidInstruction1; // UINT8 InvalidInstruction2; // UINT8 InvalidInstruction3; // UINT8 InvalidInstruction4; // UINT8 InvalidInstruction5; // UINT8 InvalidInstruction6; // UINT8 InvalidInstruction7; // } FLASH_DESCRIPTOR_COMPONENT_SECTION_V2; // Region section // All base and limit register are storing upper part of actual UINT32 base and limit // If limit is zero - region is not present typedef struct _FLASH_DESCRIPTOR_REGION_SECTION { UINT16 DescriptorBase; // Descriptor UINT16 DescriptorLimit; // UINT16 BiosBase; // BIOS UINT16 BiosLimit; // UINT16 MeBase; // ME UINT16 MeLimit; // UINT16 GbeBase; // GbE UINT16 GbeLimit; // UINT16 PdrBase; // PDR UINT16 PdrLimit; // UINT16 Region5Base; // Reserved region UINT16 Region5Limit; // UINT16 Region6Base; // Reserved region UINT16 Region6Limit; // UINT16 Region7Base; // Reserved region UINT16 Region7Limit; // UINT16 EcBase; // EC UINT16 EcLimit; // } FLASH_DESCRIPTOR_REGION_SECTION; // Master section typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION { UINT16 BiosId; UINT8 BiosRead; UINT8 BiosWrite; UINT16 MeId; UINT8 MeRead; UINT8 MeWrite; UINT16 GbeId; UINT8 GbeRead; UINT8 GbeWrite; } FLASH_DESCRIPTOR_MASTER_SECTION; // Master section v2 (Skylake+) typedef struct _FLASH_DESCRIPTOR_MASTER_SECTION_V2 { UINT32 : 8; UINT32 BiosRead : 12; UINT32 BiosWrite : 12; UINT32 : 8; UINT32 MeRead : 12; UINT32 MeWrite : 12; UINT32 : 8; UINT32 GbeRead : 12; UINT32 GbeWrite : 12; UINT32 :32; UINT32 : 8; UINT32 EcRead : 12; UINT32 EcWrite : 12; } FLASH_DESCRIPTOR_MASTER_SECTION_V2; // Region access bits in master section #define FLASH_DESCRIPTOR_REGION_ACCESS_DESC 0x01 #define FLASH_DESCRIPTOR_REGION_ACCESS_BIOS 0x02 #define FLASH_DESCRIPTOR_REGION_ACCESS_ME 0x04 #define FLASH_DESCRIPTOR_REGION_ACCESS_GBE 0x08 #define FLASH_DESCRIPTOR_REGION_ACCESS_PDR 0x10 #define FLASH_DESCRIPTOR_REGION_ACCESS_EC 0x20 // Base address of descriptor upper map #define FLASH_DESCRIPTOR_UPPER_MAP_BASE 0x0EFC // Descriptor upper map structure typedef struct _FLASH_DESCRIPTOR_UPPER_MAP { UINT8 VsccTableBase; // Base address of VSCC Table for ME, bits [11:4] UINT8 VsccTableSize; // Counted in UINT32s UINT16 ReservedZero; // Still unknown, zero in all descriptors I have seen } FLASH_DESCRIPTOR_UPPER_MAP; // VSCC table entry structure typedef struct _VSCC_TABLE_ENTRY { UINT8 VendorId; // JEDEC VendorID byte UINT8 DeviceId0; // JEDEC DeviceID first byte UINT8 DeviceId1; // JEDEC DeviceID second byte UINT8 ReservedZero; // Reserved, must be zero UINT32 VsccRegisterValue; // VSCC register value } VSCC_TABLE_ENTRY; // Base address and size of OEM section #define FLASH_DESCRIPTOR_OEM_SECTION_BASE 0x0F00 #define FLASH_DESCRIPTOR_OEM_SECTION_SIZE 0x100 // Restore previous packing rules #pragma pack(pop) // Calculate address of data structure addressed by descriptor address format // 8 bit base or limit extern const UINT8* calculateAddress8(const UINT8* baseAddress, const UINT8 baseOrLimit); // 16 bit base or limit extern const UINT8* calculateAddress16(const UINT8* baseAddress, const UINT16 baseOrLimit); // Calculate offset of region using it's base extern UINT32 calculateRegionOffset(const UINT16 base); // Calculate size of region using it's base and limit extern UINT32 calculateRegionSize(const UINT16 base, const UINT16 limit); #endif UEFITool-0.28.0/ffs.cpp000066400000000000000000000233741363631100000144530ustar00rootroot00000000000000/* ffs.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include #include "ffs.h" const QVector FFSv2Volumes = QVector() << EFI_FIRMWARE_FILE_SYSTEM_GUID << EFI_FIRMWARE_FILE_SYSTEM2_GUID << EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM_GUID << EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM2_GUID << EFI_INTEL_FILE_SYSTEM_GUID << EFI_INTEL_FILE_SYSTEM2_GUID << EFI_SONY_FILE_SYSTEM_GUID; const QVector FFSv3Volumes = QVector() << EFI_FIRMWARE_FILE_SYSTEM3_GUID; const UINT8 ffsAlignmentTable[] = { 0, 4, 7, 9, 10, 12, 15, 16 }; const UINT8 ffsAlignment2Table[] = { 17, 18, 19, 20, 21, 22, 23, 24 }; UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize) { if (!buffer) return 0; UINT8 counter = 0; while (bufferSize--) counter += buffer[bufferSize]; return counter; } UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize) { if (!buffer) return 0; return (UINT8)0x100 - calculateSum8(buffer, bufferSize); } 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; } VOID uint32ToUint24(UINT32 size, UINT8* ffsSize) { ffsSize[2] = (UINT8)((size) >> 16); ffsSize[1] = (UINT8)((size) >> 8); ffsSize[0] = (UINT8)((size)); } UINT32 uint24ToUint32(const UINT8* ffsSize) { return (ffsSize[2] << 16) + (ffsSize[1] << 8) + ffsSize[0]; } QString guidToQString(const EFI_GUID& guid) { QByteArray baGuid = QByteArray::fromRawData((const char*)guid.Data, sizeof(EFI_GUID)); const UINT32 i32 = *(const UINT32*)baGuid.left(4).constData(); const UINT16 i16_0 = *(const UINT16*)baGuid.mid(4, 2).constData(); const UINT16 i16_1 = *(const UINT16*)baGuid.mid(6, 2).constData(); const UINT8 i8_0 = *(const UINT8*)baGuid.mid(8, 1).constData(); const UINT8 i8_1 = *(const UINT8*)baGuid.mid(9, 1).constData(); const UINT8 i8_2 = *(const UINT8*)baGuid.mid(10, 1).constData(); const UINT8 i8_3 = *(const UINT8*)baGuid.mid(11, 1).constData(); const UINT8 i8_4 = *(const UINT8*)baGuid.mid(12, 1).constData(); const UINT8 i8_5 = *(const UINT8*)baGuid.mid(13, 1).constData(); const UINT8 i8_6 = *(const UINT8*)baGuid.mid(14, 1).constData(); const UINT8 i8_7 = *(const UINT8*)baGuid.mid(15, 1).constData(); return QString("%1-%2-%3-%4%5-%6%7%8%9%10%11") .arg(i32, 8, 16, QChar('0')) .arg(i16_0, 4, 16, QChar('0')) .arg(i16_1, 4, 16, QChar('0')) .arg(i8_0, 2, 16, QChar('0')) .arg(i8_1, 2, 16, QChar('0')) .arg(i8_2, 2, 16, QChar('0')) .arg(i8_3, 2, 16, QChar('0')) .arg(i8_4, 2, 16, QChar('0')) .arg(i8_5, 2, 16, QChar('0')) .arg(i8_6, 2, 16, QChar('0')) .arg(i8_7, 2, 16, QChar('0')).toUpper(); } QString fileTypeToQString(const UINT8 type) { switch (type) { case EFI_FV_FILETYPE_RAW: return QObject::tr("Raw"); case EFI_FV_FILETYPE_FREEFORM: return QObject::tr("Freeform"); case EFI_FV_FILETYPE_SECURITY_CORE: return QObject::tr("SEC core"); case EFI_FV_FILETYPE_PEI_CORE: return QObject::tr("PEI core"); case EFI_FV_FILETYPE_DXE_CORE: return QObject::tr("DXE core"); case EFI_FV_FILETYPE_PEIM: return QObject::tr("PEI module"); case EFI_FV_FILETYPE_DRIVER: return QObject::tr("DXE driver"); case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: return QObject::tr("Combined PEI/DXE"); case EFI_FV_FILETYPE_APPLICATION: return QObject::tr("Application"); case EFI_FV_FILETYPE_SMM: return QObject::tr("SMM module"); case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: return QObject::tr("Volume image"); case EFI_FV_FILETYPE_COMBINED_SMM_DXE: return QObject::tr("Combined SMM/DXE"); case EFI_FV_FILETYPE_SMM_CORE: return QObject::tr("SMM core"); case EFI_FV_FILETYPE_SMM_STANDALONE: return QObject::tr("SMM standalone"); case EFI_FV_FILETYPE_SMM_CORE_STANDALONE: return QObject::tr("SMM core standalone"); case EFI_FV_FILETYPE_PAD: return QObject::tr("Pad"); default: return QObject::tr("Unknown"); }; } QString sectionTypeToQString(const UINT8 type) { switch (type) { case EFI_SECTION_COMPRESSION: return QObject::tr("Compressed"); case EFI_SECTION_GUID_DEFINED: return QObject::tr("GUID defined"); case EFI_SECTION_DISPOSABLE: return QObject::tr("Disposable"); case EFI_SECTION_PE32: return QObject::tr("PE32 image"); case EFI_SECTION_PIC: return QObject::tr("PIC image"); case EFI_SECTION_TE: return QObject::tr("TE image"); case EFI_SECTION_DXE_DEPEX: return QObject::tr("DXE dependency"); case EFI_SECTION_VERSION: return QObject::tr("Version"); case EFI_SECTION_USER_INTERFACE: return QObject::tr("User interface"); case EFI_SECTION_COMPATIBILITY16: return QObject::tr("16-bit image"); case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return QObject::tr("Volume image"); case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return QObject::tr("Freeform subtype GUID"); case EFI_SECTION_RAW: return QObject::tr("Raw"); case EFI_SECTION_PEI_DEPEX: return QObject::tr("PEI dependency"); case EFI_SECTION_SMM_DEPEX: return QObject::tr("SMM dependency"); case INSYDE_SECTION_POSTCODE: return QObject::tr("Insyde postcode"); case SCT_SECTION_POSTCODE: return QObject::tr("SCT postcode"); default: return QObject::tr("Unknown"); } } UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header) { if (!header) return 0; bool extended = false; if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) { extended = true; } switch (header->Type) { case EFI_SECTION_GUID_DEFINED: { if (!extended) { const EFI_GUID_DEFINED_SECTION* gdsHeader = (const EFI_GUID_DEFINED_SECTION*)header; if (QByteArray((const char*)&gdsHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)(gdsHeader + 1); return gdsHeader->DataOffset + certificateHeader->Length; } return gdsHeader->DataOffset; } else { const EFI_GUID_DEFINED_SECTION2* gdsHeader = (const EFI_GUID_DEFINED_SECTION2*)header; if (QByteArray((const char*)&gdsHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)(gdsHeader + 1); return gdsHeader->DataOffset + certificateHeader->Length; } return gdsHeader->DataOffset; } } case EFI_SECTION_COMPRESSION: return extended ? sizeof(EFI_COMPRESSION_SECTION2) : sizeof(EFI_COMPRESSION_SECTION); case EFI_SECTION_DISPOSABLE: return extended ? sizeof(EFI_DISPOSABLE_SECTION2) : sizeof(EFI_DISPOSABLE_SECTION); case EFI_SECTION_PE32: return extended ? sizeof(EFI_PE32_SECTION2) : sizeof(EFI_PE32_SECTION); case EFI_SECTION_PIC: return extended ? sizeof(EFI_PIC_SECTION2) : sizeof(EFI_PIC_SECTION); case EFI_SECTION_TE: return extended ? sizeof(EFI_TE_SECTION2) : sizeof(EFI_TE_SECTION); case EFI_SECTION_DXE_DEPEX: return extended ? sizeof(EFI_DXE_DEPEX_SECTION2) : sizeof(EFI_DXE_DEPEX_SECTION); case EFI_SECTION_VERSION: return extended ? sizeof(EFI_VERSION_SECTION2) : sizeof(EFI_VERSION_SECTION); case EFI_SECTION_USER_INTERFACE: return extended ? sizeof(EFI_USER_INTERFACE_SECTION2) : sizeof(EFI_USER_INTERFACE_SECTION); case EFI_SECTION_COMPATIBILITY16: return extended ? sizeof(EFI_COMPATIBILITY16_SECTION2) : sizeof(EFI_COMPATIBILITY16_SECTION); case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: return extended ? sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION2) : sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION); case EFI_SECTION_FREEFORM_SUBTYPE_GUID: return extended ? sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION2) : sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION); case EFI_SECTION_RAW: return extended ? sizeof(EFI_RAW_SECTION2) : sizeof(EFI_RAW_SECTION); case EFI_SECTION_PEI_DEPEX: return extended ? sizeof(EFI_PEI_DEPEX_SECTION2) : sizeof(EFI_PEI_DEPEX_SECTION); case EFI_SECTION_SMM_DEPEX: return extended ? sizeof(EFI_SMM_DEPEX_SECTION2) : sizeof(EFI_SMM_DEPEX_SECTION); case INSYDE_SECTION_POSTCODE: return extended ? sizeof(POSTCODE_SECTION2) : sizeof(POSTCODE_SECTION); case SCT_SECTION_POSTCODE: return extended ? sizeof(POSTCODE_SECTION2) : sizeof(POSTCODE_SECTION); default: return extended ? sizeof(EFI_COMMON_SECTION_HEADER2) : sizeof(EFI_COMMON_SECTION_HEADER); } } UEFITool-0.28.0/ffs.h000066400000000000000000000546411363631100000141210ustar00rootroot00000000000000/* ffs.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef __FFS_H__ #define __FFS_H__ #include #include #include #include "basetypes.h" // C++ functions // GUID to QString routine extern QString guidToQString(const EFI_GUID& guid); // File type to QString routine extern QString fileTypeToQString(const UINT8 type); // Section type to QString routine extern QString sectionTypeToQString(const UINT8 type); // Make sure we use right packing rules #pragma pack(push,1) //***************************************************************************** // EFI Capsule //***************************************************************************** // Standard EFI Capsule header typedef struct _EFI_CAPSULE_HEADER { EFI_GUID CapsuleGuid; UINT32 HeaderSize; UINT32 Flags; UINT32 CapsuleImageSize; } EFI_CAPSULE_HEADER; // Capsule flags #define EFI_CAPSULE_HEADER_FLAG_SETUP 0x00000001 #define EFI_CAPSULE_HEADER_FLAG_PERSIST_ACROSS_RESET 0x00010000 #define EFI_CAPSULE_HEADER_FLAG_POPULATE_SYSTEM_TABLE 0x00020000 // Standard EFI capsule GUID const QByteArray EFI_CAPSULE_GUID ("\xBD\x86\x66\x3B\x76\x0D\x30\x40\xB7\x0E\xB5\x51\x9E\x2F\xC5\xA0", 16); // Intel capsule GUID const QByteArray INTEL_CAPSULE_GUID ("\xB9\x82\x91\x53\xB5\xAB\x91\x43\xB6\x9A\xE3\xA9\x43\xF7\x2F\xCC", 16); // Toshiba EFI Capsule header typedef struct _TOSHIBA_CAPSULE_HEADER { EFI_GUID CapsuleGuid; UINT32 HeaderSize; UINT32 FullSize; UINT32 Flags; } TOSHIBA_CAPSULE_HEADER; // Toshiba capsule GUID const QByteArray TOSHIBA_CAPSULE_GUID ("\x62\x70\xE0\x3B\x51\x1D\xD2\x45\x83\x2B\xF0\x93\x25\x7E\xD4\x61", 16); // AMI Aptio extended capsule header typedef struct _APTIO_CAPSULE_HEADER { EFI_CAPSULE_HEADER CapsuleHeader; UINT16 RomImageOffset; // offset in bytes from the beginning of the capsule header to the start of // the capsule volume //!TODO: Enable certificate and ROM layout reading //UINT16 RomLayoutOffset; // offset to the table of the module descriptors in the capsule's volume // that are included in the signature calculation //FW_CERTIFICATE FWCert; //ROM_AREA RomAreaMap[1]; } APTIO_CAPSULE_HEADER; // AMI Aptio signed extended capsule GUID const QByteArray APTIO_SIGNED_CAPSULE_GUID ("\x8B\xA6\x3C\x4A\x23\x77\xFB\x48\x80\x3D\x57\x8C\xC1\xFE\xC4\x4D", 16); // AMI Aptio unsigned extended capsule GUID const QByteArray APTIO_UNSIGNED_CAPSULE_GUID ("\x90\xBB\xEE\x14\x0A\x89\xDB\x43\xAE\xD1\x5D\x3C\x45\x88\xA4\x18", 16); //14EEBB90-890A-43DB-AED1-5D3C4588A418 //***************************************************************************** // EFI Firmware Volume //***************************************************************************** // Firmware block map entry // FvBlockMap ends with an entry {0x00000000, 0x00000000} typedef struct _EFI_FV_BLOCK_MAP_ENTRY { UINT32 NumBlocks; UINT32 Length; } EFI_FV_BLOCK_MAP_ENTRY; // Volume header typedef struct _EFI_FIRMWARE_VOLUME_HEADER { UINT8 ZeroVector[16]; EFI_GUID FileSystemGuid; UINT64 FvLength; UINT32 Signature; UINT32 Attributes; UINT16 HeaderLength; UINT16 Checksum; UINT16 ExtHeaderOffset; //Reserved in Revision 1 UINT8 Reserved; UINT8 Revision; //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2]; } EFI_FIRMWARE_VOLUME_HEADER; // Standard file system GUIDs const QByteArray EFI_FIRMWARE_FILE_SYSTEM_GUID ("\xD9\x54\x93\x7A\x68\x04\x4A\x44\x81\xCE\x0B\xF6\x17\xD8\x90\xDF", 16); const QByteArray EFI_FIRMWARE_FILE_SYSTEM2_GUID ("\x78\xE5\x8C\x8C\x3D\x8A\x1C\x4F\x99\x35\x89\x61\x85\xC3\x2D\xD3", 16); // Vendor-specific file system GUIDs const QByteArray EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM_GUID ("\xAD\xEE\xAD\x04\xFF\x61\x31\x4D\xB6\xBA\x64\xF8\xBF\x90\x1F\x5A", 16); const QByteArray EFI_APPLE_BOOT_VOLUME_FILE_SYSTEM2_GUID ("\x8C\x1B\x00\xBD\x71\x6A\x7B\x48\xA1\x4F\x0C\x2A\x2D\xCF\x7A\x5D", 16); const QByteArray EFI_INTEL_FILE_SYSTEM_GUID ("\xFF\xFF\x3F\xAD\x8B\xD2\xC4\x44\x9F\x13\x9E\xA9\x8A\x97\xF9\xF0", 16); // AD3FFFFF-D28B-44C4-9F13-9EA98A97F9F0 // Intel 1 const QByteArray EFI_INTEL_FILE_SYSTEM2_GUID ("\x70\xCD\xA1\xD6\x33\x4B\x94\x49\xA6\xEA\x37\x5F\x2C\xCC\x54\x37", 16); // D6A1CD70-4B33-4994-A6EA-375F2CCC5437 // Intel 2 const QByteArray EFI_SONY_FILE_SYSTEM_GUID ("\x56\x41\x49\x4F\xD6\xAE\x64\x4D\xA5\x37\xB8\xA5\x55\x7B\xCE\xEC", 16); // 4F494156-AED6-4D64-A537-B8A5557BCEEC // Sony 1 // Vector of volume GUIDs with FFSv2-compatible files extern const QVector FFSv2Volumes; const QByteArray EFI_FIRMWARE_FILE_SYSTEM3_GUID // 5473C07A-3DCB-4dca-BD6F-1E9689E7349A ("\x7A\xC0\x73\x54\xCB\x3D\xCA\x4D\xBD\x6F\x1E\x96\x89\xE7\x34\x9A", 16); // Vector of volume GUIDs with FFSv3-compatible files extern const QVector FFSv3Volumes; // Firmware volume signature const QByteArray EFI_FV_SIGNATURE("_FVH", 4); #define EFI_FV_SIGNATURE_OFFSET 0x28 // Firmware volume attributes // Revision 1 #define EFI_FVB_READ_DISABLED_CAP 0x00000001 #define EFI_FVB_READ_ENABLED_CAP 0x00000002 #define EFI_FVB_READ_STATUS 0x00000004 #define EFI_FVB_WRITE_DISABLED_CAP 0x00000008 #define EFI_FVB_WRITE_ENABLED_CAP 0x00000010 #define EFI_FVB_WRITE_STATUS 0x00000020 #define EFI_FVB_LOCK_CAP 0x00000040 #define EFI_FVB_LOCK_STATUS 0x00000080 #define EFI_FVB_STICKY_WRITE 0x00000200 #define EFI_FVB_MEMORY_MAPPED 0x00000400 #define EFI_FVB_ERASE_POLARITY 0x00000800 #define EFI_FVB_ALIGNMENT_CAP 0x00008000 #define EFI_FVB_ALIGNMENT_2 0x00010000 #define EFI_FVB_ALIGNMENT_4 0x00020000 #define EFI_FVB_ALIGNMENT_8 0x00040000 #define EFI_FVB_ALIGNMENT_16 0x00080000 #define EFI_FVB_ALIGNMENT_32 0x00100000 #define EFI_FVB_ALIGNMENT_64 0x00200000 #define EFI_FVB_ALIGNMENT_128 0x00400000 #define EFI_FVB_ALIGNMENT_256 0x00800000 #define EFI_FVB_ALIGNMENT_512 0x01000000 #define EFI_FVB_ALIGNMENT_1K 0x02000000 #define EFI_FVB_ALIGNMENT_2K 0x04000000 #define EFI_FVB_ALIGNMENT_4K 0x08000000 #define EFI_FVB_ALIGNMENT_8K 0x10000000 #define EFI_FVB_ALIGNMENT_16K 0x20000000 #define EFI_FVB_ALIGNMENT_32K 0x40000000 #define EFI_FVB_ALIGNMENT_64K 0x80000000 // Revision 2 #define EFI_FVB2_READ_DISABLED_CAP 0x00000001 #define EFI_FVB2_READ_ENABLED_CAP 0x00000002 #define EFI_FVB2_READ_STATUS 0x00000004 #define EFI_FVB2_WRITE_DISABLED_CAP 0x00000008 #define EFI_FVB2_WRITE_ENABLED_CAP 0x00000010 #define EFI_FVB2_WRITE_STATUS 0x00000020 #define EFI_FVB2_LOCK_CAP 0x00000040 #define EFI_FVB2_LOCK_STATUS 0x00000080 #define EFI_FVB2_STICKY_WRITE 0x00000200 #define EFI_FVB2_MEMORY_MAPPED 0x00000400 #define EFI_FVB2_ERASE_POLARITY 0x00000800 #define EFI_FVB2_READ_LOCK_CAP 0x00001000 #define EFI_FVB2_READ_LOCK_STATUS 0x00002000 #define EFI_FVB2_WRITE_LOCK_CAP 0x00004000 #define EFI_FVB2_WRITE_LOCK_STATUS 0x00008000 #define EFI_FVB2_ALIGNMENT 0x001F0000 #define EFI_FVB2_ALIGNMENT_1 0x00000000 #define EFI_FVB2_ALIGNMENT_2 0x00010000 #define EFI_FVB2_ALIGNMENT_4 0x00020000 #define EFI_FVB2_ALIGNMENT_8 0x00030000 #define EFI_FVB2_ALIGNMENT_16 0x00040000 #define EFI_FVB2_ALIGNMENT_32 0x00050000 #define EFI_FVB2_ALIGNMENT_64 0x00060000 #define EFI_FVB2_ALIGNMENT_128 0x00070000 #define EFI_FVB2_ALIGNMENT_256 0x00080000 #define EFI_FVB2_ALIGNMENT_512 0x00090000 #define EFI_FVB2_ALIGNMENT_1K 0x000A0000 #define EFI_FVB2_ALIGNMENT_2K 0x000B0000 #define EFI_FVB2_ALIGNMENT_4K 0x000C0000 #define EFI_FVB2_ALIGNMENT_8K 0x000D0000 #define EFI_FVB2_ALIGNMENT_16K 0x000E0000 #define EFI_FVB2_ALIGNMENT_32K 0x000F0000 #define EFI_FVB2_ALIGNMENT_64K 0x00100000 #define EFI_FVB2_ALIGNMENT_128K 0x00110000 #define EFI_FVB2_ALIGNMENT_256K 0x00120000 #define EFI_FVB2_ALIGNMENT_512K 0x00130000 #define EFI_FVB2_ALIGNMENT_1M 0x00140000 #define EFI_FVB2_ALIGNMENT_2M 0x00150000 #define EFI_FVB2_ALIGNMENT_4M 0x00160000 #define EFI_FVB2_ALIGNMENT_8M 0x00170000 #define EFI_FVB2_ALIGNMENT_16M 0x00180000 #define EFI_FVB2_ALIGNMENT_32M 0x00190000 #define EFI_FVB2_ALIGNMENT_64M 0x001A0000 #define EFI_FVB2_ALIGNMENT_128M 0x001B0000 #define EFI_FVB2_ALIGNMENT_256M 0x001C0000 #define EFI_FVB2_ALIGNMENT_512M 0x001D0000 #define EFI_FVB2_ALIGNMENT_1G 0x001E0000 #define EFI_FVB2_ALIGNMENT_2G 0x001F0000 #define EFI_FVB2_WEAK_ALIGNMENT 0x80000000 // Extended firmware volume header typedef struct _EFI_FIRMWARE_VOLUME_EXT_HEADER { EFI_GUID FvName; UINT32 ExtHeaderSize; } EFI_FIRMWARE_VOLUME_EXT_HEADER; // Extended header entry // The extended header entries follow each other and are // terminated by ExtHeaderType EFI_FV_EXT_TYPE_END #define EFI_FV_EXT_TYPE_END 0x0000 typedef struct _EFI_FIRMWARE_VOLUME_EXT_ENTRY { UINT16 ExtEntrySize; UINT16 ExtEntryType; } EFI_FIRMWARE_VOLUME_EXT_ENTRY; // GUID that maps OEM file types to GUIDs #define EFI_FV_EXT_TYPE_OEM_TYPE 0x0001 typedef struct _EFI_FIRMWARE_VOLUME_EXT_HEADER_OEM_TYPE { EFI_FIRMWARE_VOLUME_EXT_ENTRY Header; UINT32 TypeMask; //EFI_GUID Types[1]; } EFI_FIRMWARE_VOLUME_EXT_HEADER_OEM_TYPE; #define EFI_FV_EXT_TYPE_GUID_TYPE 0x0002 typedef struct _EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE { EFI_FIRMWARE_VOLUME_EXT_ENTRY Header; EFI_GUID FormatType; //UINT8 Data[]; } EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE; //!TODO: add proper NVRAM parsing //const QByteArray EFI_FIRMWARE_VOLUME_NVRAM_SIGNATURE("$VSS", 4); // Volume header 16bit checksum calculation routine extern UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize); //***************************************************************************** // EFI FFS File //***************************************************************************** // Integrity check typedef union { struct { UINT8 Header; UINT8 File; } Checksum; UINT16 TailReference; // Revision 1 UINT16 Checksum16; // Revision 2 } EFI_FFS_INTEGRITY_CHECK; // File header typedef struct _EFI_FFS_FILE_HEADER { EFI_GUID Name; EFI_FFS_INTEGRITY_CHECK IntegrityCheck; UINT8 Type; UINT8 Attributes; UINT8 Size[3]; UINT8 State; } EFI_FFS_FILE_HEADER; // Large file header typedef struct _EFI_FFS_FILE_HEADER2 { EFI_GUID Name; EFI_FFS_INTEGRITY_CHECK IntegrityCheck; UINT8 Type; UINT8 Attributes; UINT8 Size[3]; // Set to 0xFFFFFF UINT8 State; UINT64 ExtendedSize; } EFI_FFS_FILE_HEADER2; // Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear #define FFS_FIXED_CHECKSUM 0x5A #define FFS_FIXED_CHECKSUM2 0xAA // File types #define EFI_FV_FILETYPE_ALL 0x00 #define EFI_FV_FILETYPE_RAW 0x01 #define EFI_FV_FILETYPE_FREEFORM 0x02 #define EFI_FV_FILETYPE_SECURITY_CORE 0x03 #define EFI_FV_FILETYPE_PEI_CORE 0x04 #define EFI_FV_FILETYPE_DXE_CORE 0x05 #define EFI_FV_FILETYPE_PEIM 0x06 #define EFI_FV_FILETYPE_DRIVER 0x07 #define EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER 0x08 #define EFI_FV_FILETYPE_APPLICATION 0x09 #define EFI_FV_FILETYPE_SMM 0x0A #define EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE 0x0B #define EFI_FV_FILETYPE_COMBINED_SMM_DXE 0x0C #define EFI_FV_FILETYPE_SMM_CORE 0x0D #define EFI_FV_FILETYPE_SMM_STANDALONE 0x0E #define EFI_FV_FILETYPE_SMM_CORE_STANDALONE 0x0F #define EFI_FV_FILETYPE_OEM_MIN 0xC0 #define EFI_FV_FILETYPE_OEM_MAX 0xDF #define EFI_FV_FILETYPE_DEBUG_MIN 0xE0 #define EFI_FV_FILETYPE_DEBUG_MAX 0xEF #define EFI_FV_FILETYPE_PAD 0xF0 #define EFI_FV_FILETYPE_FFS_MIN 0xF0 #define EFI_FV_FILETYPE_FFS_MAX 0xFF // File attributes #define FFS_ATTRIB_TAIL_PRESENT 0x01 // Valid only for revision 1 volumes #define FFS_ATTRIB_RECOVERY 0x02 // Valid only for revision 1 volumes #define FFS_ATTRIB_LARGE_FILE 0x01 // Valid only for FFSv3 volumes #define FFS_ATTRIB_DATA_ALIGNMENT2 0x02 // Valid only for revision 2 volumes #define FFS_ATTRIB_FIXED 0x04 #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 // FFS alignment table extern const UINT8 ffsAlignmentTable[]; // Extended FFS alignment table extern const UINT8 ffsAlignment2Table[]; // File states #define EFI_FILE_HEADER_CONSTRUCTION 0x01 #define EFI_FILE_HEADER_VALID 0x02 #define EFI_FILE_DATA_VALID 0x04 #define EFI_FILE_MARKED_FOR_UPDATE 0x08 #define EFI_FILE_DELETED 0x10 #define EFI_FILE_HEADER_INVALID 0x20 #define EFI_FILE_ERASE_POLARITY 0x80 // Defined as "all other bits must be set to ERASE_POLARITY" in UEFI PI // PEI apriori file const QByteArray EFI_PEI_APRIORI_FILE_GUID ("\x0A\xCC\x45\x1B\x6A\x15\x8A\x42\xAF\x62\x49\x86\x4D\xA0\xE6\xE6", 16); // DXE apriori file const QByteArray EFI_DXE_APRIORI_FILE_GUID ("\xE7\x0E\x51\xFC\xDC\xFF\xD4\x11\xBD\x41\x00\x80\xC7\x3C\x88\x81", 16); // Volume top file const QByteArray EFI_FFS_VOLUME_TOP_FILE_GUID ("\x2E\x06\xA0\x1B\x79\xC7\x82\x45\x85\x66\x33\x6A\xE8\xF7\x8F\x09", 16); // Pad file GUID const QByteArray EFI_FFS_PAD_FILE_GUID ("\x85\x65\x53\xE4\x09\x79\x60\x4A\xB5\xC6\xEC\xDE\xA6\xEB\xFB\x54", 16); // FFS size conversion routines extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); extern UINT32 uint24ToUint32(const UINT8* ffsSize); // FFS file 8bit checksum calculation routines extern UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize); extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize); //***************************************************************************** // EFI FFS File Section //***************************************************************************** // Common section header typedef struct _EFI_COMMON_SECTION_HEADER { UINT8 Size[3]; UINT8 Type; } EFI_COMMON_SECTION_HEADER; // Large file common section header typedef struct _EFI_COMMON_SECTION_HEADER2 { UINT8 Size[3]; //Must be 0xFFFFFF for this header to be used UINT8 Type; UINT32 ExtendedSize; } EFI_COMMON_SECTION_HEADER2; // Section2 usage indicator #define EFI_SECTION2_IS_USED 0xFFFFFF // File section types #define EFI_SECTION_ALL 0x00 // Impossible attribute for file in the FS // Encapsulation section types #define EFI_SECTION_COMPRESSION 0x01 #define EFI_SECTION_GUID_DEFINED 0x02 #define EFI_SECTION_DISPOSABLE 0x03 // Leaf section types #define EFI_SECTION_PE32 0x10 #define EFI_SECTION_PIC 0x11 #define EFI_SECTION_TE 0x12 #define EFI_SECTION_DXE_DEPEX 0x13 #define EFI_SECTION_VERSION 0x14 #define EFI_SECTION_USER_INTERFACE 0x15 #define EFI_SECTION_COMPATIBILITY16 0x16 #define EFI_SECTION_FIRMWARE_VOLUME_IMAGE 0x17 #define EFI_SECTION_FREEFORM_SUBTYPE_GUID 0x18 #define EFI_SECTION_RAW 0x19 #define EFI_SECTION_PEI_DEPEX 0x1B #define EFI_SECTION_SMM_DEPEX 0x1C #define SCT_SECTION_POSTCODE 0xF0 // Specific to Phoenix SCT images #define INSYDE_SECTION_POSTCODE 0x20 // Specific to Insyde images // Compression section typedef struct _EFI_COMPRESSION_SECTION { UINT8 Size[3]; UINT8 Type; UINT32 UncompressedLength; UINT8 CompressionType; } EFI_COMPRESSION_SECTION; typedef struct _EFI_COMPRESSION_SECTION2 { UINT8 Size[3]; UINT8 Type; UINT32 ExtendedSize; UINT32 UncompressedLength; UINT8 CompressionType; } EFI_COMPRESSION_SECTION2; // Compression types #define EFI_NOT_COMPRESSED 0x00 #define EFI_STANDARD_COMPRESSION 0x01 #define EFI_CUSTOMIZED_COMPRESSION 0x02 //GUID defined section typedef struct _EFI_GUID_DEFINED_SECTION { UINT8 Size[3]; UINT8 Type; EFI_GUID SectionDefinitionGuid; UINT16 DataOffset; UINT16 Attributes; } EFI_GUID_DEFINED_SECTION; typedef struct _EFI_GUID_DEFINED_SECTION2 { UINT8 Size[3]; UINT8 Type; UINT32 ExtendedSize; EFI_GUID SectionDefinitionGuid; UINT16 DataOffset; UINT16 Attributes; } EFI_GUID_DEFINED_SECTION2; // Attributes for GUID defined section #define EFI_GUIDED_SECTION_PROCESSING_REQUIRED 0x01 #define EFI_GUIDED_SECTION_AUTH_STATUS_VALID 0x02 // GUIDs of GUID-defined sections const QByteArray EFI_GUIDED_SECTION_CRC32 // FC1BCDB0-7D31-49AA-936A-A4600D9DD083 ("\xB0\xCD\x1B\xFC\x31\x7D\xAA\x49\x93\x6A\xA4\x60\x0D\x9D\xD0\x83", 16); const QByteArray EFI_GUIDED_SECTION_TIANO // A31280AD-481E-41B6-95E8-127F4C984779 ("\xAD\x80\x12\xA3\x1E\x48\xB6\x41\x95\xE8\x12\x7F\x4C\x98\x47\x79", 16); const QByteArray EFI_GUIDED_SECTION_LZMA // EE4E5898-3914-4259-9D6E-DC7BD79403CF ("\x98\x58\x4E\xEE\x14\x39\x59\x42\x9D\x6E\xDC\x7B\xD7\x94\x03\xCF", 16); const QByteArray EFI_GUIDED_SECTION_LZMAF86 //D42AE6BD-1352-4BFB-909A-CA72A6EAE889 ("\xbd\xe6\x2a\xd4\x52\x13\xfb\x4b\x90\x9a\xca\x72\xa6\xea\xe8\x89", 16); const QByteArray EFI_FIRMWARE_CONTENTS_SIGNED_GUID //0F9D89E8-9259-4F76-A5AF-0C89E34023DF ("\xE8\x89\x9D\x0F\x59\x92\x76\x4F\xA5\xAF\x0C\x89\xE3\x40\x23\xDF", 16); // Version section typedef struct _EFI_VERSION_SECTION { UINT8 Size[3]; UINT8 Type; UINT16 BuildNumber; } EFI_VERSION_SECTION; typedef struct _EFI_VERSION_SECTION2 { UINT8 Size[3]; UINT8 Type; UINT32 ExtendedSize; UINT16 BuildNumber; } EFI_VERSION_SECTION2; // Freeform subtype GUID section typedef struct _EFI_FREEFORM_SUBTYPE_GUID_SECTION { UINT8 Size[3]; UINT8 Type; EFI_GUID SubTypeGuid; } EFI_FREEFORM_SUBTYPE_GUID_SECTION; typedef struct _EFI_FREEFORM_SUBTYPE_GUID_SECTION2 { UINT8 Size[3]; UINT8 Type; UINT32 ExtendedSize; EFI_GUID SubTypeGuid; } EFI_FREEFORM_SUBTYPE_GUID_SECTION2; // Phoenix SCT and HP postcode section typedef struct _POSTCODE_SECTION { UINT8 Size[3]; UINT8 Type; UINT32 Postcode; } POSTCODE_SECTION; typedef struct _POSTCODE_SECTION2 { UINT8 Size[3]; UINT8 Type; UINT32 ExtendedSize; UINT32 Postcode; } POSTCODE_SECTION2; // Other sections typedef EFI_COMMON_SECTION_HEADER EFI_DISPOSABLE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_DISPOSABLE_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_RAW_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_RAW_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_DXE_DEPEX_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_DXE_DEPEX_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_PEI_DEPEX_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_PEI_DEPEX_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_SMM_DEPEX_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_SMM_DEPEX_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_PE32_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_PE32_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_PIC_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_PIC_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_TE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_TE_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_COMPATIBILITY16_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_COMPATIBILITY16_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_FIRMWARE_VOLUME_IMAGE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_FIRMWARE_VOLUME_IMAGE_SECTION2; typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION; typedef EFI_COMMON_SECTION_HEADER2 EFI_USER_INTERFACE_SECTION2; // Section routines extern UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header); //***************************************************************************** // EFI Dependency Expression //***************************************************************************** #define EFI_DEP_OPCODE_SIZE 1 /// /// If present, this must be the first and only opcode, /// EFI_DEP_BEFORE is only used by DXE driver. /// #define EFI_DEP_BEFORE 0x00 /// /// If present, this must be the first and only opcode, /// EFI_DEP_AFTER is only used by DXE driver. /// #define EFI_DEP_AFTER 0x01 #define EFI_DEP_PUSH 0x02 #define EFI_DEP_AND 0x03 #define EFI_DEP_OR 0x04 #define EFI_DEP_NOT 0x05 #define EFI_DEP_TRUE 0x06 #define EFI_DEP_FALSE 0x07 #define EFI_DEP_END 0x08 /// /// If present, this must be the first opcode, /// EFI_DEP_SOR is only used by DXE driver. /// #define EFI_DEP_SOR 0x09 //***************************************************************************** // UEFI Crypto-signed Stuff //***************************************************************************** #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 #define WIN_CERT_TYPE_EFI_GUID 0x0EF1 typedef struct _WIN_CERTIFICATE { UINT32 Length; UINT16 Revision; UINT16 CertificateType; //UINT8 CertData[]; } WIN_CERTIFICATE; typedef struct _WIN_CERTIFICATE_UEFI_GUID { WIN_CERTIFICATE Header; // Standard WIN_CERTIFICATE EFI_GUID CertType; // Determines format of CertData // UINT8 CertData[]; // Certificate data follows } WIN_CERTIFICATE_UEFI_GUID; // WIN_CERTIFICATE_UEFI_GUID.CertType const QByteArray EFI_CERT_TYPE_RSA2048_SHA256_GUID ("\x14\x74\x71\xA7\x16\xC6\x77\x49\x94\x20\x84\x47\x12\xA7\x35\xBF"); const QByteArray EFI_CERT_TYPE_PKCS7_GUID ("\x9D\xD2\xAF\x4A\xDF\x68\xEE\x49\x8A\xA9\x34\x7D\x37\x56\x65\xA7"); // WIN_CERTIFICATE_UEFI_GUID.CertData typedef struct _EFI_CERT_BLOCK_RSA_2048_SHA256 { UINT32 HashType; UINT8 PublicKey[256]; UINT8 Signature[256]; } EFI_CERT_BLOCK_RSA_2048_SHA256; // Restore previous packing rules #pragma pack(pop) #endif UEFITool-0.28.0/ffsengine.cpp000066400000000000000000006323731363631100000156460ustar00rootroot00000000000000/* ffsengine.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include #include "ffsengine.h" #include "types.h" #include "treemodel.h" #include "descriptor.h" #include "ffs.h" #include "gbe.h" #include "me.h" #include "Tiano/EfiTianoCompress.h" #include "Tiano/EfiTianoDecompress.h" #include "LZMA/LzmaCompress.h" #include "LZMA/LzmaDecompress.h" #include "LZMA/x86Convert.h" #ifdef _CONSOLE #include #endif QString errorMessage(UINT8 errorCode) { switch (errorCode) { case ERR_SUCCESS: return QObject::tr("Success"); case ERR_NOT_IMPLEMENTED: return QObject::tr("Not implemented"); case ERR_INVALID_PARAMETER: return QObject::tr("Function called with invalid parameter"); case ERR_BUFFER_TOO_SMALL: return QObject::tr("Buffer too small"); case ERR_OUT_OF_RESOURCES: return QObject::tr("Out of resources"); case ERR_OUT_OF_MEMORY: return QObject::tr("Out of memory"); case ERR_FILE_OPEN: return QObject::tr("File can't be opened"); case ERR_FILE_READ: return QObject::tr("File can't be read"); case ERR_FILE_WRITE: return QObject::tr("File can't be written"); case ERR_ITEM_NOT_FOUND: return QObject::tr("Item not found"); case ERR_UNKNOWN_ITEM_TYPE: return QObject::tr("Unknown item type"); case ERR_INVALID_FLASH_DESCRIPTOR: return QObject::tr("Invalid flash descriptor"); case ERR_INVALID_REGION: return QObject::tr("Invalid region"); case ERR_EMPTY_REGION: return QObject::tr("Empty region"); case ERR_BIOS_REGION_NOT_FOUND: return QObject::tr("BIOS region not found"); case ERR_VOLUMES_NOT_FOUND: return QObject::tr("UEFI volumes not found"); case ERR_INVALID_VOLUME: return QObject::tr("Invalid UEFI volume"); case ERR_VOLUME_REVISION_NOT_SUPPORTED: return QObject::tr("Volume revision not supported"); case ERR_VOLUME_GROW_FAILED: return QObject::tr("Volume grow failed"); case ERR_UNKNOWN_FFS: return QObject::tr("Unknown file system"); case ERR_INVALID_FILE: return QObject::tr("Invalid file"); case ERR_INVALID_SECTION: return QObject::tr("Invalid section"); case ERR_UNKNOWN_SECTION: return QObject::tr("Unknown section"); case ERR_STANDARD_COMPRESSION_FAILED: return QObject::tr("Standard compression failed"); case ERR_CUSTOMIZED_COMPRESSION_FAILED: return QObject::tr("Customized compression failed"); case ERR_STANDARD_DECOMPRESSION_FAILED: return QObject::tr("Standard decompression failed"); case ERR_CUSTOMIZED_DECOMPRESSION_FAILED: return QObject::tr("Customized compression failed"); case ERR_UNKNOWN_COMPRESSION_ALGORITHM: return QObject::tr("Unknown compression method"); case ERR_UNKNOWN_EXTRACT_MODE: return QObject::tr("Unknown extract mode"); case ERR_UNKNOWN_INSERT_MODE: return QObject::tr("Unknown insert mode"); case ERR_UNKNOWN_IMAGE_TYPE: return QObject::tr("Unknown executable image type"); case ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE: return QObject::tr("Unknown PE optional header type"); case ERR_UNKNOWN_RELOCATION_TYPE: return QObject::tr("Unknown relocation type"); case ERR_GENERIC_CALL_NOT_SUPPORTED: return QObject::tr("Generic call not supported"); case ERR_VOLUME_BASE_NOT_FOUND: return QObject::tr("Volume base address not found"); case ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND: return QObject::tr("PEI core entry point not found"); case ERR_COMPLEX_BLOCK_MAP: return QObject::tr("Block map structure too complex for correct analysis"); case ERR_DIR_ALREADY_EXIST: return QObject::tr("Directory already exists"); case ERR_DIR_CREATE: return QObject::tr("Directory can't be created"); case ERR_UNKNOWN_PATCH_TYPE: return QObject::tr("Unknown patch type"); case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: return QObject::tr("Patch offset out of bounds"); case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol"); case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch"); case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed"); case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated"); case ERR_BAD_RELOCATION_ENTRY: return QObject::tr("Bad image relocation entry"); default: return QObject::tr("Unknown error %1").arg(errorCode); } } FfsEngine::FfsEngine(QObject *parent) : QObject(parent) { model = new TreeModel(); oldPeiCoreEntryPoint = 0; newPeiCoreEntryPoint = 0; dumped = false; } FfsEngine::~FfsEngine(void) { delete model; } TreeModel* FfsEngine::treeModel() const { return model; } void FfsEngine::msg(const QString & message, const QModelIndex & index) { #ifndef _DISABLE_ENGINE_MESSAGES #ifndef _CONSOLE messageItems.enqueue(MessageListItem(message, NULL, 0, index)); #else (void) index; std::cout << message.toLatin1().constData() << std::endl; #endif #else (void)message; (void)index; #endif } #ifndef _CONSOLE QQueue FfsEngine::messages() const { return messageItems; } void FfsEngine::clearMessages() { messageItems.clear(); } #endif bool FfsEngine::hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2) { if (begin1 < begin2 && begin2 < end1) return true; if (begin1 < end2 && end2 < end1) return true; if (begin2 < begin1 && begin1 < end2) return true; if (begin2 < end1 && end1 < end2) return true; return false; } // Firmware image parsing UINT8 FfsEngine::parseImageFile(const QByteArray & buffer) { oldPeiCoreEntryPoint = 0; newPeiCoreEntryPoint = 0; // Check buffer size to be more then or equal to size of EFI_CAPSULE_HEADER if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) { msg(tr("parseImageFile: image file is smaller then minimum size of %1h (%2) bytes").hexarg(sizeof(EFI_CAPSULE_HEADER)).arg(sizeof(EFI_CAPSULE_HEADER))); return ERR_INVALID_PARAMETER; } // Check buffer for being normal EFI capsule header UINT32 capsuleHeaderSize = 0; QModelIndex index; if (buffer.startsWith(EFI_CAPSULE_GUID) || buffer.startsWith(INTEL_CAPSULE_GUID)) { // Get info const EFI_CAPSULE_HEADER* capsuleHeader = (const EFI_CAPSULE_HEADER*)buffer.constData(); capsuleHeaderSize = capsuleHeader->HeaderSize; QByteArray header = buffer.left(capsuleHeaderSize); QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); QString name = tr("UEFI capsule"); QString info = tr("Capsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h") .arg(guidToQString(capsuleHeader->CapsuleGuid)) .hexarg(buffer.size()).arg(buffer.size()) .hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize) .hexarg(capsuleHeader->CapsuleImageSize).arg(capsuleHeader->CapsuleImageSize) .hexarg2(capsuleHeader->Flags, 8); // Add tree item index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); } // Check buffer for being Toshiba capsule header else if (buffer.startsWith(TOSHIBA_CAPSULE_GUID)) { // Get info const TOSHIBA_CAPSULE_HEADER* capsuleHeader = (const TOSHIBA_CAPSULE_HEADER*)buffer.constData(); capsuleHeaderSize = capsuleHeader->HeaderSize; QByteArray header = buffer.left(capsuleHeaderSize); QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); QString name = tr("UEFI capsule"); QString info = tr("Capsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h") .arg(guidToQString(capsuleHeader->CapsuleGuid)) .hexarg(buffer.size()).arg(buffer.size()) .hexarg(capsuleHeader->HeaderSize).arg(capsuleHeader->HeaderSize) .hexarg(capsuleHeader->FullSize - capsuleHeader->HeaderSize).arg(capsuleHeader->FullSize - capsuleHeader->HeaderSize) .hexarg2(capsuleHeader->Flags, 8); // Add tree item index = model->addItem(Types::Capsule, Subtypes::ToshibaCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); } // Check buffer for being extended Aptio signed capsule header else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { // Get info bool signedCapsule = buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID); const APTIO_CAPSULE_HEADER* capsuleHeader = (const APTIO_CAPSULE_HEADER*)buffer.constData(); capsuleHeaderSize = capsuleHeader->RomImageOffset; QByteArray header = buffer.left(capsuleHeaderSize); QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); QString name = tr("AMI Aptio capsule"); QString info = tr("Capsule GUID: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nImage size: %6h (%7)\nFlags: %8h") .arg(guidToQString(capsuleHeader->CapsuleHeader.CapsuleGuid)) .hexarg(buffer.size()).arg(buffer.size()) .hexarg(capsuleHeaderSize).arg(capsuleHeaderSize) .hexarg(capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize).arg(capsuleHeader->CapsuleHeader.CapsuleImageSize - capsuleHeaderSize) .hexarg2(capsuleHeader->CapsuleHeader.Flags, 8); //!TODO: more info about Aptio capsule // Add tree item index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body); // Show message about possible Aptio signature break if (signedCapsule) { msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index); } } // Skip capsule header to have flash chip image QByteArray flashImage = buffer.right(buffer.size() - capsuleHeaderSize); // Check for Intel flash descriptor presence const FLASH_DESCRIPTOR_HEADER* descriptorHeader = (const FLASH_DESCRIPTOR_HEADER*)flashImage.constData(); // Check descriptor signature UINT8 result; if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) { // Parse as Intel image QModelIndex imageIndex; result = parseIntelImage(flashImage, imageIndex, index); if (result != ERR_INVALID_FLASH_DESCRIPTOR) return result; } // Get info QString name = tr("UEFI image"); QString info = tr("Full size: %1h (%2)") .hexarg(flashImage.size()).arg(flashImage.size()); // Add tree item index = model->addItem(Types::Image, Subtypes::UefiImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, index); return parseBios(flashImage, index); } UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & index, const QModelIndex & parent) { // Sanity check if (intelImage.isEmpty()) return EFI_INVALID_PARAMETER; // Store the beginning of descriptor as descriptor base address const UINT8* descriptor = (const UINT8*)intelImage.constData(); UINT32 descriptorBegin = 0; UINT32 descriptorEnd = FLASH_DESCRIPTOR_SIZE; // Check for buffer size to be greater or equal to descriptor region size if (intelImage.size() < FLASH_DESCRIPTOR_SIZE) { msg(tr("parseIntelImage: input file is smaller than minimum descriptor size of 1000h (4096) bytes")); return ERR_INVALID_FLASH_DESCRIPTOR; } // Parse descriptor map const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor + sizeof(FLASH_DESCRIPTOR_HEADER)); const FLASH_DESCRIPTOR_UPPER_MAP* upperMap = (const FLASH_DESCRIPTOR_UPPER_MAP*)(descriptor + FLASH_DESCRIPTOR_UPPER_MAP_BASE); // Check sanity of base values if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE || descriptorMap->MasterBase == descriptorMap->RegionBase || descriptorMap->MasterBase == descriptorMap->ComponentBase) { msg(tr("parseIntelImage: invalid descriptor master base %1h").hexarg2(descriptorMap->MasterBase, 2)); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE || descriptorMap->RegionBase == descriptorMap->ComponentBase) { msg(tr("parseIntelImage: invalid descriptor region base %1h").hexarg2(descriptorMap->RegionBase, 2)); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) { msg(tr("parseIntelImage: invalid descriptor component base %1h").hexarg2(descriptorMap->ComponentBase, 2)); return ERR_INVALID_FLASH_DESCRIPTOR; } const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8(descriptor, descriptorMap->RegionBase); const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase); // Check for legacy descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency UINT8 descriptorVersion = 2; // Skylake+ descriptor if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Legacy descriptor descriptorVersion = 1; // ME region QByteArray me; UINT32 meBegin = 0; UINT32 meEnd = 0; if (regionSection->MeLimit) { meBegin = calculateRegionOffset(regionSection->MeBase); meEnd = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); me = intelImage.mid(meBegin, meEnd); meEnd += meBegin; } // BIOS region QByteArray bios; UINT32 biosBegin = 0; UINT32 biosEnd = 0; if (regionSection->BiosLimit) { biosBegin = calculateRegionOffset(regionSection->BiosBase); biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); // Check for Gigabyte specific descriptor map if (biosEnd - biosBegin == (UINT32)intelImage.size()) { if (!meEnd) { msg(tr("parseIntelImage: can't determine BIOS region start from Gigabyte-specific descriptor")); return ERR_INVALID_FLASH_DESCRIPTOR; } biosBegin = meEnd; bios = intelImage.mid(biosBegin, biosEnd); // biosEnd will point to the end of the image file // it may be wrong, but it's pretty hard to detect a padding after BIOS region // with malformed descriptor } // Normal descriptor map else { bios = intelImage.mid(biosBegin, biosEnd); // Calculate biosEnd biosEnd += biosBegin; } } else { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region not found in descriptor")); return ERR_INVALID_FLASH_DESCRIPTOR; } // GbE region QByteArray gbe; UINT32 gbeBegin = 0; UINT32 gbeEnd = 0; if (regionSection->GbeLimit) { gbeBegin = calculateRegionOffset(regionSection->GbeBase); gbeEnd = calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); gbe = intelImage.mid(gbeBegin, gbeEnd); gbeEnd += gbeBegin; } // PDR region QByteArray pdr; UINT32 pdrBegin = 0; UINT32 pdrEnd = 0; if (regionSection->PdrLimit) { pdrBegin = calculateRegionOffset(regionSection->PdrBase); pdrEnd = calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); pdr = intelImage.mid(pdrBegin, pdrEnd); pdrEnd += pdrBegin; } // EC region QByteArray ec; UINT32 ecBegin = 0; UINT32 ecEnd = 0; if (descriptorVersion == 2) { if (regionSection->EcLimit) { ecBegin = calculateRegionOffset(regionSection->EcBase); ecEnd = calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); ec = intelImage.mid(ecBegin, ecEnd); ecEnd += ecBegin; } } // Check for intersections between regions // Descriptor if (hasIntersection(descriptorBegin, descriptorEnd, gbeBegin, gbeEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with GbE region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (hasIntersection(descriptorBegin, descriptorEnd, meBegin, meEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with ME region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (hasIntersection(descriptorBegin, descriptorEnd, biosBegin, biosEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with BIOS region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (hasIntersection(descriptorBegin, descriptorEnd, pdrBegin, pdrEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorVersion == 2 && hasIntersection(descriptorBegin, descriptorEnd, ecBegin, ecEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, descriptor region has intersection with EC region")); return ERR_INVALID_FLASH_DESCRIPTOR; } // GbE if (hasIntersection(gbeBegin, gbeEnd, meBegin, meEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with ME region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (hasIntersection(gbeBegin, gbeEnd, biosBegin, biosEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with BIOS region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (hasIntersection(gbeBegin, gbeEnd, pdrBegin, pdrEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorVersion == 2 && hasIntersection(gbeBegin, gbeEnd, ecBegin, ecEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, GbE region has intersection with EC region")); return ERR_INVALID_FLASH_DESCRIPTOR; } // ME if (hasIntersection(meBegin, meEnd, biosBegin, biosEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with BIOS region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (hasIntersection(meBegin, meEnd, pdrBegin, pdrEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorVersion == 2 && hasIntersection(meBegin, meEnd, ecBegin, ecEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, ME region has intersection with EC region")); return ERR_INVALID_FLASH_DESCRIPTOR; } // BIOS if (hasIntersection(biosBegin, biosEnd, pdrBegin, pdrEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with PDR region")); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorVersion == 2 && hasIntersection(biosBegin, biosEnd, ecBegin, ecEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, BIOS region has intersection with EC region")); return ERR_INVALID_FLASH_DESCRIPTOR; } // PDR if (descriptorVersion == 2 && hasIntersection(pdrBegin, pdrEnd, ecBegin, ecEnd)) { msg(tr("parseIntelImage: descriptor parsing failed, PDR region has intersection with EC region")); return ERR_INVALID_FLASH_DESCRIPTOR; } // Region map is consistent // Intel image QString name = tr("Intel image"); QString info = tr("Full size: %1h (%2)\nFlash chips: %3\nMasters: %4\nPCH straps: %5\nCPU straps: %6\n") .hexarg(intelImage.size()).arg(intelImage.size()) .arg(descriptorMap->NumberOfFlashChips + 1) .arg(descriptorMap->NumberOfMasters + 1) .arg(descriptorMap->NumberOfPchStraps) .arg(descriptorMap->NumberOfProcStraps); // Add Intel image tree item index = model->addItem(Types::Image, Subtypes::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), intelImage, parent); // Descriptor // Get descriptor info QByteArray body = intelImage.left(FLASH_DESCRIPTOR_SIZE); name = tr("Descriptor region"); info = tr("Full size: %1h (%2)").hexarg(FLASH_DESCRIPTOR_SIZE).arg(FLASH_DESCRIPTOR_SIZE); // Check regions presence once again QVector offsets; if (regionSection->GbeLimit) { offsets.append(gbeBegin); info += tr("\nGbE region offset: %1h").hexarg(gbeBegin); } if (regionSection->MeLimit) { offsets.append(meBegin); info += tr("\nME region offset: %1h").hexarg(meBegin); } if (regionSection->BiosLimit) { offsets.append(biosBegin); info += tr("\nBIOS region offset: %1h").hexarg(biosBegin); } if (regionSection->PdrLimit) { offsets.append(pdrBegin); info += tr("\nPDR region offset: %1h").hexarg(pdrBegin); } if (descriptorVersion == 2 && regionSection->EcLimit) { offsets.append(ecBegin); info += tr("\nEC region offset: %1h").hexarg(ecBegin); } // Region access settings if (descriptorVersion == 1) { const FLASH_DESCRIPTOR_MASTER_SECTION* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION*)calculateAddress8(descriptor, descriptorMap->MasterBase); info += tr("\nRegion access settings:"); info += tr("\nBIOS:%1%2h ME:%3%4h GbE:%5%6h") .hexarg2(masterSection->BiosRead, 2) .hexarg2(masterSection->BiosWrite, 2) .hexarg2(masterSection->MeRead, 2) .hexarg2(masterSection->MeWrite, 2) .hexarg2(masterSection->GbeRead, 2) .hexarg2(masterSection->GbeWrite, 2); // BIOS access table info += tr("\nBIOS access table:"); info += tr("\n Read Write"); info += tr("\nDesc %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); info += tr("\nBIOS Yes Yes"); info += tr("\nME %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); info += tr("\nGbE %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); info += tr("\nPDR %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); } else if (descriptorVersion == 2) { const FLASH_DESCRIPTOR_MASTER_SECTION_V2* masterSection = (const FLASH_DESCRIPTOR_MASTER_SECTION_V2*)calculateAddress8(descriptor, descriptorMap->MasterBase); info += tr("\nRegion access settings:"); info += tr("\nBIOS: %1h %2h ME: %3h %4h\nGbE: %5h %6h EC: %7h %8h") .hexarg2(masterSection->BiosRead, 3) .hexarg2(masterSection->BiosWrite, 3) .hexarg2(masterSection->MeRead, 3) .hexarg2(masterSection->MeWrite, 3) .hexarg2(masterSection->GbeRead, 3) .hexarg2(masterSection->GbeWrite, 3) .hexarg2(masterSection->EcRead, 3) .hexarg2(masterSection->EcWrite, 3); // BIOS access table info += tr("\nBIOS access table:"); info += tr("\n Read Write"); info += tr("\nDesc %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_DESC ? "Yes " : "No "); info += tr("\nBIOS Yes Yes"); info += tr("\nME %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_ME ? "Yes " : "No "); info += tr("\nGbE %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_GBE ? "Yes " : "No "); info += tr("\nPDR %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_PDR ? "Yes " : "No "); info += tr("\nEC %1 %2") .arg(masterSection->BiosRead & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No ") .arg(masterSection->BiosWrite & FLASH_DESCRIPTOR_REGION_ACCESS_EC ? "Yes " : "No "); // Prepend descriptor version if present if (descriptorMap->DescriptorVersion != FLASH_DESCRIPTOR_VERSION_INVALID) { const FLASH_DESCRIPTOR_VERSION* version = (const FLASH_DESCRIPTOR_VERSION*)&descriptorMap->DescriptorVersion; QString versionStr = tr("Flash descriptor version: %1.%2").arg(version->Major).arg(version->Minor); if (version->Major != FLASH_DESCRIPTOR_VERSION_MAJOR || version->Minor != FLASH_DESCRIPTOR_VERSION_MINOR) { versionStr += tr(", unknown"); msg(tr("parseIntelImage: unknown flash descriptor version %1.%2").arg(version->Major).arg(version->Minor)); } info = versionStr + "\n" + info; } } // VSCC table const VSCC_TABLE_ENTRY* vsccTableEntry = (const VSCC_TABLE_ENTRY*)(descriptor + ((UINT16)upperMap->VsccTableBase << 4)); info += tr("\nFlash chips in VSCC table:"); UINT8 vsscTableSize = upperMap->VsccTableSize * sizeof(UINT32) / sizeof(VSCC_TABLE_ENTRY); for (int i = 0; i < vsscTableSize; i++) { info += tr("\n%1%2%3h") .hexarg2(vsccTableEntry->VendorId, 2) .hexarg2(vsccTableEntry->DeviceId0, 2) .hexarg2(vsccTableEntry->DeviceId1, 2); vsccTableEntry++; } // Add descriptor tree item model->addItem(Types::Region, Subtypes::DescriptorRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index); // Sort regions in ascending order qSort(offsets); // Parse regions UINT8 result = 0; for (int i = 0; i < offsets.count(); i++) { // Parse GbE region if (offsets.at(i) == gbeBegin) { QModelIndex gbeIndex; result = parseGbeRegion(gbe, gbeIndex, index); } // Parse ME region else if (offsets.at(i) == meBegin) { QModelIndex meIndex; result = parseMeRegion(me, meIndex, index); } // Parse BIOS region else if (offsets.at(i) == biosBegin) { QModelIndex biosIndex; result = parseBiosRegion(bios, biosIndex, index); } // Parse PDR region else if (offsets.at(i) == pdrBegin) { QModelIndex pdrIndex; result = parsePdrRegion(pdr, pdrIndex, index); } // Parse EC region else if (descriptorVersion == 2 && offsets.at(i) == ecBegin) { QModelIndex ecIndex; result = parseEcRegion(ec, ecIndex, index); } if (result) return result; } // Add the data after the last region as padding UINT32 IntelDataEnd = 0; UINT32 LastRegionOffset = offsets.last(); if (LastRegionOffset == gbeBegin) IntelDataEnd = gbeEnd; else if (LastRegionOffset == meBegin) IntelDataEnd = meEnd; else if (LastRegionOffset == biosBegin) IntelDataEnd = biosEnd; else if (LastRegionOffset == pdrBegin) IntelDataEnd = pdrEnd; else if (descriptorVersion == 2 && LastRegionOffset == ecBegin) IntelDataEnd = ecEnd; if (IntelDataEnd > (UINT32)intelImage.size()) { // Image file is truncated msg(tr("parseIntelImage: image size %1 (%2) is smaller than the end of last region %3 (%4), may be damaged") .hexarg(intelImage.size()).arg(intelImage.size()) .hexarg(IntelDataEnd).arg(IntelDataEnd), index); return ERR_TRUNCATED_IMAGE; } else if (IntelDataEnd < (UINT32)intelImage.size()) { // Insert padding QByteArray padding = intelImage.mid(IntelDataEnd); // Get info name = tr("Padding"); info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, index); } return ERR_SUCCESS; } UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { // Check sanity if (gbe.isEmpty()) return ERR_EMPTY_REGION; // Get info QString name = tr("GbE region"); const GBE_MAC_ADDRESS* mac = (const GBE_MAC_ADDRESS*)gbe.constData(); const GBE_VERSION* version = (const GBE_VERSION*)(gbe.constData() + GBE_VERSION_OFFSET); QString info = tr("Full size: %1h (%2)\nMAC: %3:%4:%5:%6:%7:%8\nVersion: %9.%10") .hexarg(gbe.size()).arg(gbe.size()) .hexarg2(mac->vendor[0], 2) .hexarg2(mac->vendor[1], 2) .hexarg2(mac->vendor[2], 2) .hexarg2(mac->device[0], 2) .hexarg2(mac->device[1], 2) .hexarg2(mac->device[2], 2) .arg(version->major) .arg(version->minor); // Add tree item index = model->addItem(Types::Region, Subtypes::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, parent, mode); return ERR_SUCCESS; } UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { // Check sanity if (me.isEmpty()) return ERR_EMPTY_REGION; // Get info QString name = tr("ME region"); QString info = tr("Full size: %1h (%2)"). hexarg(me.size()).arg(me.size()); // Parse region bool versionFound = true; bool emptyRegion = false; // Check for empty region if (me.count() == me.count('\xFF') || me.count() == me.count('\x00')) { // Further parsing not needed emptyRegion = true; info += tr("\nState: empty"); } else { // Search for new signature INT32 versionOffset = me.indexOf(ME_VERSION_SIGNATURE2); if (versionOffset < 0){ // New signature not found // Search for old signature versionOffset = me.indexOf(ME_VERSION_SIGNATURE); if (versionOffset < 0){ info += tr("\nVersion: unknown"); versionFound = false; } } // Add version information if (versionFound) { const ME_VERSION* version = (const ME_VERSION*)(me.constData() + versionOffset); info += tr("\nVersion: %1.%2.%3.%4") .arg(version->major) .arg(version->minor) .arg(version->bugfix) .arg(version->build); } } // Add tree item index = model->addItem(Types::Region, Subtypes::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, parent, mode); // Show messages if (emptyRegion) { msg(tr("parseRegion: ME region is empty"), index); } else if (!versionFound) { msg(tr("parseRegion: ME region version is unknown, it can be damaged"), index); } return ERR_SUCCESS; } UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { // Check sanity if (pdr.isEmpty()) return ERR_EMPTY_REGION; // Get info QString name = tr("PDR region"); QString info = tr("Full size: %1h (%2)"). hexarg(pdr.size()).arg(pdr.size()); // Add tree item index = model->addItem(Types::Region, Subtypes::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, parent, mode); // Parse PDR region as BIOS space UINT8 result = parseBios(pdr, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; return ERR_SUCCESS; } UINT8 FfsEngine::parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { // Check sanity if (ec.isEmpty()) return ERR_EMPTY_REGION; // Get info QString name = tr("EC region"); QString info = tr("Full size: %1h (%2)"). hexarg(ec.size()).arg(ec.size()); // Add tree item index = model->addItem(Types::Region, Subtypes::EcRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), ec, parent, mode); return ERR_SUCCESS; } UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { if (bios.isEmpty()) return ERR_EMPTY_REGION; // Get info QString name = tr("BIOS region"); QString info = tr("Full size: %1h (%2)"). hexarg(bios.size()).arg(bios.size()); // Add tree item index = model->addItem(Types::Region, Subtypes::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, parent, mode); return parseBios(bios, index); } UINT32 FfsEngine::getPaddingType(const QByteArray & padding) { if (padding.count('\x00') == padding.count()) return Subtypes::ZeroPadding; if (padding.count('\xFF') == padding.count()) return Subtypes::OnePadding; return Subtypes::DataPadding; } UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) { // Search for first volume UINT32 prevVolumeOffset; UINT8 result; result = findNextVolume(bios, 0, prevVolumeOffset); if (result) return result; // First volume is not at the beginning of BIOS space QString name; QString info; if (prevVolumeOffset > 0) { // Get info QByteArray padding = bios.left(prevVolumeOffset); name = tr("Padding"); info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } // Search for and parse all volumes UINT32 volumeOffset = prevVolumeOffset; UINT32 prevVolumeSize = 0; UINT32 volumeSize = 0; UINT32 bmVolumeSize = 0; while (true) { bool msgAlignmentBitsSet = false; bool msgUnaligned = false; bool msgUnknownRevision = false; bool msgSizeMismach = false; // Padding between volumes if (volumeOffset > prevVolumeOffset + prevVolumeSize) { UINT32 paddingSize = volumeOffset - prevVolumeOffset - prevVolumeSize; QByteArray padding = bios.mid(prevVolumeOffset + prevVolumeSize, paddingSize); // Get info name = tr("Padding"); info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } // Get volume size result = getVolumeSize(bios, volumeOffset, volumeSize, bmVolumeSize); if (result) { msg(tr("parseBios: getVolumeSize failed with error \"%1\"").arg(errorMessage(result)), parent); return result; } // Check that volume is fully present in input if (volumeSize > (UINT32)bios.size() || volumeOffset + volumeSize > (UINT32)bios.size()) { msg(tr("parseBios: one of volumes inside overlaps the end of data"), parent); return ERR_INVALID_VOLUME; } // Check reported size against a size calculated using block map if (volumeSize != bmVolumeSize) msgSizeMismach = true; // Check volume revision and alignment const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); UINT32 alignment; if (volumeHeader->Revision == 1) { // Acquire alignment capability bit bool alignmentCap = volumeHeader->Attributes & EFI_FVB_ALIGNMENT_CAP; if (!alignmentCap) { if (volumeHeader->Attributes & 0xFFFF0000) msgAlignmentBitsSet = true; } } else if (volumeHeader->Revision == 2) { // Acquire alignment alignment = (UINT32)(1UL << ((volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16)); // Check alignment if (volumeOffset % alignment) msgUnaligned = true; } else msgUnknownRevision = true; // Parse volume QModelIndex index; UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), index, parent); if (result) msg(tr("parseBios: volume parsing failed with error \"%1\"").arg(errorMessage(result)), parent); // Show messages if (msgAlignmentBitsSet) msg("parseBios: alignment bits set on volume without alignment capability", index); if (msgUnaligned) msg(tr("parseBios: unaligned revision 2 volume"), index); if (msgUnknownRevision) msg(tr("parseBios: unknown volume revision %1").arg(volumeHeader->Revision), index); if (msgSizeMismach) msg(tr("parseBios: volume size stored in header %1h differs from calculated using block map %2h") .hexarg(volumeSize).arg(bmVolumeSize), index); // Go to next volume prevVolumeOffset = volumeOffset; prevVolumeSize = volumeSize; result = findNextVolume(bios, volumeOffset + prevVolumeSize, volumeOffset); if (result) { UINT32 endPaddingSize = bios.size() - prevVolumeOffset - prevVolumeSize; // Padding at the end of BIOS space if (endPaddingSize > 0) { QByteArray padding = bios.right(endPaddingSize); // Get info name = tr("Padding"); info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); // Add tree item model->addItem(Types::Padding, getPaddingType(padding), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), padding, parent); } break; } } return ERR_SUCCESS; } UINT8 FfsEngine::findNextVolume(const QByteArray & bios, UINT32 volumeOffset, UINT32 & nextVolumeOffset) { int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); if (nextIndex < EFI_FV_SIGNATURE_OFFSET) { return ERR_VOLUMES_NOT_FOUND; } nextVolumeOffset = nextIndex - EFI_FV_SIGNATURE_OFFSET; return ERR_SUCCESS; } UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize) { // Check that there is space for the volume header and at least two block map entries. if ((UINT32)bios.size() < volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 2 * sizeof(EFI_FV_BLOCK_MAP_ENTRY)) return ERR_INVALID_VOLUME; // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + volumeOffset); // Check volume signature if (QByteArray((const char*)&volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE) return ERR_INVALID_VOLUME; // Calculate volume size using BlockMap const EFI_FV_BLOCK_MAP_ENTRY* entry = (const EFI_FV_BLOCK_MAP_ENTRY*)(bios.constData() + volumeOffset + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); UINT32 calcVolumeSize = 0; while (entry->NumBlocks != 0 && entry->Length != 0) { if ((void*)entry > bios.constData() + bios.size()) return ERR_INVALID_VOLUME; calcVolumeSize += entry->NumBlocks * entry->Length; entry += 1; } volumeSize = volumeHeader->FvLength; bmVolumeSize = calcVolumeSize; if (volumeSize == 0) return ERR_INVALID_VOLUME; return ERR_SUCCESS; } UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { // Check that there is space for the volume header if ((UINT32)volume.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { msg(tr("parseVolume: input volume size %1h (%2) is smaller than volume header size 40h (64)").hexarg(volume.size()).arg(volume.size())); return ERR_INVALID_VOLUME; } // Populate volume header const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(volume.constData()); // Check sanity of HeaderLength value if (ALIGN8(volumeHeader->HeaderLength) > volume.size()) { msg(tr("parseVolume: volume header overlaps the end of data")); return ERR_INVALID_VOLUME; } // Check sanity of ExtHeaderOffset value if (volumeHeader->ExtHeaderOffset > 0 && (UINT32)volume.size() < ALIGN8(volumeHeader->ExtHeaderOffset + sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER))) { msg(tr("parseVolume: extended volume header overlaps the end of data")); return ERR_INVALID_VOLUME; } // Calculate volume header size UINT32 headerSize; if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; } else headerSize = volumeHeader->HeaderLength; // Sanity check after some crazy MSI images headerSize = ALIGN8(headerSize); // Check for FFS v2/v3 volume UINT8 subtype = Subtypes::UnknownVolume; if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))){ subtype = Subtypes::Ffs2Volume; } else if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) { subtype = Subtypes::Ffs3Volume; } // Check attributes // Determine value of empty byte char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; // Get volume size UINT32 volumeSize; UINT32 bmVolumeSize; UINT8 result = getVolumeSize(volume, 0, volumeSize, bmVolumeSize); if (result) return result; // Check for Apple CRC32 in ZeroVector bool volumeHasZVCRC = false; bool volumeHasZVFSO = false; UINT32 crc32FromZeroVector = *(UINT32*)(volume.constData() + 8); UINT32 freeSpaceOffsetFromZeroVector = *(UINT32*)(volume.constData() + 12); if (crc32FromZeroVector != 0) { // Calculate CRC32 of the volume body UINT32 crc = crc32(0, (const UINT8*)(volume.constData() + volumeHeader->HeaderLength), volumeSize - volumeHeader->HeaderLength); if (crc == crc32FromZeroVector) { volumeHasZVCRC = true; } // Check for free space size in zero vector if (freeSpaceOffsetFromZeroVector != 0) { volumeHasZVFSO = true; } } // Check header checksum by recalculating it bool msgInvalidChecksum = false; if (calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength)) msgInvalidChecksum = true; // Get info QString name = guidToQString(volumeHeader->FileSystemGuid); QString info = tr("ZeroVector:\n%1 %2 %3 %4 %5 %6 %7 %8\n%9 %10 %11 %12 %13 %14 %15 %16\nFileSystem GUID: %17\nFull size: %18h (%19)\n" "Header size: %20h (%21)\nBody size: %22h (%23)\nRevision: %24\nAttributes: %25h\nErase polarity: %26") .hexarg2(volumeHeader->ZeroVector[0], 2).hexarg2(volumeHeader->ZeroVector[1], 2).hexarg2(volumeHeader->ZeroVector[2], 2).hexarg2(volumeHeader->ZeroVector[3], 2) .hexarg2(volumeHeader->ZeroVector[4], 2).hexarg2(volumeHeader->ZeroVector[5], 2).hexarg2(volumeHeader->ZeroVector[6], 2).hexarg2(volumeHeader->ZeroVector[7], 2) .hexarg2(volumeHeader->ZeroVector[8], 2).hexarg2(volumeHeader->ZeroVector[9], 2).hexarg2(volumeHeader->ZeroVector[10], 2).hexarg2(volumeHeader->ZeroVector[11], 2) .hexarg2(volumeHeader->ZeroVector[12], 2).hexarg2(volumeHeader->ZeroVector[13], 2).hexarg2(volumeHeader->ZeroVector[14], 2).hexarg2(volumeHeader->ZeroVector[15], 2) .arg(guidToQString(volumeHeader->FileSystemGuid)) .hexarg(volumeSize).arg(volumeSize) .hexarg(headerSize).arg(headerSize) .hexarg(volumeSize - headerSize).arg(volumeSize - headerSize) .arg(volumeHeader->Revision) .hexarg2(volumeHeader->Attributes, 8) .arg(empty ? "1" : "0"); // Extended header present if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)(volume.constData() + volumeHeader->ExtHeaderOffset); info += tr("\nExtended header size: %1h (%2)\nVolume GUID: %3") .hexarg(extendedHeader->ExtHeaderSize).arg(extendedHeader->ExtHeaderSize) .arg(guidToQString(extendedHeader->FvName)); } // Add text QString text; if (volumeHasZVCRC) text += tr("AppleCRC32 "); if (volumeHasZVFSO) text += tr("AppleFSO "); // Add tree item QByteArray header = volume.left(headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize); index = model->addItem(Types::Volume, subtype, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode); // Show messages if (subtype == Subtypes::UnknownVolume) { msg(tr("parseVolume: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index); // Do not parse unknown volumes return ERR_SUCCESS; } if (msgInvalidChecksum) { msg(tr("parseVolume: volume header checksum is invalid"), index); } // Search for and parse all files UINT32 fileOffset = headerSize; UINT32 fileSize; QQueue files; while (fileOffset < volumeSize) { bool msgUnalignedFile = false; bool msgDuplicateGuid = false; // Check if it's possibly the latest file in the volume if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER)) { // No files are possible after this point // All the rest is either free space or non-UEFI data QByteArray rest = volume.right(volumeSize - fileOffset); if (rest.count(empty) == rest.size()) { // It's a free space model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index); } else { //It's non-UEFI data QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } // Exit from loop break; } QByteArray tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER)); const EFI_FFS_FILE_HEADER* tempFileHeader = (const EFI_FFS_FILE_HEADER*)tempFile.constData(); UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER); fileSize = uint24ToUint32(tempFileHeader->Size); if (volumeHeader->Revision > 1 && (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { // Check if it's possibly the latest file in the volume if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER2)) { // No files are possible after this point // All the rest is either free space or non-UEFI data QByteArray rest = volume.right(volumeSize - fileOffset); if (rest.count(empty) == rest.size()) { // It's a free space model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index); } else { //It's non-UEFI data QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } // Exit from loop break; } fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2); tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER2)); const EFI_FFS_FILE_HEADER2* tempFileHeader2 = (const EFI_FFS_FILE_HEADER2*)tempFile.constData(); fileSize = (UINT32)tempFileHeader2->ExtendedSize; } // Check file size to be at least size of the header if (fileSize < fileHeaderSize) { msg(tr("parseVolume: volume has FFS file with invalid size"), index); return ERR_INVALID_FILE; } QByteArray file = volume.mid(fileOffset, fileSize); QByteArray header = file.left(fileHeaderSize); // If we are at empty space in the end of volume if (header.count(empty) == header.size()) { // Check free space to be actually free QByteArray freeSpace = volume.mid(fileOffset); if (freeSpace.count(empty) != freeSpace.count()) { // Search for the first non-empty byte UINT32 i; UINT32 size = freeSpace.size(); const CHAR8* current = freeSpace.constData(); for (i = 0; i < size; i++) { if (*current++ != empty) break; } // Align found index to file alignment // It must be possible because minimum 16 bytes of empty were found before if (i != ALIGN8(i)) i = ALIGN8(i) - 8; // Add all bytes before as free space... if (i > 0) { QByteArray free = freeSpace.left(i); model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index); } // ... and all bytes after as a padding QByteArray padding = freeSpace.mid(i); QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index); msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex); } else { // Add free space element model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(freeSpace.size()).arg(freeSpace.size()), QByteArray(), freeSpace, index); } break; // Exit from loop } // Check file alignment const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData(); UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT2)) alignmentPower = ffsAlignment2Table[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; UINT32 alignment = (UINT32)(1UL << alignmentPower); if ((fileOffset + fileHeaderSize) % alignment) msgUnalignedFile = true; // Check file GUID if (fileHeader->Type != EFI_FV_FILETYPE_PAD && files.indexOf(header.left(sizeof(EFI_GUID))) != -1) msgDuplicateGuid = true; // Add file GUID to queue files.enqueue(header.left(sizeof(EFI_GUID))); // Parse file QModelIndex fileIndex; result = parseFile(file, fileIndex, volumeHeader->Revision, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index); // Show messages if (msgUnalignedFile) msg(tr("parseVolume: unaligned file %1").arg(guidToQString(fileHeader->Name)), fileIndex); if (msgDuplicateGuid) msg(tr("parseVolume: file with duplicate GUID %1").arg(guidToQString(fileHeader->Name)), fileIndex); // Move to next file fileOffset += fileSize; fileOffset = ALIGN8(fileOffset); } return ERR_SUCCESS; } UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode) { bool msgInvalidHeaderChecksum = false; bool msgInvalidDataChecksum = false; bool msgInvalidTailValue = false; bool msgInvalidType = false; // Populate file header if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER)) return ERR_INVALID_FILE; const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData(); // Construct empty byte for this file char empty = erasePolarity ? '\xFF' : '\x00'; // Get file header QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2)) return ERR_INVALID_FILE; header = file.left(sizeof(EFI_FFS_FILE_HEADER2)); } // Check header checksum UINT8 calculatedHeader = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State); if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader) msgInvalidHeaderChecksum = true; // Get file body QByteArray body = file.mid(header.size()); // Check for file tail presence QByteArray tail; if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { //Check file tail; tail = body.right(sizeof(UINT16)); UINT16 tailValue = *(UINT16*)tail.constData(); if (fileHeader->IntegrityCheck.TailReference != (UINT16)~tailValue) msgInvalidTailValue = true; // Remove tail from file body body = body.left(body.size() - sizeof(UINT16)); } // Check data checksum // Data checksum must be calculated UINT8 calculatedData = 0; if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { calculatedData = calculateChecksum8((const UINT8*)body.constData(), body.size()); } // Data checksum must be one of predefined values else if (revision == 1) { calculatedData = FFS_FIXED_CHECKSUM; } else { calculatedData = FFS_FIXED_CHECKSUM2; } if (fileHeader->IntegrityCheck.Checksum.File != calculatedData) msgInvalidDataChecksum = true; // Parse current file by default bool parseCurrentFile = false; bool parseAsBios = false; // Check file type switch (fileHeader->Type) { case EFI_FV_FILETYPE_ALL: case EFI_FV_FILETYPE_RAW: parseAsBios = true; case EFI_FV_FILETYPE_FREEFORM: case EFI_FV_FILETYPE_SECURITY_CORE: case EFI_FV_FILETYPE_PEI_CORE: case EFI_FV_FILETYPE_DXE_CORE: case EFI_FV_FILETYPE_PEIM: case EFI_FV_FILETYPE_DRIVER: case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: case EFI_FV_FILETYPE_APPLICATION: case EFI_FV_FILETYPE_SMM: case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: case EFI_FV_FILETYPE_COMBINED_SMM_DXE: case EFI_FV_FILETYPE_SMM_CORE: case EFI_FV_FILETYPE_SMM_STANDALONE: case EFI_FV_FILETYPE_SMM_CORE_STANDALONE: case EFI_FV_FILETYPE_PAD: parseCurrentFile = true; break; default: msgInvalidType = true; }; // Check for empty file bool parseAsNonEmptyPadFile = false; if (body.count(empty) == body.size()) { // No need to parse empty files parseCurrentFile = false; } // Check for non-empty pad file else if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { parseAsNonEmptyPadFile = true; } // Get info QString name; QString info; if (fileHeader->Type != EFI_FV_FILETYPE_PAD) name = guidToQString(fileHeader->Name); else name = parseAsNonEmptyPadFile ? tr("Non-empty pad-file") : tr("Pad-file"); info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h\nHeader checksum: %11h\nData checksum: %12h") .arg(guidToQString(fileHeader->Name)) .hexarg2(fileHeader->Type, 2) .hexarg2(fileHeader->Attributes, 2) .hexarg(header.size() + body.size() + tail.size()).arg(header.size() + body.size() + tail.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .hexarg2(fileHeader->State, 2) .hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2) .hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2); // Add tree item index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidHeaderChecksum) msg(tr("parseFile: invalid header checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2).hexarg2(calculatedHeader, 2), index); if (msgInvalidDataChecksum) msg(tr("parseFile: invalid data checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2).hexarg2(calculatedData, 2), index); if (msgInvalidTailValue) msg(tr("parseFile: invalid tail value %1h").hexarg(*(UINT16*)tail.data()), index); if (msgInvalidType) msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index); // No parsing needed if (!parseCurrentFile) return ERR_SUCCESS; // Parse non-empty pad file if (parseAsNonEmptyPadFile) { // Search for the first non-empty byte UINT32 i; UINT32 size = body.size(); const CHAR8* current = body.constData(); for (i = 0; i < size; i++) { if (*current++ != empty) break; } // Add all bytes before as free space... if (i > 0) { QByteArray free = body.left(i); model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Free space"), "", tr("Full size: %1h (%2)").hexarg(free.size()).arg(free.size()), QByteArray(), free, index, mode); } // ... and all bytes after as a padding QByteArray padding = body.mid(i); QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(padding.size()).arg(padding.size()), QByteArray(), padding, index, mode); // Show message msg(tr("parseFile: non-empty pad-file contents will be destroyed after volume modifications"), dataIndex); return ERR_SUCCESS; } // Parse file as BIOS space UINT8 result; if (parseAsBios) { result = parseBios(body, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) msg(tr("parseFile: parsing file as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } // Parse sections result = parseSections(body, index); if (result) return result; return ERR_SUCCESS; } UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize) { if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER)) return ERR_INVALID_FILE; const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset); sectionSize = uint24ToUint32(sectionHeader->Size); // This may introduce a very rare error with a non-extended section of size equal to 0xFFFFFF if (sectionSize != 0xFFFFFF) return ERR_SUCCESS; if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER2)) return ERR_INVALID_FILE; const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset); sectionSize = sectionHeader2->ExtendedSize; return ERR_SUCCESS; } UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & parent) { // Search for and parse all sections UINT32 sectionOffset = 0; UINT32 sectionSize; UINT32 bodySize = body.size(); UINT8 result; while (true) { // Get section size result = getSectionSize(body, sectionOffset, sectionSize); if (result) return result; // Exit from loop if no sections left if (sectionSize == 0) break; // Parse section QModelIndex sectionIndex; result = parseSection(body.mid(sectionOffset, sectionSize), sectionIndex, parent); if (result) return result; // Move to next section sectionOffset += sectionSize; sectionOffset = ALIGN4(sectionOffset); // Exit from loop if no sections left if (sectionOffset >= bodySize) break; } return ERR_SUCCESS; } void FfsEngine::parseAprioriRawSection(const QByteArray & body, QString & parsed) { parsed.clear(); UINT32 count = body.size() / sizeof(EFI_GUID); if (count > 0) { for (UINT32 i = 0; i < count; i++) { const EFI_GUID* guid = (const EFI_GUID*)body.constData() + i; parsed += tr("\n%1").arg(guidToQString(*guid)); } } } UINT8 FfsEngine::parseDepexSection(const QByteArray & body, QString & parsed) { parsed.clear(); // Check data to be present if (!body.size()) return ERR_INVALID_PARAMETER; const EFI_GUID * guid; const UINT8* current = (const UINT8*)body.constData(); // Special cases of first opcode switch (*current) { case EFI_DEP_BEFORE: if (body.size() != 2*EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) return ERR_DEPEX_PARSE_FAILED; guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); parsed += tr("\nBEFORE %1").arg(guidToQString(*guid)); current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); if (*current != EFI_DEP_END) return ERR_DEPEX_PARSE_FAILED; return ERR_SUCCESS; case EFI_DEP_AFTER: if (body.size() != 2 * EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) return ERR_DEPEX_PARSE_FAILED; guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); parsed += tr("\nAFTER %1").arg(guidToQString(*guid)); current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); if (*current != EFI_DEP_END) return ERR_DEPEX_PARSE_FAILED; return ERR_SUCCESS; case EFI_DEP_SOR: if (body.size() <= 2 * EFI_DEP_OPCODE_SIZE) { return ERR_DEPEX_PARSE_FAILED; } parsed += tr("\nSOR"); current += EFI_DEP_OPCODE_SIZE; break; } // Parse the rest of depex while (current - (const UINT8*)body.constData() < body.size()) { switch (*current) { case EFI_DEP_BEFORE: case EFI_DEP_AFTER: case EFI_DEP_SOR: return ERR_DEPEX_PARSE_FAILED; case EFI_DEP_PUSH: // Check that the rest of depex has correct size if ((UINT32)body.size() - (UINT32)(current - (const UINT8*)body.constData()) <= EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID)) { parsed.clear(); return ERR_DEPEX_PARSE_FAILED; } guid = (const EFI_GUID*)(current + EFI_DEP_OPCODE_SIZE); parsed += tr("\nPUSH %1").arg(guidToQString(*guid)); current += EFI_DEP_OPCODE_SIZE + sizeof(EFI_GUID); break; case EFI_DEP_AND: parsed += tr("\nAND"); current += EFI_DEP_OPCODE_SIZE; break; case EFI_DEP_OR: parsed += tr("\nOR"); current += EFI_DEP_OPCODE_SIZE; break; case EFI_DEP_NOT: parsed += tr("\nNOT"); current += EFI_DEP_OPCODE_SIZE; break; case EFI_DEP_TRUE: parsed += tr("\nTRUE"); current += EFI_DEP_OPCODE_SIZE; break; case EFI_DEP_FALSE: parsed += tr("\nFALSE"); current += EFI_DEP_OPCODE_SIZE; break; case EFI_DEP_END: parsed += tr("\nEND"); current += EFI_DEP_OPCODE_SIZE; // Check that END is the last opcode if (current - (const UINT8*)body.constData() < body.size()) { parsed.clear(); return ERR_DEPEX_PARSE_FAILED; } break; default: return ERR_DEPEX_PARSE_FAILED; break; } } return ERR_SUCCESS; } UINT8 x86Convert(QByteArray & input, int mode) { unsigned char* source = (unsigned char*)input.data(); UINT32 sourceSize = input.size(); UINT32 state; x86_Convert_Init(state); UINT32 converted = x86_Convert(source, sourceSize, 0, &state, mode); const UINT8 x86LookAhead = 4; if (converted + x86LookAhead != sourceSize) { return ERR_INVALID_VOLUME; } return ERR_SUCCESS; } UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(section.constData()); QString name = sectionTypeToQString(sectionHeader->Type) + tr(" section"); QString info; QByteArray header; QByteArray body; UINT32 headerSize = sizeOfSectionHeader(sectionHeader); UINT8 result; switch (sectionHeader->Type) { // Encapsulated sections case EFI_SECTION_COMPRESSION: { bool parseCurrentSection = true; QByteArray decompressed; UINT8 algorithm; const EFI_COMPRESSION_SECTION* compressedSectionHeader = (const EFI_COMPRESSION_SECTION*)sectionHeader; header = section.left(headerSize); body = section.mid(headerSize); algorithm = COMPRESSION_ALGORITHM_UNKNOWN; // Decompress section result = decompress(body, compressedSectionHeader->CompressionType, decompressed, &algorithm); if (result) parseCurrentSection = false; // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nCompression type: %8\nDecompressed size: %9h (%10)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .arg(compressionTypeToQString(algorithm)) .hexarg(compressedSectionHeader->UncompressedLength).arg(compressedSectionHeader->UncompressedLength); UINT32 dictionarySize = DEFAULT_LZMA_DICTIONARY_SIZE; if (algorithm == COMPRESSION_ALGORITHM_LZMA) { // Dictionary size is stored in bytes 1-4 of LZMA-compressed data dictionarySize = *(UINT32*)(body.constData() + 1); info += tr("\nLZMA dictionary size: %1h").hexarg(dictionarySize); } // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); model->setDictionarySize(index, dictionarySize); // Show message if (!parseCurrentSection) msg(tr("parseSection: decompression failed with error \"%1\"").arg(errorMessage(result)), index); else { // Parse decompressed data result = parseSections(decompressed, index); if (result) return result; } } break; case EFI_SECTION_GUID_DEFINED: { bool parseCurrentSection = true; bool msgUnknownGuid = false; bool msgInvalidCrc = false; bool msgUnknownAuth = false; bool msgSigned = false; bool msgInvalidSignatureLength = false; bool msgUnknownSignature = false; bool msgUnknownUefiGuidSignature = false; header = section.left(headerSize); body = section.mid(headerSize); const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(header.constData()); QByteArray processed = body; // Get info name = guidToQString(guidDefinedSectionHeader->SectionDefinitionGuid); info = tr("Section GUID: %1\nType: %2h\nFull size: %3h (%4)\nHeader size: %5h (%6)\nBody size: %7h (%8)\nData offset: %9h\nAttributes: %10h") .arg(name) .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .hexarg(guidDefinedSectionHeader->DataOffset) .hexarg2(guidDefinedSectionHeader->Attributes, 4); UINT8 algorithm = COMPRESSION_ALGORITHM_NONE; UINT32 dictionarySize = DEFAULT_LZMA_DICTIONARY_SIZE; // Check if section requires processing QByteArray parsedGuid = QByteArray((const char*)&guidDefinedSectionHeader->SectionDefinitionGuid, sizeof(EFI_GUID)); if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { // Tiano compressed section if (parsedGuid == EFI_GUIDED_SECTION_TIANO) { algorithm = COMPRESSION_ALGORITHM_UNKNOWN; result = decompress(body, EFI_STANDARD_COMPRESSION, processed, &algorithm); if (result) parseCurrentSection = false; if (algorithm == COMPRESSION_ALGORITHM_TIANO) { info += tr("\nCompression type: Tiano"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); } else if (algorithm == COMPRESSION_ALGORITHM_EFI11) { info += tr("\nCompression type: EFI 1.1"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); } else info += tr("\nCompression type: unknown"); } // LZMA compressed section else if (parsedGuid == EFI_GUIDED_SECTION_LZMA || parsedGuid == EFI_GUIDED_SECTION_LZMAF86) { algorithm = COMPRESSION_ALGORITHM_UNKNOWN; result = decompress(body, EFI_CUSTOMIZED_COMPRESSION, processed, &algorithm); if (result) parseCurrentSection = false; if (parsedGuid == EFI_GUIDED_SECTION_LZMAF86) { if (x86Convert(processed, 0) != ERR_SUCCESS) { msg(tr("parseSection: unable to convert LZMAF86 compressed data")); } } if (algorithm == COMPRESSION_ALGORITHM_LZMA) { info += tr("\nCompression type: LZMA"); info += tr("\nDecompressed size: %1h (%2)").hexarg(processed.length()).arg(processed.length()); // Dictionary size is stored in bytes 1-4 of LZMA-compressed data dictionarySize = *(UINT32*)(body.constData() + 1); info += tr("\nLZMA dictionary size: %1h").hexarg(dictionarySize); } else info += tr("\nCompression type: unknown"); } // Signed section else if (parsedGuid == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { msgSigned = true; const WIN_CERTIFICATE* certificateHeader = (const WIN_CERTIFICATE*)body.constData(); if ((UINT32)body.size() < sizeof(WIN_CERTIFICATE)) { info += tr("\nSignature type: invalid, wrong length"); msgInvalidSignatureLength = true; parseCurrentSection = false; } else if (certificateHeader->CertificateType == WIN_CERT_TYPE_EFI_GUID) { info += tr("\nSignature type: UEFI"); const WIN_CERTIFICATE_UEFI_GUID* guidCertificateHeader = (const WIN_CERTIFICATE_UEFI_GUID*)certificateHeader; if (QByteArray((const char*)&guidCertificateHeader->CertType, sizeof(EFI_GUID)) == EFI_CERT_TYPE_RSA2048_SHA256_GUID) { info += tr("\nSignature subtype: RSA2048/SHA256"); // TODO: show signature info in Information panel } else if (QByteArray((const char*)&guidCertificateHeader->CertType, sizeof(EFI_GUID)) == EFI_CERT_TYPE_PKCS7_GUID) { info += tr("\nSignature subtype: PCKS7"); // TODO: show signature info in Information panel } else { info += tr("\nSignature subtype: unknown"); msgUnknownUefiGuidSignature = true; } } else if (certificateHeader->CertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { info += tr("\nSignature type: PKCS7"); // TODO: show signature info in Information panel } else { info += tr("\nSignature type: unknown"); msgUnknownSignature = true; } if ((UINT32)body.size() < certificateHeader->Length) { info += tr("\nSignature type: invalid, wrong length"); msgInvalidSignatureLength = true; parseCurrentSection = false; } else { // Add additional data to the header header.append(body.left(certificateHeader->Length)); // Get new body processed = body = body.mid(certificateHeader->Length); } } // Unknown GUIDed section else { msgUnknownGuid = true; parseCurrentSection = false; } } // Check if section requires checksum calculation else if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { // CRC32 section if (parsedGuid == EFI_GUIDED_SECTION_CRC32) { info += tr("\nChecksum type: CRC32"); // Calculate CRC32 of section data UINT32 crc = crc32(0, (const UINT8*)body.constData(), body.size()); // Check stored CRC32 if (crc == *(const UINT32*)(header.constData() + sizeof(EFI_GUID_DEFINED_SECTION))) { info += tr("\nChecksum: valid"); } else { info += tr("\nChecksum: invalid"); msgInvalidCrc = true; } } else msgUnknownAuth = true; } // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, algorithm, name, "", info, header, body, parent, mode); model->setDictionarySize(index, dictionarySize); // Show messages if (msgUnknownGuid) msg(tr("parseSection: GUID defined section with unknown processing method"), index); if (msgUnknownAuth) msg(tr("parseSection: GUID defined section with unknown authentication method"), index); if (msgInvalidCrc) msg(tr("parseSection: GUID defined section with invalid CRC32"), index); if (msgSigned) msg(tr("parseSection: signature may become invalid after any modification"), index); if (msgUnknownUefiGuidSignature) msg(tr("parseSection: GUID defined section with unknown signature subtype"), index); if (msgInvalidSignatureLength) msg(tr("parseSection: GUID defined section with invalid signature length"), index); if (msgUnknownSignature) msg(tr("parseSection: GUID defined section with unknown signature type"), index); if (!parseCurrentSection) { msg(tr("parseSection: GUID defined section can not be processed"), index); } else { // Parse processed data result = parseSections(processed, index); if (result) return result; } } break; case EFI_SECTION_DISPOSABLE: { header = section.left(headerSize); body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body result = parseSections(body, index); if (result) return result; } break; // Leaf sections case EFI_SECTION_DXE_DEPEX: case EFI_SECTION_PEI_DEPEX: case EFI_SECTION_SMM_DEPEX: { bool msgDepexParseFailed = false; header = section.left(headerSize); body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Parse dependency expression QString str; result = parseDepexSection(body, str); if (result) msgDepexParseFailed = true; else if (str.count()) info += tr("\nParsed expression:%1").arg(str); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgDepexParseFailed) msg(tr("parseSection: dependency expression parsing failed"), index); } break; case EFI_SECTION_TE: { header = section.left(headerSize); body = section.mid(headerSize); // Get standard info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Get TE info bool msgInvalidSignature = false; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)body.constData(); // Most EFI images today include teFixup in ImageBase value, // which doesn't follow the UEFI spec, but is so popular that // only a few images out of thousands are different UINT32 teFixup = 0; //teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); if (teHeader->Signature != EFI_IMAGE_TE_SIGNATURE) { info += tr("\nSignature: %1h, invalid").hexarg2(teHeader->Signature, 4); msgInvalidSignature = true; } else { info += tr("\nSignature: %1h\nMachine type: %2\nNumber of sections: %3\nSubsystem: %4h\nStrippedSize: %5h (%6)\nBaseOfCode: %7h\nRelativeEntryPoint: %8h\nImageBase: %9h\nEntryPoint: %10h") .hexarg2(teHeader->Signature, 4) .arg(machineTypeToQString(teHeader->Machine)) .arg(teHeader->NumberOfSections) .hexarg2(teHeader->Subsystem, 2) .hexarg(teHeader->StrippedSize).arg(teHeader->StrippedSize) .hexarg(teHeader->BaseOfCode) .hexarg(teHeader->AddressOfEntryPoint) .hexarg(teHeader->ImageBase) .hexarg(teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup); } // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidSignature) { msg("parseSection: TE image with invalid TE signature", index); } // Special case of PEI Core QModelIndex core = model->findParentOfType(index, Types::File); if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE && oldPeiCoreEntryPoint == 0) { result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); if (result) msg(tr("parseSection: can't get original PEI core entry point"), index); } } break; case EFI_SECTION_PE32: case EFI_SECTION_PIC: { header = section.left(headerSize); body = section.mid(headerSize); // Get standard info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Get PE info bool msgInvalidDosSignature = false; bool msgInvalidDosHeader = false; bool msgInvalidPeSignature = false; bool msgUnknownOptionalHeaderSignature = false; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)body.constData(); if (dosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) { info += tr("\nDOS signature: %1h, invalid").hexarg2(dosHeader->e_magic, 4); msgInvalidDosSignature = true; } else { const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); if ((UINT32)body.size() < dosHeader->e_lfanew + sizeof(EFI_IMAGE_PE_HEADER)) { info += tr("\nDOS lfanew: %1h, invalid").hexarg2(dosHeader->e_lfanew, 8); msgInvalidDosHeader = true; } else if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) { info += tr("\nPE signature: %1h, invalid").hexarg2(peHeader->Signature, 8); msgInvalidPeSignature = true; } else { const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1); info += tr("\nDOS signature: %1h\nPE signature: %2h\nMachine type: %3\nNumber of sections: %4\nCharacteristics: %5h") .hexarg2(dosHeader->e_magic, 4) .hexarg2(peHeader->Signature, 8) .arg(machineTypeToQString(imageFileHeader->Machine)) .arg(imageFileHeader->NumberOfSections) .hexarg2(imageFileHeader->Characteristics, 4); EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader; optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1); if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") .hexarg2(optionalHeader.H32->Magic, 4) .hexarg2(optionalHeader.H32->Subsystem, 4) .hexarg(optionalHeader.H32->AddressOfEntryPoint) .hexarg(optionalHeader.H32->BaseOfCode) .hexarg(optionalHeader.H32->ImageBase) .hexarg(optionalHeader.H32->ImageBase + optionalHeader.H32->AddressOfEntryPoint); } else if (optionalHeader.H32->Magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { info += tr("\nOptional header signature: %1h\nSubsystem: %2h\nRelativeEntryPoint: %3h\nBaseOfCode: %4h\nImageBase: %5h\nEntryPoint: %6h") .hexarg2(optionalHeader.H64->Magic, 4) .hexarg2(optionalHeader.H64->Subsystem, 4) .hexarg(optionalHeader.H64->AddressOfEntryPoint) .hexarg(optionalHeader.H64->BaseOfCode) .hexarg(optionalHeader.H64->ImageBase) .hexarg(optionalHeader.H64->ImageBase + optionalHeader.H64->AddressOfEntryPoint); } else { info += tr("\nOptional header signature: %1h, unknown").hexarg2(optionalHeader.H64->Magic, 4); msgUnknownOptionalHeaderSignature = true; } } } // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Show messages if (msgInvalidDosSignature) { msg("parseSection: PE32 image with invalid DOS signature", index); } if (msgInvalidDosHeader) { msg("parseSection: PE32 image with invalid DOS header", index); } if (msgInvalidPeSignature) { msg("parseSection: PE32 image with invalid PE signature", index); } if (msgUnknownOptionalHeaderSignature) { msg("parseSection: PE32 image with unknown optional header signature", index); } // Special case of PEI Core QModelIndex core = model->findParentOfType(index, Types::File); if (core.isValid() && model->subtype(core) == EFI_FV_FILETYPE_PEI_CORE && oldPeiCoreEntryPoint == 0) { result = getEntryPoint(model->body(index), oldPeiCoreEntryPoint); if (result) msg(tr("parseSection: can't get original PEI core entry point"), index); } } break; case EFI_SECTION_COMPATIBILITY16: { header = section.left(headerSize); body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; case EFI_SECTION_FREEFORM_SUBTYPE_GUID: { header = section.left(headerSize); body = section.mid(headerSize); const EFI_FREEFORM_SUBTYPE_GUID_SECTION* fsgHeader = (const EFI_FREEFORM_SUBTYPE_GUID_SECTION*)sectionHeader; // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nSubtype GUID: %8") .hexarg2(fsgHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .arg(guidToQString(fsgHeader->SubTypeGuid)); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Rename section model->setName(index, guidToQString(fsgHeader->SubTypeGuid)); } break; case EFI_SECTION_VERSION: { header = section.left(headerSize); body = section.mid(headerSize); const EFI_VERSION_SECTION* versionHeader = (const EFI_VERSION_SECTION*)sectionHeader; // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nBuild number: %8\nVersion string: %9") .hexarg2(versionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .arg(versionHeader->BuildNumber) .arg(QString::fromUtf16((const ushort*)body.constData())); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; case EFI_SECTION_USER_INTERFACE: { header = section.left(headerSize); body = section.mid(headerSize); QString text = QString::fromUtf16((const ushort*)body.constData()); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nText: %8") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .arg(text); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Rename parent file model->setText(model->findParentOfType(parent, Types::File), text); } break; case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: { header = section.left(headerSize); body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space result = parseBios(body, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { msg(tr("parseSection: parsing firmware volume image section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } } break; case EFI_SECTION_RAW: { bool parsed = false; header = section.left(headerSize); body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Check for apriori file QModelIndex parentFile = model->findParentOfType(parent, Types::File); QByteArray parentFileGuid = model->header(parentFile).left(sizeof(EFI_GUID)); if (parentFileGuid == EFI_PEI_APRIORI_FILE_GUID) { // Mark file as parsed parsed = true; // Parse apriori file list QString str; parseAprioriRawSection(body, str); if (str.count()) info += tr("\nFile list:%1").arg(str); // Set parent file text model->setText(parentFile, tr("PEI apriori file")); } else if (parentFileGuid == EFI_DXE_APRIORI_FILE_GUID) { // Mark file as parsed parsed = true; // Parse apriori file list QString str; parseAprioriRawSection(body, str); if (str.count()) info += tr("\nFile list:%1").arg(str); // Set parent file text model->setText(parentFile, tr("DXE apriori file")); } // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); // Parse section body as BIOS space if (!parsed) { result = parseBios(body, index); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) { msg(tr("parseSection: parsing raw section as BIOS failed with error \"%1\"").arg(errorMessage(result)), index); return result; } } } break; case SCT_SECTION_POSTCODE: case INSYDE_SECTION_POSTCODE: { header = section.left(headerSize); body = section.mid(headerSize); const POSTCODE_SECTION* postcodeHeader = (const POSTCODE_SECTION*)sectionHeader; // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nPostcode: %8h") .hexarg2(postcodeHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()) .hexarg(postcodeHeader->Postcode); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); } break; default: header = section.left(headerSize); body = section.mid(headerSize); // Get info info = tr("Type: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)") .hexarg2(sectionHeader->Type, 2) .hexarg(section.size()).arg(section.size()) .hexarg(header.size()).arg(header.size()) .hexarg(body.size()).arg(body.size()); // Add tree item index = model->addItem(Types::Section, sectionHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode); msg(tr("parseSection: section with unknown type %1h").hexarg2(sectionHeader->Type, 2), index); } return ERR_SUCCESS; } // Operations on tree items UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByteArray & header, const QByteArray & body, const UINT8 mode, const UINT8 action, const UINT8 algorithm) { QByteArray created; UINT8 result; QModelIndex fileIndex; UINT32 defaultDictionarySize = DEFAULT_LZMA_DICTIONARY_SIZE; if (!index.isValid() || !index.parent().isValid()) return ERR_INVALID_PARAMETER; QModelIndex parent; if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) parent = index.parent(); else parent = index; // Create item if (type == Types::Region) { UINT8 type = model->subtype(index); switch (type) { case Subtypes::BiosRegion: result = parseBiosRegion(body, fileIndex, index, mode); break; case Subtypes::MeRegion: result = parseMeRegion(body, fileIndex, index, mode); break; case Subtypes::GbeRegion: result = parseGbeRegion(body, fileIndex, index, mode); break; case Subtypes::PdrRegion: result = parsePdrRegion(body, fileIndex, index, mode); break; default: return ERR_NOT_IMPLEMENTED; } if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set action model->setAction(fileIndex, action); } else if (type == Types::Padding) { // Get info QString name = tr("Padding"); QString info = tr("Full size: %1h (%2)") .hexarg(body.size()).arg(body.size()); // Add tree item QModelIndex fileIndex = model->addItem(Types::Padding, getPaddingType(body), COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), body, index, mode); // Set action model->setAction(fileIndex, action); } else if (type == Types::Volume) { QByteArray volume; if (header.isEmpty()) // Whole volume volume.append(body); else { // Body only volume.append(header).append(body); INT32 sizeDiff = model->body(index).size() - body.size(); if (sizeDiff > 0) { const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(index).constData(); bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY; volume.append(QByteArray(sizeDiff, erasePolarity ? '\xFF' : '\x00')); } } result = parseVolume(volume, fileIndex, index, mode); if (result) return result; // Set action model->setAction(fileIndex, action); } else if (type == Types::File) { if (model->type(parent) != Types::Volume) return ERR_INVALID_FILE; const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(parent).constData(); UINT8 revision = volumeHeader->Revision; bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY; if (header.size() != sizeof(EFI_FFS_FILE_HEADER)) return ERR_INVALID_FILE; QByteArray newObject = header + body; EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newObject.data(); // Determine correct file header size bool largeFile = false; UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { largeFile = true; headerSize = sizeof(EFI_FFS_FILE_HEADER2); } QByteArray newHeader = newObject.left(headerSize); QByteArray newBody = newObject.mid(headerSize); // Check if the file has a tail UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; if (tailSize) { // Remove the tail, it will then be added back for revision 1 volumes newBody = newBody.left(newBody.size() - tailSize); } // Correct file size if (!largeFile) { if (newBody.size() >= 0xFFFFFF) { return ERR_INVALID_FILE; } uint32ToUint24(headerSize + newBody.size() + tailSize, fileHeader->Size); } else { uint32ToUint24(0xFFFFFF, fileHeader->Size); EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*)newHeader.data(); fileHeader2->ExtendedSize = headerSize + newBody.size() + tailSize; } // Set file state UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; if (erasePolarity) state = ~state; fileHeader->State = state; // Recalculate header checksum fileHeader->IntegrityCheck.Checksum.Header = 0; fileHeader->IntegrityCheck.Checksum.File = 0; fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)newHeader.constData(), headerSize) - fileHeader->State); // Recalculate data checksum, if needed if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)newBody.constData(), newBody.size()); else if (revision == 1) fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; else fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; // Append new body created.append(newBody); // Append tail, if needed if (revision == 1 && tailSize) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; created.append(ht).append(ft); } // Prepend header created.prepend(newHeader); // Parse file result = parseFile(created, fileIndex, revision, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set action model->setAction(fileIndex, action); // Rebase all PEI-files that follow rebasePeiFiles(fileIndex); } else if (type == Types::Section) { if (model->type(parent) != Types::File && model->type(parent) != Types::Section) return ERR_INVALID_SECTION; if ((UINT32)header.size() < sizeof(EFI_COMMON_SECTION_HEADER)) return ERR_INVALID_SECTION; QByteArray newHeader = header; EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)newHeader.data(); if (uint24ToUint32(commonHeader->Size) == EFI_SECTION2_IS_USED) { msg(tr("create: creation of large sections not supported yet"), index); return ERR_NOT_IMPLEMENTED; } switch (commonHeader->Type) { case EFI_SECTION_COMPRESSION: { EFI_COMPRESSION_SECTION* sectionHeader = (EFI_COMPRESSION_SECTION*)newHeader.data(); // Correct uncompressed size sectionHeader->UncompressedLength = body.size(); // Set compression type if (algorithm == COMPRESSION_ALGORITHM_NONE) { sectionHeader->CompressionType = EFI_NOT_COMPRESSED; } else if (algorithm == COMPRESSION_ALGORITHM_EFI11 || algorithm == COMPRESSION_ALGORITHM_TIANO) { sectionHeader->CompressionType = EFI_STANDARD_COMPRESSION; } else if (algorithm == COMPRESSION_ALGORITHM_LZMA || algorithm == COMPRESSION_ALGORITHM_IMLZMA) { sectionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; } else return ERR_UNKNOWN_COMPRESSION_ALGORITHM; // Compress body QByteArray compressed; result = compress(body, algorithm, defaultDictionarySize, compressed); if (result) return result; // Correct section size uint32ToUint24(header.size() + compressed.size(), commonHeader->Size); // Append header and body created.append(newHeader).append(compressed); // Parse section QModelIndex sectionIndex; result = parseSection(created, sectionIndex, index, mode); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set create action model->setAction(sectionIndex, action); // Find parent file for rebase fileIndex = model->findParentOfType(parent, Types::File); } break; case EFI_SECTION_GUID_DEFINED:{ // Compress body QByteArray compressed; result = compress(body, algorithm, defaultDictionarySize, compressed); if (result) return result; // Correct section size uint32ToUint24(header.size() + compressed.size(), commonHeader->Size); // Append header and body created.append(newHeader).append(compressed); // Parse section QModelIndex sectionIndex; result = parseSection(created, sectionIndex, index, mode); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set create action model->setAction(sectionIndex, action); // Find parent file for rebase fileIndex = model->findParentOfType(parent, Types::File); } break; default: // Correct section size uint32ToUint24(header.size() + body.size(), commonHeader->Size); // Append header and body created.append(newHeader).append(body); // Parse section QModelIndex sectionIndex; result = parseSection(created, sectionIndex, index, mode); if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME) return result; // Set create action model->setAction(sectionIndex, action); // Find parent file for rebase fileIndex = model->findParentOfType(parent, Types::File); } // Rebase all PEI-files that follow rebasePeiFiles(fileIndex); } else return ERR_NOT_IMPLEMENTED; return ERR_SUCCESS; } void FfsEngine::rebasePeiFiles(const QModelIndex & index) { // Rebase all PE32 and TE sections in PEI-files after modified file for (int i = index.row(); i < model->rowCount(index.parent()); i++) { // PEI-file QModelIndex currentFileIndex = index.parent().child(i, 0); if (model->subtype(currentFileIndex) == EFI_FV_FILETYPE_PEI_CORE || model->subtype(currentFileIndex) == EFI_FV_FILETYPE_PEIM || model->subtype(currentFileIndex) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER) { for (int j = 0; j < model->rowCount(currentFileIndex); j++) { // Section in that file QModelIndex currentSectionIndex = currentFileIndex.child(j, 0); // If section stores PE32 or TE image if (model->subtype(currentSectionIndex) == EFI_SECTION_PE32 || model->subtype(currentSectionIndex) == EFI_SECTION_TE) // Set rebase action if (model->action(currentSectionIndex) != Actions::Remove) model->setAction(currentSectionIndex, Actions::Rebase); } } } // Rebase VTF in subsequent volumes. QModelIndex parent = index.parent(); while (parent.isValid() && model->type(parent) != Types::Volume) parent = parent.parent(); if (parent.isValid()) { QModelIndex volumeContainer = parent.parent(); // Iterate over volumes starting from the one after. for (int i = parent.row() + 1; i < model->rowCount(volumeContainer); i++) { QModelIndex currentVolumeIndex = volumeContainer.child(i, 0); // Iterate over files within each volume after the current one. for (int j = 0; j < model->rowCount(currentVolumeIndex); j++) { QModelIndex currentFileIndex = currentVolumeIndex.child(j, 0); if (model->header(currentFileIndex).left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { for (int k = 0; k < model->rowCount(currentFileIndex); k++) { QModelIndex currentSectionIndex = currentFileIndex.child(k, 0); // If section stores PE32 or TE image if (model->subtype(currentSectionIndex) == EFI_SECTION_PE32 || model->subtype(currentSectionIndex) == EFI_SECTION_TE) // Set rebase action if (model->action(currentSectionIndex) != Actions::Remove) model->setAction(currentSectionIndex, Actions::Rebase); } } } } } } UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, const UINT8 mode) { if (!index.isValid() || !index.parent().isValid()) return ERR_INVALID_PARAMETER; QModelIndex parent; if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) parent = index.parent(); else parent = index; // Determine type of item to insert UINT8 type; UINT32 headerSize; if (model->type(parent) == Types::Volume) { type = Types::File; headerSize = sizeof(EFI_FFS_FILE_HEADER); } else if (model->type(parent) == Types::File) { type = Types::Section; const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData(); headerSize = sizeOfSectionHeader(commonHeader); } else if (model->type(parent) == Types::Section) { type = Types::Section; const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData(); headerSize = sizeOfSectionHeader(commonHeader); } else return ERR_NOT_IMPLEMENTED; if ((UINT32)object.size() < headerSize) return ERR_BUFFER_TOO_SMALL; return create(index, type, object.left(headerSize), object.right(object.size() - headerSize), mode, Actions::Insert); } UINT8 FfsEngine::replace(const QModelIndex & index, const QByteArray & object, const UINT8 mode) { if (!index.isValid()) return ERR_INVALID_PARAMETER; // Determine type of item to replace UINT32 headerSize; UINT8 result; if (model->type(index) == Types::Region) { if (mode == REPLACE_MODE_AS_IS) result = create(index, Types::Region, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); else return ERR_NOT_IMPLEMENTED; } else if (model->type(index) == Types::Padding) { if (mode == REPLACE_MODE_AS_IS) result = create(index, Types::Padding, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); else return ERR_NOT_IMPLEMENTED; } else if (model->type(index) == Types::Volume) { if (mode == REPLACE_MODE_AS_IS) { result = create(index, Types::Volume, QByteArray(), object, CREATE_MODE_AFTER, Actions::Replace); } else if (mode == REPLACE_MODE_BODY) { result = create(index, Types::Volume, model->header(index), object, CREATE_MODE_AFTER, Actions::Replace); } else return ERR_NOT_IMPLEMENTED; } else if (model->type(index) == Types::File) { if (mode == REPLACE_MODE_AS_IS) { headerSize = sizeof(EFI_FFS_FILE_HEADER); result = create(index, Types::File, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, Actions::Replace); } else if (mode == REPLACE_MODE_BODY) result = create(index, Types::File, model->header(index), object, CREATE_MODE_AFTER, Actions::Replace); else return ERR_NOT_IMPLEMENTED; } else if (model->type(index) == Types::Section) { if (mode == REPLACE_MODE_AS_IS) { const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData(); headerSize = sizeOfSectionHeader(commonHeader); result = create(index, Types::Section, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, Actions::Replace); } else if (mode == REPLACE_MODE_BODY) { result = create(index, Types::Section, model->header(index), object, CREATE_MODE_AFTER, Actions::Replace, model->compression(index)); } else return ERR_NOT_IMPLEMENTED; } else return ERR_NOT_IMPLEMENTED; // Check create result if (result) return result; // Set remove action to replaced item model->setAction(index, Actions::Remove); return ERR_SUCCESS; } UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode) { if (!index.isValid()) return ERR_INVALID_PARAMETER; if (mode == EXTRACT_MODE_AS_IS) { // Extract as is, with header and body extracted.clear(); extracted.append(model->header(index)); extracted.append(model->body(index)); if (model->type(index) == Types::File) { UINT8 revision = 2; QModelIndex parent = model->parent(index); if (parent.isValid() && model->type(parent) == Types::Volume) { const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)model->header(parent).constData(); revision = volumeHeader->Revision; } const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; extracted.append(ht).append(ft); } } } else if (mode == EXTRACT_MODE_BODY) { // Extract without header and tail extracted.clear(); // Special case of compressed bodies if (model->type(index) == Types::Section) { QByteArray decompressed; UINT8 result; if (model->subtype(index) == EFI_SECTION_COMPRESSION) { const EFI_COMPRESSION_SECTION* compressedHeader = (const EFI_COMPRESSION_SECTION*)model->header(index).constData(); result = decompress(model->body(index), compressedHeader->CompressionType, decompressed); if (result) return result; extracted.append(decompressed); return ERR_SUCCESS; } else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { QByteArray decompressed; // Check if section requires processing const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)model->header(index).constData(); if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { // Try to decompress section body using both known compression algorithms result = decompress(model->body(index), EFI_STANDARD_COMPRESSION, decompressed); if (result) { result = decompress(model->body(index), EFI_CUSTOMIZED_COMPRESSION, decompressed); if (result) return result; } extracted.append(decompressed); return ERR_SUCCESS; } } } extracted.append(model->body(index)); } else return ERR_UNKNOWN_EXTRACT_MODE; return ERR_SUCCESS; } UINT8 FfsEngine::remove(const QModelIndex & index) { if (!index.isValid()) return ERR_INVALID_PARAMETER; // Set action for the item model->setAction(index, Actions::Remove); QModelIndex fileIndex; if (model->type(index) == Types::Volume && model->rowCount(index) > 0) fileIndex = index.child(0, 0); else if (model->type(index) == Types::File) fileIndex = index; else if (model->type(index) == Types::Section) fileIndex = model->findParentOfType(index, Types::File); else return ERR_SUCCESS; // Rebase all PEI-files that follow rebasePeiFiles(fileIndex); return ERR_SUCCESS; } UINT8 FfsEngine::rebuild(const QModelIndex & index) { if (!index.isValid()) return ERR_INVALID_PARAMETER; // Set action for the item model->setAction(index, Actions::Rebuild); QModelIndex fileIndex; if (model->type(index) == Types::Volume && model->rowCount(index) > 0) fileIndex = index.child(0, 0); else if (model->type(index) == Types::File) fileIndex = index; else if (model->type(index) == Types::Section) fileIndex = model->findParentOfType(index, Types::File); else return ERR_SUCCESS; // Rebase all PEI-files that follow rebasePeiFiles(fileIndex); return ERR_SUCCESS; } UINT8 FfsEngine::doNotRebuild(const QModelIndex & index) { if (!index.isValid()) return ERR_INVALID_PARAMETER; // Set action for the item model->setAction(index, Actions::DoNotRebuild); return ERR_SUCCESS; } // Compression routines UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm) { const UINT8* data; UINT32 dataSize; UINT8* decompressed; UINT32 decompressedSize = 0; UINT8* scratch; UINT32 scratchSize = 0; const EFI_TIANO_HEADER* header; switch (compressionType) { case EFI_NOT_COMPRESSED: decompressedData = compressedData; if (algorithm) *algorithm = COMPRESSION_ALGORITHM_NONE; return ERR_SUCCESS; case EFI_STANDARD_COMPRESSION: // 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) return ERR_STANDARD_DECOMPRESSION_FAILED; // Get info function is the same for both algorithms if (ERR_SUCCESS != EfiTianoGetInfo(data, dataSize, &decompressedSize, &scratchSize)) return ERR_STANDARD_DECOMPRESSION_FAILED; // Allocate memory decompressed = new UINT8[decompressedSize]; scratch = new UINT8[scratchSize]; // Decompress section data //TODO: separate EFI1.1 from Tiano another way // Try Tiano decompression first if (ERR_SUCCESS != TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { // Not Tiano, try EFI 1.1 if (ERR_SUCCESS != EfiDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize)) { if (algorithm) *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; delete[] decompressed; delete[] scratch; return ERR_STANDARD_DECOMPRESSION_FAILED; } else if (algorithm) *algorithm = COMPRESSION_ALGORITHM_EFI11; } else if (algorithm) *algorithm = COMPRESSION_ALGORITHM_TIANO; if (decompressedSize > INT32_MAX) { delete[] decompressed; delete[] scratch; return ERR_STANDARD_DECOMPRESSION_FAILED; } decompressedData = QByteArray((const char*)decompressed, (int)decompressedSize); delete[] decompressed; delete[] scratch; return ERR_SUCCESS; case EFI_CUSTOMIZED_COMPRESSION: // Get buffer sizes data = (const UINT8*)compressedData.constData(); dataSize = compressedData.size(); // Get info if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; // Allocate memory decompressed = new UINT8[decompressedSize]; // Decompress section data if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { // Intel modified LZMA workaround EFI_COMMON_SECTION_HEADER* shittySectionHeader; UINT32 shittySectionSize; // Shitty compressed section with a section header between COMPRESSED_SECTION_HEADER and LZMA_HEADER // We must determine section header size by checking it's type before we can unpack that non-standard compressed section shittySectionHeader = (EFI_COMMON_SECTION_HEADER*)data; shittySectionSize = sizeOfSectionHeader(shittySectionHeader); // Decompress section data once again data += shittySectionSize; // Get info again if (ERR_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) { delete[] decompressed; return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; } // Decompress section data again if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed) || decompressedSize > INT32_MAX) { if (algorithm) *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; delete[] decompressed; return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; } else { if (algorithm) *algorithm = COMPRESSION_ALGORITHM_IMLZMA; decompressedData = QByteArray((const char*)decompressed, (int)decompressedSize); } } else { if (decompressedSize > INT32_MAX) { delete[] decompressed; return ERR_CUSTOMIZED_DECOMPRESSION_FAILED; } if (algorithm) *algorithm = COMPRESSION_ALGORITHM_LZMA; decompressedData = QByteArray((const char*)decompressed, (int)decompressedSize); } delete[] decompressed; return ERR_SUCCESS; default: msg(tr("decompress: unknown compression type %1").arg(compressionType)); if (algorithm) *algorithm = COMPRESSION_ALGORITHM_UNKNOWN; return ERR_UNKNOWN_COMPRESSION_ALGORITHM; } } UINT8 FfsEngine::compress(const QByteArray & data, const UINT8 algorithm, const UINT32 dictionarySize, QByteArray & compressedData) { UINT8* compressed; switch (algorithm) { case COMPRESSION_ALGORITHM_NONE: { compressedData = data; return ERR_SUCCESS; } break; case COMPRESSION_ALGORITHM_EFI11: { // Try legacy function first UINT32 compressedSize = 0; if (EfiCompressLegacy(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) return ERR_STANDARD_COMPRESSION_FAILED; compressed = new UINT8[compressedSize]; if (EfiCompressLegacy(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { delete[] compressed; return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); // Check that compressed data can be decompressed normally QByteArray decompressed; if (decompress(compressedData, EFI_STANDARD_COMPRESSION, decompressed, NULL) == ERR_SUCCESS && decompressed == data) { delete[] compressed; return ERR_SUCCESS; } delete[] compressed; // Legacy function failed, use current one compressedSize = 0; if (EfiCompress(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) return ERR_STANDARD_COMPRESSION_FAILED; compressed = new UINT8[compressedSize]; if (EfiCompress(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { delete[] compressed; return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); // New functions will be trusted here, because another check will reduce performance delete[] compressed; return ERR_SUCCESS; } break; case COMPRESSION_ALGORITHM_TIANO: { // Try legacy function first UINT32 compressedSize = 0; if (TianoCompressLegacy(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) return ERR_STANDARD_COMPRESSION_FAILED; compressed = new UINT8[compressedSize]; if (TianoCompressLegacy(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { delete[] compressed; return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); // Check that compressed data can be decompressed normally QByteArray decompressed; if (decompress(compressedData, EFI_STANDARD_COMPRESSION, decompressed, NULL) == ERR_SUCCESS && decompressed == data) { delete[] compressed; return ERR_SUCCESS; } delete[] compressed; // Legacy function failed, use current one compressedSize = 0; if (TianoCompress(data.constData(), data.size(), NULL, &compressedSize) != ERR_BUFFER_TOO_SMALL) return ERR_STANDARD_COMPRESSION_FAILED; compressed = new UINT8[compressedSize]; if (TianoCompress(data.constData(), data.size(), compressed, &compressedSize) != ERR_SUCCESS) { delete[] compressed; return ERR_STANDARD_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); // New functions will be trusted here, because another check will reduce performance delete[] compressed; return ERR_SUCCESS; } break; case COMPRESSION_ALGORITHM_LZMA: { UINT32 compressedSize = 0; if (LzmaCompress((const UINT8*)data.constData(), data.size(), NULL, &compressedSize, dictionarySize) != ERR_BUFFER_TOO_SMALL) return ERR_CUSTOMIZED_COMPRESSION_FAILED; compressed = new UINT8[compressedSize]; if (LzmaCompress((const UINT8*)data.constData(), data.size(), compressed, &compressedSize, dictionarySize) != ERR_SUCCESS) { delete[] compressed; return ERR_CUSTOMIZED_COMPRESSION_FAILED; } compressedData = QByteArray((const char*)compressed, compressedSize); delete[] compressed; return ERR_SUCCESS; } break; case COMPRESSION_ALGORITHM_IMLZMA: { UINT32 compressedSize = 0; QByteArray header = data.left(sizeof(EFI_COMMON_SECTION_HEADER)); const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)header.constData(); UINT32 headerSize = sizeOfSectionHeader(sectionHeader); header = data.left(headerSize); QByteArray newData = data.mid(headerSize); if (LzmaCompress((const UINT8*)newData.constData(), newData.size(), NULL, &compressedSize, dictionarySize) != ERR_BUFFER_TOO_SMALL) return ERR_CUSTOMIZED_COMPRESSION_FAILED; compressed = new UINT8[compressedSize]; if (LzmaCompress((const UINT8*)newData.constData(), newData.size(), compressed, &compressedSize, dictionarySize) != ERR_SUCCESS) { delete[] compressed; return ERR_CUSTOMIZED_COMPRESSION_FAILED; } compressedData = header.append(QByteArray((const char*)compressed, compressedSize)); delete[] compressed; return ERR_SUCCESS; } break; default: msg(tr("compress: unknown compression algorithm %1").arg(algorithm)); return ERR_UNKNOWN_COMPRESSION_ALGORITHM; } } // Construction routines UINT8 FfsEngine::constructPadFile(const QByteArray &guid, const UINT32 size, const UINT8 revision, const UINT8 erasePolarity, QByteArray & pad) { if (size < sizeof(EFI_FFS_FILE_HEADER) || erasePolarity == ERASE_POLARITY_UNKNOWN) return ERR_INVALID_PARAMETER; if (size >= 0xFFFFFF) // TODO: large file support return ERR_INVALID_PARAMETER; pad = QByteArray(size - guid.size(), erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00'); pad.prepend(guid); EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*)pad.data(); uint32ToUint24(size, header->Size); header->Attributes = 0x00; header->Type = EFI_FV_FILETYPE_PAD; header->State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID; // Invert state bits if erase polarity is true if (erasePolarity == ERASE_POLARITY_TRUE) header->State = ~header->State; // Calculate header checksum header->IntegrityCheck.Checksum.Header = 0; header->IntegrityCheck.Checksum.File = 0; header->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)header, sizeof(EFI_FFS_FILE_HEADER) - 1); // Set data checksum if (revision == 1) header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; else header->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; return ERR_SUCCESS; } UINT8 FfsEngine::reconstructIntelImage(const QModelIndex& index, QByteArray& reconstructed) { if (!index.isValid()) return ERR_SUCCESS; UINT8 result; // No action if (model->action(index) == Actions::NoAction || model->action(index) == Actions::DoNotRebuild) { reconstructed = model->header(index).append(model->body(index)); return ERR_SUCCESS; } // Other supported actions else if (model->action(index) == Actions::Rebuild) { reconstructed.clear(); // First child will always be descriptor for this type of image QByteArray descriptor; result = reconstructRegion(index.child(0, 0), descriptor); if (result) return result; reconstructed.append(descriptor); // Check descriptor size if ((UINT32)descriptor.size() < FLASH_DESCRIPTOR_SIZE) { msg(tr("reconstructIntelImage: descriptor is smaller than minimum size of 1000h (4096) bytes")); return ERR_INVALID_FLASH_DESCRIPTOR; } const FLASH_DESCRIPTOR_MAP* descriptorMap = (const FLASH_DESCRIPTOR_MAP*)(descriptor.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); // Check sanity of base values if (descriptorMap->MasterBase > FLASH_DESCRIPTOR_MAX_BASE || descriptorMap->MasterBase == descriptorMap->RegionBase || descriptorMap->MasterBase == descriptorMap->ComponentBase) { msg(tr("reconstructIntelImage: invalid descriptor master base %1h").hexarg2(descriptorMap->MasterBase, 2)); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorMap->RegionBase > FLASH_DESCRIPTOR_MAX_BASE || descriptorMap->RegionBase == descriptorMap->ComponentBase) { msg(tr("reconstructIntelImage: invalid descriptor region base %1h").hexarg2(descriptorMap->RegionBase, 2)); return ERR_INVALID_FLASH_DESCRIPTOR; } if (descriptorMap->ComponentBase > FLASH_DESCRIPTOR_MAX_BASE) { msg(tr("reconstructIntelImage: invalid descriptor component base %1h").hexarg2(descriptorMap->ComponentBase, 2)); return ERR_INVALID_FLASH_DESCRIPTOR; } const FLASH_DESCRIPTOR_REGION_SECTION* regionSection = (const FLASH_DESCRIPTOR_REGION_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->RegionBase); QByteArray gbe; UINT32 gbeBegin = calculateRegionOffset(regionSection->GbeBase); UINT32 gbeEnd = gbeBegin + calculateRegionSize(regionSection->GbeBase, regionSection->GbeLimit); QByteArray me; UINT32 meBegin = calculateRegionOffset(regionSection->MeBase); UINT32 meEnd = meBegin + calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); QByteArray bios; UINT32 biosBegin = calculateRegionOffset(regionSection->BiosBase); UINT32 biosEnd = calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit); // Gigabyte descriptor map if (biosEnd - biosBegin == (UINT32)(model->header(index).size() + model->body(index).size())) { biosBegin = meEnd; biosEnd = model->header(index).size() + model->body(index).size(); } // Normal descriptor map else biosEnd += biosBegin; QByteArray pdr; UINT32 pdrBegin = calculateRegionOffset(regionSection->PdrBase); UINT32 pdrEnd = pdrBegin + calculateRegionSize(regionSection->PdrBase, regionSection->PdrLimit); QByteArray ec; UINT32 ecBegin = 0; UINT32 ecEnd = 0; // Check for legacy descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency UINT8 descriptorVersion = 2; // Skylake+ descriptor const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8((const UINT8*)descriptor.constData(), descriptorMap->ComponentBase); if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Legacy descriptor descriptorVersion = 1; if (descriptorVersion == 2 && descriptorMap->DescriptorVersion != FLASH_DESCRIPTOR_VERSION_INVALID) { // Warn about incompatible version descriptor in case it is different. const FLASH_DESCRIPTOR_VERSION *version = (const FLASH_DESCRIPTOR_VERSION *)&descriptorMap->DescriptorVersion; if (version->Major != FLASH_DESCRIPTOR_VERSION_MAJOR || version->Minor != FLASH_DESCRIPTOR_VERSION_MINOR) msg(tr("reconstructIntelImage: discovered unexpected %1.%2 descriptor version, trying to continue...") .arg(version->Major).arg(version->Minor)); } if (descriptorVersion == 2) { ecBegin = calculateRegionOffset(regionSection->EcBase); ecEnd = ecBegin + calculateRegionSize(regionSection->EcBase, regionSection->EcLimit); } UINT32 offset = descriptor.size(); // Reconstruct other regions char empty = '\xFF'; for (int i = 1; i < model->rowCount(index); i++) { QByteArray region; // Padding after the end of all Intel regions if (model->type(index.child(i, 0)) == Types::Padding) { region = model->body(index.child(i, 0)); reconstructed.append(region); offset += region.size(); continue; } result = reconstructRegion(index.child(i, 0), region); if (result) return result; switch (model->subtype(index.child(i, 0))) { case Subtypes::GbeRegion: gbe = region; if (gbeBegin > offset) reconstructed.append(QByteArray(gbeBegin - offset, empty)); reconstructed.append(gbe); offset = gbeEnd; break; case Subtypes::MeRegion: me = region; if (meBegin > offset) reconstructed.append(QByteArray(meBegin - offset, empty)); reconstructed.append(me); offset = meEnd; break; case Subtypes::BiosRegion: bios = region; if (biosBegin > offset) reconstructed.append(QByteArray(biosBegin - offset, empty)); reconstructed.append(bios); offset = biosEnd; break; case Subtypes::PdrRegion: pdr = region; if (pdrBegin > offset) reconstructed.append(QByteArray(pdrBegin - offset, empty)); reconstructed.append(pdr); offset = pdrEnd; break; case Subtypes::EcRegion: if (descriptorVersion == 1) { msg(tr("reconstructIntelImage: incompatible region type found"), index); return ERR_INVALID_REGION; } ec = region; if (ecBegin > offset) reconstructed.append(QByteArray(ecBegin - offset, empty)); reconstructed.append(ec); offset = ecEnd; break; default: msg(tr("reconstructIntelImage: unknown region type found"), index); return ERR_INVALID_REGION; } } if ((UINT32)model->body(index).size() > offset) reconstructed.append(QByteArray((UINT32)model->body(index).size() - offset, empty)); // Check size of reconstructed image, it must be same if (reconstructed.size() > model->body(index).size()) { msg(tr("reconstructIntelImage: reconstructed body size %1h (%2) is bigger then original %3h (%4) ") .hexarg(reconstructed.size()).arg(reconstructed.size()) .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } else if (reconstructed.size() < model->body(index).size()) { msg(tr("reconstructIntelImage: reconstructed body size %1h (%2) is smaller then original %3h (%4) ") .hexarg(reconstructed.size()).arg(reconstructed.size()) .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } // Reconstruction successful return ERR_SUCCESS; } // All other actions are not supported return ERR_NOT_IMPLEMENTED; } UINT8 FfsEngine::reconstructRegion(const QModelIndex& index, QByteArray& reconstructed, bool includeHeader) { if (!index.isValid()) return ERR_SUCCESS; UINT8 result; // No action if (model->action(index) == Actions::NoAction || model->action(index) == Actions::DoNotRebuild) { reconstructed = model->header(index).append(model->body(index)); return ERR_SUCCESS; } else if (model->action(index) == Actions::Remove) { reconstructed.clear(); return ERR_SUCCESS; } else if (model->action(index) == Actions::Rebuild || model->action(index) == Actions::Replace) { if (model->rowCount(index)) { reconstructed.clear(); // Reconstruct children for (int i = 0; i < model->rowCount(index); i++) { QByteArray child; result = reconstruct(index.child(i, 0), child); if (result) return result; reconstructed.append(child); } } // Use stored item body else reconstructed = model->body(index); // Check size of reconstructed region, it must be same if (reconstructed.size() > model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is bigger then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } else if (reconstructed.size() < model->body(index).size()) { msg(tr("reconstructRegion: reconstructed region size %1h (%2) is smaller then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } // Reconstruction successful if (includeHeader) reconstructed = model->header(index).append(reconstructed); return ERR_SUCCESS; } // All other actions are not supported return ERR_NOT_IMPLEMENTED; } UINT8 FfsEngine::reconstructPadding(const QModelIndex& index, QByteArray& reconstructed) { if (!index.isValid()) return ERR_SUCCESS; // No action if (model->action(index) == Actions::NoAction || model->action(index) == Actions::DoNotRebuild) { reconstructed = model->body(index); return ERR_SUCCESS; } else if (model->action(index) == Actions::Remove) { reconstructed.clear(); return ERR_SUCCESS; } else if (model->action(index) == Actions::Rebuild || model->action(index) == Actions::Replace) { // Use stored item body reconstructed = model->body(index); // Check size of reconstructed region, it must be same if (reconstructed.size() > model->body(index).size()) { msg(tr("reconstructPadding: reconstructed padding size %1h (%2) is bigger then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } else if (reconstructed.size() < model->body(index).size()) { msg(tr("reconstructPadding: reconstructed padding size %1h (%2) is smaller then original %3h (%4)") .hexarg(reconstructed.size()).arg(reconstructed.size()) .hexarg(model->body(index).size()).arg(model->body(index).size()), index); return ERR_INVALID_PARAMETER; } // Reconstruction successful return ERR_SUCCESS; } // All other actions are not supported return ERR_NOT_IMPLEMENTED; } UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & reconstructed) { if (!index.isValid()) return ERR_SUCCESS; UINT8 result; // No action if (model->action(index) == Actions::NoAction || model->action(index) == Actions::DoNotRebuild) { reconstructed = model->header(index).append(model->body(index)); return ERR_SUCCESS; } else if (model->action(index) == Actions::Remove) { reconstructed.clear(); return ERR_SUCCESS; } else if (model->action(index) == Actions::Replace || model->action(index) == Actions::Rebuild) { QByteArray header = model->header(index); QByteArray body = model->body(index); EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); // Check sanity of HeaderLength if (volumeHeader->HeaderLength > header.size()) { msg(tr("reconstructVolume: invalid volume header length, reconstruction is not possible"), index); return ERR_INVALID_VOLUME; } // Recalculate volume header checksum volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); // Get volume size UINT32 volumeSize = header.size() + body.size(); // Reconstruct volume body UINT32 freeSpaceOffset = 0; if (model->rowCount(index)) { reconstructed.clear(); UINT8 polarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE; char empty = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY ? '\xFF' : '\x00'; // Calculate volume base for volume UINT32 volumeBase; QByteArray file; bool baseFound = false; // Search for VTF for (int i = 0; i < model->rowCount(index); i++) { file = model->header(index.child(i, 0)); // VTF found if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { baseFound = true; volumeBase = (UINT32)(0x100000000 - volumeSize); break; } } // Determine if volume is inside compressed item if (!baseFound) { // Iterate up to the root, checking for compression type to be other then none for (QModelIndex parentIndex = index.parent(); model->type(parentIndex) != Types::Root; parentIndex = parentIndex.parent()) if (model->compression(parentIndex) != COMPRESSION_ALGORITHM_NONE) { // No rebase needed for compressed PEI files baseFound = true; volumeBase = 0; break; } } // Find volume base address using first PEI file in it if (!baseFound) { // Search for first PEI-file and use it as base source UINT32 fileOffset = header.size(); for (int i = 0; i < model->rowCount(index); i++) { if ((model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_PEI_CORE || model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_PEIM || model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)){ QModelIndex peiFile = index.child(i, 0); UINT32 sectionOffset = sizeof(EFI_FFS_FILE_HEADER); // BUGBUG: this parsing is bad and doesn't support large files, but it needs to be performed only for very old images with uncompressed DXE volumes, so whatever // Search for PE32 or TE section for (int j = 0; j < model->rowCount(peiFile); j++) { if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 || model->subtype(peiFile.child(j, 0)) == EFI_SECTION_TE) { QModelIndex image = peiFile.child(j, 0); // Check for correct action if (model->action(image) == Actions::Remove || model->action(image) == Actions::Insert) continue; // Calculate relative base address UINT32 relbase = fileOffset + sectionOffset + model->header(image).size(); // Calculate offset of image relative to file base UINT32 imagebase = 0; result = getBase(model->body(image), imagebase); // imagebase passed by reference if (!result) { // Calculate volume base volumeBase = imagebase - relbase; baseFound = true; goto out; } } sectionOffset += model->header(peiFile.child(j, 0)).size() + model->body(peiFile.child(j, 0)).size(); sectionOffset = ALIGN4(sectionOffset); } } fileOffset += model->header(index.child(i, 0)).size() + model->body(index.child(i, 0)).size(); fileOffset = ALIGN8(fileOffset); } } out: // Do not set volume base if (!baseFound) volumeBase = 0; // Reconstruct files in volume UINT32 offset = 0; QByteArray padFileGuid = EFI_FFS_PAD_FILE_GUID; QByteArray vtf; QModelIndex vtfIndex; UINT32 nonUefiDataOffset = 0; QByteArray nonUefiData; for (int i = 0; i < model->rowCount(index); i++) { // Inside a volume can be files, free space or padding with non-UEFI data if (model->type(index.child(i, 0)) == Types::File) { // Next item is a file // Align to 8 byte boundary UINT32 alignment = offset % 8; if (alignment) { alignment = 8 - alignment; offset += alignment; reconstructed.append(QByteArray(alignment, empty)); } // Calculate file base UINT32 fileBase = volumeBase ? volumeBase + header.size() + offset : 0; // Reconstruct file result = reconstructFile(index.child(i, 0), volumeHeader->Revision, polarity, fileBase, file); if (result) return result; // Empty file if (file.isEmpty()) continue; EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data(); UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER); if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2); // Pad file if (fileHeader->Type == EFI_FV_FILETYPE_PAD) { padFileGuid = file.left(sizeof(EFI_GUID)); if (model->action(index.child(i, 0)) == Actions::DoNotRebuild) { // User asked not to touch this file, do nothing here } // Parse non-empty pad file else if (model->rowCount(index.child(i, 0))) { // TODO: handle this special case continue; } // Skip empty pad-file else { continue; } } // Volume Top File if (file.left(sizeof(EFI_GUID)) == EFI_FFS_VOLUME_TOP_FILE_GUID) { vtf = file; vtfIndex = index.child(i, 0); continue; } // Normal file // Ensure correct alignment UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; alignment = (UINT32)(1UL <Revision, polarity, pad); if (result) return result; // Append constructed pad file to volume body reconstructed.append(pad); offset += size; } // Append current file to new volume body reconstructed.append(file); // Change current file offset offset += file.size(); } else if (model->type(index.child(i, 0)) == Types::FreeSpace) { //Next item is a free space // Some data are located beyond free space if (offset + (UINT32)model->body(index.child(i, 0)).size() < (UINT32)model->body(index).size()) { // Get non-UEFI data and it's offset nonUefiData = model->body(index.child(i + 1, 0)); nonUefiDataOffset = body.size() - nonUefiData.size(); break; } } } // Check volume sanity if (!vtf.isEmpty() && !nonUefiData.isEmpty()) { msg(tr("reconstructVolume: both VTF and non-UEFI data found in the volume, reconstruction is not possible"), index); return ERR_INVALID_VOLUME; } // Check for free space offset in ZeroVector if (model->text(index).contains("AppleFSO ")) { // Align current offset to 8 byte boundary UINT32 alignment = offset % 8; freeSpaceOffset = model->header(index).size() + offset; if (alignment) { alignment = 8 - alignment; freeSpaceOffset += alignment; } } // Insert VTF or non-UEFI data to it's correct place if (!vtf.isEmpty()) { // VTF found // Determine correct VTF offset UINT32 vtfOffset = model->body(index).size() - vtf.size(); if (vtfOffset % 8) { msg(tr("reconstructVolume: wrong size of the Volume Top File"), index); return ERR_INVALID_FILE; } // Insert pad file to fill the gap if (vtfOffset > offset) { // Determine pad file size UINT32 size = vtfOffset - offset; // Construct pad file QByteArray pad; result = constructPadFile(padFileGuid, size, volumeHeader->Revision, polarity, pad); if (result) return result; // Append constructed pad file to volume body reconstructed.append(pad); } // No more space left in volume else if (offset > vtfOffset) { msg(tr("reconstructVolume: no space left to insert VTF, need %1h (%2) byte(s) more") .hexarg(offset - vtfOffset).arg(offset - vtfOffset), index); return ERR_INVALID_VOLUME; } // Calculate VTF base UINT32 vtfBase = volumeBase ? volumeBase + vtfOffset : 0; // Reconstruct VTF again result = reconstructFile(vtfIndex, volumeHeader->Revision, polarity, vtfBase, vtf); if (result) return result; // Patch PEI core entry point in VTF result = patchVtf(vtf); if (result) return result; // Append VTF reconstructed.append(vtf); } else if (!nonUefiData.isEmpty()) { //Non-UEFI data found // No space left if (offset > nonUefiDataOffset) { msg(tr("reconstructVolume: no space left to insert non-UEFI data, need %1h (%2) byte(s) more") .hexarg(offset - nonUefiDataOffset).arg(offset - nonUefiDataOffset), index); return ERR_INVALID_VOLUME; } // Append additional free space else if (nonUefiDataOffset > offset) { reconstructed.append(QByteArray(nonUefiDataOffset - offset, empty)); } // Append VTF reconstructed.append(nonUefiData); } else { // Fill the rest of volume space with empty char if (body.size() > reconstructed.size()) { // Fill volume end with empty char reconstructed.append(QByteArray(body.size() - reconstructed.size(), empty)); } else if (body.size() < reconstructed.size()) { // Check if volume can be grown // Root volume can't be grown UINT8 parentType = model->type(index.parent()); if (parentType != Types::File && parentType != Types::Section) { msg(tr("reconstructVolume: root volume can't be grown"), index); return ERR_INVALID_VOLUME; } // Grow volume to fit new body UINT32 newSize = header.size() + reconstructed.size(); result = growVolume(header, volumeSize, newSize); if (result) return result; // Fill volume end with empty char reconstructed.append(QByteArray(newSize - header.size() - reconstructed.size(), empty)); volumeSize = newSize; } } } // Use current volume body else { reconstructed = model->body(index); // BUGBUG: volume size may change during this operation for volumes withour files in them // but such volumes are fairly rare } // Check new volume size if ((UINT32)(header.size() + reconstructed.size()) != volumeSize) { msg(tr("reconstructVolume: volume size can't be changed"), index); return ERR_INVALID_VOLUME; } // Reconstruction successful reconstructed = header.append(reconstructed); // Recalculate CRC32 in ZeroVector, if needed if (model->text(index).contains("AppleCRC32 ")) { // Get current CRC32 value from volume header const UINT32 currentCrc = *(const UINT32*)(reconstructed.constData() + 8); // Calculate new value UINT32 crc = crc32(0, (const UINT8*)reconstructed.constData() + volumeHeader->HeaderLength, reconstructed.size() - volumeHeader->HeaderLength); // Update the value if (currentCrc != crc) { *(UINT32*)(reconstructed.data() + 8) = crc; // Recalculate header checksum volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); } } // Store new free space offset, if needed if (model->text(index).contains("AppleFSO ")) { // Get current CRC32 value from volume header const UINT32 currentFso = *(const UINT32*)(reconstructed.constData() + 12); // Update the value if (freeSpaceOffset != 0 && currentFso != freeSpaceOffset) { *(UINT32*)(reconstructed.data() + 12) = freeSpaceOffset; // Recalculate header checksum volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)reconstructed.data(); volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); } } return ERR_SUCCESS; } // All other actions are not supported return ERR_NOT_IMPLEMENTED; } UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed) { if (!index.isValid()) return ERR_SUCCESS; UINT8 result; // No action if (model->action(index) == Actions::NoAction || model->action(index) == Actions::DoNotRebuild) { reconstructed = model->header(index).append(model->body(index)); const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)model->header(index).constData(); // Append tail, if needed if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; reconstructed.append(ht).append(ft); } return ERR_SUCCESS; } else if (model->action(index) == Actions::Remove) { reconstructed.clear(); return ERR_SUCCESS; } else if (model->action(index) == Actions::Insert || model->action(index) == Actions::Replace || model->action(index) == Actions::Rebuild) { QByteArray header = model->header(index); EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data(); // Check erase polarity if (erasePolarity == ERASE_POLARITY_UNKNOWN) { msg(tr("reconstructFile: unknown erase polarity"), index); return ERR_INVALID_PARAMETER; } // Check file state // Check top reserved bit of file state to determine it's original erase polarity UINT8 state = fileHeader->State; if (state & EFI_FILE_ERASE_POLARITY) state = ~state; // Order of this checks must be preserved // Check file to have valid state, or delete it otherwise if (state & EFI_FILE_HEADER_INVALID) { // File marked to have invalid header and must be deleted // Do not add anything to queue msg(tr("reconstructFile: file is HEADER_INVALID state, and will be removed from reconstructed image"), index); return ERR_SUCCESS; } else if (state & EFI_FILE_DELETED) { // File marked to have been deleted form and must be deleted // Do not add anything to queue msg(tr("reconstructFile: file is in DELETED state, and will be removed from reconstructed image"), index); return ERR_SUCCESS; } else if (state & EFI_FILE_MARKED_FOR_UPDATE) { // File is marked for update, the mark must be removed msg(tr("reconstructFile: file's MARKED_FOR_UPDATE state cleared"), index); } else if (state & EFI_FILE_DATA_VALID) { // File is in good condition, reconstruct it } else if (state & EFI_FILE_HEADER_VALID) { // Header is valid, but data is not, so file must be deleted msg(tr("reconstructFile: file is in HEADER_VALID (but not in DATA_VALID) state, and will be removed from reconstructed image"), index); return ERR_SUCCESS; } else if (state & EFI_FILE_HEADER_CONSTRUCTION) { // Header construction not finished, so file must be deleted msg(tr("reconstructFile: file is in HEADER_CONSTRUCTION (but not in DATA_VALID) state, and will be removed from reconstructed image"), index); return ERR_SUCCESS; } // Reconstruct file body if (model->rowCount(index)) { reconstructed.clear(); // Construct new file body // File contains raw data, must be parsed as region without header if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) { result = reconstructRegion(index, reconstructed, false); if (result) return result; } // File contains sections else { UINT32 offset = 0; UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER); if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { headerSize = sizeof(EFI_FFS_FILE_HEADER2); } for (int i = 0; i < model->rowCount(index); i++) { // Align to 4 byte boundary UINT8 alignment = offset % 4; if (alignment) { alignment = 4 - alignment; offset += alignment; reconstructed.append(QByteArray(alignment, '\x00')); } // Calculate section base UINT32 sectionBase = base ? base + headerSize + offset : 0; UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; UINT32 fileAlignment = (UINT32)(1UL << alignmentPower); UINT32 alignmentBase = base + headerSize; if (alignmentBase % fileAlignment) { // File will be unaligned if added as is, so we must add pad file before it // Determine pad file size UINT32 size = fileAlignment - (alignmentBase % fileAlignment); // Required padding is smaller then minimal pad file size while (size < sizeof(EFI_FFS_FILE_HEADER)) { size += fileAlignment; } // Adjust file base to incorporate pad file that will be added to align it sectionBase += size; } // Reconstruct section QByteArray section; result = reconstructSection(index.child(i, 0), sectionBase, section); if (result) return result; // Check for empty section if (section.isEmpty()) continue; // Append current section to new file body reconstructed.append(section); // Change current file offset offset += section.size(); } } } // Use current file body else reconstructed = model->body(index); // Correct file size UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0; if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) { uint32ToUint24(EFI_SECTION2_IS_USED, fileHeader->Size); EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*)fileHeader; fileHeader2->ExtendedSize = sizeof(EFI_FFS_FILE_HEADER2) + reconstructed.size() + tailSize; } else { if (sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize > 0xFFFFFF) { msg(tr("reconstructFile: resulting file size is too big"), index); return ERR_INVALID_FILE; } uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size); } // Recalculate header checksum fileHeader->IntegrityCheck.Checksum.Header = 0; fileHeader->IntegrityCheck.Checksum.File = 0; fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->State); // Recalculate data checksum, if needed if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((const UINT8*)reconstructed.constData(), reconstructed.size()); } else if (revision == 1) fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; else fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; // Append tail, if needed if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header; UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File; reconstructed.append(ht).append(ft); } // Set file state state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; if (erasePolarity == ERASE_POLARITY_TRUE) state = ~state; fileHeader->State = state; // Reconstruction successful reconstructed = header.append(reconstructed); return ERR_SUCCESS; } // All other actions are not supported return ERR_NOT_IMPLEMENTED; } UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base, QByteArray& reconstructed) { if (!index.isValid()) return ERR_SUCCESS; UINT8 result; // No action if (model->action(index) == Actions::NoAction || model->action(index) == Actions::DoNotRebuild) { reconstructed = model->header(index).append(model->body(index)); return ERR_SUCCESS; } else if (model->action(index) == Actions::Remove) { reconstructed.clear(); return ERR_SUCCESS; } else if (model->action(index) == Actions::Insert || model->action(index) == Actions::Replace || model->action(index) == Actions::Rebuild || model->action(index) == Actions::Rebase) { QByteArray header = model->header(index); EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data(); bool extended = false; if (uint24ToUint32(commonHeader->Size) == 0xFFFFFF) { extended = true; } // Reconstruct section with children if (model->rowCount(index)) { reconstructed.clear(); // Construct new section body UINT32 offset = 0; // Reconstruct section body for (int i = 0; i < model->rowCount(index); i++) { // Align to 4 byte boundary UINT8 alignment = offset % 4; if (alignment) { alignment = 4 - alignment; offset += alignment; reconstructed.append(QByteArray(alignment, '\x00')); } // Reconstruct subsections QByteArray section; result = reconstruct(index.child(i, 0), section); if (result) return result; // Check for empty queue if (section.isEmpty()) continue; // Append current subsection to new section body reconstructed.append(section); // Change current file offset offset += section.size(); } // Only this 2 sections can have compressed body if (model->subtype(index) == EFI_SECTION_COMPRESSION) { EFI_COMPRESSION_SECTION* compessionHeader = (EFI_COMPRESSION_SECTION*)header.data(); // Set new uncompressed size compessionHeader->UncompressedLength = reconstructed.size(); // Correct compression type if (model->compression(index) == COMPRESSION_ALGORITHM_NONE) { compessionHeader->CompressionType = EFI_NOT_COMPRESSED; } else if (model->compression(index) == COMPRESSION_ALGORITHM_EFI11 || model->compression(index) == COMPRESSION_ALGORITHM_TIANO) { compessionHeader->CompressionType = EFI_STANDARD_COMPRESSION; } else if (model->compression(index) == COMPRESSION_ALGORITHM_LZMA) { compessionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; } else if (model->compression(index) == COMPRESSION_ALGORITHM_IMLZMA) { compessionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; } else return ERR_UNKNOWN_COMPRESSION_ALGORITHM; // Compress new section body QByteArray compressed; result = compress(reconstructed, model->compression(index), model->dictionarySize(index), compressed); if (result) return result; // Replace new section body reconstructed = compressed; } else if (model->subtype(index) == EFI_SECTION_GUID_DEFINED) { EFI_GUID_DEFINED_SECTION* guidDefinedHeader = (EFI_GUID_DEFINED_SECTION*)header.data(); // Convert x86 if (QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_LZMAF86) { result = x86Convert(reconstructed, 1); if (result) return result; } // Compress new section body QByteArray compressed; result = compress(reconstructed, model->compression(index), model->dictionarySize(index), compressed); if (result) return result; // Check for authentication status valid attribute if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { // CRC32 section if (QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_GUIDED_SECTION_CRC32) { // Check header size if ((UINT32)header.size() != sizeof(EFI_GUID_DEFINED_SECTION) + sizeof(UINT32)) { msg(tr("reconstructSection: invalid CRC32 section size %1h (%2)") .hexarg(header.size()).arg(header.size()), index); return ERR_INVALID_SECTION; } // Calculate CRC32 of section data UINT32 crc = crc32(0, (const UINT8*)compressed.constData(), compressed.size()); // Store new CRC32 *(UINT32*)(header.data() + sizeof(EFI_GUID_DEFINED_SECTION)) = crc; } else { msg(tr("reconstructSection: GUID defined section authentication info can become invalid") .arg(guidToQString(guidDefinedHeader->SectionDefinitionGuid)), index); } } // Check for Intel signed section if (guidDefinedHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED && QByteArray((const char*)&guidDefinedHeader->SectionDefinitionGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_CONTENTS_SIGNED_GUID) { msg(tr("reconstructSection: GUID defined section signature can become invalid") .arg(guidToQString(guidDefinedHeader->SectionDefinitionGuid)), index); } // Replace new section body reconstructed = compressed; } else if (model->compression(index) != COMPRESSION_ALGORITHM_NONE) { msg(tr("reconstructSection: incorrectly required compression for section of type %1") .arg(model->subtype(index)), index); return ERR_INVALID_SECTION; } } // Leaf section else { reconstructed = model->body(index); } // Correct section size if (extended) { EFI_COMMON_SECTION_HEADER2 * extHeader = (EFI_COMMON_SECTION_HEADER2*)commonHeader; extHeader->ExtendedSize = header.size() + reconstructed.size(); uint32ToUint24(0xFFFFFF, commonHeader->Size); } else { uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size); } // Rebase PE32 or TE image, if needed if ((model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE) && (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE || model->subtype(index.parent()) == EFI_FV_FILETYPE_PEIM || model->subtype(index.parent()) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) { UINT16 teFixup = 0; // Most EFI images today include teFixup in ImageBase value, // which doesn't follow the UEFI spec, but is so popular that // only a few images out of thousands are different // There are some heuristics possible here to detect if an entry point is calculated correctly // or needs a proper fixup, but new_engine already have them and it's better to work on proper // builder for it than trying to fix this mess //if (model->subtype(index) == EFI_SECTION_TE) { // const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)model->body(index).constData(); // teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); // if (base) { result = rebase(reconstructed, base - teFixup + header.size(), index); if (result) { msg(tr("reconstructSection: executable section rebase failed"), index); return result; } // Special case of PEI Core rebase if (model->subtype(index.parent()) == EFI_FV_FILETYPE_PEI_CORE) { result = getEntryPoint(reconstructed, newPeiCoreEntryPoint); if (result) msg(tr("reconstructSection: can't get entry point of PEI core"), index); } } } // Reconstruction successful reconstructed = header.append(reconstructed); return ERR_SUCCESS; } // All other actions are not supported return ERR_NOT_IMPLEMENTED; } UINT8 FfsEngine::reconstruct(const QModelIndex &index, QByteArray& reconstructed) { if (!index.isValid()) return ERR_SUCCESS; UINT8 result; switch (model->type(index)) { case Types::Image: if (model->subtype(index) == Subtypes::IntelImage) { result = reconstructIntelImage(index, reconstructed); if (result) return result; } else { //Other images types can be reconstructed like regions result = reconstructRegion(index, reconstructed); if (result) return result; } break; case Types::Capsule: // Capsules can be reconstructed like regions result = reconstructRegion(index, reconstructed); if (result) return result; break; case Types::Region: result = reconstructRegion(index, reconstructed); if (result) return result; break; case Types::Padding: result = reconstructPadding(index, reconstructed); if (result) return result; break; case Types::Volume: result = reconstructVolume(index, reconstructed); if (result) return result; break; case Types::File: //Must not be called that way msg(tr("reconstruct: call of generic function is not supported for files").arg(model->type(index)), index); return ERR_GENERIC_CALL_NOT_SUPPORTED; break; case Types::Section: result = reconstructSection(index, 0, reconstructed); if (result) return result; break; default: msg(tr("reconstruct: unknown item type %1").arg(model->type(index)), index); return ERR_UNKNOWN_ITEM_TYPE; } return ERR_SUCCESS; } UINT8 FfsEngine::growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize) { // Check sanity if ((UINT32)header.size() < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) return ERR_INVALID_VOLUME; // Adjust new size to be representable by current FvBlockMap EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)header.data(); EFI_FV_BLOCK_MAP_ENTRY* blockMap = (EFI_FV_BLOCK_MAP_ENTRY*)(header.data() + sizeof(EFI_FIRMWARE_VOLUME_HEADER)); // Get block map size UINT32 blockMapSize = volumeHeader->HeaderLength - sizeof(EFI_FIRMWARE_VOLUME_HEADER); if (blockMapSize % sizeof(EFI_FV_BLOCK_MAP_ENTRY)) return ERR_INVALID_VOLUME; UINT32 blockMapCount = blockMapSize / sizeof(EFI_FV_BLOCK_MAP_ENTRY); // Check blockMap validity if (blockMap[blockMapCount - 1].NumBlocks != 0 || blockMap[blockMapCount - 1].Length != 0) return ERR_INVALID_VOLUME; // Case of complex blockMap if (blockMapCount > 2) return ERR_COMPLEX_BLOCK_MAP; // Calculate new size if (newSize <= size) return ERR_INVALID_PARAMETER; newSize += blockMap[0].Length - newSize % blockMap[0].Length; // Recalculate number of blocks blockMap[0].NumBlocks = newSize / blockMap[0].Length; // Set new volume size volumeHeader->FvLength = 0; for (UINT8 i = 0; i < blockMapCount; i++) { volumeHeader->FvLength += blockMap[i].NumBlocks * blockMap[i].Length; } // Recalculate volume header checksum volumeHeader->Checksum = 0; volumeHeader->Checksum = calculateChecksum16((const UINT16*)volumeHeader, volumeHeader->HeaderLength); return ERR_SUCCESS; } UINT8 FfsEngine::reconstructImageFile(QByteArray & reconstructed) { return reconstruct(model->index(0, 0), reconstructed); } // Search routines UINT8 FfsEngine::findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode) { if (!index.isValid()) return ERR_SUCCESS; if (hexPattern.isEmpty()) return ERR_INVALID_PARAMETER; // Check for "all substrings" pattern if (hexPattern.count('.') == hexPattern.length()) return ERR_SUCCESS; bool hasChildren = (model->rowCount(index) > 0); for (int i = 0; i < model->rowCount(index); i++) { findHexPattern(index.child(i, index.column()), hexPattern, mode); } QByteArray data; if (hasChildren) { if (mode != SEARCH_MODE_BODY) data = model->header(index); } else { if (mode == SEARCH_MODE_HEADER) data.append(model->header(index)); else if (mode == SEARCH_MODE_BODY) data.append(model->body(index)); else data.append(model->header(index)).append(model->body(index)); } QString hexBody = QString(data.toHex()); QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); INT32 offset = regexp.indexIn(hexBody); while (offset >= 0) { if (offset % 2 == 0) { msg(tr("Hex pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") .arg(QString(hexPattern)) .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) .arg(model->name(index)) .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) .hexarg(offset / 2), index); } offset = regexp.indexIn(hexBody, offset + 1); } return ERR_SUCCESS; } UINT8 FfsEngine::findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode) { if (guidPattern.isEmpty()) return ERR_INVALID_PARAMETER; if (!index.isValid()) return ERR_SUCCESS; bool hasChildren = (model->rowCount(index) > 0); for (int i = 0; i < model->rowCount(index); i++) { findGuidPattern(index.child(i, index.column()), guidPattern, mode); } QByteArray data; if (hasChildren) { if (mode != SEARCH_MODE_BODY) data = model->header(index); } else { if (mode == SEARCH_MODE_HEADER) data.append(model->header(index)); else if (mode == SEARCH_MODE_BODY) data.append(model->body(index)); else data.append(model->header(index)).append(model->body(index)); } QString hexBody = QString(data.toHex()); QList list = guidPattern.split('-'); if (list.count() != 5) return ERR_INVALID_PARAMETER; QByteArray hexPattern; // Reverse first GUID block hexPattern.append(list.at(0).mid(6, 2)); hexPattern.append(list.at(0).mid(4, 2)); hexPattern.append(list.at(0).mid(2, 2)); hexPattern.append(list.at(0).mid(0, 2)); // Reverse second GUID block hexPattern.append(list.at(1).mid(2, 2)); hexPattern.append(list.at(1).mid(0, 2)); // Reverse third GUID block hexPattern.append(list.at(2).mid(2, 2)); hexPattern.append(list.at(2).mid(0, 2)); // Append fourth and fifth GUID blocks as is hexPattern.append(list.at(3)).append(list.at(4)); // Check for "all substrings" pattern if (hexPattern.count('.') == hexPattern.length()) return ERR_SUCCESS; QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); INT32 offset = regexp.indexIn(hexBody); while (offset >= 0) { if (offset % 2 == 0) { msg(tr("GUID pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") .arg(QString(guidPattern)) .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) .arg(model->name(index)) .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) .hexarg(offset / 2), index); } offset = regexp.indexIn(hexBody, offset + 1); } return ERR_SUCCESS; } UINT8 FfsEngine::findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive) { if (pattern.isEmpty()) return ERR_INVALID_PARAMETER; if (!index.isValid()) return ERR_SUCCESS; bool hasChildren = (model->rowCount(index) > 0); for (int i = 0; i < model->rowCount(index); i++) { findTextPattern(index.child(i, index.column()), pattern, unicode, caseSensitive); } if (hasChildren) return ERR_SUCCESS; QString data; if (unicode) data = QString::fromUtf16((const ushort*)model->body(index).data(), model->body(index).length() / 2); else data = QString::fromLatin1((const char*)model->body(index).data(), model->body(index).length()); int offset = -1; while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) { msg(tr("%1 text \"%2\" found in %3 at offset %4h") .arg(unicode ? "Unicode" : "ASCII") .arg(pattern) .arg(model->name(index)) .hexarg(unicode ? offset * 2 : offset), index); } return ERR_SUCCESS; } UINT8 FfsEngine::rebase(QByteArray &executable, const UINT32 base, const QModelIndex & index) { UINT32 delta; // Difference between old and new base addresses UINT32 relocOffset; // Offset of relocation region UINT32 relocSize; // Size of relocation region UINT32 teFixup = 0; // Bytes removed form PE header for TE images // Copy input data to local storage QByteArray file = executable; // Populate DOS header if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) return ERR_INVALID_FILE; EFI_IMAGE_DOS_HEADER* dosHeader = (EFI_IMAGE_DOS_HEADER*)file.data(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) return ERR_UNKNOWN_IMAGE_TYPE; EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(file.data() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; offset += sizeof(EFI_IMAGE_PE_HEADER); // Skip file header offset += sizeof(EFI_IMAGE_FILE_HEADER); // Check optional header magic if ((UINT32)file.size() < offset + sizeof(UINT16)) return ERR_UNKNOWN_IMAGE_TYPE; UINT16 magic = *(UINT16*)(file.data() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (EFI_IMAGE_OPTIONAL_HEADER32*)(file.data() + offset); delta = base - optHeader->ImageBase; if (!delta) // No need to rebase return ERR_SUCCESS; relocOffset = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; relocSize = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; // Set new base optHeader->ImageBase = base; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (EFI_IMAGE_OPTIONAL_HEADER64*)(file.data() + offset); delta = base - optHeader->ImageBase; if (!delta) // No need to rebase return ERR_SUCCESS; relocOffset = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; relocSize = optHeader->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; // Set new base optHeader->ImageBase = base; } else return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) return ERR_INVALID_FILE; EFI_IMAGE_TE_HEADER* teHeader = (EFI_IMAGE_TE_HEADER*)file.data(); delta = base - teHeader->ImageBase; if (!delta) // No need to rebase return ERR_SUCCESS; relocOffset = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); relocSize = teHeader->DataDirectory[EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC].Size; // Set new base teHeader->ImageBase = base; // Warn the user about possible outcome of incorrect rebase of TE image msg(tr("rebase: can't determine if TE image base is adjusted or not, rebased TE image may stop working"), index); } else return ERR_UNKNOWN_IMAGE_TYPE; // No relocations if (relocOffset == 0) { // No need to fix relocations executable = file; return ERR_SUCCESS; } EFI_IMAGE_BASE_RELOCATION *RelocBase; EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd; UINT16 *Reloc; UINT16 *RelocEnd; UINT16 *F16; UINT32 *F32; UINT64 *F64; // Run the whole relocation block RelocBase = (EFI_IMAGE_BASE_RELOCATION*)(file.data() + relocOffset - teFixup); RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION*)(file.data() + relocOffset - teFixup + relocSize); while (RelocBase < RelocBaseEnd) { Reloc = (UINT16*)((UINT8*)RelocBase + sizeof(EFI_IMAGE_BASE_RELOCATION)); RelocEnd = (UINT16*)((UINT8*)RelocBase + RelocBase->SizeOfBlock); // Run this relocation record while (Reloc < RelocEnd) { if (*Reloc == 0x0000) { // Skip last emtpy reloc entry Reloc += 1; continue; } UINT32 RelocLocation = RelocBase->VirtualAddress - teFixup + (*Reloc & 0x0FFF); if ((UINT32)file.size() < RelocLocation) return ERR_BAD_RELOCATION_ENTRY; UINT8* data = (UINT8*)(file.data() + RelocLocation); switch ((*Reloc) >> 12) { case EFI_IMAGE_REL_BASED_ABSOLUTE: // Do nothing break; case EFI_IMAGE_REL_BASED_HIGH: // Add second 16 bits of delta F16 = (UINT16*)data; *F16 = (UINT16)(*F16 + (UINT16)(((UINT32)delta) >> 16)); break; case EFI_IMAGE_REL_BASED_LOW: // Add first 16 bits of delta F16 = (UINT16*)data; *F16 = (UINT16)(*F16 + (UINT16)delta); break; case EFI_IMAGE_REL_BASED_HIGHLOW: // Add first 32 bits of delta F32 = (UINT32*)data; *F32 = *F32 + (UINT32)delta; break; case EFI_IMAGE_REL_BASED_DIR64: // Add all 64 bits of delta F64 = (UINT64*)data; *F64 = *F64 + (UINT64)delta; break; default: return ERR_UNKNOWN_RELOCATION_TYPE; } // Next relocation record Reloc += 1; } // Next relocation block RelocBase = (EFI_IMAGE_BASE_RELOCATION*)RelocEnd; } executable = file; return ERR_SUCCESS; } UINT8 FfsEngine::patchVtf(QByteArray &vtf) { if (!oldPeiCoreEntryPoint) { msg(tr("patchVtf: PEI Core entry point can't be determined. VTF can't be patched.")); return ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND; } if (!newPeiCoreEntryPoint || oldPeiCoreEntryPoint == newPeiCoreEntryPoint) // No need to patch anything return ERR_SUCCESS; // Replace last occurrence of oldPeiCoreEntryPoint with newPeiCoreEntryPoint QByteArray old((char*)&oldPeiCoreEntryPoint, sizeof(oldPeiCoreEntryPoint)); int i = vtf.lastIndexOf(old); if (i == -1) { msg(tr("patchVtf: PEI Core entry point can't be found in VTF. VTF not patched.")); return ERR_SUCCESS; } UINT32* data = (UINT32*)(vtf.data() + i); *data = newPeiCoreEntryPoint; return ERR_SUCCESS; } UINT8 FfsEngine::getEntryPoint(const QByteArray &file, UINT32& entryPoint) { if (file.isEmpty()) return ERR_INVALID_FILE; // Populate DOS header if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) return ERR_INVALID_FILE; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) return ERR_UNKNOWN_IMAGE_TYPE; const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; offset += sizeof(EFI_IMAGE_PE_HEADER); // Skip file header offset += sizeof(EFI_IMAGE_FILE_HEADER); // Check optional header magic const UINT16 magic = *(const UINT16*)(file.constData() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); entryPoint = optHeader->ImageBase + optHeader->AddressOfEntryPoint; } else return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) return ERR_INVALID_FILE; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); UINT32 teFixup = teHeader->StrippedSize - sizeof(EFI_IMAGE_TE_HEADER); entryPoint = teHeader->ImageBase + teHeader->AddressOfEntryPoint - teFixup; } return ERR_SUCCESS; } UINT8 FfsEngine::getBase(const QByteArray& file, UINT32& base) { if (file.isEmpty()) return ERR_INVALID_FILE; // Populate DOS header if ((UINT32)file.size() < sizeof(EFI_IMAGE_DOS_HEADER)) return ERR_INVALID_FILE; const EFI_IMAGE_DOS_HEADER* dosHeader = (const EFI_IMAGE_DOS_HEADER*)file.constData(); // Check signature if (dosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE){ UINT32 offset = dosHeader->e_lfanew; if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_PE_HEADER)) return ERR_UNKNOWN_IMAGE_TYPE; const EFI_IMAGE_PE_HEADER* peHeader = (const EFI_IMAGE_PE_HEADER*)(file.constData() + offset); if (peHeader->Signature != EFI_IMAGE_PE_SIGNATURE) return ERR_UNKNOWN_IMAGE_TYPE; offset += sizeof(EFI_IMAGE_PE_HEADER); // Skip file header offset += sizeof(EFI_IMAGE_FILE_HEADER); // Check optional header magic const UINT16 magic = *(const UINT16*)(file.constData() + offset); if (magic == EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC) { if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER32* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER32*)(file.constData() + offset); base = optHeader->ImageBase; } else if (magic == EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC) { if ((UINT32)file.size() < offset + sizeof(EFI_IMAGE_OPTIONAL_HEADER64)) return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; const EFI_IMAGE_OPTIONAL_HEADER64* optHeader = (const EFI_IMAGE_OPTIONAL_HEADER64*)(file.constData() + offset); base = optHeader->ImageBase; } else return ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE; } else if (dosHeader->e_magic == EFI_IMAGE_TE_SIGNATURE){ // Populate TE header if ((UINT32)file.size() < sizeof(EFI_IMAGE_TE_HEADER)) return ERR_INVALID_FILE; const EFI_IMAGE_TE_HEADER* teHeader = (const EFI_IMAGE_TE_HEADER*)file.constData(); //!TODO: add handling base = teHeader->ImageBase; } return ERR_SUCCESS; } UINT32 FfsEngine::crc32(UINT32 initial, const UINT8* buffer, UINT32 length) { static const UINT32 crcTable[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; UINT32 crc32; UINT32 i; // Accumulate crc32 for buffer crc32 = initial ^ 0xFFFFFFFF; for (i = 0; i < length; i++) { crc32 = (crc32 >> 8) ^ crcTable[(crc32 ^ buffer[i]) & 0xFF]; } return(crc32 ^ 0xFFFFFFFF); } UINT8 FfsEngine::dump(const QModelIndex & index, const QString & path, const QString & guid) { dumped = false; UINT8 result = recursiveDump(index, path, guid); if (result) return result; else if (!dumped) return ERR_ITEM_NOT_FOUND; return ERR_SUCCESS; } UINT8 FfsEngine::recursiveDump(const QModelIndex & index, const QString & path, const QString & guid) { if (!index.isValid()) return ERR_INVALID_PARAMETER; QDir dir; if (guid.isEmpty() || guidToQString(*(const EFI_GUID*)model->header(index).constData()) == guid || guidToQString(*(const EFI_GUID*)model->header(model->findParentOfType(index, Types::File)).constData()) == guid) { if (dir.cd(path)) return ERR_DIR_ALREADY_EXIST; if (!dir.mkpath(path)) return ERR_DIR_CREATE; QFile file; if (!model->header(index).isEmpty()) { file.setFileName(tr("%1/header.bin").arg(path)); if (!file.open(QFile::WriteOnly)) return ERR_FILE_OPEN; file.write(model->header(index)); file.close(); } if (!model->body(index).isEmpty()) { file.setFileName(tr("%1/body.bin").arg(path)); if (!file.open(QFile::WriteOnly)) return ERR_FILE_OPEN; file.write(model->body(index)); file.close(); } QString info = tr("Type: %1\nSubtype: %2\n%3%4") .arg(itemTypeToQString(model->type(index))) .arg(itemSubtypeToQString(model->type(index), model->subtype(index))) .arg(model->text(index).isEmpty() ? "" : tr("Text: %1\n").arg(model->text(index))) .arg(model->info(index)); file.setFileName(tr("%1/info.txt").arg(path)); if (!file.open(QFile::Text | QFile::WriteOnly)) return ERR_FILE_OPEN; file.write(info.toLatin1()); file.close(); dumped = true; } UINT8 result; for (int i = 0; i < model->rowCount(index); i++) { QModelIndex childIndex = index.child(i, 0); QString childPath = QString("%1/%2 %3").arg(path).arg(i).arg(model->text(childIndex).isEmpty() ? model->name(childIndex) : model->text(childIndex)); result = recursiveDump(childIndex, childPath, guid); if (result) return result; } return ERR_SUCCESS; } UINT8 FfsEngine::patch(const QModelIndex & index, const QVector & patches) { if (!index.isValid() || patches.isEmpty() || model->rowCount(index)) return ERR_INVALID_PARAMETER; // Skip removed items if (model->action(index) == Actions::Remove) return ERR_NOTHING_TO_PATCH; UINT8 result; // Apply patches to item's body QByteArray body = model->body(index); PatchData current; Q_FOREACH(current, patches) { if (current.type == PATCH_TYPE_OFFSET) { result = patchViaOffset(body, current.offset, current.hexReplacePattern); if (result) return result; } else if (current.type == PATCH_TYPE_PATTERN) { result = patchViaPattern(body, current.hexFindPattern, current.hexReplacePattern); if (result) return result; } else return ERR_UNKNOWN_PATCH_TYPE; } if (body != model->body(index)) { QByteArray patched = model->header(index); patched.append(body); return replace(index, patched, REPLACE_MODE_AS_IS); } return ERR_NOTHING_TO_PATCH; } UINT8 FfsEngine::patchViaOffset(QByteArray & data, const UINT32 offset, const QByteArray & hexReplacePattern) { QByteArray body = data; // Skip patterns with odd length if (hexReplacePattern.length() % 2 > 0) return ERR_INVALID_PARAMETER; // Check offset bounds if (offset > (UINT32)(body.length() - hexReplacePattern.length() / 2)) return ERR_PATCH_OFFSET_OUT_OF_BOUNDS; // Parse replace pattern QByteArray replacePattern; bool converted; for (int i = 0; i < hexReplacePattern.length() / 2; i++) { QByteArray hex = hexReplacePattern.mid(2 * i, 2); UINT8 value = 0; if (!hex.contains('.')) { // Normal byte pattern value = (UINT8)hex.toUShort(&converted, 16); if (!converted) return ERR_INVALID_SYMBOL; } else { // Placeholder byte pattern if (hex[0] == '.' && hex[1] == '.') { // Full byte placeholder value = body.at(offset + i); } else if (hex[0] == '.') {// Upper byte part placeholder hex[0] = '0'; value = (UINT8)(body.at(offset + i) & 0xF0); value += (UINT8)hex.toUShort(&converted, 16); if (!converted) return ERR_INVALID_SYMBOL; } else if (hex[1] == '.') { // Lower byte part placeholder hex[1] = '0'; value = (UINT8)(body.at(offset + i) & 0x0F); value += (UINT8)hex.toUShort(&converted, 16); if (!converted) return ERR_INVALID_SYMBOL; } else return ERR_INVALID_SYMBOL; } // Append calculated value to real pattern replacePattern.append(value); } body.replace(offset, replacePattern.length(), replacePattern); msg(tr("patch: replaced %1 bytes at offset %2h %3 -> %4") .arg(replacePattern.length()) .hexarg(offset) .arg(QString(data.mid(offset, replacePattern.length()).toHex()).toUpper()) .arg(QString(replacePattern.toHex()).toUpper())); data = body; return ERR_SUCCESS; } UINT8 FfsEngine::patchViaPattern(QByteArray & data, const QByteArray & hexFindPattern, const QByteArray & hexReplacePattern) { QByteArray body = data; // Skip patterns with odd length if (hexFindPattern.length() % 2 > 0 || hexReplacePattern.length() % 2 > 0) return ERR_INVALID_PARAMETER; // Convert file body to hex; QString hexBody = QString(body.toHex()); QRegExp regexp = QRegExp(QString(hexFindPattern), Qt::CaseInsensitive); INT32 offset = regexp.indexIn(hexBody); while (offset >= 0) { if (offset % 2 == 0) { UINT8 result = patchViaOffset(body, offset / 2, hexReplacePattern); if (result) return result; } offset = regexp.indexIn(hexBody, offset + 1); } data = body; return ERR_SUCCESS; } UEFITool-0.28.0/ffsengine.h000066400000000000000000000162401363631100000153000ustar00rootroot00000000000000/* ffsengine.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef __FFSENGINE_H__ #define __FFSENGINE_H__ #include #include #include #include #include #include #include #include #include "basetypes.h" #include "treemodel.h" #include "peimage.h" #ifndef _CONSOLE #include "messagelistitem.h" #endif class TreeModel; QString errorMessage(UINT8 errorCode); struct PatchData { UINT8 type; UINT32 offset; QByteArray hexFindPattern; QByteArray hexReplacePattern; }; class FfsEngine : public QObject { Q_OBJECT public: // Default constructor and destructor FfsEngine(QObject *parent = 0); ~FfsEngine(void); // Returns model for Qt view classes TreeModel* treeModel() const; #ifndef _CONSOLE // Returns message items queue QQueue messages() const; // Clears message items queue void clearMessages(); #endif // Firmware image parsing UINT8 parseImageFile(const QByteArray & buffer); UINT8 parseIntelImage(const QByteArray & intelImage, QModelIndex & index, const QModelIndex & parent = QModelIndex()); UINT8 parseGbeRegion(const QByteArray & gbe, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision = 2, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 parseSections(const QByteArray & body, const QModelIndex & parent = QModelIndex()); UINT8 parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); // Compression routines UINT8 decompress(const QByteArray & compressed, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm = NULL); UINT8 compress(const QByteArray & data, const UINT8 algorithm, const UINT32 dictionarySize, QByteArray & compressedData); // Construction routines UINT8 reconstructImageFile(QByteArray &reconstructed); UINT8 reconstruct(const QModelIndex &index, QByteArray & reconstructed); UINT8 reconstructIntelImage(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructRegion(const QModelIndex& index, QByteArray & reconstructed, bool includeHeader = true); UINT8 reconstructPadding(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructVolume(const QModelIndex& index, QByteArray & reconstructed); UINT8 reconstructFile(const QModelIndex& index, const UINT8 revision, const UINT8 erasePolarity, const UINT32 base, QByteArray& reconstructed); UINT8 reconstructSection(const QModelIndex& index, const UINT32 base, QByteArray & reconstructed); // Operations on tree items UINT8 extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode); UINT8 create(const QModelIndex & index, const UINT8 type, const QByteArray & header, const QByteArray & body, const UINT8 mode, const UINT8 action, const UINT8 algorithm = COMPRESSION_ALGORITHM_NONE); UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 mode); UINT8 replace(const QModelIndex & index, const QByteArray & object, const UINT8 mode); UINT8 remove(const QModelIndex & index); UINT8 rebuild(const QModelIndex & index); UINT8 doNotRebuild(const QModelIndex & index); UINT8 dump(const QModelIndex & index, const QString & path, const QString & filter = QString()); UINT8 patch(const QModelIndex & index, const QVector & patches); // Search routines UINT8 findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode); UINT8 findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode); UINT8 findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); private: TreeModel *model; // PEI Core entry point UINT32 oldPeiCoreEntryPoint; UINT32 newPeiCoreEntryPoint; // Parsing helpers UINT32 getPaddingType(const QByteArray & padding); void parseAprioriRawSection(const QByteArray & body, QString & parsed); UINT8 parseDepexSection(const QByteArray & body, QString & parsed); UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize); // Reconstruction helpers UINT8 constructPadFile(const QByteArray &guid, const UINT32 size, const UINT8 revision, const UINT8 erasePolarity, QByteArray & pad); UINT8 growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize); // Rebase routines UINT8 getBase(const QByteArray& file, UINT32& base); UINT8 getEntryPoint(const QByteArray& file, UINT32 &entryPoint); UINT8 rebase(QByteArray & executable, const UINT32 base, const QModelIndex & index); void rebasePeiFiles(const QModelIndex & index); // Patch routines UINT8 patchVtf(QByteArray &vtf); // Patch helpers UINT8 patchViaOffset(QByteArray & data, const UINT32 offset, const QByteArray & hexReplacePattern); UINT8 patchViaPattern(QByteArray & data, const QByteArray & hexFindPattern, const QByteArray & hexReplacePattern); #ifndef _CONSOLE QQueue messageItems; #endif // Message helper void msg(const QString & message, const QModelIndex &index = QModelIndex()); // Internal operations bool hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2); UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length); // Recursive dump bool dumped; UINT8 recursiveDump(const QModelIndex & index, const QString & path, const QString & filter); }; #endif UEFITool-0.28.0/gbe.h000066400000000000000000000015631363631100000140730ustar00rootroot00000000000000/* gbe.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef __GBE_H__ #define __GBE_H__ #include "basetypes.h" // Make sure we use right packing rules #pragma pack(push,1) typedef struct _GBE_MAC_ADDRESS { UINT8 vendor[3]; UINT8 device[3]; } GBE_MAC_ADDRESS; #define GBE_VERSION_OFFSET 10 typedef struct _GBE_VERSION { UINT8 id : 4; UINT8 minor : 4; UINT8 major; } GBE_VERSION; // Restore previous packing rules #pragma pack(pop) #endif UEFITool-0.28.0/guidlineedit.cpp000066400000000000000000000031141363631100000163310ustar00rootroot00000000000000/* guidlineedit.cpp Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #include "guidlineedit.h" GuidLineEdit::GuidLineEdit(QWidget * parent) :QLineEdit(parent) { } GuidLineEdit::GuidLineEdit(const QString & contents, QWidget * parent) :QLineEdit(contents, parent) { } GuidLineEdit::~GuidLineEdit() { } void GuidLineEdit::keyPressEvent(QKeyEvent * event) { if (event == QKeySequence::Delete || event->key() == Qt::Key_Backspace) { int pos = cursorPosition(); if (event->key() == Qt::Key_Backspace && pos > 0) { cursorBackward(false); pos = cursorPosition(); } QString txt = text(); QString selected = selectedText(); if (!selected.isEmpty()) { pos = QLineEdit::selectionStart(); for (int i = pos; i < pos + selected.count(); i++) if (txt[i] != QChar('-')) txt[i] = QChar('.'); } else txt[pos] = QChar('.'); setCursorPosition(0); insert(txt); setCursorPosition(pos); return; } // Call original event handler QLineEdit::keyPressEvent(event); }UEFITool-0.28.0/guidlineedit.h000066400000000000000000000016151363631100000160020ustar00rootroot00000000000000/* guidlineedit.h Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __GUIDLINEEDIT_H__ #define __GUIDLINEEDIT_H__ #include #include #include #include #include "basetypes.h" class GuidLineEdit : public QLineEdit { public: GuidLineEdit(QWidget * parent = 0); GuidLineEdit(const QString & contents, QWidget * parent = 0); ~GuidLineEdit(); protected: void keyPressEvent(QKeyEvent * event); }; #endif UEFITool-0.28.0/me.h000066400000000000000000000017121363631100000137330ustar00rootroot00000000000000/* me.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #ifndef __ME_H__ #define __ME_H__ #include "basetypes.h" // Make sure we use right packing rules #pragma pack(push,1) const QByteArray ME_VERSION_SIGNATURE("\x24\x4D\x41\x4E", 4); //$MAN const QByteArray ME_VERSION_SIGNATURE2("\x24\x4D\x4E\x32", 4); //$MN2 typedef struct _ME_VERSION { UINT32 signature; UINT32 reserved; // Unknown for me UINT16 major; UINT16 minor; UINT16 bugfix; UINT16 build; } ME_VERSION; // Restore previous packing rules #pragma pack(pop) #endif UEFITool-0.28.0/messagelistitem.cpp000066400000000000000000000023741363631100000170710ustar00rootroot00000000000000/* messagelistitem.cpp Copyright (c) 2013, Nikolaj Schlej. All rights reserved. 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. */ #include "messagelistitem.h" MessageListItem::MessageListItem(QListWidget * parent, int type, const QModelIndex & index) : QListWidgetItem(parent, type) { itemIndex = index; } MessageListItem::MessageListItem(const QString & text, QListWidget * parent, int type, const QModelIndex & index) : QListWidgetItem(text, parent, type) { itemIndex = index; } MessageListItem::MessageListItem(const QIcon & icon, const QString & text, QListWidget * parent, int type, const QModelIndex & index) : QListWidgetItem(icon, text, parent, type) { itemIndex = index; } MessageListItem::~MessageListItem() { } QModelIndex MessageListItem::index() const { return itemIndex; } void MessageListItem::setIndex(QModelIndex & index) { itemIndex = index; }UEFITool-0.28.0/messagelistitem.h000066400000000000000000000023131363631100000165270ustar00rootroot00000000000000/* messagelistitem.h Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __MESSAGELISTITEM_H__ #define __MESSAGELISTITEM_H__ #include #include #include "basetypes.h" class MessageListItem : public QListWidgetItem { public: MessageListItem(QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex()); MessageListItem(const QString & text, QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex()); MessageListItem(const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex()); ~MessageListItem(); QModelIndex index() const; void setIndex(QModelIndex & index); private: QModelIndex itemIndex; }; #endif UEFITool-0.28.0/peimage.cpp000066400000000000000000000022011363631100000152660ustar00rootroot00000000000000/* peimage.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #include #include "peimage.h" QString machineTypeToQString(UINT16 machineType) { switch (machineType){ case IMAGE_FILE_MACHINE_AMD64: return QObject::tr("x86-64"); case IMAGE_FILE_MACHINE_ARM: return QObject::tr("ARM"); case IMAGE_FILE_MACHINE_ARMV7: return QObject::tr("ARMv7"); case IMAGE_FILE_MACHINE_EBC: return QObject::tr("EBC"); case IMAGE_FILE_MACHINE_I386: return QObject::tr("x86"); case IMAGE_FILE_MACHINE_IA64: return QObject::tr("IA64"); case IMAGE_FILE_MACHINE_THUMB: return QObject::tr("Thumb"); default: return QObject::tr("Unknown %1").hexarg2(machineType, 4); } }UEFITool-0.28.0/peimage.h000066400000000000000000000564561363631100000147600ustar00rootroot00000000000000/* peimage.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved. Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved. 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. */ #ifndef __PE_IMAGE_H__ #define __PE_IMAGE_H__ #include #include "basetypes.h" // Make sure we use right packing rules #pragma pack(push,1) extern QString machineTypeToQString(UINT16 machineType); // // PE32+ Subsystem type for EFI images // #define EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION 10 #define EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 #define EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 #define EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13 // // PE32+ Machine type for EFI images // #define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_ARM 0x01c0 #define IMAGE_FILE_MACHINE_ARMV7 0x01c4 #define IMAGE_FILE_MACHINE_EBC 0x0ebc #define IMAGE_FILE_MACHINE_I386 0x014c #define IMAGE_FILE_MACHINE_IA64 0x0200 #define IMAGE_FILE_MACHINE_THUMB 0x01c2 // // EXE file formats // #define EFI_IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define EFI_IMAGE_PE_SIGNATURE 0x00004550 // PE // // PE images can start with an optional DOS header, so if an image is run // under DOS it can print an error message. // typedef struct { UINT16 e_magic; // Magic number UINT16 e_cblp; // Bytes on last page of file UINT16 e_cp; // Pages in file UINT16 e_crlc; // Relocations UINT16 e_cparhdr; // Size of header in paragraphs UINT16 e_minalloc; // Minimum extra paragraphs needed UINT16 e_maxalloc; // Maximum extra paragraphs needed UINT16 e_ss; // Initial (relative) SS value UINT16 e_sp; // Initial SP value UINT16 e_csum; // Checksum UINT16 e_ip; // Initial IP value UINT16 e_cs; // Initial (relative) CS value UINT16 e_lfarlc; // File address of relocation table UINT16 e_ovno; // Overlay number UINT16 e_res[4]; // Reserved words UINT16 e_oemid; // OEM identifier (for e_oeminfo) UINT16 e_oeminfo; // OEM information; e_oemid specific UINT16 e_res2[10]; // Reserved words UINT32 e_lfanew; // File address of new header } EFI_IMAGE_DOS_HEADER; // // COFF File Header (Object and Image) // typedef struct { UINT16 Machine; UINT16 NumberOfSections; UINT32 TimeDateStamp; UINT32 PointerToSymbolTable; UINT32 NumberOfSymbols; UINT16 SizeOfOptionalHeader; UINT16 Characteristics; } EFI_IMAGE_FILE_HEADER; // // Size of EFI_IMAGE_FILE_HEADER. // #define EFI_IMAGE_SIZEOF_FILE_HEADER 20 // // Characteristics // #define EFI_IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file #define EFI_IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references) #define EFI_IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line numbers stripped from file #define EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file #define EFI_IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed #define EFI_IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine #define EFI_IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file #define EFI_IMAGE_FILE_SYSTEM 0x1000 // System File #define EFI_IMAGE_FILE_DLL 0x2000 // File is a DLL #define EFI_IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed // // Header Data Directories. // typedef struct { UINT32 VirtualAddress; UINT32 Size; } EFI_IMAGE_DATA_DIRECTORY; // // Directory Entries // #define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 #define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC 5 #define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG 6 #define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 #define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define EFI_IMAGE_DIRECTORY_ENTRY_TLS 9 #define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 // // EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and // EFI_IMAGE_OPTIONAL_HEADER32 must be used. // The data structures only vary after NT additional fields // #define EFI_IMAGE_PE_OPTIONAL_HDR32_MAGIC 0x10b // // Optional Header Standard Fields for PE32 // typedef struct { // // Standard fields. // UINT16 Magic; UINT8 MajorLinkerVersion; UINT8 MinorLinkerVersion; UINT32 SizeOfCode; UINT32 SizeOfInitializedData; UINT32 SizeOfUninitializedData; UINT32 AddressOfEntryPoint; UINT32 BaseOfCode; UINT32 BaseOfData; // PE32 contains this additional field, which is absent in PE32+. // // Optional Header Windows-Specific Fields. // UINT32 ImageBase; UINT32 SectionAlignment; UINT32 FileAlignment; UINT16 MajorOperatingSystemVersion; UINT16 MinorOperatingSystemVersion; UINT16 MajorImageVersion; UINT16 MinorImageVersion; UINT16 MajorSubsystemVersion; UINT16 MinorSubsystemVersion; UINT32 Win32VersionValue; UINT32 SizeOfImage; UINT32 SizeOfHeaders; UINT32 CheckSum; UINT16 Subsystem; UINT16 DllCharacteristics; UINT32 SizeOfStackReserve; UINT32 SizeOfStackCommit; UINT32 SizeOfHeapReserve; UINT32 SizeOfHeapCommit; UINT32 LoaderFlags; UINT32 NumberOfRvaAndSizes; EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; } EFI_IMAGE_OPTIONAL_HEADER32; // // EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and // EFI_IMAGE_OPTIONAL_HEADER64 must be used. // The data structures only vary after NT additional fields // #define EFI_IMAGE_PE_OPTIONAL_HDR64_MAGIC 0x20b // // Optional Header Standard Fields for PE32+. // typedef struct { // // Standard fields. // UINT16 Magic; UINT8 MajorLinkerVersion; UINT8 MinorLinkerVersion; UINT32 SizeOfCode; UINT32 SizeOfInitializedData; UINT32 SizeOfUninitializedData; UINT32 AddressOfEntryPoint; UINT32 BaseOfCode; // // Optional Header Windows-Specific Fields. // UINT64 ImageBase; UINT32 SectionAlignment; UINT32 FileAlignment; UINT16 MajorOperatingSystemVersion; UINT16 MinorOperatingSystemVersion; UINT16 MajorImageVersion; UINT16 MinorImageVersion; UINT16 MajorSubsystemVersion; UINT16 MinorSubsystemVersion; UINT32 Win32VersionValue; UINT32 SizeOfImage; UINT32 SizeOfHeaders; UINT32 CheckSum; UINT16 Subsystem; UINT16 DllCharacteristics; UINT64 SizeOfStackReserve; UINT64 SizeOfStackCommit; UINT64 SizeOfHeapReserve; UINT64 SizeOfHeapCommit; UINT32 LoaderFlags; UINT32 NumberOfRvaAndSizes; EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; } EFI_IMAGE_OPTIONAL_HEADER64; // Union for pointers to either PE32 or PE32+ headers typedef union _EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION { const EFI_IMAGE_OPTIONAL_HEADER32* H32; const EFI_IMAGE_OPTIONAL_HEADER64* H64; } EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION; typedef struct { UINT32 Signature; //EFI_IMAGE_FILE_HEADER FileHeader; //EFI_IMAGE_OPTIONAL_HEADER OptionalHeader; } EFI_IMAGE_PE_HEADER; // // Other Windows Subsystem Values // #define EFI_IMAGE_SUBSYSTEM_UNKNOWN 0 #define EFI_IMAGE_SUBSYSTEM_NATIVE 1 #define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2 #define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3 #define EFI_IMAGE_SUBSYSTEM_OS2_CUI 5 #define EFI_IMAGE_SUBSYSTEM_POSIX_CUI 7 // // Length of ShortName // #define EFI_IMAGE_SIZEOF_SHORT_NAME 8 // // Section Table. This table immediately follows the optional header. // typedef struct { UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME]; union { UINT32 PhysicalAddress; UINT32 VirtualSize; } Misc; UINT32 VirtualAddress; UINT32 SizeOfRawData; UINT32 PointerToRawData; UINT32 PointerToRelocations; UINT32 PointerToLinenumbers; UINT16 NumberOfRelocations; UINT16 NumberOfLinenumbers; UINT32 Characteristics; } EFI_IMAGE_SECTION_HEADER; // // Size of EFI_IMAGE_SECTION_HEADER // #define EFI_IMAGE_SIZEOF_SECTION_HEADER 40 // // Section Flags Values // #define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved #define EFI_IMAGE_SCN_CNT_CODE 0x00000020 #define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 #define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 #define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved #define EFI_IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information #define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image #define EFI_IMAGE_SCN_LNK_COMDAT 0x00001000 #define EFI_IMAGE_SCN_ALIGN_1BYTES 0x00100000 #define EFI_IMAGE_SCN_ALIGN_2BYTES 0x00200000 #define EFI_IMAGE_SCN_ALIGN_4BYTES 0x00300000 #define EFI_IMAGE_SCN_ALIGN_8BYTES 0x00400000 #define EFI_IMAGE_SCN_ALIGN_16BYTES 0x00500000 #define EFI_IMAGE_SCN_ALIGN_32BYTES 0x00600000 #define EFI_IMAGE_SCN_ALIGN_64BYTES 0x00700000 #define EFI_IMAGE_SCN_MEM_DISCARDABLE 0x02000000 #define EFI_IMAGE_SCN_MEM_NOT_CACHED 0x04000000 #define EFI_IMAGE_SCN_MEM_NOT_PAGED 0x08000000 #define EFI_IMAGE_SCN_MEM_SHARED 0x10000000 #define EFI_IMAGE_SCN_MEM_EXECUTE 0x20000000 #define EFI_IMAGE_SCN_MEM_READ 0x40000000 #define EFI_IMAGE_SCN_MEM_WRITE 0x80000000 // // Size of a Symbol Table Record // #define EFI_IMAGE_SIZEOF_SYMBOL 18 // // Symbols have a section number of the section in which they are // defined. Otherwise, section numbers have the following meanings: // #define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0 ///< Symbol is undefined or is common #define EFI_IMAGE_SYM_ABSOLUTE (UINT16) -1 ///< Symbol is an absolute value #define EFI_IMAGE_SYM_DEBUG (UINT16) -2 ///< Symbol is a special debug item // // Symbol Type (fundamental) values. // #define EFI_IMAGE_SYM_TYPE_NULL 0 // no type #define EFI_IMAGE_SYM_TYPE_VOID 1 // no valid type #define EFI_IMAGE_SYM_TYPE_CHAR 2 // type character #define EFI_IMAGE_SYM_TYPE_SHORT 3 // type short integer #define EFI_IMAGE_SYM_TYPE_INT 4 #define EFI_IMAGE_SYM_TYPE_LONG 5 #define EFI_IMAGE_SYM_TYPE_FLOAT 6 #define EFI_IMAGE_SYM_TYPE_DOUBLE 7 #define EFI_IMAGE_SYM_TYPE_STRUCT 8 #define EFI_IMAGE_SYM_TYPE_UNION 9 #define EFI_IMAGE_SYM_TYPE_ENUM 10 // enumeration #define EFI_IMAGE_SYM_TYPE_MOE 11 // member of enumeration #define EFI_IMAGE_SYM_TYPE_BYTE 12 #define EFI_IMAGE_SYM_TYPE_WORD 13 #define EFI_IMAGE_SYM_TYPE_UINT 14 #define EFI_IMAGE_SYM_TYPE_DWORD 15 // // Symbol Type (derived) values // #define EFI_IMAGE_SYM_DTYPE_NULL 0 // no derived type #define EFI_IMAGE_SYM_DTYPE_POINTER 1 #define EFI_IMAGE_SYM_DTYPE_FUNCTION 2 #define EFI_IMAGE_SYM_DTYPE_ARRAY 3 // // Storage classes // #define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION ((UINT8) -1) #define EFI_IMAGE_SYM_CLASS_NULL 0 #define EFI_IMAGE_SYM_CLASS_AUTOMATIC 1 #define EFI_IMAGE_SYM_CLASS_EXTERNAL 2 #define EFI_IMAGE_SYM_CLASS_STATIC 3 #define EFI_IMAGE_SYM_CLASS_REGISTER 4 #define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF 5 #define EFI_IMAGE_SYM_CLASS_LABEL 6 #define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 #define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 #define EFI_IMAGE_SYM_CLASS_ARGUMENT 9 #define EFI_IMAGE_SYM_CLASS_STRUCT_TAG 10 #define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 #define EFI_IMAGE_SYM_CLASS_UNION_TAG 12 #define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION 13 #define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 #define EFI_IMAGE_SYM_CLASS_ENUM_TAG 15 #define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 #define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM 17 #define EFI_IMAGE_SYM_CLASS_BIT_FIELD 18 #define EFI_IMAGE_SYM_CLASS_BLOCK 100 #define EFI_IMAGE_SYM_CLASS_FUNCTION 101 #define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT 102 #define EFI_IMAGE_SYM_CLASS_FILE 103 #define EFI_IMAGE_SYM_CLASS_SECTION 104 #define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 // // Type packing constants // #define EFI_IMAGE_N_BTMASK 017 #define EFI_IMAGE_N_TMASK 060 #define EFI_IMAGE_N_TMASK1 0300 #define EFI_IMAGE_N_TMASK2 0360 #define EFI_IMAGE_N_BTSHFT 4 #define EFI_IMAGE_N_TSHIFT 2 // // Communal selection types // #define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES 1 #define EFI_IMAGE_COMDAT_SELECT_ANY 2 #define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 #define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 #define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 // // The following values only be referred in PeCoff, not defined in PECOFF // #define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 #define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 #define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 // // Relocation format // typedef struct { UINT32 VirtualAddress; UINT32 SymbolTableIndex; UINT16 Type; } EFI_IMAGE_RELOCATION; // // Size of EFI_IMAGE_RELOCATION // #define EFI_IMAGE_SIZEOF_RELOCATION 10 // // I386 relocation types // #define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000 // Reference is absolute, no relocation is necessary #define EFI_IMAGE_REL_I386_DIR16 0x0001 // Direct 16-bit reference to the symbols virtual address #define EFI_IMAGE_REL_I386_REL16 0x0002 // PC-relative 16-bit reference to the symbols virtual address #define EFI_IMAGE_REL_I386_DIR32 0x0006 // Direct 32-bit reference to the symbols virtual address #define EFI_IMAGE_REL_I386_DIR32NB 0x0007 // Direct 32-bit reference to the symbols virtual address, base not included #define EFI_IMAGE_REL_I386_SEG12 0x0009 // Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address #define EFI_IMAGE_REL_I386_SECTION 0x000A #define EFI_IMAGE_REL_I386_SECREL 0x000B #define EFI_IMAGE_REL_I386_REL32 0x0014 // PC-relative 32-bit reference to the symbols virtual address // // x64 processor relocation types. // #define IMAGE_REL_AMD64_ABSOLUTE 0x0000 #define IMAGE_REL_AMD64_ADDR64 0x0001 #define IMAGE_REL_AMD64_ADDR32 0x0002 #define IMAGE_REL_AMD64_ADDR32NB 0x0003 #define IMAGE_REL_AMD64_REL32 0x0004 #define IMAGE_REL_AMD64_REL32_1 0x0005 #define IMAGE_REL_AMD64_REL32_2 0x0006 #define IMAGE_REL_AMD64_REL32_3 0x0007 #define IMAGE_REL_AMD64_REL32_4 0x0008 #define IMAGE_REL_AMD64_REL32_5 0x0009 #define IMAGE_REL_AMD64_SECTION 0x000A #define IMAGE_REL_AMD64_SECREL 0x000B #define IMAGE_REL_AMD64_SECREL7 0x000C #define IMAGE_REL_AMD64_TOKEN 0x000D #define IMAGE_REL_AMD64_SREL32 0x000E #define IMAGE_REL_AMD64_PAIR 0x000F #define IMAGE_REL_AMD64_SSPAN32 0x0010 // // Based relocation format // typedef struct { UINT32 VirtualAddress; UINT32 SizeOfBlock; } EFI_IMAGE_BASE_RELOCATION; // // Size of EFI_IMAGE_BASE_RELOCATION // #define EFI_IMAGE_SIZEOF_BASE_RELOCATION 8 // // Based relocation types // #define EFI_IMAGE_REL_BASED_ABSOLUTE 0 #define EFI_IMAGE_REL_BASED_HIGH 1 #define EFI_IMAGE_REL_BASED_LOW 2 #define EFI_IMAGE_REL_BASED_HIGHLOW 3 #define EFI_IMAGE_REL_BASED_HIGHADJ 4 #define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5 #define EFI_IMAGE_REL_BASED_ARM_MOV32A 5 #define EFI_IMAGE_REL_BASED_ARM_MOV32T 7 #define EFI_IMAGE_REL_BASED_IA64_IMM64 9 #define EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 9 #define EFI_IMAGE_REL_BASED_DIR64 10 // // Line number format // typedef struct { union { UINT32 SymbolTableIndex; // Symbol table index of function name if line number is 0 UINT32 VirtualAddress; // Virtual address of line number } Type; UINT16 Linenumber; // Line number } EFI_IMAGE_LINENUMBER; // // Size of EFI_IMAGE_LINENUMBER // #define EFI_IMAGE_SIZEOF_LINENUMBER 6 // // Archive format // #define EFI_IMAGE_ARCHIVE_START_SIZE 8 #define EFI_IMAGE_ARCHIVE_START "!\n" #define EFI_IMAGE_ARCHIVE_END "`\n" #define EFI_IMAGE_ARCHIVE_PAD "\n" #define EFI_IMAGE_ARCHIVE_LINKER_MEMBER "/ " #define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " // // Archive Member Headers // typedef struct { UINT8 Name[16]; // File member name - `/' terminated UINT8 Date[12]; // File member date - decimal UINT8 UserID[6]; // File member user id - decimal UINT8 GroupID[6]; // File member group id - decimal UINT8 Mode[8]; // File member mode - octal UINT8 Size[10]; // File member size - decimal UINT8 EndHeader[2]; // String to end header. (0x60 0x0A) } EFI_IMAGE_ARCHIVE_MEMBER_HEADER; // // Size of EFI_IMAGE_ARCHIVE_MEMBER_HEADER // #define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 // // DLL Support // // // Export Directory Table // typedef struct { UINT32 Characteristics; UINT32 TimeDateStamp; UINT16 MajorVersion; UINT16 MinorVersion; UINT32 Name; UINT32 Base; UINT32 NumberOfFunctions; UINT32 NumberOfNames; UINT32 AddressOfFunctions; UINT32 AddressOfNames; UINT32 AddressOfNameOrdinals; } EFI_IMAGE_EXPORT_DIRECTORY; // // Hint/Name Table // typedef struct { UINT16 Hint; UINT8 Name[1]; } EFI_IMAGE_IMPORT_BY_NAME; // // Import Address Table RVA (Thunk Table) // typedef struct { union { UINT32 Function; UINT32 Ordinal; EFI_IMAGE_IMPORT_BY_NAME *AddressOfData; } u1; } EFI_IMAGE_THUNK_DATA; #define EFI_IMAGE_ORDINAL_FLAG 0x80000000 // Flag for PE32. #define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0) #define EFI_IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) // // Import Directory Table // typedef struct { UINT32 Characteristics; UINT32 TimeDateStamp; UINT32 ForwarderChain; UINT32 Name; EFI_IMAGE_THUNK_DATA *FirstThunk; } EFI_IMAGE_IMPORT_DESCRIPTOR; // // Debug Directory Format // typedef struct { UINT32 Characteristics; UINT32 TimeDateStamp; UINT16 MajorVersion; UINT16 MinorVersion; UINT32 Type; UINT32 SizeOfData; UINT32 RVA; // The address of the debug data when loaded, relative to the image base UINT32 FileOffset; // The file pointer to the debug data } EFI_IMAGE_DEBUG_DIRECTORY_ENTRY; #define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2 // The Visual C++ debug information. // // Debug Data Structure defined in Microsoft C++ // #define CODEVIEW_SIGNATURE_NB10 0x3031424E // NB10 typedef struct { UINT32 Signature; UINT32 Unknown; UINT32 Unknown2; UINT32 Unknown3; // // Filename of .PDB goes here // } EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY; // // Debug Data Structure defined in Microsoft C++ // #define CODEVIEW_SIGNATURE_RSDS 0x53445352 // RSDS typedef struct { UINT32 Signature; UINT32 Unknown; UINT32 Unknown2; UINT32 Unknown3; UINT32 Unknown4; UINT32 Unknown5; // // Filename of .PDB goes here // } EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY; // // Debug Data Structure defined by Apple Mach-O to COFF utility. // #define CODEVIEW_SIGNATURE_MTOC 0x434F544D // MTOC typedef struct { UINT32 Signature; UINT8 MachOUuid[16]; // // Filename of .DLL (Mach-O with debug info) goes here // } EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY; // // Resource format // typedef struct { UINT32 Characteristics; UINT32 TimeDateStamp; UINT16 MajorVersion; UINT16 MinorVersion; UINT16 NumberOfNamedEntries; UINT16 NumberOfIdEntries; // // Array of EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY entries goes here // } EFI_IMAGE_RESOURCE_DIRECTORY; // // Resource directory entry format // typedef struct { union { struct { UINT32 NameOffset : 31; UINT32 NameIsString : 1; } s; UINT32 Id; } u1; union { UINT32 OffsetToData; struct { UINT32 OffsetToDirectory : 31; UINT32 DataIsDirectory : 1; } s; } u2; } EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY; // // Resource directory entry for string // typedef struct { UINT16 Length; CHAR16 String[1]; } EFI_IMAGE_RESOURCE_DIRECTORY_STRING; // // Resource directory entry for data array // typedef struct { UINT32 OffsetToData; UINT32 Size; UINT32 CodePage; UINT32 Reserved; } EFI_IMAGE_RESOURCE_DATA_ENTRY; // // Header format for TE images, defined in the PI Specification 1.0. // typedef struct { UINT16 Signature; // The signature for TE format = "VZ" UINT16 Machine; // From original file header UINT8 NumberOfSections; // From original file header UINT8 Subsystem; // From original optional header UINT16 StrippedSize; // Number of bytes we removed from header UINT32 AddressOfEntryPoint; // Offset to entry point -- from original optional header UINT32 BaseOfCode; // From original image -- required for ITP debug UINT64 ImageBase; // From original file header EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; // Only base relocation and debug directory } EFI_IMAGE_TE_HEADER; #define EFI_IMAGE_TE_SIGNATURE 0x5A56 // VZ // // Data directory indexes in our TE image header // #define EFI_IMAGE_TE_DIRECTORY_ENTRY_BASERELOC 0 #define EFI_IMAGE_TE_DIRECTORY_ENTRY_DEBUG 1 // Restore previous packing rules #pragma pack(pop) #endif UEFITool-0.28.0/searchdialog.cpp000066400000000000000000000026521363631100000163160ustar00rootroot00000000000000/* searchdialog.cpp Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #include "searchdialog.h" SearchDialog::SearchDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SearchDialog), hexValidator(QRegExp("([0-9a-fA-F\\. ])*")), guidValidator(QRegExp("[0-9a-fA-F\\.]{8}-[0-9a-fA-F\\.]{4}-[0-9a-fA-F\\.]{4}-[0-9a-fA-F\\.]{4}-[0-9a-fA-F\\.]{12}")) { // Create UI ui->setupUi(this); ui->hexEdit->setValidator(&hexValidator); ui->guidEdit->setValidator(&guidValidator); // Connect connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(setEditFocus(int))); // Set initial focus setEditFocus(ui->tabWidget->currentIndex()); } SearchDialog::~SearchDialog() { delete ui; } void SearchDialog::setEditFocus(int index) { if (index == 0) // Hex pattern ui->hexEdit->setFocus(); else if (index == 1) { // GUID ui->guidEdit->setFocus(); ui->guidEdit->setCursorPosition(0); } else if (index == 2) // Text ui->textEdit->setFocus(); }UEFITool-0.28.0/searchdialog.h000066400000000000000000000016221363631100000157570ustar00rootroot00000000000000/* searchdialog.h Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #ifndef SEARCHDIALOG_H #define SEARCHDIALOG_H #include #include #include "ui_searchdialog.h" class SearchDialog : public QDialog { Q_OBJECT public: SearchDialog(QWidget *parent = 0); ~SearchDialog(); Ui::SearchDialog* ui; private slots: void setEditFocus(int index); private: QRegExpValidator hexValidator; QRegExpValidator guidValidator; }; #endif UEFITool-0.28.0/searchdialog.ui000066400000000000000000000160661363631100000161550ustar00rootroot00000000000000 SearchDialog 0 0 400 237 Search false 0 Hex pattern Hex pattern: Search scope Header and body true Header only Body only GUID GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ........-....-....-....-............ Search scope Header and body false Header only true Body only Text Text: Text search options Unicode true Case sensitive Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok GuidLineEdit QLineEdit
guidlineedit.h
tabWidget hexEdit hexScopeFullRadioButton hexScopeHeaderRadioButton hexScopeBodyRadioButton buttonBox textEdit textUnicodeCheckBox textCaseSensitiveCheckBox buttonBox accepted() SearchDialog accept() 182 185 157 194 buttonBox rejected() SearchDialog reject() 182 185 286 194
UEFITool-0.28.0/treeitem.cpp000066400000000000000000000103731363631100000155060ustar00rootroot00000000000000/* treeitem.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #include #include "treeitem.h" #include "types.h" TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, TreeItem *parent) : itemAction(Actions::NoAction), itemType(type), itemSubtype(subtype), itemCompression(compression), itemDictionarySize(0), itemName(name), itemText(text), itemInfo(info), itemHeader(header), itemBody(body), parentItem(parent) { } TreeItem::~TreeItem() { qDeleteAll(childItems); } void TreeItem::appendChild(TreeItem *item) { childItems.append(item); } void TreeItem::prependChild(TreeItem *item) { childItems.prepend(item); } UINT8 TreeItem::insertChildBefore(TreeItem *item, TreeItem *newItem) { int index = childItems.indexOf(item); if (index == -1) return ERR_ITEM_NOT_FOUND; childItems.insert(index, newItem); return ERR_SUCCESS; } UINT8 TreeItem::insertChildAfter(TreeItem *item, TreeItem *newItem) { int index = childItems.indexOf(item); if (index == -1) return ERR_ITEM_NOT_FOUND; childItems.insert(index + 1, newItem); return ERR_SUCCESS; } TreeItem *TreeItem::child(int row) { return childItems.value(row, NULL); } int TreeItem::childCount() const { return childItems.count(); } int TreeItem::columnCount() const { return 5; } QVariant TreeItem::data(int column) const { switch (column) { case 0: // Name return itemName; case 1: // Action return actionTypeToQString(itemAction); case 2: // Type return itemTypeToQString(itemType); case 3: // Subtype return itemSubtypeToQString(itemType, itemSubtype); case 4: // Text return itemText; default: return QVariant(); } } TreeItem *TreeItem::parent() { return parentItem; } QString TreeItem::name() const { return itemName; } void TreeItem::setName(const QString &name) { itemName = name; } QString TreeItem::text() const { return itemText; } void TreeItem::setText(const QString &text) { itemText = text; } QString TreeItem::info() const { return itemInfo; } void TreeItem::addInfo(const QString &info) { itemInfo += info; } void TreeItem::setInfo(const QString &info) { itemInfo = info; } int TreeItem::row() const { if (parentItem) return parentItem->childItems.indexOf(const_cast(this)); return 0; } UINT8 TreeItem::type() const { return itemType; } void TreeItem::setType(const UINT8 type) { itemType = type; } UINT8 TreeItem::subtype() const { return itemSubtype; } void TreeItem::setSubtype(const UINT8 subtype) { itemSubtype = subtype; } UINT8 TreeItem::compression() const { return itemCompression; } QByteArray TreeItem::header() const { return itemHeader; } QByteArray TreeItem::body() const { return itemBody; } bool TreeItem::hasEmptyHeader() const { return itemHeader.isEmpty(); } bool TreeItem::hasEmptyBody() const { return itemBody.isEmpty(); } UINT8 TreeItem::action() const { return itemAction; } UINT32 TreeItem::dictionarySize() const { return itemDictionarySize; } void TreeItem::setDictionarySize(const UINT32 dictionarySize) { itemDictionarySize = dictionarySize; } void TreeItem::setAction(const UINT8 action) { itemAction = action; // On insert action, set insert action for children if (action == Actions::Insert) for (int i = 0; i < childCount(); i++) child(i)->setAction(Actions::Insert); // Set rebuild action for parent, if it has no action now if (parentItem && parentItem->type() != Types::Root && parentItem->action() == Actions::NoAction) parentItem->setAction(Actions::Rebuild); } UEFITool-0.28.0/treeitem.h000066400000000000000000000047521363631100000151570ustar00rootroot00000000000000/* treeitem.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __TREEITEM_H__ #define __TREEITEM_H__ #include #include #include #include #include "basetypes.h" class TreeItem { public: TreeItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString &name = QString(), const QString &text = QString(), const QString &info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0); ~TreeItem(); // Operations with items void appendChild(TreeItem *item); void prependChild(TreeItem *item); UINT8 insertChildBefore(TreeItem *item, TreeItem *newItem); UINT8 insertChildAfter(TreeItem *item, TreeItem *newItem); // Model support operations TreeItem *child(int row); int childCount() const; int columnCount() const; QVariant data(int column) const; int row() const; TreeItem *parent(); // Reading operations for item parameters QString name() const; void setName(const QString &text); UINT8 type() const; void setType(const UINT8 type); UINT8 subtype() const; void setSubtype(const UINT8 subtype); QString text() const; void setText(const QString &text); QByteArray header() const; bool hasEmptyHeader() const; QByteArray body() const; bool hasEmptyBody() const; QString info() const; void addInfo(const QString &info); void setInfo(const QString &info); UINT8 action() const; void setAction(const UINT8 action); UINT8 compression() const; UINT32 dictionarySize() const; void setDictionarySize(const UINT32 dictionarySize); private: QList childItems; UINT8 itemAction; UINT8 itemType; UINT8 itemSubtype; UINT8 itemCompression; UINT32 itemDictionarySize; QString itemName; QString itemText; QString itemInfo; QByteArray itemHeader; QByteArray itemBody; TreeItem *parentItem; }; #endif UEFITool-0.28.0/treemodel.cpp000066400000000000000000000224171363631100000156520ustar00rootroot00000000000000/* treemodel.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #include "treeitem.h" #include "treemodel.h" TreeModel::TreeModel(QObject *parent) : QAbstractItemModel(parent) { rootItem = new TreeItem(Types::Root); } TreeModel::~TreeModel() { delete rootItem; } int TreeModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return static_cast(parent.internalPointer())->columnCount(); else return rootItem->columnCount(); } QVariant TreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role != Qt::DisplayRole && role != Qt::UserRole) return QVariant(); TreeItem *item = static_cast(index.internalPointer()); if (role == Qt::DisplayRole) return item->data(index.column()); else return item->info(); } Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch (section) { case 0: return tr("Name"); case 1: return tr("Action"); case 2: return tr("Type"); case 3: return tr("Subtype"); case 4: return tr("Text"); } } return QVariant(); } QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex TreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = static_cast(index.internalPointer()); if (childItem == rootItem) return QModelIndex(); TreeItem *parentItem = childItem->parent(); if (parentItem == rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int TreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); } UINT8 TreeModel::type(const QModelIndex &index) const { if (!index.isValid()) return 0; TreeItem *item = static_cast(index.internalPointer()); return item->type(); } UINT8 TreeModel::subtype(const QModelIndex &index) const { if (!index.isValid()) return 0; TreeItem *item = static_cast(index.internalPointer()); return item->subtype(); } QByteArray TreeModel::header(const QModelIndex &index) const { if (!index.isValid()) return QByteArray(); TreeItem *item = static_cast(index.internalPointer()); return item->header(); } bool TreeModel::hasEmptyHeader(const QModelIndex &index) const { if (!index.isValid()) return true; TreeItem *item = static_cast(index.internalPointer()); return item->hasEmptyHeader(); } QByteArray TreeModel::body(const QModelIndex &index) const { if (!index.isValid()) return QByteArray(); TreeItem *item = static_cast(index.internalPointer()); return item->body(); } bool TreeModel::hasEmptyBody(const QModelIndex &index) const { if (!index.isValid()) return true; TreeItem *item = static_cast(index.internalPointer()); return item->hasEmptyBody(); } QString TreeModel::name(const QModelIndex &index) const { if (!index.isValid()) return QString(); TreeItem *item = static_cast(index.internalPointer()); return item->name(); } QString TreeModel::text(const QModelIndex &index) const { if (!index.isValid()) return QString(); TreeItem *item = static_cast(index.internalPointer()); return item->text(); } QString TreeModel::info(const QModelIndex &index) const { if (!index.isValid()) return QString(); TreeItem *item = static_cast(index.internalPointer()); return item->info(); } UINT8 TreeModel::action(const QModelIndex &index) const { if (!index.isValid()) return Actions::NoAction; TreeItem *item = static_cast(index.internalPointer()); return item->action(); } UINT8 TreeModel::compression(const QModelIndex &index) const { if (!index.isValid()) return COMPRESSION_ALGORITHM_UNKNOWN; TreeItem *item = static_cast(index.internalPointer()); return item->compression(); } UINT32 TreeModel::dictionarySize(const QModelIndex &index) const { if (!index.isValid()) return 0; TreeItem *item = static_cast(index.internalPointer()); return item->dictionarySize(); } void TreeModel::setSubtype(const QModelIndex & index, const UINT8 subtype) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); item->setSubtype(subtype); emit dataChanged(index, index); } void TreeModel::setName(const QModelIndex &index, const QString &data) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); item->setName(data); emit dataChanged(index, index); } void TreeModel::setType(const QModelIndex &index, const UINT8 data) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); item->setType(data); emit dataChanged(index, index); } void TreeModel::setText(const QModelIndex &index, const QString &data) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); item->setText(data); emit dataChanged(index, index); } void TreeModel::setAction(const QModelIndex &index, const UINT8 action) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); item->setAction(action); emit dataChanged(this->index(0, 0), index); } void TreeModel::setDictionarySize(const QModelIndex &index, const UINT32 dictionarySize) { if (!index.isValid()) return; TreeItem *item = static_cast(index.internalPointer()); item->setDictionarySize(dictionarySize); emit dataChanged(this->index(0, 0), index); } QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, const QModelIndex & parent, const UINT8 mode) { TreeItem *item = 0; TreeItem *parentItem = 0; int parentColumn = 0; if (!parent.isValid()) parentItem = rootItem; else { if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) { item = static_cast(parent.internalPointer()); parentItem = item->parent(); parentColumn = parent.parent().column(); } else { parentItem = static_cast(parent.internalPointer()); parentColumn = parent.column(); } } TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, parentItem); if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); } else if (mode == CREATE_MODE_PREPEND) { emit layoutAboutToBeChanged(); parentItem->prependChild(newItem); } else if (mode == CREATE_MODE_BEFORE) { emit layoutAboutToBeChanged(); parentItem->insertChildBefore(item, newItem); } else if (mode == CREATE_MODE_AFTER) { emit layoutAboutToBeChanged(); parentItem->insertChildAfter(item, newItem); } else { delete newItem; return QModelIndex(); } emit layoutChanged(); return createIndex(newItem->row(), parentColumn, newItem); } QModelIndex TreeModel::findParentOfType(const QModelIndex& index, UINT8 type) const { if (!index.isValid()) return QModelIndex(); TreeItem *item; QModelIndex parent = index; for (item = static_cast(parent.internalPointer()); item != NULL && item != rootItem && item->type() != type; item = static_cast(parent.internalPointer())) parent = parent.parent(); if (item != NULL && item != rootItem) return parent; return QModelIndex(); }UEFITool-0.28.0/treemodel.h000066400000000000000000000055401363631100000153150ustar00rootroot00000000000000/* treemodel.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __TREEMODEL_H__ #define __TREEMODEL_H__ #include #include #include #include #include "basetypes.h" #include "types.h" class TreeItem; class TreeModel : public QAbstractItemModel { Q_OBJECT public: TreeModel(QObject *parent = 0); ~TreeModel(); QVariant data(const QModelIndex &index, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; void setAction(const QModelIndex &index, const UINT8 action); void setType(const QModelIndex &index, const UINT8 type); void setSubtype(const QModelIndex &index, const UINT8 subtype); void setName(const QModelIndex &index, const QString &name); void setText(const QModelIndex &index, const QString &text); void setDictionarySize(const QModelIndex &index, const UINT32 dictionarySize); QString name(const QModelIndex &index) const; QString text(const QModelIndex &index) const; QString info(const QModelIndex &index) const; UINT8 type(const QModelIndex &index) const; UINT8 subtype(const QModelIndex &index) const; QByteArray header(const QModelIndex &index) const; bool hasEmptyHeader(const QModelIndex &index) const; QByteArray body(const QModelIndex &index) const; bool hasEmptyBody(const QModelIndex &index) const; UINT8 action(const QModelIndex &index) const; UINT8 compression(const QModelIndex &index) const; UINT32 dictionarySize(const QModelIndex &index) const; QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString & name = QString(), const QString & text = QString(), const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); QModelIndex findParentOfType(const QModelIndex & index, UINT8 type) const; private: TreeItem *rootItem; }; #endif UEFITool-0.28.0/types.cpp000066400000000000000000000114121363631100000150270ustar00rootroot00000000000000/* types.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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, WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include #include #include "types.h" #include "ffs.h" QString regionTypeToQString(const UINT8 type) { switch (type) { case Subtypes::DescriptorRegion: return QObject::tr("Descriptor"); case Subtypes::GbeRegion: return QObject::tr("GbE"); case Subtypes::MeRegion: return QObject::tr("ME"); case Subtypes::BiosRegion: return QObject::tr("BIOS"); case Subtypes::PdrRegion: return QObject::tr("PDR"); case Subtypes::EcRegion: return QObject::tr("EC"); default: return QObject::tr("Unknown"); }; } QString itemTypeToQString(const UINT8 type) { switch (type) { case Types::Root: return QObject::tr("Root"); case Types::Image: return QObject::tr("Image"); case Types::Capsule: return QObject::tr("Capsule"); case Types::Region: return QObject::tr("Region"); case Types::Volume: return QObject::tr("Volume"); case Types::Padding: return QObject::tr("Padding"); case Types::File: return QObject::tr("File"); case Types::Section: return QObject::tr("Section"); case Types::FreeSpace: return QObject::tr("Free space"); default: return QObject::tr("Unknown"); } } QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) { switch (type) { case Types::Root: case Types::Image: if (subtype == Subtypes::IntelImage) return QObject::tr("Intel"); else if (subtype == Subtypes::UefiImage) return QObject::tr("UEFI"); else return QObject::tr("Unknown subtype"); case Types::Padding: if (subtype == Subtypes::ZeroPadding) return QObject::tr("Empty (0x00)"); else if (subtype == Subtypes::OnePadding) return QObject::tr("Empty (0xFF)"); else if (subtype == Subtypes::DataPadding) return QObject::tr("Non-empty"); else return QObject::tr("Unknown subtype"); case Types::Volume: if (subtype == Subtypes::UnknownVolume) return QObject::tr("Unknown"); else if (subtype == Subtypes::Ffs2Volume) return QObject::tr("FFSv2"); else if (subtype == Subtypes::Ffs3Volume) return QObject::tr("FFSv3"); else return QObject::tr("Unknown subtype"); case Types::Capsule: if (subtype == Subtypes::AptioSignedCapsule) return QObject::tr("Aptio signed"); else if (subtype == Subtypes::AptioUnsignedCapsule) return QObject::tr("Aptio unsigned"); else if (subtype == Subtypes::UefiCapsule) return QObject::tr("UEFI 2.0"); else if (subtype == Subtypes::ToshibaCapsule) return QObject::tr("Toshiba"); else return QObject::tr("Unknown subtype"); case Types::Region: return regionTypeToQString(subtype); case Types::File: return fileTypeToQString(subtype); case Types::Section: return sectionTypeToQString(subtype); case Types::FreeSpace: return QString(); default: return QObject::tr("Unknown subtype"); } } QString compressionTypeToQString(const UINT8 algorithm) { switch (algorithm) { case COMPRESSION_ALGORITHM_NONE: return QObject::tr("None"); case COMPRESSION_ALGORITHM_EFI11: return QObject::tr("EFI 1.1"); case COMPRESSION_ALGORITHM_TIANO: return QObject::tr("Tiano"); case COMPRESSION_ALGORITHM_LZMA: return QObject::tr("LZMA"); case COMPRESSION_ALGORITHM_IMLZMA: return QObject::tr("Intel modified LZMA"); default: return QObject::tr("Unknown"); } } QString actionTypeToQString(const UINT8 action) { switch (action) { case Actions::NoAction: return ""; case Actions::Create: return QObject::tr("Create"); case Actions::Insert: return QObject::tr("Insert"); case Actions::Replace: return QObject::tr("Replace"); case Actions::Remove: return QObject::tr("Remove"); case Actions::Rebuild: return QObject::tr("Rebuild"); case Actions::Rebase: return QObject::tr("Rebase"); case Actions::DoNotRebuild: return QObject::tr("Do not rebuild"); default: return QObject::tr("Unknown"); } } UEFITool-0.28.0/types.h000066400000000000000000000035511363631100000145010ustar00rootroot00000000000000/* types.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __TYPES_H__ #define __TYPES_H__ #include "basetypes.h" // Actions namespace Actions { enum ActionTypes { NoAction = 50, Create, Insert, Replace, Remove, Rebuild, Rebase, DoNotRebuild }; } // Types namespace Types { enum ItemTypes { Root = 60, Capsule, Image, Region, Padding, Volume, File, Section, FreeSpace }; } namespace Subtypes { enum ImageSubtypes{ IntelImage = 70, UefiImage }; enum CapsuleSubtypes { AptioSignedCapsule = 80, AptioUnsignedCapsule, UefiCapsule, ToshibaCapsule }; enum VolumeSubtypes { UnknownVolume = 90, Ffs2Volume, Ffs3Volume }; enum RegionSubtypes { DescriptorRegion = 100, GbeRegion, MeRegion, BiosRegion, PdrRegion, EcRegion }; enum PaddingSubtypes { ZeroPadding = 110, OnePadding, DataPadding }; }; // *ToQString conversion routines extern QString actionTypeToQString(const UINT8 action); extern QString itemTypeToQString(const UINT8 type); extern QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype); extern QString compressionTypeToQString(const UINT8 algorithm); extern QString regionTypeToQString(const UINT8 type); #endifUEFITool-0.28.0/uefitool.cpp000066400000000000000000000726021363631100000155210ustar00rootroot00000000000000/* uefitool.cpp Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #include "version.h" #include "uefitool.h" #include "ui_uefitool.h" UEFITool::UEFITool(QWidget *parent) : QMainWindow(parent), ui(new Ui::UEFITool), version(tr(PROGRAM_VERSION)) { clipboard = QApplication::clipboard(); // Create UI ui->setupUi(this); searchDialog = new SearchDialog(this); ffsEngine = NULL; // Set window title this->setWindowTitle(tr("UEFITool %1").arg(version)); // Connect signals to slots connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile())); connect(ui->actionOpenImageFileInNewWindow, SIGNAL(triggered()), this, SLOT(openImageFileInNewWindow())); connect(ui->actionSaveImageFile, SIGNAL(triggered()), this, SLOT(saveImageFile())); connect(ui->actionSearch, SIGNAL(triggered()), this, SLOT(search())); connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(extractAsIs())); connect(ui->actionExtractBody, SIGNAL(triggered()), this, SLOT(extractBody())); connect(ui->actionInsertInto, SIGNAL(triggered()), this, SLOT(insertInto())); connect(ui->actionInsertBefore, SIGNAL(triggered()), this, SLOT(insertBefore())); connect(ui->actionInsertAfter, SIGNAL(triggered()), this, SLOT(insertAfter())); connect(ui->actionReplace, SIGNAL(triggered()), this, SLOT(replaceAsIs())); connect(ui->actionReplaceBody, SIGNAL(triggered()), this, SLOT(replaceBody())); connect(ui->actionRemove, SIGNAL(triggered()), this, SLOT(remove())); connect(ui->actionRebuild, SIGNAL(triggered()), this, SLOT(rebuild())); connect(ui->actionDoNotRebuild, SIGNAL(triggered()), this, SLOT(doNotRebuild())); connect(ui->actionMessagesCopy, SIGNAL(triggered()), this, SLOT(copyMessage())); connect(ui->actionMessagesCopyAll, SIGNAL(triggered()), this, SLOT(copyAllMessages())); connect(ui->actionMessagesClear, SIGNAL(triggered()), this, SLOT(clearMessages())); connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about())); connect(ui->actionAboutQt, SIGNAL(triggered()), this, SLOT(aboutQt())); connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(exit())); connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(writeSettings())); // Enable Drag-and-Drop actions this->setAcceptDrops(true); // Set current directory currentDir = "."; // Set monospace font for some controls QFont font("Courier New", 10); #if defined Q_OS_OSX font = QFont("Menlo", 10); #elif defined Q_OS_WIN font = QFont("Consolas", 9); #endif ui->infoEdit->setFont(font); ui->messageListWidget->setFont(font); ui->structureTreeView->setFont(font); searchDialog->ui->guidEdit->setFont(font); searchDialog->ui->hexEdit->setFont(font); // Initialize non-persistent data init(); // Read stored settings readSettings(); } UEFITool::~UEFITool() { delete ui; delete ffsEngine; delete searchDialog; } void UEFITool::setProgramPath(QString path) { currentProgramPath = path; }; void UEFITool::init() { // Clear components ui->messageListWidget->clear(); ui->infoEdit->clear(); // Set window title this->setWindowTitle(tr("UEFITool %1").arg(version)); // Disable menus ui->menuCapsuleActions->setDisabled(true); ui->menuImageActions->setDisabled(true); ui->menuRegionActions->setDisabled(true); ui->menuPaddingActions->setDisabled(true); ui->menuVolumeActions->setDisabled(true); ui->menuFileActions->setDisabled(true); ui->menuSectionActions->setDisabled(true); ui->actionMessagesCopy->setDisabled(true); ui->actionMessagesCopyAll->setDisabled(true); // Make new ffsEngine if (ffsEngine) delete ffsEngine; ffsEngine = new FfsEngine(this); ui->structureTreeView->setModel(ffsEngine->treeModel()); // Connect connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(populateUi(const QModelIndex &))); connect(ui->messageListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); connect(ui->messageListWidget, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(enableMessagesCopyActions(QListWidgetItem*))); } void UEFITool::populateUi(const QModelIndex ¤t) { if (!current.isValid()) return; TreeModel* model = ffsEngine->treeModel(); UINT8 type = model->type(current); UINT8 subtype = model->subtype(current); // Set info text ui->infoEdit->setPlainText(model->info(current)); // Enable menus ui->menuCapsuleActions->setEnabled(type == Types::Capsule); ui->menuImageActions->setEnabled(type == Types::Image); ui->menuRegionActions->setEnabled(type == Types::Region); ui->menuPaddingActions->setEnabled(type == Types::Padding); ui->menuVolumeActions->setEnabled(type == Types::Volume); ui->menuFileActions->setEnabled(type == Types::File); ui->menuSectionActions->setEnabled(type == Types::Section); // Enable actions ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionDoNotRebuild->setEnabled(type== Types::Region || type == Types::Volume || type == Types::File || type == Types::Section); ui->actionExtractBody->setDisabled(model->hasEmptyBody(current)); ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionInsertInto->setEnabled((type == Types::Volume && subtype != Subtypes::UnknownVolume) || (type == Types::File && subtype != EFI_FV_FILETYPE_ALL && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD) || (type == Types::Section && (subtype == EFI_SECTION_COMPRESSION || subtype == EFI_SECTION_GUID_DEFINED || subtype == EFI_SECTION_DISPOSABLE))); ui->actionInsertBefore->setEnabled(type == Types::File || type == Types::Section); ui->actionInsertAfter->setEnabled(type == Types::File || type == Types::Section); ui->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Padding || type == Types::Volume || type == Types::File || type == Types::Section); ui->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); ui->actionMessagesCopy->setEnabled(false); } void UEFITool::search() { if (searchDialog->exec() != QDialog::Accepted) return; QModelIndex rootIndex = ffsEngine->treeModel()->index(0, 0); int index = searchDialog->ui->tabWidget->currentIndex(); if (index == 0) { // Hex pattern searchDialog->ui->hexEdit->setFocus(); QByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", ""); if (pattern.isEmpty()) return; UINT8 mode; if (searchDialog->ui->hexScopeHeaderRadioButton->isChecked()) mode = SEARCH_MODE_HEADER; else if (searchDialog->ui->hexScopeBodyRadioButton->isChecked()) mode = SEARCH_MODE_BODY; else mode = SEARCH_MODE_ALL; ffsEngine->findHexPattern(rootIndex, pattern, mode); showMessages(); } else if (index == 1) { // GUID searchDialog->ui->guidEdit->setFocus(); searchDialog->ui->guidEdit->setCursorPosition(0); QByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1(); if (pattern.isEmpty()) return; UINT8 mode; if (searchDialog->ui->guidScopeHeaderRadioButton->isChecked()) mode = SEARCH_MODE_HEADER; else if (searchDialog->ui->guidScopeBodyRadioButton->isChecked()) mode = SEARCH_MODE_BODY; else mode = SEARCH_MODE_ALL; ffsEngine->findGuidPattern(rootIndex, pattern, mode); showMessages(); } else if (index == 2) { // Text string searchDialog->ui->textEdit->setFocus(); QString pattern = searchDialog->ui->textEdit->text(); if (pattern.isEmpty()) return; ffsEngine->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), (Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked()); showMessages(); } } void UEFITool::rebuild() { QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; UINT8 result = ffsEngine->rebuild(index); if (result == ERR_SUCCESS) ui->actionSaveImageFile->setEnabled(true); } void UEFITool::doNotRebuild() { QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; UINT8 result = ffsEngine->doNotRebuild(index); if (result == ERR_SUCCESS) ui->actionSaveImageFile->setEnabled(true); } void UEFITool::remove() { QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; UINT8 result = ffsEngine->remove(index); if (result == ERR_SUCCESS) ui->actionSaveImageFile->setEnabled(true); } void UEFITool::insert(const UINT8 mode) { QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; TreeModel* model = ffsEngine->treeModel(); UINT8 type; if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) type = model->type(index.parent()); else type = model->type(index); QString path; switch (type) { case Types::Volume: path = QFileDialog::getOpenFileName(this, tr("Select FFS file to insert"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); break; case Types::File: case Types::Section: path = QFileDialog::getOpenFileName(this, tr("Select section file to insert"), currentDir, "Section files (*.sct *.bin);;All files (*)"); break; default: return; } if (path.trimmed().isEmpty()) return; QFileInfo fileInfo = QFileInfo(path); if (!fileInfo.exists()) { ui->statusBar->showMessage(tr("Please select existing file")); return; } QFile inputFile; inputFile.setFileName(path); if (!inputFile.open(QFile::ReadOnly)) { QMessageBox::critical(this, tr("Insertion failed"), tr("Can't open output file for reading"), QMessageBox::Ok); return; } QByteArray buffer = inputFile.readAll(); inputFile.close(); UINT8 result = ffsEngine->insert(index, buffer, mode); if (result) { QMessageBox::critical(this, tr("Insertion failed"), errorMessage(result), QMessageBox::Ok); return; } ui->actionSaveImageFile->setEnabled(true); } void UEFITool::insertInto() { insert(CREATE_MODE_PREPEND); } void UEFITool::insertBefore() { insert(CREATE_MODE_BEFORE); } void UEFITool::insertAfter() { insert(CREATE_MODE_AFTER); } void UEFITool::replaceAsIs() { replace(REPLACE_MODE_AS_IS); } void UEFITool::replaceBody() { replace(REPLACE_MODE_BODY); } void UEFITool::replace(const UINT8 mode) { QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; TreeModel* model = ffsEngine->treeModel(); QString path; if (model->type(index) == Types::Region) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select region file to replace selected object"), currentDir, "Region files (*.rgn *.bin);;All files (*)"); } else return; } else if (model->type(index) == Types::Padding) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select padding file to replace selected object"), currentDir, "Padding files (*.pad *.bin);;All files (*)"); } else return; } else if (model->type(index) == Types::Volume) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace selected object"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); } else if (mode == REPLACE_MODE_BODY) { path = QFileDialog::getOpenFileName(this, tr("Select volume body file to replace body"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); } else return; } else if (model->type(index) == Types::File) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select FFS file to replace selected object"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); } else if (mode == REPLACE_MODE_BODY) { if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); else if (model->subtype(index) == EFI_FV_FILETYPE_PAD) // Pad file body can't be replaced //!TODO: handle non-empty pad files return; else path = QFileDialog::getOpenFileName(this, tr("Select FFS file body to replace body"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); } else return; } else if (model->type(index) == Types::Section) { if (mode == REPLACE_MODE_AS_IS) { path = QFileDialog::getOpenFileName(this, tr("Select section file to replace selected object"), currentDir, "Section files (*.sct *.bin);;All files (*)"); } else if (mode == REPLACE_MODE_BODY) { if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) path = QFileDialog::getOpenFileName(this, tr("Select FFS file body file to replace body"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace body"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_RAW) path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_PE32 || model->subtype(index) == EFI_SECTION_TE || model->subtype(index) == EFI_SECTION_PIC) path = QFileDialog::getOpenFileName(this, tr("Select EFI executable file to replace body"), currentDir, "EFI executable files (*.efi *.dxe *.pei *.bin);;All files (*)"); else path = QFileDialog::getOpenFileName(this, tr("Select file to replace body"), currentDir, "Binary files (*.bin);;All files (*)"); } else return; } else return; if (path.trimmed().isEmpty()) return; QFileInfo fileInfo = QFileInfo(path); if (!fileInfo.exists()) { ui->statusBar->showMessage(tr("Please select existing file")); return; } QFile inputFile; inputFile.setFileName(path); if (!inputFile.open(QFile::ReadOnly)) { QMessageBox::critical(this, tr("Replacing failed"), tr("Can't open input file for reading"), QMessageBox::Ok); return; } QByteArray buffer = inputFile.readAll(); inputFile.close(); UINT8 result = ffsEngine->replace(index, buffer, mode); if (result) { QMessageBox::critical(this, tr("Replacing failed"), errorMessage(result), QMessageBox::Ok); return; } ui->actionSaveImageFile->setEnabled(true); } void UEFITool::extractAsIs() { extract(EXTRACT_MODE_AS_IS); } void UEFITool::extractBody() { extract(EXTRACT_MODE_BODY); } void UEFITool::extract(const UINT8 mode) { QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); if (!index.isValid()) return; TreeModel* model = ffsEngine->treeModel(); UINT8 type = model->type(index); QString path; if (mode == EXTRACT_MODE_AS_IS) { switch (type) { case Types::Capsule: path = QFileDialog::getSaveFileName(this, tr("Save capsule to file"), currentDir, "Capsule files (*.cap *.bin);;All files (*)"); break; case Types::Image: path = QFileDialog::getSaveFileName(this, tr("Save image to file"), currentDir, "Image files (*.rom *.bin);;All files (*)"); break; case Types::Region: path = QFileDialog::getSaveFileName(this, tr("Save region to file"), currentDir, "Region files (*.rgn *.bin);;All files (*)"); break; case Types::Padding: path = QFileDialog::getSaveFileName(this, tr("Save padding to file"), currentDir, "Padding files (*.pad *.bin);;All files (*)"); break; case Types::Volume: path = QFileDialog::getSaveFileName(this, tr("Save volume to file"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); break; case Types::File: path = QFileDialog::getSaveFileName(this, tr("Save FFS file to file"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); break; case Types::Section: path = QFileDialog::getSaveFileName(this, tr("Save section file to file"), currentDir, "Section files (*.sct *.bin);;All files (*)"); break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), currentDir, "Binary files (*.bin);;All files (*)"); } } else if (mode == EXTRACT_MODE_BODY) { switch (type) { case Types::Capsule: path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"), currentDir, "Image files (*.rom *.bin);;All files (*)"); break; case Types::Volume: path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); break; case Types::File: { if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to raw file"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); else path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to file"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); } break; case Types::Section: { if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) path = QFileDialog::getSaveFileName(this, tr("Save encapsulation section body to FFS body file"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) path = QFileDialog::getSaveFileName(this, tr("Save section body to volume file"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); else if (model->subtype(index) == EFI_SECTION_RAW) path = QFileDialog::getSaveFileName(this, tr("Save section body to raw file"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); else path = QFileDialog::getSaveFileName(this, tr("Save section body to file"), currentDir, "Binary files (*.bin);;All files (*)"); } break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), currentDir, "Binary files (*.bin);;All files (*)"); } } else path = QFileDialog::getSaveFileName(this, tr("Save object to file"), currentDir, "Binary files (*.bin);;All files (*)"); if (path.trimmed().isEmpty()) return; QByteArray extracted; UINT8 result = ffsEngine->extract(index, extracted, mode); if (result) { QMessageBox::critical(this, tr("Extraction failed"), errorMessage(result), QMessageBox::Ok); return; } QFile outputFile; outputFile.setFileName(path); if (!outputFile.open(QFile::WriteOnly)) { QMessageBox::critical(this, tr("Extraction failed"), tr("Can't open output file for rewriting"), QMessageBox::Ok); return; } outputFile.resize(0); outputFile.write(extracted); outputFile.close(); } void UEFITool::about() { QMessageBox::about(this, tr("About UEFITool"), tr( "Copyright (c) 2015, Nikolaj Schlej aka CodeRush.
" "Program icon made by Alexander Zhidkov.

" "The program is dedicated to RevoGirl. Rest in peace, young genius.

" "The program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License.
" "The full text of the license may be found at OpenSource.org.

" "THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN \"AS IS\" BASIS, " "WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, " "EITHER EXPRESS OR IMPLIED.")); } void UEFITool::aboutQt() { QMessageBox::aboutQt(this, tr("About Qt")); } void UEFITool::exit() { QCoreApplication::exit(0); } void UEFITool::saveImageFile() { QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); if (path.isEmpty()) return; QByteArray reconstructed; UINT8 result = ffsEngine->reconstructImageFile(reconstructed); showMessages(); if (result) { QMessageBox::critical(this, tr("Image reconstruction failed"), errorMessage(result), QMessageBox::Ok); return; } QFile outputFile; outputFile.setFileName(path); if (!outputFile.open(QFile::WriteOnly)) { QMessageBox::critical(this, tr("Image reconstruction failed"), tr("Can't open output file for rewriting"), QMessageBox::Ok); return; } outputFile.resize(0); outputFile.write(reconstructed); outputFile.close(); if (QMessageBox::information(this, tr("Image reconstruction successful"), tr("Open reconstructed file?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) openImageFile(path); } void UEFITool::openImageFile() { QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); openImageFile(path); } void UEFITool::openImageFileInNewWindow() { QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.dec);;All files (*)"); if (path.trimmed().isEmpty()) return; QProcess::startDetached(currentProgramPath, QStringList(path)); } void UEFITool::openImageFile(QString path) { if (path.trimmed().isEmpty()) return; QFileInfo fileInfo = QFileInfo(path); if (!fileInfo.exists()) { ui->statusBar->showMessage(tr("Please select existing file")); return; } QFile inputFile; inputFile.setFileName(path); if (!inputFile.open(QFile::ReadOnly)) { QMessageBox::critical(this, tr("Image parsing failed"), tr("Can't open input file for reading"), QMessageBox::Ok); return; } QByteArray buffer = inputFile.readAll(); inputFile.close(); init(); this->setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName())); UINT8 result = ffsEngine->parseImageFile(buffer); showMessages(); if (result) QMessageBox::critical(this, tr("Image parsing failed"), errorMessage(result), QMessageBox::Ok); else ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); // Enable search ui->actionSearch->setEnabled(true); // Set current directory currentDir = fileInfo.absolutePath(); } void UEFITool::copyMessage() { clipboard->clear(); clipboard->setText(ui->messageListWidget->currentItem()->text()); } void UEFITool::copyAllMessages() { QString text; clipboard->clear(); for(INT32 i = 0; i < ui->messageListWidget->count(); i++) text.append(ui->messageListWidget->item(i)->text()).append("\n"); clipboard->clear(); clipboard->setText(text); } void UEFITool::enableMessagesCopyActions(QListWidgetItem* item) { ui->actionMessagesCopy->setEnabled(item != NULL); ui->actionMessagesCopyAll->setEnabled(item != NULL); } void UEFITool::clearMessages() { ffsEngine->clearMessages(); messageItems.clear(); ui->messageListWidget->clear(); ui->actionMessagesCopy->setEnabled(false); ui->actionMessagesCopyAll->setEnabled(false); } void UEFITool::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasFormat("text/uri-list")) event->acceptProposedAction(); } void UEFITool::dropEvent(QDropEvent* event) { QString path = event->mimeData()->urls().at(0).toLocalFile(); openImageFile(path); } void UEFITool::showMessages() { ui->messageListWidget->clear(); if (!ffsEngine) return; messageItems = ffsEngine->messages(); for (int i = 0; i < messageItems.count(); i++) { ui->messageListWidget->addItem(new MessageListItem(messageItems.at(i))); } ui->messageListWidget->scrollToBottom(); } void UEFITool::scrollTreeView(QListWidgetItem* item) { MessageListItem* messageItem = static_cast(item); QModelIndex index = messageItem->index(); if (index.isValid()) { ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter); ui->structureTreeView->selectionModel()->clearSelection(); ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select); } } void UEFITool::contextMenuEvent(QContextMenuEvent* event) { if (ui->messageListWidget->underMouse()) { ui->menuMessages->exec(event->globalPos()); return; } if (!ui->structureTreeView->underMouse()) return; QPoint pt = event->pos(); QModelIndex index = ui->structureTreeView->indexAt(ui->structureTreeView->viewport()->mapFrom(this, pt)); if (!index.isValid()) return; TreeModel* model = ffsEngine->treeModel(); switch (model->type(index)) { case Types::Capsule: ui->menuCapsuleActions->exec(event->globalPos()); break; case Types::Image: ui->menuImageActions->exec(event->globalPos()); break; case Types::Region: ui->menuRegionActions->exec(event->globalPos()); break; case Types::Padding: ui->menuPaddingActions->exec(event->globalPos()); break; case Types::Volume: ui->menuVolumeActions->exec(event->globalPos()); break; case Types::File: ui->menuFileActions->exec(event->globalPos()); break; case Types::Section: ui->menuSectionActions->exec(event->globalPos()); break; } } void UEFITool::readSettings() { QSettings settings(this); resize(settings.value("mainWindow/size", QSize(800, 600)).toSize()); move(settings.value("mainWindow/position", QPoint(0, 0)).toPoint()); QList horList, vertList; horList.append(settings.value("mainWindow/treeWidth", 600).toInt()); horList.append(settings.value("mainWindow/infoWidth", 180).toInt()); vertList.append(settings.value("mainWindow/treeHeight", 400).toInt()); vertList.append(settings.value("mainWindow/messageHeight", 180).toInt()); ui->infoSplitter->setSizes(horList); ui->messagesSplitter->setSizes(vertList); ui->structureTreeView->setColumnWidth(0, settings.value("tree/columnWidth0", ui->structureTreeView->columnWidth(0)).toInt()); ui->structureTreeView->setColumnWidth(1, settings.value("tree/columnWidth1", ui->structureTreeView->columnWidth(1)).toInt()); ui->structureTreeView->setColumnWidth(2, settings.value("tree/columnWidth2", ui->structureTreeView->columnWidth(2)).toInt()); ui->structureTreeView->setColumnWidth(3, settings.value("tree/columnWidth3", ui->structureTreeView->columnWidth(3)).toInt()); } void UEFITool::writeSettings() { QSettings settings(this); settings.setValue("mainWindow/size", size()); settings.setValue("mainWindow/position", pos()); settings.setValue("mainWindow/treeWidth", ui->structureGroupBox->width()); settings.setValue("mainWindow/infoWidth", ui->infoGroupBox->width()); settings.setValue("mainWindow/treeHeight", ui->structureGroupBox->height()); settings.setValue("mainWindow/messageHeight", ui->messageGroupBox->height()); settings.setValue("tree/columnWidth0", ui->structureTreeView->columnWidth(0)); settings.setValue("tree/columnWidth1", ui->structureTreeView->columnWidth(1)); settings.setValue("tree/columnWidth2", ui->structureTreeView->columnWidth(2)); settings.setValue("tree/columnWidth3", ui->structureTreeView->columnWidth(3)); } UEFITool-0.28.0/uefitool.h000066400000000000000000000047411363631100000151650ustar00rootroot00000000000000/* uefitool.h Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #ifndef __UEFITOOL_H__ #define __UEFITOOL_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "basetypes.h" #include "ffs.h" #include "ffsengine.h" #include "searchdialog.h" namespace Ui { class UEFITool; } class UEFITool : public QMainWindow { Q_OBJECT public: explicit UEFITool(QWidget *parent = 0); ~UEFITool(); void openImageFile(QString path); void setProgramPath(QString path); private slots: void init(); void populateUi(const QModelIndex ¤t); void scrollTreeView(QListWidgetItem* item); void openImageFile(); void openImageFileInNewWindow(); void saveImageFile(); void search(); void extract(const UINT8 mode); void extractAsIs(); void extractBody(); void insert(const UINT8 mode); void insertInto(); void insertBefore(); void insertAfter(); void replace(const UINT8 mode); void replaceAsIs(); void replaceBody(); void rebuild(); void doNotRebuild(); void remove(); void copyMessage(); void copyAllMessages(); void enableMessagesCopyActions(QListWidgetItem* item); void clearMessages(); void about(); void aboutQt(); void exit(); void writeSettings(); private: Ui::UEFITool* ui; FfsEngine* ffsEngine; SearchDialog* searchDialog; QClipboard* clipboard; QString currentDir; QString currentProgramPath; QQueue messageItems; const QString version; void showMessages(); void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); void contextMenuEvent(QContextMenuEvent* event); void readSettings(); }; #endif UEFITool-0.28.0/uefitool.icns000066400000000000000000003016521363631100000156730ustar00rootroot00000000000000icnsƒЊTOC hics#Hics8is32,s8mkICN#icl8il32йl8mkit32%"t8mk@ic08UЧic09Гдics#H€р№№јј№№Р€р№№јј№№Рics8аШЧаєЯШШЯєѓШЩЯШШШШЯЩШаШШШШШШШШШШШШєЯШШШyіOЄШШЯаШШyџOyѕ+ШШЯаЯШШOііѕOOyШШЯаШШШШіџіЄЄOѕOШШШШШШШШіѕѕyyіyOШШШШѓаШШOѕ+OyџіOШЩаѓаШЄііyyѕyЄШаЯШЩЄOzzUШШШЯЯШШШЯЯЩЯШШШЯєєЯЯаЯЯЯЯааЯѓаЯЯЯѓЯЯаis32,Д ‰ZаА8‡TџБrмЕ…G–РЫпІЏm„ вћТ,Апžƒ зу№tQд[Ÿ„ мКЎvљФЅ…$ежv|о_(†"•TX†Уƒrwzt† umsxxtUo‚nr€qrrqppso€unrqskpsoqrqnjџkokmŸфб‹{jnk€=gl_™џаЋъгfkhbcdbПйпыЩЯЅbdcbdd`aуќйzpЮъТ^add]_\_хэѕІф–Т\]_\€YTСшбЪЅћиФTXZZTUkуфЄЇщ”oUT€?PSMgЖ‰ЌNOSPENNQPNHIMJ€PMO€MˆK‚ UGKKI:†GGIHƒƒЊЎГ­† І‘ЊББЋЊЇ‚ ЈЊЇЇЈЉЉЈІЈЋІ€œЄЊЈЋЃЅЇЅЊЉЈЄЄџЁІ ЁТютЕЊ ЅЁЊ€[›Ђ–МџрЩђт›Ђœ—˜™—ДехъёлпФ—˜˜—™™””ь§цЅžоёе’•™™”эѓјТГьЗе”‰Œ‹ˆгяпкРќфжˆŠ‹‹…‰–ыьОСяГ™‰…€€†‘ЪЊ­У€€…€‹u}ƒ‚z{~|€‚}{€€{|{|z|z‚ qx||wu†uwzvƒs8mk<ўџ^yџџ• 5сС‰чџџьŠМэN иџџџџџџџџџџ№‘џџџџџџџџџџДvџџџџџџџџџџ‰`œюџџџџџџџџџџяš`џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ?ƒъџџџџџџџџџџщ|>|џџџџџџџџџџ‹ЌџџџџџџџџџџФ Эџџџџџџџџџџх!ЪЉэџџѓƒ˜з6 ‡џџЄ =ўџ_ICN#Р№ќ?ў?ўџџџџП€ў€џw€џ€џџџџі?t№РР№ќ?ў?ўџџџџП€ў€џw€џ€џџџџі?t№Рicl8ЯЧШШШШЩШШШШШєєШШШШШШааЩѓаШШШШШШЯєЯЯаШШШЯаЯШШШШШШШШЯаЯШШШЯаШШШШШШШШШШШШШШШШШШШШШШЯаШШШШШШШШШШШШШШШШШШШШШШШШЯаШШШШШШШШШШШЄЄШШШШШШШШШШШЯЯШШШШШШШШ*џџѕOШШШШШШШШШєўШШШШШШЄOџџџџOЄyOШШШШШШаѓШШШШШШѕџџџOЄЄOџџіЄШШШШаЯШШШШШШѕџџOШyѕџџџOШШШШШЯєаЯШШШШШЄЄyѕџџѕџџџ+yyyШШШШШЯаєЯШШШШШШШyџџіyOіѕѕіyіџџOШШШШШШШЯШШШШШШШШіџџџџyШШШШžџџџџіШШШШШШШШШШШШШШШШѕџџѕџџЄШШШѕџOѕіžШШШШШШШШШШШШШШШЄѕџџyOџ+ШШyџіШžyOШШШШШШШШШШШШШШШШѕџџџѕџџЄШіџ+ШOџіШШШШШШШШаЯШШШШШШOџџіџџџyШіџџO+џ+ШШШШШШЯаєаШШШШzџџžzіџOШіџџO+џzШШШШаєЯШШШШ+џѕOyyUШіџѕЄі+ШШШШЯѓШШШШЄіџџџOЄШіџ+ШOЄШШШШааШШШШШЄOџџџžШіџ+ШШШШШШШаЯШШШШШЩШžUzШШOOЄЩШШШШШШЩєаШЩЩЩЩЩЩЩЯЯЯЩШШЯЯЩЩЩЩЩЩЩШЯѓЯШЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩЩаєЯЩЯЩЩЯШЯЯЯЯЯЯЯЯШЯЯШЯЯЩаєЯШЯаѓЯЯЯЯЯЯЯЯЯЯѓѓЯЯЯаєааЯЯЯЯЯЯЯѓаєЯЯЯЯЯЯаЯЯЯЯЯЯєаЯЯЯЯЯil32йџщ ˜EУѓєчŠ” "Ѕќџџѕ‹P—‘ йџџћЉ&Ÿўџд юџџ˜mшџџњž&1aщџѕю€џЛW_i\ѓ§УqŽиёъТVGгџџœŒв€џєW9ѓ€џб‹яџўуџѕ3н§”хТH‹яџќ`АџЖzџЦAeœ‹ оџџћпџљ+УџЕ‘џгŒЏџјЯ€џgжџѓ™МџИTџћDRЭџ‘зџџЈЖџVМџмЋay€зџн1ЦНŽ е€џЉ+жџЖ В€џJзџО’H‡`ВЇ@™џщŠw{vvyu—uyuuvus•ptu€tvsrvt€svsxs€xtt‡qsws€qss†rsvwq_…prqpqtrtqƒp qtstqppupqƒnqp…onmmn†otnџ‚pon„miotk…mrm„jp‚l ih’мјљёЛjfjlmln„mkk€j g|ЪўџџљКw—Тwg€jmk…hiihhgrшџџ§Ь{tЦўџхwfhhjh…fk€gbpѕџџСoІђџџќФocggjf‚dffeb|Ÿёџљѕ€џе˜œЄpcefgbbfgdcc^šјўлЈИчіђй•ŽфџџТ^€cgfbbdba`cу€џј–`qo\ƒї€џтƒabdƒ_]oѕџџэџљY\jъўМяй‹^„_ƒ][mѕџ§™ЬџаZVЊџкW„Р_„]_^\€[ZbщџџќыџћwVйџЯaИџу‚[\^_€Y]ZYYUЫџњп€џ œVфџїНгџаVYYZ\€YY€XWRџќ„‰оџЗTхџџЦЯџRWXWYZƒVYUUVвџшХ–ІЊUфџшsйг€UXU…QUTTPgу€џ ХnUуџЮMГiQTTVU…ST€RMdЪ€џ„PфџгPUO€RUR„UPR€P QMPЌ’OSЪТ{N‚P€QƒOSƒONIGINOSJJ„OROƒMMOMNˆMN€M€N„IMNLNMKO…LOLKNLLMJ†KLNLJLLJKJKKLKMLKLJˆILL€HLILILII‚HKI•FIGGHGG–GIFFHGŠŠЎД­­БЌ—ЋБЌЌЎЋ­•­ЋЌЊЊЋЎЋЊ­І€ЊЎЉАЊ€ЌЌЋŸ‡ЈЋАЊ€ЈЉЉЈ€ЉЊЇЉЏАЈŸ…ІЊЈІЈЌЉЌЈƒІ Ј­ЊЌЈІІ­ЇЊƒЅЉІ…ЅЄЃЃЄ†Ѕ­Єџ‚ІЅ…Ѓ ЄЎЎЇЂ…ЃЊЂ„ Ї‚Ё ŸžЙщњћід  ЁЃЁІ„Ё €Ÿ ЋнўџџћвЇМиЇ€ŸЄ …œžžœЃ№џџ§оЉЅкџџюЇœ …›Ђ€›˜ЁјџџжЁХіџџ§иЁ™››Ё›‚š€™ š™™—ЈЋПіџћј€џ%уЛОУ ˜™š™››—–—›˜——“МњўчХаяљїцИГэџџз”€—›––™–•”–ь€џћЙ”Ÿž‘Ќњ€џь”€•”•–™ƒ’‘јџџѓџћЈŽšёўбєхА…’ƒŽ›јџўЙмџпŽ‹ХџцŒЋМд‘„“‘Ž’№џџ§ёџќЁŠфџо’Юџы‚Ž’“€Œ‘Œ‹‹ˆлџћщ€џ К‰ьџњбрџп‰‹‹Œ€ŒŒ‹ŠŠ‰…Аџ§ЈЌчџЬ‡эџџзнџБ…‰ŠŠŒ‹ƒ‡Œ‡‡ˆпџяжЕРТ†ьџяœфр€‡Œ‡…„‡……ƒ“ы€џ ж˜†ыџм€Щ”ƒ……‰‡…ƒ‡€ƒ€й€џЇьџр…€ƒ‡‚„„ ЄУА€ƒйг ‚€‚ƒ†„{z{€‚||„…џ‚~€}~~‡}~~€}~€„z}|~{€…|€|{€|}~{†}|€|yx|{{z{{|{}{|}{ˆz{€x~y}yzz|‚x|v•uyvvwvw–uzuuyuŠl8mkxџџџџУЗџџџџыюџџџџџIiІ.]џџџџџџ‹"‘žgќџѓJЂэџџџџџџёЁNŒэџџІhќџџџџџџџџџџџџџџџџџџџџџІ X§џџџџџџџџџџџџџџџџџџџџџџџBћџџџџџџџџџџџџџџџџџџџџџџџŠЇџџџџџџџџџџџџџџџџџџџџџџц*ђџџџџџџџџџџџџџџџџџџџџџt,ѓџџџџџџџџџџџџџџџџџџџџўL•џџџџџџџџџџџџџџџџџџџџџџЎUšђџџџџџџџџџџџџџџџџџџџџџџі—RФэџџџџџџџџџџџџџџџџџџџџџџџџџџџџэФџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ{ОѕџџџџџџџџџџџџџџџџџџџџџџџџџџяЙz(s№џџџџџџџџџџџџџџџџџџџџџџёd™џџџџџџџџџџџџџџџџџџџџџџЋ2љџџџџџџџџџџџџџџџџџџџџџQ_§џџџџџџџџџџџџџџџџџџџџџ” зџџџџџџџџџџџџџџџџџџџџџџі/jџџџџџџџџџџџџџџџџџџџџџџџџЎ8хџџџџџџџџџџџџџџџџџџџџџџ§n4фџџџџ§џџџџџџџџџџџќџџџ§m3фџЯ\9ЄѕџџџџџџњЎD;Бџџm4a …џџџџџџЕI^-љџџџџџeУџџџџё zџџџџФit32%"џџџџџџџџџџџџџџџџџџџџџџџџџџџџџў&+*$ ђkЩрюѕљјєьнЦЄuBьFЦ‹џцБdч(Єќџѓ|у}эџю†тWеџќЄ)уCМ‘џТD„™sи#ЂћџмbƒhуџџЏжgчџю„„8М‚џй:г ЁџќЅ)„‹і„џюOбЎџТE…Wй‡џѕWаbџнe„+ЎŠџіPЯЛџё†„ {юŒџ№<ЭиŒџЙ0…HЬџхЬЮŠџ§ƒ„žќџшwЭžŠџ‚„ kхџћž"ЯCў‰џh‚]ЬџТBбЉ‰џї™E3Pйџпgгв‹џ§‘џѓŒж$бžџ­1 ':=* Ц0/€Е›џШL AœкіџџјиƒТvЬяќћэЪ‡6zё—џлg€8Жў…џЩР/Я…џњЗF/Њћ“џш}€ †іˆџАО$пˆџўЈ%=Ѕ№џыЁ1ЧŠџўBНЙ‹џш^€$rП№‰џ§в ‚TьŒџ—НRŽџ“ !R„ЎЬнхшхлЦЃp6ƒgљџЭЛЛџЕ„  ‡fќŽџчЛ&ѕџС’Sљџѓ!Л`’џМ3юџі'Л”“џЄŽд‘џћ#ЛК”џuЁ‘џэ{Кв”џѕ:ŒY†џйšО„џћЂ'М н•џЫ Šр…џ™п‚џУCОу‰џк…Ћњ†џvŠ‹…џЄ€­€џнe€ Кфˆџњ=Vѕ…џъˆ%ё„џф‘џ№‡€yм Кпˆџп€z†џŒˆ…џ’‚‡Б-€VбџєКжˆџѕ€ л…џь†х…џ_‚€7Е€џ№КФŠџбQ…†џy†Y†џH…•іџ№ЛЌŒџА2>ў…џе …Ѕ†џ?ƒ sцƒџ№ЛŒџєЗљ…џўC„ к†џ;‚>Ю…џ№Лe™џ•„'ї†џ=€†џ№Л:§˜џи ƒC‡џ@‹"Іџ№Лч˜џћ2ƒX‡џThуџ'ІџёМИ™џjƒb†џўъџџі&ІџфМu‡џкЦџ ƒhŠџі&ІџЄН.ј†џТbи‹џЪ‚lŠџі&ІџKНУ†џс zч‰џц‚nŠџі&Š…џе Оe†џњ1€ ”ѕ‡џј*‚oŠџі&Š…џqПс†џv‚3А‡џ>‚pŠџі&Š„џтРz†џз ƒLЧ…џS‚pŠџљ'Š„џqСо†џ • fнƒџb‚pŠџуŠƒџб Т_‡џЅ2<лФ9€ƒэџi‚oˆџ§Ђ$Š‚џќIУГ‡џћќџџі‹€'љџџk‚n‡џЯJŠ‚џ—Х"хŒџм`€;Гџm‚m‡џS‚ЁџЯЦMљџЛ:R@‚k‡џS‚Š€џь1ШrŽџј’†i‡џT‚ŠџџјPЪ‡џр`„g‡џT‚ŠџќdЫ‹џГ+‚e‡џT‚ŽўhЮ~ўџШ‚b‡џT‚fаa№Žџƒ_‡џT‚ в9аŒџљ2ƒ\‡џTйљŠџЌƒY‡џTл?Мˆџш'„U‡џWмOИї„џтA…Q…џїЌ"п/vВзфтЩ…†Nƒџр2фˆMџџјжšOѕ,„X, џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџјВuwvyxцvwuzuцuzuxuфmuxuvt{фsv’uxvфuz’uzvфtx’twtтwtu’tutxтvx”txsтtx”tyuсqsu”susqШtws’tu–stt’vwrЏvuytuvtx–sxtvuuxur­tturswssrfru–rwr uttvsrsusЋtuurswtsn‹t˜rtrfŠUuttv‚rstsЉruu„rswtrj†pstss™rssr†rttv…rsurЇruu‡rtvuuUftsrvusšrvv€swf€tuˆrstrЅrstŠq ttrroqsturŸq rutrrqrstt‹qrspЃpstqurqrutЅqsvpqttŽqrspЁpstqusЉqrt‘qrspŸprsжpqrqnrsиpqro›rrsкpqrq™pqrмopqp—lqrоopqp•oqqрoppo”ntтnso”mnqрnopl•mqрnso–mnpЈmllkjklЊmnmo—loІmlhmu|‚„„{smhgikЅmrn˜lpЄlgoЉпэѕњћћљєыоЪЏ‘xifjЁlmmo™mnЂlih’о‹џёбЄ{j lqm›kpŸlje€Щ§џјГkklmlk›pllœkjerВєџѕЗyjžkolkp›kfjœцџ§Щ€ei€kiggj—kmlmjkl—jkgfз‘џкfgjdzФ­jgk•jojŸko–jid|Ш§џъЂkejfjЅюџџаxe”jlk_Ÿfil”jhgЄёџѕЕsdi€jhdˆз‚џщ‰c“jnkЁjm“ignЧџўЩcgicrЙњ„џѕ–c‘ikis ikj’ihjЯџлefief›щ‡џљ›ciliЁil“hcЁџыЂichgbЮŠџљ—bhifŸgii“heжџїЖsbghcmЏѕŒџіŠchliŸgl“gflшŒџеaegecсџ№veŽghhiihh“gfhтŠџўДgcgfawФўџёЌmfgkggk•gaФŠџД_f€gfajЅ№џ§Фy`fgiff›Ugh•faŽў‰џЄ]aba`lœрџк‹ac“fge›dg–fecЫ‰џњС„•ЛшџыЂga•fjf™hffg—ecqу‹џў‘џ јИq_ddbaabdegffj“ffefegh’e dcaacdeaxуžџ Ь€_ba_k|‡‰~k_cefefgfdaedfdhhe’d c^fv€te^`_rв›џнa``bŠТшљџџћчВmadfhgdefc_…dfddfhfe”d`lЋпѕ§§єоЖƒb\e­і—џщЁf_b^„вў…џоra’degheefdhbcddgge—d_~т…џќвŒ_Z~Ъќ“џёЎm]c_hДљˆџЯbc•d eggcddbfged™c`wыˆџўЩwY_†ЧіџѓЖs]ac]~м‹џ‹^˜cdfgfœcbbг‹џё›^\^wЇзѕ‰џ ўтЎt\`cb\•ѓŒџП\œcœb\“ŽџМe__[at“ГЬпъяёюшлЦІƒf\_€b\ ћџрcaКb`дџбk^bb_\[^cimolha][^ab[ §Žџ№m`›b›a^xљџиk^ƒa`__``…a[“ћџїu^Йa[œ’џеf_Ža\єџљx^›a›`ZМ“џЦ^_Œ`^jф‘џ§u]Й`]д”џЈYŒ`]Ф‘џєЌb_›`š_^bт”џљ‚ZŠ_Y–†џчМж„џ§УuYК_^fъ•џоc^ˆ_]kь…џОfVgы‚џй‡Z\]^œ_š^]iэ‰џчЏЩќ†џЈXˆ^XЖ…џХ[\^YЪ€џщœ_Y]XoeЙ^\iюˆџќƒRW“љ…џђo[†^[tі„џэl[^^WЙџіБiW\XdЉшr\›^š]\eъˆџъ_Z]TЊ†џЕW†]WЖ…џЙV€] WВЭwV[Y[‘тџјo[И]\`фˆџјЎcW[dш…џѓnZ„][iя…џ™V€]\k]YZW~а€џіo[›]š\[[кŠџсYPА†џЈU„\V”†џ‰W\ZZUlКљџіnZЙ\WЩŒџЬv‚ў…џф`[ƒ\WХ†џƒW\WaЃяƒџіnZ›\›[TЕџљаћ…џў†V‚[Zaч†џ€W€[Xп…џѕnYЙ[T›™џКU‚[Xsњ†џVXV[TЖ†џѕnY›[›ZVў˜џх_YZU…‡џ‚NjГpQГ†џѕmX›Z[šZXfя˜џќzVZT’‡џŽšэџsPГ†џіnXšZ[Z\][˜ZWб™џžSZS˜†џўёџџљrPГ†џэdX—Z [][Z[YZY[\Z•YRЅ‡џцйџРSYR›ŠџљqOВ†џУT•Y[]YZXY_€Y€Z[\Z’YUvћ†џз[—х‹џмYX€YRŠџљqOВ†џ‰S’Y[\YYXY\†\ZYX[[YXWз†џы`PaІя‰џяdV€XQŸŠџљpNВ…џу]WŽXY[[XYXX?Œ\WXY[[ŽXQš†џќwTVQjЗљ‡џњrU€XQŸŠџљpNВ…џЁQŽXZYYZZ“UXŒWUbы†џЄOWWTQwЪ‡џ€R€WP ŠџљpMВ„џыcUWXWU™VZWPІ†џ ф]UWWUQT‡к…џQ€WP ŠџћqMВ„џЁPWYXšXYVTaщ†џ ЙRQPh\QPY˜шƒџ–O€VOŸŠџэhMБƒџр\UVZW›]VWVO”‡џ Уt{чзyOROaЋѓџ›O€VOžˆџўРkTOБ‚џ§…PŽVWWœVYŽUTЬ‡џќ§џџљБaNRNlНћџџœN€UN‡џпƒQRUNБ‚џЙPŽUWU_œUVVUQiюŒџ ч“TPQPyЬџœN€UNœ‡џŒJ€UNБџп]SŽUYUžUXŽUN‡ћџбyORPR‹~Q€UNš‡џO€UNБ€џђsPŽUVVRžUUTM ŽџњЕbMSORTM˜‡џŒN€TMАџџњ‰MTWU UWŽTSO­џъ“RP‚TM—‡џŒN€TMАџ§–MTUU RUSRPАџЫnQSL–‡џ‹M€SLВў˜LSTR_ SUSROЇўџйTSL”‡џ‹M€SMЈ—L’SUSŸUST‘SRM’ѕŽџВLSL‘‡џ‹M€SR[OR’STSUžQU”RKvпŒџћsNRK‡џ‹LRQ•RURRSS•RL[ГћŠџЦNQRL‡џ‹LšRSRQœQT—QOKyбˆџ№iM‚QKЇџŒK›QTP›UQR™QLQ…Юњ„џы|KƒQKˆ…џњХfO›QRRUšQT›Q PJNoЁЪуьъйЊcK„QK…ƒџщВqMNQTP™SQQžPLILU[ZQIM…P J…џџњтИ„ZILЁPQ˜PSЂPONNO‡PLnЊ‹lTIJNЂPSQ—QPВOMHIKNЇON–NRрORO?”NOOсNPN”NRтNQM?“POQрNPON•POPоMOON—PNPмMONM™RNPкMNOM›KMOиLMMLMMOжLMMLŸMMOРLMLMMKЁMLNKMMLNЉKLNJLMKLMNЃEKNŠKLNKLMЅKLNLLKMKMMŠKLLJЅGKN‡KLNLML€NLMMЁKLNLKO€3KLMM‡K€LЇGLM„JKMJLMƒ?KKJMKœJMLKLH„ULKML„JKKLЉGJMJKMKKNˆL€KJNˆNKKMKJKLJЋGKMJKMKKHŒKJK˜JLJ€KMKJKLJ­JKNJLJJM˜JMKEKJMKJЏKLK‘KJ˜IJM‘KJHЧLIK–IKIUпIL–ILJрHI–HIGр?GJ”HJGfсHK”HKIтF–HGуFI’GIH?уHJ’GJGфHFH‘GHGхGJGHGEхHIGJGцJFGFHFБВ­ЌЏ­В­цЌЏЌГ­цЎГЌБЌфЖЌАЌЎЌЏфЊЎ­‘ЌБЎф­Г’ЋВЌуџЌА’ЋЏЌПтЏЋЌ’ЋЌЌЎтЋБ”ЋБЋтЋА”ЊБЋсЊЊ­”Њ­ЊЊШЌБЊ’­­•ЊЋЋЏ‘џЏАЊЏ­ЌБЋЌЊЊА–ЉАЉЎЌЋА­Љ­ЊЌ­ЉЊЏЋЌЎ™Њ­–ЉЏЉ џЋЌЌЎЉЉЊЌЊЋЊЌ­ЉЊАЌЋБ‹ЋЊЊ–ЉЌЊ™ŠЊ­­ЌЎ‚ЉЊ­ЊЉЇЌ­„ЈЊА€Њ†ЌЊЋЊЊ˜ЈЉЊЊЇ†ПЌЋЌЎЉ„ЈЊЋЊЇЇЌЌ‡ЈЋЎЋЌЊ™ЋЊЊЏ­ЉšЈЎЎЉЋЊЊ™€Ќ­ˆЈЉЋЊЅЇЋЌЇ‰Ј ЌЌЊЇЏЊЊЌЎЉŸЈ ЉЎ­ЊЉЊЊЋЊЋЌЈЇˆЈЇЉЋЉЃЈЊЋЇЌЇЈЈЎЋЅЇЊЎЇЇЋЌŽЇЈЊЉЁЈЊЋЇЈЌЊЉЇЉЋ‘ЇЈЊІŸЈЊЋІдЇІЈЉІЅЊЊиІЇЉІ›ЊЉЊкІЈЊІ™ЇЈЉмЅЇЉЇ—ЇЈЉоЅЇЉЅ•ЇЇЈрЄІЈІ”Є­тЄЌЅџ“ЃЄЈрЄЅІЈ•ЄЈрЃЊЃ–‘ЄЇЈЃЂЂ‚Ё€ЂЉЃЄЄЃ—ЃІІЂЁŸЂЇЌЏББЏЋІЂŸžŸЁЅЂЉЂ˜ЃЈЄЂŸЃШыєљќ§§ћјђънЬЙЉ žЁЁЂЄЃЇ™ЂЄЂЁЁŸŸКъ‹џітХЋ  ЁЈЂ›ЁЇŸЁ ЎнўџћЮŸЁЃЂІ›ІЁЂ ЅЮјџљбЊŸ ЇЃЁЇ›  ПяџўмЎœŸ€ žŸ— ЃЁœЁЂ˜ŸЗц‘џчЗŸ›ЉйЪŸž •ŸІЁŸŸІ—Ÿ›ЋлўџђУ œŸœŸХєџџсЈœ”ŸЂ ŸŸЅŸЁ”ŸФіџљЯЅ›ž€Ÿ›Вц‚џёГ›“ŸЅЁЁŸЃ“žЁлџўм­šžšЄвћ„џјЛš‘žЁžЂ › Ÿ’žžрџчЖ››ž›œО№‡џћОšžЂžЁžЃ“™ТџђУžšœ™ЌпŠџћЛ™Ÿ Ÿž“œ›фџљЯЃ˜‚œ™ŸЫљŒџљГ™œЃžŸžЃ“œ›ŸяŒџф­˜›œš™ЗыџѕЅ›Žœž›ž“œ›ьŠџўЮœ™œ›—ІйўџіШ ›œЂœЂ•›—йŠџЮ–š€›š—Фѕџ§иЇ—š›ž›™›Њ›Ÿ•›˜Еў‰џУ•—˜—–ŸОъџчГ—™“›™››œ—š˜н‰џќжГЎЙв№џђТ›—•šЁœ™Ђœ›—™˜Ёэ‹џў‘џ њаЁ•™™——–—‘™œš”“™ž››š“™€—˜™™–Іэžџ оЋ•—–•ЈАБЊ•˜™›š›š›š™šš™Ÿžš™‘˜ —”™ЄЌЋЃ™”–•Ёс›џшЕ––•—Вз№ћџџќяЬž–˜™›žž€™›Ÿ…š›™™›žœ™”˜•Чъљў§јщЮЌ—’˜Шљ—џ№Сš”—”­сў…џщЁ–’˜™œŸ™€šœ™€˜œ™——”Љы…џ§сВ”‘Љм§“џѕЩž“—”šЭћˆџп––— ™—™˜™››˜™—•Єђ‰џлЄ”ЎкљџїЮЂ“–—“Љш‹џБ”˜—˜››ž–т‹џѕМ“’“ЄФфљ‰џ ўьЩЂ’”––’ИїŒџд’Л–’ЗŽџв˜””’•ЃЗЬнщёєѕє№чйФЌ˜’”€–’Р§џъ—•›–œ•”тџр›“••“‘‘’–šœ™•‘‘’‚•‘ПўŽџѕ”Й•“Єћџх›“‚•„”„•Ж§џњЂ“›•›”М’џт˜’Ž”‘ЈјџћЃ’Й”в“џи’“Œ”’šэ‘џўЂ’›”›“‘с”џФŒ“‘з‘џјЧ•Й“’”ь”џћЊŠ“И†џябу„џўжЁŽ’œ“š’‘—ё•џщ•‘ˆ’šђ…џг—Œ—ё‚џх­‘’š‘˜ѓ‰џяЩ꧆џФˆ‘Э…џз‘Žл€џ№Л’ސœ–Й‘˜ѓˆџ§Њ‰ŒЕћ…џі†‘ љ„џѓš‘‘ŒЯџљЪ˜Œ•Ф№ž›‘š–ёˆџё‘ސŠХ†џЬŒ†ŒЭ…џЯ‹€ ‹ЪнЂ‹ДыџћŽ››’ьˆџћЧ”‹Ž”я…џі›Ž„Ž˜є…џЙ‹™ŽŒІп€џјœЙŽхŠџъБ‡Щ†џФŠ„‹Е†џЎ‹ŽŽŠšаћџјœ››Ž‹кŒџмЁЉў…џь‘„Ž‹з†џЉ‹€Ž‹’Рєƒџј›ŒЙŽ‰Ьџћпќ†џЋ‹‚Ž’я†џЇ‹€Ž‹Јщ…џј›Œ›Ž›‰Й™џЯ‰Ž‹žћ†џЈ‹ŒŠŽˆЭ†џјš‹ЙŠІў˜џэŒ‰Њ‡џЈ…˜Ъœ†Ъ†џјš‹›ŽšŒ‹•є˜џ§Ђ‰ŒˆГ‡џБЙђџ…Ъ†џљ™ŠšŒŽ‘Ž˜Œ‰п™џЛ‡Œ‡З‡џѕџџћ…Ъ†џђ”‹—Œ ’ŽŒŽŒŽŒ•‹†П‡џюфџг‡‹†ЙŠџћœ„Щ†џеˆ”‹Œ‘ŒŒ”€ŒŒŒ’‹ˆ ќ†џуŒЗэ‹џч‚‹†ЛŠџћœ„Щ†џ­‡‘‹Œ‘‹Œ‹†‹Œ‹ŽŒ‘Šу†џё…Сє‰џє“‰€Š…МŠџћ›ƒЩ…џь‰ŽŠŒ‹ŒŒ‹ŒŒ‹ŽŽŠ…И†џ§Ÿ‡ˆ…–Эћ‡џќœ‡€Š…МŠџћ›ƒЩ…џН…ŽŠ‹Œ‹Ž“Љ‹ŠŠŒ‰ˆ‘ё†џПƒ‰‰‡…Ÿк‡џЅ†€‰„МŠџћš‚Ш„џё‘ˆ‰€Š™‰Ž‰„С†џ ь‡‰‰‡…‡Њх…џЎ„€‰„МŠџќ›‚Ш„џН„‰Œ‰š‰ˆ‡я†џ Э……„”Œ„„ŠЖяƒџЕƒ€ˆƒЛŠџђ”ШƒџъŒ‡ˆŽˆ›†ˆ‰Œ‡ˆƒГ‡џ еЂюу ƒ…ƒФїџИ‚€‡‚Кˆџўг–†‚Ш‚џ§Љƒ‡ˆŠ‰œ†‡††л‡џ§ўџџћШ‚…‚˜аќџџЙ‚€‡‚Й‡џшЈ„…‡‚Ш‚џЮƒŽ‡Їœ‹ˆˆ‡„•ѓŒџ юВ†ƒ„ƒ кџЙ€‡‚Й‡џЎ†‡‡‚Шџш…އ‡ž‡‹Ž†ЊќџоŸ‚„ƒ„­Ѓƒ€†З‡џЎ‚€†Ч€џіœ‚ކ‡‡‰ž‡‡†ЛŽџќЪ…‚„†Ж‡џЎ‚€†ЧџџћЋ†‹ˆ …Š…‚Хџ№В„‚‚…€Е‡џ­€…€Чџ§Д€…†‡ˆ …‡…„‚Цџк˜ƒ„Г‡џ­€€„ШўЕ€„‡„ „ˆ„ƒРџф‚„В‡џ­€€„€СД’„ˆ„Ÿˆ„…‘„ƒБјŽџЧ„Б‡џ­€€„ƒ‰ƒ’„†„ˆž„‰”ƒ~œшŒџќ›€ƒ~Џ‡џЌƒ‚•ƒˆ„‡„„•ƒ‰ШќŠџ戂ƒ~­‡џЌšƒ…„„œƒ‡—‚€~Ÿоˆџє”‚‚}Ћ‡џ­}›‚ˆƒ›‚‚ƒ™‚~Їмћ„џ№ }ƒ‚~Љ…џће€›‚ƒƒš‚†› €}—Лйыё№фТŽ}„}Їƒџяט~†™€‚ž~|~„‰ˆ|… }ІџџћъЬЇˆ|~Ÿ‚ƒ˜…Ђ€€ˆ€~–СЋ”„{|Ђ€…€—‚Б€{{}І€‚–€…р„”€€с‚”†т~ƒ~“|€‚р~€•{‚о~€—{м}€|™~к}€›}€и|~~}ƒ~€ж|~}Ÿ{}€Р|}|~~|Ё{|{}|Љ{|€{{{}}|Ѓ{|€Š{}€|z}|}Ѕ{|€|}y||~~Š{€}Ѕw|‡z |||џ||}~{Ÿz{}€€|€f|}~}‡z|}|Їw{„z||{yƒ||{|œz}{}{„q{}~|„z€|Љw{~z{|}uˆ}||{›z{}uˆu{||z||yЋw{~yz~{{Œzy{˜y}zxyz~{y{{y­{{z{{x~˜y~yszz{yЏx~x‘{{—yzy|‘z|xЧx{–x{xUпx}–x}yрxy•xyyzрw{”xzx™сx|”w|wтrxx“wxxуx{’wywуx{’v{vфvvw‘vwsхwzvxvsхyyv{wцtvwuyuБt8mk@ыџџџџџџџџџџџџџџџџџџџџNGўџџџџџџџџџџџџџџџџџџџџ„џџџџџџџџџџџџџџџџџџџџџУгџџџџџџџџџџџџџџџџџџџџџ№3њџџџџџџџџџџџџџџџџџџџџџџRwџџџџџџџџџџџџџџџџџџџџџџџ’ПџџџџџџџџџџџџџџџџџџџџџџџЭ ђџџџџџџџџџџџџџџџџџџџџџџџѕ&_џџџџџџџџџџџџџџџџџџџџџџџџџ_ЈџџџџџџџџџџџџџџџџџџџџџџџџџŸфџџџџџџџџџџџџџџџџџџџџџџџџџи ;БKHџџџџџџџџџџџџџџџџџџџџџџџџџџњ0GИv8цџўА6џџџџџџџџџџџџџџџџџџџџџџџџџџџkIКџџўt9цџџџџїš&гџџџџџџџџџџџџџџџџџџџџџџџџџџџЌLМџџџџџўs9чџџџџџџџь„Fћџџџџџџџџџџџџџџџџџџџџџџџџџџџс NОџџџџџџџџўs:чџџџџџџџџџџрm "lКѓџџџџџџџџџџџџџџџџџџџџџџџџџџџџўБ]PРџџџџџџџџџџџўr:чџџџџџџџџџџџџџЯW OЎ№џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџьЌTSТџџџџџџџџџџџџџџўr:чџџџџџџџџџџџџџџџџНClаџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџл}UХџџџџџџџџџџџџџџџџџўq;шџџџџџџџџџџџџџџџџџџќДжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџфЭџџџџџџџџџџџџџџџџџџџџўq;шџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўq;шџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўp<шџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўp<щџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§p=щџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§o=щџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§o@ъџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§pqџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЉжџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕ/]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџœЧџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђ'K§џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ’Зџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџэ :љџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‡Іџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџш+єџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ}“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџтыџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџs€џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџллџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџh­џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЯ 3ѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЖІџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћ>/їџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋ™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕ)эџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџƒtџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџи ЭџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўD3ћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ“KЖџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџр< -lБчџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§йžZ!IŽЭјџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёТ‚@+iЏцџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЇc'P‹ЬїџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕЩ‰PџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџыџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџъPЁуџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўз•JRЃхџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџјЫ„< UЅцџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёНs.WЇчџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџш­c"YЊјџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћiАџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџгYџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŒфџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§=Žџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџг*ѕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ~Єџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓ%3јџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅЂџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕ6;ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџлƒџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўbьџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџкџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџu"яџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџх“џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆ'ђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџя$™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ›+єџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџі2Ÿџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ­/іџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџќAЅџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџО7јџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўT˜џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџХ)еџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёN#дџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёK#дџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёL"гџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђL"гџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђM!вџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђM!вџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓN!бџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѕџџџџџџџџџџџџџџџџџџџѓN!бџџџџџџџџџџџџџџџџпpGКџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓš,LФџџџџџџџџџџџџџџџџѓO бџџџџџџџџџџџџџтwNТџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџю—-XЫџџџџџџџџџџџџџєP аџџџџџџџџџџф|NЙќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџи~# aвџџџџџџџџџџєP аџџџџџџџц€9’оџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџљЁK iйџџџџџџџєR Яџџџџш„фџџџџџџџџџџџџџџџџџџџџџџџџџџџџџРsрџџџџєRЯџыˆšџџџџџџџџџџџџџџџџџџџџџџџџџџџџџs}хџѕR"{"Dўџџџџџџџџџџџџџџџџџџџџџџџџџџџї+…U зџџџџџџџџџџџџџџџџџџџџџџџџџџџЧˆџџџџџџџџџџџџџџџџџџџџџџџџџџџ|5ћџџџџџџџџџџџџџџџџџџџџџџџџџњ2ЪџџџџџџџџџџџџџџџџџџџџџџџџџЮwџџџџџџџџџџџџџџџџџџџџџџџџџ„(ѕџџџџџџџџџџџџџџџџџџџџџџџќ9ЛџџџџџџџџџџџџџџџџџџџџџџџдfџџџџџџџџџџџџџџџџџџџџџџџŒюџџџџџџџџџџџџџџџџџџџџџ§@Ћџџџџџџџџџџџџџџџџџџџџџк Tўџџџџџџџџџџџџџџџџџџџџ“ыџџџџџџџџџџџџџџџџџџџџPic08UЧ‰PNG  IHDR\rЈf№iCCPICC Profile8UнoлT?‰o\Є? БŽ‹ЏUS[Й­ЦI“ЅщBЙЭи*ЄЩunSз6ЖгmUŸіo ј€ВxB ФіВэДISAе$Є=tк@h“і‚ЊpЎЏSЛ]ЦИ‘Џ9чw>яб5@ЧWšу˜I`ођ]5Ÿ‘Ÿ˜–;V! ЯA'є@ЇІ{NК\..Ц…GжУ_!Со7кыџsuVЉЇ$žBlW=}ёi€”Љ;Ў оFљ№)пAмё<т.&ˆXax–у,У38Sъ(b–‹ЄзЕ*т%Ф§31љl ѓ‚Е#O-ъКЬzQvэšaвXКOPџЯ5o6Zёzёщђц&тЛе^wЧдЎkЙIФ/#Оцј&ёНЦ\%x/@ђ™š{ЄТљЩ7ыSя о‰Иjј…ЉPОhЭ”&ИmryЮ>Њ†œkК7Š=ƒпЊгB‘ч#@•fsЌ_ˆ{ыБаП0ю-LцZ~ыЃ%юGpпгЦЫˆ{шкъЯYXІf^хў…+Ž_sж-ГTф>‰DН Ц@юзЇЦИ-9ру!r[2]3ŽBў’cГˆЙ‘ѓnC­„œš›Ыs?ф>Е*ЁЯдЎЊ–eНD|Ž%4 `У ю:XА2Ј‡ ОpQSL”PдR”‰Їaeэyх€УqԘ Ќз™5FiЯсю„›t…ьЧч )’Cd˜Œ€Lо$o‘У$‹вrpгЖ‹Яbнйєѓ>4а+уƒЬЙžF_яЌ{ваЏєїkћ‘œi+ŸxРУĘёњЏ.§А+ђБB.М{ГыъL<ЉПЉлЉѕд юЋЉЕˆ‘њ=Е†ПUHcnfб<>F‡Ы ^Уe||аpџyvŒЗ%bЭ:зiX'шЅ‡%8лI•п”?•х хМrw[—лvIјTјVјQјNј^јdс’pYјIИ"|#\Œеучcѓьƒz[е2MЛ^S0ЅŒД[zIЪJ/HЏHХШŸд- Icвдьо<ЗxМx-œРНееіБ8Џ‚ZNxA‡-8ГmўCkвK†HaлдГYn1Фœ˜г ‹{ХqHgИ•ŸИu#ИчЖLў˜ hŒЏs ˜:6ЋЬz!аy@}zкgэЈэœqйК/яS”зх4~ЊЈ\АєС~Y3M9PyВK=ъ.аъ Ая ПЂЈСї-Бѓz$ѓп8єоY7"йtрk ћЕHж‡wтГŸ\8 7м…№ЮO$~№jћїё]М›n5›№Оъј`ууfѓŸхfsуKєПpЩќYqxЕс†@IDATxэ]œTеї?ЛK‡tHЗRвˆt Ђ €Š ˆ„ ўUDQT,~ˆ((ЈHƒ %ннвн]Kьюдџ{юьР0Ю,/gо§|fwтНћЮ=їžsO_M?xwzr97“ЩœџЕ w\‘У6š>oѓ’v4 c ˜ 4шWE2YГ’ЫAфriїхА9QЏЩщu„н˜е`zšv35%‹йL }M7'Q)Šw–в4œp† ›5№іŒЬиђ’C—ЖСfщФ—œqеД Ј!шe ˜џЭХ…јЏ˜M&"“щ=€Ы0 @?Гп,ж8ЁћыfЖИœЕЉЧи,z7Va4€fўеŸт@MM`XгДnйKaВЂєЪщшиƒд`z˜ѓyJ’ЫT‰œиUѕв„РOg-Н€‹p @Гюp<ё?“nФџл8…1аj­sћЃёFs0€цІФ@&sS?пjџ+v:]еЉЧф\к66!4€жч§эёљЩщx‘u€”tєb`І|gЊ u4Ч*|ањЬЛЌ5ШbЩ­щап€8фxЋL vРKŒTХ€СTE7YšРšnвгЦЌN–X,а€ЦZ bК•ОԘЅ1Ъѓ8њЯХб:ВўћŽa79+Sb™О?ŸеЧ€СдŸƒР8“*#šб:ђџћŽ†н&s2ХUђ§ЩјЌ>  ў†Рdm@ж8ЋўмоCv™M†;а-yo0LФРбШўгSєп‘њ…ƒыr)аUЦї*`РЊТ3Gƒl™KaчЏHNћ/˜›4zˆ`t=@™В„ћ4 eL‚eHZvWЂџт2ъ[ќOE.л0ЬV$™ЋhнБ —С4;ѓ&}FџТ'ъ˜ ˜‰‘?*}o0•Ÿцcпž‘–DџС…Цnєhxq!ЇщaЃLXš3ЏјQЪ\&zkj1ХБ)еэIЕPћ/WT=8сА`•„ п2a§&— žЪz† џЃ“є™ж—тжPŸЉ?RППJшnЂЬˆўГ њ/šš;=EBеu7Ќ>“‹влг‘УМ’тцQЯЩQ“т]‹ŒWVп)}ЩlљЦ3 таЙ<ѕ9Мџ‘ьцщЛ'Ok~ёq„ts$”.$НЖЯЄзЯВЇHк­І:c;€Ыy’RЈ2 k^ињLFЅb3~'lї…Eјо€ЛmGЩ•мОюЈ+& _аkЪЛdЕ|6ё{O  šOЂУ~лгJІ‘Š-NbњNaИ>^= Ђ”‰:ыП7Ў @fГИn кœЛ~’ћУ[у 8k7Ј€1пй#&|ox=LР&0T?L@Ÿ  ЯЄwQ%ч3ь$8'њВTw'~9ьЛЁћ}CІЄ єѕs7ЄъўЎ~x2!лЯE-AєU№мьЂьЗЏСщЎ›Ђф3\rВчЩ>’зzњюYљ\ДН&—'ЋЉDѕg уg“”№НЇ„ЅI—§(йьКaњc}Аѓ›Эвьќо“ч§žн†ЌИœЋ@ CшЈk6MiЙSОї„ћЁs4CЧmAєЕ`EЮ&˜06IШШМЧЂеїЬhEœ‡;УЅFІ žYdq­ !OIvЏ‰‘аg*tЎГ ыН”†? …$рq Й„OOA­Ш$М(Њу ђzqЙCЕw@›L@ћ @ПE]тїІ‘зюnЗ…П~Х9Fгч.#ЭЕX~wˆЏьZЪ-‚Hєt–ŸїЕіž†bGu$СpКљУ(Ћy9]ЃJРїј’–%“0юЩ­у‡Š†›РR’:аџžеœ$ m№ж„їРб?^(­Е?дIєw=яLюнi3 zА'=ТЯ#vћXАфћУ‰мп‰И С|SАГnƒ”U–, к$|o\h˜ h•˜ЈїЄў ,mџ]“›*Іr|Ижvo8Ѓщ=3fОТhxЗ6 йaК ƒ‡ЩЎ-I@›  ї„џCAŒ5Йѓkv…€iЬ\NЈвˆ: 5€bПAќš_Ы€abРэ8 7g{њЎуЦ0{‘ь6-1€;ФяTбк/jŽ јУ€Ч;р8'\„э7љЛJЉяДТLдsтџ!\ѓcјNqІNє:ЅfЩxNєa@Ф–8Q2lУЛЈ& h˜PTсџP`С ўш[цЦˆвТ€‡ иЁќЏ“*’geЈйPНwТћёЋ9ЦГUУ€Ј,d)їё$zsœ*‡ІР—ЂRs!|#‘‰п2аћUšуБъc€]™ы}py7ЂOЎЄЕSхЫŠє3Zu$Юя3щ}Єє‚ј†ЮяgbŒЏb\Цl.Ix2Н5і!%GЎ‚тП2ў 8uч72т”œpуYХ€G ScЊжv­ŸvF H6‚ј{‚ј-жŒ_‰щ5žЁ; pQr@Ъі4ЌуЙсWPёї2ˆ_ю 5њз9PL–(d2™z ЎЬM€uўЋ>DzЌБѓЫ<ЁFїQ€VИX)Q#Ј+хTфWъ АRхв№ѓ›И­§†ЮKд‚рzЈT6’IŸє‡уКЌ_E1”X1Ц3Ђ‚ иїQ Œƒ#ќ ‰pРШe•Й™L(Ѕ>c„їЪŒhЃћЈФгЫu№’ОЩoLБў†нНЈш"=ќFЂМqЂ­Ыє@е)šќ р‡ізССОФH`й_уGF—дС€КгО’Ў%Я” eМ…ЪЄLйQ?ЎxtŸz#з4§ЦxГ4йQwВ'§єм.ЙЦ/ПРOШ5мО7KnШ5Ѓ_б‚‘)шXD7ЏЯ“sHЪH<‚ЕЁy;ЪУ№€Ј˜+чЈŒО шBUNAбг7hј фŠ2Рdr‘нФюРыШ|’sLFпєо§ЎЙ”uпbЙЂœР#й0эеxЂ0ŽРЎ u@юБ§амЇђљншЫ7Ъ=љу|G’ђ-%Xк"20"}5>K…ЎЊці!ЇўчЯмyхжИмЖЇё{ў(Jp{}яљнј/ИhЈ=eЮD\%ЯююU™}sьg86Љ?й’я†Цј9˜шэNŠOGїЅЇ№Ъ”ŽВeŒЇьщ( ОЯoЁЋхЖSжюtQ’нAз“mtљ–ЎмLІЫ7SшRb2]К…їI6œn/.ЎЛЭЬЬ дY>‘#IЃ=№юo2AEЖ7 я”ЉЈМРИOv Ч*эˆЌЇТ†[PТХhsPzјs—Ђ—j—ЄќYгSІ„8ЪgЁ8Khv—[шыfŠЎ‚јO]ЙIЮ_ЃНчЎбПЇЏвўГWщшх›t+ ™k,P03р#г ~йdВюoГMІя•!~VН)ыё{_и†ˆƒ#C›qЗЊvўЦeѓгћVЄz%ђШŠ“ЫЗRшјЅДёиEZ}шHЊЇx&>…["Є„н э=MѓwЄM` зop™ЋTf 0<К|œ5s™<ЛџJТЏрQіјэE2Ч§ŒS]е…CIŒKё,ЛэvЪнўЕGЪPЯ†х(OцtRєq6H#{Ю^ЃYлгєэGiѓёKф„:#ге”7#—ЌАњDІѓˆЉƒ3іЩњ,ŸЮе%МЮПgЄьІEЈ \CэьœёбАу[,8Лb!zЏEEЊ\ыFhГнДйi§с 4vу!šАўнь**кDCХЛП#e {юЅTЧшхЯн з§Втdd<окŒц,ю;єP‘\BЯМТ§Аехп~сєњ2Cœ•JцЩBй`ˆd№щѕBŽч­8bоvŠЬŽ<_)љ_]Р#Е%ЯІxЫ"$ 53 ‚~ІžнzЁ м—‰z6*GЏе-M™APZoWa(ќeе~њvёПtтRйс‰0м†~fЭМ8ьЃhш Gќќ*ћWкиBон+dЭ "xEіaыфьжKАRЇХщнІхЉDЎ,šм†5}ыQњ|ўvкё_CtAj~RШЛПЫy”\Жк4ќхSRuJ?к`ЌОўыu0ЄLŸpы5(>hY‰”ЪЪœЊvэ†ЃшгЙл„аЩQžlј3Z` АюяLюGУž‡ЌNS_p›ыž Žћ(…2ƒ+Њƒ ЕŸЪDcwQ™йЈOуђє,vўмzЁЂс№ХыєэЂiєъ§t‘ƒн†наКaІЧ6 Vyb!ЪPˆўЖ§drŒ Qв^­ Р=ЊзћЖ€n№‡тГЖ@“э>Н Оƒ чЬLЏз/G/>\’rdФю ёЦzў§7 wвqžЯФJуБУ[Pюў0nVЂƒˆ8ѕсB’…н ”Оєt-ЧќЛьoвА.Уд[+€vЧЗ ќЇ ФFЂ[ЦсЯЯš1Н _~ЯFа§й3ЊЙ‚zЖЛєŒ­Чh№МmДё№yїŽќбxЖСЦ‘љ Џ5yњт• q мъ•ЬK}ІЎЃ ЯEЇёаВsиџ%ч?‚E•\з…ШЎхУЋпзЧ|J–„їмR€зїбі>qvЫ'*Ії­@U хдХ7Вž?gЭм~ ЩœлУбѓSmM(@{ˆjЭѕŸБ_FBв{36бШ{сBS‰&CЂШјГНL#^јх?Wј mIb№Hrк:A‡ŒЮD!^ЬиљJИЖЊLэЊбМ?ŸЇхˆаѓw зžаѓЙ`eЈФŸ:і"ЙГP1=_Ћ$<РўC—ГgH kƒ9фІОгжб…kIбЁ№юяtl"гI гКпЧiO`0ЛщРWQчРЮЧ ў•GJ }7o–є~'EK_^Cœџ˜ећш›vвБ ‰nЂUЯчщЅ‹ЗR}ПЈт‚mkЁfМ2v%э<~‘ььmкМЮч$[ЪГєу‹уЕ 6@Зqй‰R– цЕbд$ ŠхЩJпДЋNCьзzcБћЏmаѓчn….=сЧa‰с"‚бI5ŠчЁ[?DMЫkшЧ}иѕ•(+yЬэeЋ•obбпi_Mч36І)эoЉ xМ6ƒжэЗoрNъECWпБћУžЉMХaщзzл$єќ­‚„­чГ-ж§<й2Roј^oP…H"лНQАфѕёkh,$§%1ЕистnOУŸŸЎ•5йŒШ5Š—GD=є6Кп§™ПџRНВєП5)C„ К=§Вžџн"шљ+їбuс„??T=Ÿ;K5pЖЏQ‚>„‘Џ\оЌžGDєŸC ~ЎeE•Ѓяw ЄчCмААќ;’Ёяjкdqq]Q>Мˆю=(Ѓѕє§jSœHљдаЬ{тбѓП…ž~xБЛ†УЌRу,”ƒДzˆž|ЈˆфбlCкОЅGnСИ!…ZЂ&Р0šœчhdW8hЇiOш:fё„5(1•WзхТ@ќЌї.шйuјpЬГFл Фэ6zў!јмУеѓylАqdE№RwH;Н‘ЗБ r6ЎњMп@_ТFЁ & €9jQCљђF9qJпк“LёЏЂTX^]яўXёp“ zМЊf‰л‰K‚№Їn:ьічsЖ7ІЌPZЊOПyљћi`ы*TщЪJ4оЙ?Q•’Сx†.@-VUДЗнAЛ@­жЬdvіР—]юќ ю;mЁьеŸ Aє_‹™|Ко§Б(?XцНй›“ЖP|>‡AўqљnКv}ыиТ1еЇЯžўсeї^Ј…GЅXњ\ЈыИ•4fйэЛнuџ ЄдЅŸ_…шЂ~г–`В>OџўЕФŽЈ%тП њиЕшЋлщр™+сыљМfaнOƒм (GжПE%Кџ>ѕТ—™щќЏCm:@Ёй[hлEШd[рВМ,В$ z ‡їЫєKуѓP|вd„еѕюьpЅže}ZRиДацџ{‚>žН™Vя?уŽл}wOX9єзсЂZˆеџјё*дИLx>§{>'Œ N_НI­†ЭЇЭЈPЌщ$"6Л\gсхЊA?М|4ŒЁJz‹џ8LIdg–[Шlе=ёsэЋLА чг@”пБKзщ•пWаc AќЌ'‡Cќl@,CоЬщщЋі5iaЏG5EќМТђeЭ@П<_— ц@„!˜”f3Qs|rZкkЦTЫЪ tŸœ ёџУ ‹це}-ЌНLЈтгiНY‘щІV›Кљ0uљy)-мqk ‚^8„ЯРУ›СЭгеKв˜ъSkD1ЊЁыƒЧМY2ицbЬš.?Ц—3m:ŽvЭA§tѕš6lіыMс‚Њ†bnoТx;ІэzВ>žЕ Бћ;ƒnfVKѕщW@–тGїлT.V7JнФБ ЃWюЁЁЈM Š(ѕрpžУуš-х)G|}мЎj`P˜Ћ#œQИЇнdRwѕE2Хƒtд!š…ї5Fq+ХF‡Я'RйМйТы#ЬЛ.РЊџђoЫщЏM‡мzАР„ьзУ-)Ъ–9сЛ•Ц[žюгxq’yџглLkі†‘ Z­КвАР ™И$ѓiМ‹q§rE Ѓ~Tьў˜Mб 6o‰B•Б!ё.,ћ ЅЙfсŽАˆŸь€Ш_!М}›WЄgЊ—P юp0sщF2§Дl7}ЗpЛ|„Э4œœ…p.е=Ђ˜J|nд€l†.’ЊлPћQ—tйёИHњбАФ*RйрЖ9ьыс’Њ],wШЗ‡sУљDd—ВоЮ-\ђ5HTтV&_6ъZЏ=џpiЪ†D­6y:Œ›ƒ юoawНgзfЬZS‘Й-Е›ќ3вƒн“Ё0Œъ2€єЖ:˜ХRnППg0РlaгOBuмяяЄZХ†h ч€_wƒзa=тљчm;тЖјћГњ3Š…/хШ@8UPmч%$+ЕЋZ\ѓEH7Л@ƒfoЁПЖpш2тЉAЗ ˆ6upРцeЂ”хj)В[ `дedzsTщџžYDl§ŸаЧaWm\Vў€Ў.4­{њqщ.ЕlŽыFVыХžU$Ж’9Гв#8o MЅЂєHЉ|"ЋЮs‰џŸBшђаE;ФИn‡. уІЁ &.o‰ЯJёіЦИS Œ‚ъ/Џў”“LVЮŠ*L" юРJ0Є-ьгŠrdRЎЬїфђ/пw†ЖœИ@WQ^;KК8*Œ\х ц вШЭзУбbЗ€ЛqkїгsЗаS]цd%…ь)Š.EЎфА/ |'ZаРрЪ6u@—oГQ|ІЮА8џъР žаbішчыk6€F)TћœЅ{OбЧ37бR…ўФГhmЬд\ІKtыV-лCбЃСЅЪ_ЛvЪкЄИˆ6™Am{Ф_nЈ QЄћZЄиЭоEŒСOжPсбіїЛO_І/чmХЮ sа=)Ък;rшD– ­„w&hd9,;hєЫЈР*“ˆ]>|ќT|І!^•§”U„h:!ђГ1*ˆŸч­НH[§№ёjєQыЊђO­~žp>1‰~\і/ ƒ[яVЯќcь &рЂЗ›WЂOкVзХ™ Жœ-ё“6 ЯчlЁ]\ђ›нzє|Ћрі^@тOqœ”\Vѓ ў?[њ88,`tвЅ›ItЧ”э;s™іŸKо‘сЯ+"чрТю$)˜pСєтА_cи‚ю–ƒVPМmш~9ью}nŒŒєš…nЅЏ(ˆнln*Џ‚ŒОlЗwysфЈhЬФR_LЬb\тF42&яЕы№}P:С3№э3SЎЬђ–Эв2кYЯџ ўќvwƒщOЯg|Б'сНхѓпGЭЫЂЦх Reф'dGМ‚5 ЂОœˆ]P)ІЃтб„ЕћшˆЇжЁ^$Яф1М|аЊpŽ9“ЫМ’СЌЧu(6Й?’:ƒМlCh.Н0Ђ(0]о БоY€€qQЛЫ3бsР ˆ;'\mХrd)Ї9‘›еŽ„ЖКŸADкQЄпžИrƒR’XŠjyњлб пVGN§ˆgыбC…s†€§_Куф%дёлB“ОыжѓxЂС(­№ѕ3СПRЗ5}  В,a1ЃП|>џ{ \њ/ОьNТшWљ[˜№šbщ@ЌЫыdІэP‡–a-%Гc+§иХƒoїf/ўœ™LЖ xъ#xHC<И*ФњьЗwљhехyЁ ђM6=я~Ђ:ќєї…ѓMпУGˆџŽƒ<О[АyJ‘љ“ŠRеЃ’K~ЏUUъ\Л$˜"v;‰л/+ірЬ™Ѕ$jsKФX$1ќюXM№ЈRvЧMH{ Н.Ч†Н˜LіЭH:‚_ѕюцfэОЯDй­хЩIuР•й€WbF!nФšХDšЇй|ћLъ€Є)Лўйu‚F,йAѓw';$ QŒгг9T‚œ0fѕlZ‘Кж *l<цщ› q3˜ŸXKtv\Ыњќ‹Ј1јaыjT@ц#впЗ‚†ƒ!нЮ#№ЬC4§gцfТfУъзp91 ІѕP–`˜Ћ(9уПєЧs7LєЪOc`LЈ•Е$Вђbg—ї7й ўтyВб„nMЈZ‘мўЎˆш;v9f\Ы~rи…DхQ иж€ч—Л?НеЌuЈVœВЈXQ(мВцДLюKXія>qї§u цW '%оЎЕyЈЈП+$џю,T‡?§“žЛъЖгHў vx[:cpиqь“k?ЄћЊжfРЭХhЙуЊуiŒБ†]Ј |ОбœЊЪ@ќŒMИrёмYЉSЭRT ХB8№хЇГВHЬ/ь‚чБ8g!уm>„пLZ?RЬГR–я;E=Ёc4c‚-DЈ;ФxV`@эXЛ§ўjcЊQ,ЇйџГ11™Ѕ2>h4T";J?4-TNЈЁ&ВТVЊA%Н4"Љнљи™3@wšђF3zДBaŸхћxікMњ еzй0–Ь‘oi€ЩіРU юЏgk—І'ЋЃВљphВл TтњЯvњkыa”uHUoРЯ6Œы%~дЖѕlRAxP^+г\0ЕкЧSщ$§й$dzЎжКЕаC-?вPŠУМoЫ*єZƒ}4яD-рaЈ‚гt6Ѓ‚аИЋ„ЎЬФ“*\Džџ’ЧшuћiЎaіžˆLH№QГqВЮBЈ3oOZEL_O;›“aц’\i?ЋYc_kJk”LѓR9ЧЧ h=М4Лf,ŽD“ѓaюлD/ўлvЃђ…rбђўO †r{ОkтTw'­ІёkіК™€яЎФVr–  ЫЬ™‰Ђ&Гђ…Љ: ŽЭ™Ex-|ћ”њ3GкэФ‘bѓwE€Э!кˆЂ.ˆвF\iQ}*$ ўъ%ѓбXˆќЌЉнF ^Cї_—К]’jЃвѓ Ђњ­kzbЖк9ё7H†љшЯѕt5ўФnъ(жх˜№@tйВЄЃŠsт вМTНh.TїЩNїУnB$щЏїлп%7'!*я:u™V!ЖaВѓЖИHЗјшp!ЁАKѓіхiПё7ЋT„ЦМмHд№Oћbe~нxфеўl:‚3ЫmТАbXр•QчITФбBcZъƒ\Ваћ_Г„Ž_DB˜Ÿ #Ё&ФcзEЛr3„y‚–СђЮ­ŒmѓeЫ@Ѕ`l,ŠWФЫ„[еі*АA1Ж<Ž0“ˆѓ|tиeєuv‰У›=tі*э…пў`ИЪg2‘ЂЧsбGH Фп8fтзR…сBїeІ|ˆшŸ<ˆG ЁЈсЮu;д)иqЂ*}2mmк1с> дћ`EЯ;Х`Lj†S‡ іЊЇiй^дШ`їg,вF { 8­чжџБЊД…6ц"u8Э@!ЙЩћР ЇjQ%фTшЁBЄу7s7гшхЛшЧX ШЅ‡1іkT”Э™)НєиTАЧєqVкЙm?vQФыЬ&P›ЅшЅzвgR2qЦ!ˆIЊв`\žl4ЮјEIOr•`–ŠaтчЅš§rЏѕРgуeNЇнcАƒХXIЄе~оЁ6u1̘Hиьн\‡ШТМЄb8ЬXŠvDџ'Ю ˜ОёРЇ9Ўћ:ђ#žЌZ"ьюy‰Якrˆ!myФ~жь џŽЕѕяƒХД  СЉYšыƒOХ?vЌYšцl=BVю‚* pP hяЁ†ŸTі”­ЧЮг›П/ЃžГ8ЯиŠCN(Cd:†ŠрMШЅјlж0•ƒю<сЧмЮчsБЇРrž@м]Щ-ў‘Ѓ‡o9ЩчшпKїœ г\^LЂјžcчPъВїK&њ/@б“ч!ЩœцЂ(З‰P€Q_Оz“fЂРhm Ж@ѓА(“Пxн9Vœƒ~ŒцшиНGtЏїљ8#NсьЏK7’DН?>?@ъЦuћ?VЭВ+uчўњ‡ўќQ›’д+\Еџ4u>—Nѓ оФяy6T›ЕИ&˜Ц*ФO њzƒІа—­Їk|H їщы›d*TA‰хц>З/–0€‰ч\xо9•hМL]CS7ьЇyьшTЇ,Ša–ŒH*^Фщ9уVяЁu(ЬyWmAЉрнˆЌ-Ца‡wDкNуР—ў‡Юc—F9&Lп†Љ:pю %`§ЪтЙœЅˆA3жA…8сNяѕ0п>E№‹—/BWn%гЦƒg”“œ<РjфьI@ЖШj`ж A]ЦСŸўкfшљm‡ЮІ_Юp ы>яі`^lO‡,Ыyя7~нŸDіdМ#–ЇЗqфч€эb˜шvГмЁ@{q№‡ hЊ6 &0Ч…=іP1jYЉшY‰р] `њxЕ4 aњ5ЈEа7W%n^Ѕ8UFѓHл HЋїpnЂПзCИ„™ ‘ў{Œ"z%пЫ<гЃч{]+о2ФЮп‰>hS“ъ—м^сћˆhј{ ‹ц& ` бOцЌU"5q/FmQtвзE‡Emƒh:6‚FJгР,ЈL(o4ЎHгзЛŽRJfЈ1/@ќ—тДоs(>z”m œдtЏ†KRРА{ќО„–Ун™t 5<;ОяНЉт~™ћsRПЧЋггЕJKjlѕ}œо?Ч  €шŠj$№фnхpцньОOа˜х;i№Œѕtт<ŠO"„з­Є>тыš}'iсЮЃдJЂњ„ЕPшЄ1™ГёvHЖ9H0RьЈe ї7h'=~!Qд&љŠэОрІ2эˆцq|ї=ЬCј3ь9PAщf5Ј[“Š”‡гІяеј>Яы^зFсяђ*СEзмП†Иp%ZzˆЈнWЂ%ДЇ—Б3[Y/‡јынœ`FЌм- rПьд/з/3D№,HТ’4шпm ўg† @Švю?'TŠ š‡БЎЯјѓ|ім ТЧawдЙўƒДєУєб“Е‚#~Я§1ќ?іˆтl—ИжН‚­тіGНв”цї{’ъrЕ–@XOх†ьФхHK=Ы0Е&'н~FD§"v"!ž•ШNС \eлˆ0ФFуФЯјœћN‹ŠIrХ$Ѓиc@ ч”„>ј Б ТU7L`TзfT*ŽЧт€ ‚rь†ƒ8Т]ЂЦgv|ИLфDЦ№АјыyХBв™~“Ѓ№|wђ`ЧЮz>№VЧ‹z­№й*Oс`я6ЎѓТ+Sјc/ˆГтд/D(љ–OчyЙAyZўсгєъчqбO‚;lЫQ"ako@іwЛXвˆ`Ž!ЊзУљЄГ' Lˆ0 =пFйPtє=ˆљЫ юПмрСУ‘C„!эљНТыK6ђaзчюЇ)‡(ФъTБpфЎШлOŽл7DЯщXКžp‚u№,ђi,ЪŽ„}€#y-ђz—ЂqЈnлъЅм мPzўЪуR)›‹wѓ`ˆ?т~Ц9Р,&bвРnИcpЩ‰xЅУeФ=ё :RП˜HисиА n0дР§…$.ЁЦžŠ xE:ЈЕ%–B‚~~_›6ьŒ'Џ$вХФ[Q<ЕюЁИ/е-ƒѓ…Ыwн_0–†*‘^)›;л,,А}@xюќЮƒ|С[wž2~BХЇЦЎЭsР.&&гСГWЄ\гšь‹s ZT,ъ†-œuB,„zь~—В‰ŠLxИc~Я€„о ЉЭu9рˆ™–ї5RрщЫгŒўAѓ`,:ўЛ ИэшљшЬ=FQЛ,јHсYqїИжпЯ!EHнвqPЇqF‚ЃЊ—ШO!rђз[RQ0В3UMN РЛHц<ђXhХѓdyяkЙОй‹№‚<Жˆ\ьІ”И%ˆX~>Ž'/œ';ѕm]К ЯРi(NьЙН§{oЯrз3Єю[ћ§ХІ€чFЈэGЯR"JИJp47>“Ѕ€Е\lг{ч fа§ГЅ—І^їуИЌyЦt "<КWЫЊ8ЇQsЄVmМЧЈ‡їЁ†rŠKFЃ Fdц€ рмх‘9э­vЩќєo6b0ƒЦњHЧ LЗ*8тlё€g іч•ИчаКу2c1л0З<Г—ЩdЭ.2дpДГˆ‹­рц‘‰ рСћsQFд%И‘„ауЖ=Ж§Х…*5AQ%ђоушpoЉпѓ>ˆ~CЙф2Ђ/З†ШŒ1жYЌШўР№HИ(Њ8џк§Їш|тMЪ•9ˆмёP5єў„х”ДюЭЂЌЊ…вgзІG]–tЖёЩ8!HєœQl GuˆVЅnОqП;%9PU!Ѕ‘њ9МyГБ—w{‡=є‹ђPЎ@г ШlпHућЃŽšЛнН&ѕ…•ˆЦ‰WЛЯ ‘ЩU=ЕЦЭѕР |eЬ ›#зю;M!з]ЪЦsˆ’рпL]EWэЁ^ЈЂЫ1џlSЋ=(тчyw Оq81ЧрG[уŠD Жv‹џб08hЯcqиo@šпioZ„“QЖгьЈЛўпv7№ў}J?–ЧŠWЧ!…ЩnЏŽЩ€ъ’5.ЇŠX2рŠЁ­)яЇЈўоesвЬM$g<А‚9сЫ†m;Вќ^ў7ZД eЊjRЋ‡Š‡Ђ†K†#.ˆiг ZšцyрZ|Š7~ЖяKB цƒјžA 3d=Ў_цЩЂч u:.‚рз`гžGз2:žИ‡– МЛђŒќfоћ(>ў&^э†…*сfфz„ЬVwЁ&xfЬћfПA,м~Xœк#ЕПЛhnИƘvФЎяЂеpУЕСС­ mє{Ђ&е„e^ЩЦЎЖИ<…х;ЈMmfaVNїГobІ…Эзх‚_РЫ_kївЂGш™‡ >P J+TЊ:_ЖL” gœсb(AюъЌXXŸ‰ЂЖљШYZОћx*cжјРїžосАcЇ?1ЙЬs)…жгєОEГ…ЮМё5Оџ!|фз/дюГ њPЊ`30=Ќ+f€tгёЈЩзЁVYIЯ d9џтl%'FЇы}5ЭмАК6­Lo4w"Нч яГ‚јяП/3gјќCGуьР$ЎН е0^#"bџі[иэw’нЙŒрК’А…цѕМ&еtEЦМЁ˜ђ8ёk=ѓEIdsсД 3$Њ”aˆ[ZUА3/љї(эРёд•ŠHWі*w–Œ(Ѓ•‹Nq5`№р sЫ №,<OZN“WяЂое gы>(IypЯcМџГ’уњ7ˆLЛ`єzЬ™Њ ВчљžџоЃ я=M[ЗЖˆбЊŽЭ~,vПssиЏAМ_RlѓБ#-ЁKё;AєвV‹q?)‡PъСќ›№ю~\ЦЏŸЈнЅЩekUђ1$Ѓ< bЁ@šбLюoЂטЅ;hшѓ$‹y“ Eh.vyrё‚ѓmXи| …{N^ЂWGЬЁ_qPHœёз’ …О—Gј™ћЫ5@$1MнЋЅ^ЃдAЊџЧCїСРњŸ›§1jсVКp Ї5A5г §ѓpQŒxУО#­ q‰ ŸахmвIрœђю^ќФЏщ™ЏJ#ыЋИкSXљѕ53МРpЈцфеЛЉ7Ž*Ьж{‰ZѓJХш#Ф\У)ДТ€ЈпT_ћjшІO|qŠžЈV’њсD›ЊХѓК#ЌяѓdЭж}бp‡}џ&r>„мƒч Л}2M|Л‹мђэ?9аїžp?ЛhBп=4хсmž8цжsТэNтћ`]=QѕC)[œDл vŠГшƒщ˜ …иІ58zŒў‡KXОLиxЧ‰Сі=l.g.B}fKКfц‚Uc“c– Љƒ‰)яœПr™{"Мх<•пуФ™_o“Д`(“ZБЬŒr_ОUnмŸ1ѓM0^C бї(ZяУ?шЫПжИkщGИJrp•`1ы~ž+dbпGјаАn—ŽO\sп~л%їqр Оу +Їѓ8РъАасMъ0Дc:8Ÿ .DЭЬ3Є“g/г№y›"DынЗ?T4ЪXзvKўж _ЮЕюљФ ћ”џѓNZxЧБkНћлbЊћСXЗт_сЋ†™q^: чwG‘OЂf` \…Ѓr№ŸЋщђv†8v0HљЛј\№сO|L@љІpФ/ч;Ј)qŒ'ЇєАЁhз‰ ’ЮFяVеЉk‹*„(œ;Dюyј ШhŒГуX<Хi7ЗУФпqD! ˜t:“Z šD‹q–`8* ЩЏч;=L)œ†qЯБ зh?ŒЂџёœ„бп2л!šВ с№qЬќ4жмŒўoЕ RLщƒvЭ┈g"й%Ъ šЖŒ™ЉOšЦ.Ия^hB§кзqЛ^јD оЩЙю^л=BѓпšІєnC"fŸgЉРЛqD!Œ…‹ЖЂ–`Я ›%\—о—мы}<œАъћtц}Ё\›fGiџШ’ЭH0пPyжь=!‰БŽЯ€ќnж.4ŠЭ_SwЇ§нЕрђч›R–ђЭЎ ШЁЈ!Ÿ  ‘э=~ž*Ы'i”gе5†[А&Њѓ\G)ЌыЈK˜B}ќ6rИR. ŸB@3Ђ­GЮ v†чЎ†ЯvЄ2oGfпф5{шђкЫ!ппSNыЎk}>œ‚ lЬвэр-`>ї$їa/6ЌH@$g[‰cП_ё7§oіzКЬA:оc#N‡lЦWUЂ™C+Oіl'у—@їчS’Ежияя‚лoтЛЃдM~7`Z#;}}хЩАљMШ‰O+ „aCЅ˜џП”j—.ђЂЛз0šЂJ/ПИ,yмY|Jn1Sђ\Cj[Г4}8q-ФŽ/ТAН‰‚Љі>Lф‹)ЋhDмЗHє|У 8Т+АЈЫЛП ћ`vu\УBP0—оkЬ~? †єхŒЕ4zбVКЮ„UG4я‡ђ{яЯ:ѓљ~фз­sKaмягєrгЅя8ј}Ж–рo”фJЮVrЙ~Ћьол‘$ Њ^,Зw:MŸ WЎЦЛ™/ё{?Ћ$…Y§лгOнZQб\ˆM№U N4{  Гэ‘sЉоаTHvэчн[˜яБHRѕхB *ГUОўћаџfЎƒDнCќ^з…ћ–SБ{џК™А8ћA‹nOЖы8œ'Шц\юЅИO]Р#HЙ5о€]šГ0lX?ЮлHГ6рOЊ4ЎЧїj“JДtаГє&д.ьсіј€I‚сн„'эОњ“ћl2-ѓSэШ Лo T М3вВнѓЇ9”ФGq4dcRхZwюEФuOCНƒBёN{ р‰Ѕ Я5еЄ;аyьjкƒЄЁ'OІЕ{OњўЊ™Я\v|bŸ'щЋ›оЉ,t Q­Јžк„_N_Mo§<—’˜Ёi.v$bHГ%#\н§ЉЪЋ0аhЃiXто’ђИw*m )M(Р#œЕЭч“hюцi^ЊцœiШб„.Ж„`(у=кq;rPН0ќ6їМїЧbъїћBlЄ€E7ФŸŠ7д0ГЅ*дJ=Lо§dm1€жŸ–ƒ.зa’wCЉѕOlD Ї‡LЃQаKЕкІ!IШЦvЮљЊ6шї6РаЩNƒЇ­Т™XЖZ7јBŽZ„Ъђ5щЃ‰ъЌЉЙ— Uј{+ѕ@jpvТІКk`\УЏлГiџЉ‹єIЧЂЪVЦqUvm?фv•…’ п&о~IDAT„kљ4#ЩwBП‰ЈЊМ”ѓЙЁн+9ь‘tШх№Э–”)G{t3&’ЎЄИW;Р“*@4}9RŒKљ>x1CLu@М2u%ŽC˜L‡P_P+mд“ѓ\7ду’Э *r<Фъ}ZКžз‰ …0MŽОдn’9дmкaЎјžрŒYuЃћš7&0Ф­ЯZЛ‡šGуфaЕ'ЦќЦQrЁ*рœAh4 1РeР-qхШn}QТ^УъJ р‰Ћ`їoЏлнпъQ•чрщ‹єŒƒяРhu•ЋнЈдсєу5|f8 2 }ƒШ0qnЦдюН2єt—к`&3яўˆV‰Вv$Xн‡L^A?ќЧCWИqо§ЋсUХЎfг‚ LаЕ{яmЖ&{њзеR}аf@MўSШt…м:RєМчй…JАUn[2žо@№ ф”jПЃ цв-lќу,KЁ†јп‰РLИ%oсРeїА[аdz™8іEЅІ6€ТlэП?JНFйюя;ЁПo!euјЌЕTЗп:kћМ@пы$ќМэШYњ`мbaœ Л[L‹]ћѕcОqt йš Џ^jсB]№јР:љmUКZ3) „ШЫ?…о‚$PŒр—…[(ЕыЅn\pЃ ŠcœН„гpюЊ&њ“фPЮ_УБїQЮѓƒТД№z™:лСTh еjЬє€љ[pПђфвЉы/\дёюЂёgЭžу€m §ЕnЮшЛиАїш-”7ш‡z!ШаdJ"iiну9"_Ђч88:r!а№xзЃ‡!E@ЌмЫ‰BDяPч>ѓщЏЁ'Щп”g )нмШMќЦVє3/Ј3Ы‰Gx s.1ш5ЙДј67ТШИЕї љщЮ9z‰‡qЅ2/щDBtџq' UЇФŒOc П(1XїЬ*ё$~FЛёd3ЯEд_CнЅќ*…#­=МњŠQБМйhн8ц)ˆљBŸgб„™;K"F‹|f€ЫЙrыа”87MоІЌln БАЎЁћЫ;Љ’і_Кэ -нФA+ЉъџЗњ,9…IЄёЮ8dлWЅн_ЄCф†V9Ж]o@:ЪfўЧ"е1€мгjєЏk №с8фЃB 0gBВмЛTo€лEš‘Ывщmа8ъЯфъ""IdШ)П§уhХ€S iCYlU1ФЕRS^ `’є1,џg`vЃ?б3ябЮБHœл#Ч`хЇЪ§ЫQ™z›ИоLт JŒf`РРРН1€tЈЮc(!ёuњѓs$‚Hпфg ѓоeGЈЬУ›а Gg6˜€єiєeщСŽ_)!Ыы FЂ…`hі-;FЅъ€ РEh0яљ1оG+˜јіq”|ѓ5š7X1пк` еОЧЈxнud.BC№)уsє`РНѓ3ёwЅ”Љўyкa схЧЉxm0и I аœпыLќvћxJQŸјкb б•ЧЉlu0Š0Шjx)F‹ Бп1;џЋjяќ|j0d{WЇ`nЁС<Гeќз/„ия˜@йВМJ3*rшG0Шв&`Шї3xdоСEЈQI€SœEwф5Љ СЌ7щЏёЬŒGЂi1ЧФНѓOЄф,ЏаŒЗ5CќŒ/э2†Ž™@Љк(‚ lк‘<‹ЮхмŠsЛV‣хg$8ёЄ)дФР‡юrюDН‰xъnœА[GmЇз7ітХWhёgš"~ž)хsB]Г?_CdыŸЩ1еsxбё„К\ЛaШyЁ› щяAэ‘Йе q`  F(ЉХPб5з‹9„ПŽ^dЕеЇ9Ÿ§€yx›ьfœ9сј іЃD1OЉGšЉ6v!іл'QЂљZіƒ,љќ‘ŽM?+Ељ{5)Ю< XHёњ‚Мшx19ћАуџБ,7.ј пD}а‹Џ8Dx2ЬЦ>ŠфЃ˜ьWNЧ^0рQ”‚9јg ЮBїгZП_1ѕ=QŽЎ™Ќмѓ АŽ&ˆп1‰Ў›_Іe5IќŒ9§0†і10ВLи…a|B Ÿ}чtФг"“уwš5ј,ƒАqёЦ,Ж–ЈфкpжGˆГЩ ЋТ 0 €zњЫ“ Ÿ›ЫЕLЮ_ШцœˆР™ѓю/яёЗеЕpХ[`УŽ”  #ЬBьЗMІєж—ЯЏYтgьщ‹0ФЭпЉIжј‰ ЌТВэАLєLќNЧЙ)>~TšМ1c„cMsП,šdНюёй(%Ѕжа`e †`oИCbП§OВиžзлЮяСet0`ЌXўЫюў%јщQ}ХёЭ§|Ÿ šјџш{П‚‰u!ОcЂ™,.ЬУS`jjМЭћч‚ЁА \‡н`Ї)а`ФЯxРЃiё^UˆвЌ`тр`S”\r^†М9zцpšѓхnОTs­eПж'g@ˆЎ9ё‡hfШ.ч)DxVЁ9_ёw‰ъп5~;?%ФБtђ*жPСЛ^гЩyэyš7ьšъАF@є-Жoƒ ФOТЋ9RЎBЇ›HvзїєЯррIў[ыAЭ`У™ ІтQЏИwЯqсэ,?b#|BЋw ‘нв[хKађˆMХaё[AќuMќŒ™шc<ЊІяР;№4РXšїХўJ­Хџ} §ѓ­ЈWX*sй:вœЯ'шb^Ш–§Š‘Ыв яюёw‹тчaE'р‘щБ5яˆAгBH-œQMˆџЎsфŠЋ":ЁУA2ЭРB ژб4ƒЬ9зCќп)ђФуue/ЄFРРЖBЇФЯK%jˆŸc0Ц‚Vк”>ЗрЅ˜#ІV ’СЯ\KпБбc8ˆт•:Дps<‹BэŒН2NЧeВZ–jг † Н5p+нџњлiАкƒ0|ˆ8й‡h5Ђ.„п‰qЇ”0$)Б)E_ЫкЩсDp vЫ(SџХxœІй@STщбRLЛZ} @-ЬЇѕ\‡c.j\>чДЎггoю4_јЭmKєvДУj0-Ю№Т! ,*5@фњгzЪrф€QЋ0 @›3яB2Ъ4‘™-z€‰Ѕ~Чп4eЪ=2lД9!б •СД:Г ‹P<фLTИнтџMЈ4‹ДŠюX…Ы`Zљ™O!*p~TЈnёнLЇЭD,­Ўр2€HћNфš;њЗ˜ГрrЭAyl 8†UуF/ DoЬЙз uћжjYLЦC .Žъ9:ˆпaOBё•t:€ЈлД<Н~™ЬЎYЂZЎ–сL 6.БNДВ& РЩhZУ€СД6#О№8э‘гб™5ОXќ'šƒ2”)іщ‹?уsš0@šшбРq‰Ш$dŠ0Z   ~Ї=•˜„r—q­r0€rИяIГGоаg† ‹џ|jм5<5š1`0-ЮŠ/L&чlxє—!шNkžGЬФŒІI  @“гтTсЋ;P(dƒЛPˆЯoZўшДУvašЇec6ƒшaŒФйxNšІЋф Qњ‹іS‚s“PЋ0 @/3я4ЯChА~2нFЫљz=0C/Ы"R8 )•Кбчћ‘!ИL7оЇн‰ъЦ†јЏдњѓ9q*м† AЈ.%ьЎќ{ˆl™зЉ€'у‘!`РYЊ_ъH^IЧj@6wЊАъљ@ИџœЋћХџЦЗZСРџeеьяд)IENDЎB`‚ic09Гд‰PNG  IHDRєxдњ№iCCPICC Profile8UнoлT?‰o\Є? БŽ‹ЏUS[Й­ЦI“ЅщBЙЭи*ЄЩunSз6ЖгmUŸіo ј€ВxB ФіВэДISAе$Є=tк@h“і‚ЊpЎЏSЛ]ЦИ‘Џ9чw>яб5@ЧWšу˜I`ођ]5Ÿ‘Ÿ˜–;V! ЯA'є@ЇІ{NК\..Ц…GжУ_!Со7кыџsuVЉЇ$žBlW=}ёi€”Љ;Ў оFљ№)пAмё<т.&ˆXax–у,У38Sъ(b–‹ЄзЕ*т%Ф§31љl ѓ‚Е#O-ъКЬzQvэšaвXКOPџЯ5o6Zёzёщђц&тЛе^wЧдЎkЙIФ/#Оцј&ёНЦ\%x/@ђ™š{ЄТљЩ7ыSя о‰Иjј…ЉPОhЭ”&ИmryЮ>Њ†œkК7Š=ƒпЊгB‘ч#@•fsЌ_ˆ{ыБаП0ю-LцZ~ыЃ%юGpпгЦЫˆ{шкъЯYXІf^хў…+Ž_sж-ГTф>‰DН Ц@юзЇЦИ-9ру!r[2]3ŽBў’cГˆЙ‘ѓnC­„œš›Ыs?ф>Е*ЁЯдЎЊ–eНD|Ž%4 `У ю:XА2Ј‡ ОpQSL”PдR”‰Їaeэyх€УqԘ Ќз™5FiЯсю„›t…ьЧч )’Cd˜Œ€Lо$o‘У$‹вrpгЖ‹Яbнйєѓ>4а+уƒЬЙžF_яЌ{ваЏєїkћ‘œi+ŸxРУĘёњЏ.§А+ђБB.М{ГыъL<ЉПЉлЉѕд юЋЉЕˆ‘њ=Е†ПUHcnfб<>F‡Ы ^Уe||аpџyvŒЗ%bЭ:зiX'шЅ‡%8лI•п”?•х хМrw[—лvIјTјVјQјNј^јdс’pYјIИ"|#\Œеучcѓьƒz[е2MЛ^S0ЅŒД[zIЪJ/HЏHХШŸд- Icвдьо<ЗxМx-œРНееіБ8Џ‚ZNxA‡-8ГmўCkвK†HaлдГYn1Фœ˜г ‹{ХqHgИ•ŸИu#ИчЖLў˜ hŒЏs ˜:6ЋЬz!аy@}zкgэЈэœqйК/яS”зх4~ЊЈ\АєС~Y3M9PyВK=ъ.аъ Ая ПЂЈСї-Бѓz$ѓп8єоY7"йtрk ћЕHж‡wтГŸ\8 7м…№ЮO$~№jћїё]М›n5›№Оъј`ууfѓŸхfsуKєПpЩќYqxЕс†@IDATxь]|TХж?[“Нїо{яНЊXAEQE)‚>пgУі|ЯŠ`ЁйшXА‹bЁ#"R”*Нїdыї?7Y В›НЛ{Ы™a“н{gЮќgіž3ЇZHš DGд'Ÿm Y-љШяЬІъеBdЕy=ЗбЫЗ|hЊЉЫd €o“4A@ˆ>[KВ;„љЋ.„(›{kІZ—в‘ `bD0ётЫд#Œ€ЯлKOџђЃїг™†MuDxѕЄ{AР№ˆ`ј%– Цq• +Е‡К:&УvPП—ШBЕ(ЁpУЮQ&&D ЂД c2ќў6dБлПЪыЮš›#‘ьдBхžЅ;AРtˆ`К%— GЋП+ьџ№Zч?еёЖи АДSН_щP0"˜lСeКQ@`ќgљ0JQџGkЬ*ъ@уŒав­ ` D0Х2Ы$ЃŠ@ВЋ1Те*“пеaM3X:Ў•)ѕDmгЬY&*D"Њtir,ОЮds:ФўЁ}Р~vgљЈe„FnS €)–Y&5,`u/bouiD€н+,"8€t- ПФ2СЈ"АЮY ~Ш'@Dqg?ђЗЅ‘Г’":Žt. МИ2ЕX ръHgЂЈџ#Œ=›,–вфHlс‘Є{AРАˆ`иЅ•‰Х?ѕŒЩИf”GœƒЌоVf›КЬWP дBRњF-.ЉФЇ{E§•ЭрSЂ,:Ee,D0 "pQeJ1B ЮнšЌ6Щў-јг§,šгШХЃ5ЄŒ# ŒДš2—X#а ij%ћ_дV§ЌХ(Ск$jCЪ@‚€Р@‹)S‰!œ§Яч—ьб\ХРi…Уeыh+c FA@ЃЌЄЬ#ЖœOmBV‹dџ‹і*АРoщDуЧЫГ,киЫxКG@О4К_B™€&АZ:“=Ю.сQ^ Хап<ЪFydNа="ш~ e1G Ÿdџ‹йАРf+HЉžц1ЃAtŠ€]Їt й‚€v(o­NфCі?%;vш2%,ФYШыmƒщ~lŠ)Ы$• €J@J7&FРъсьљD§Ѓ=РyќўЮ4lЊ#FША‚€.@—Ы&Dk kmбc2jИ№’…jRBс*&›ЙLW Т‚On6=Ѓц–їiДщЁˆŠ€#‘ьж1ЃAtˆ€:\4!YC8,m•d4>иЂёO~b„U˜§ОікBŠ  yDаќ šFРbя*йџ4АBь€iБt ё?Цk€!Aа"шb™„HM"0nf"Ž=вНџ5IЁyˆђЃ0Х_‰Ю­cžIЫL№ <ќфn3#рM@zK%ЈžЭŒ‚6цЮ~vgй,т *t€€:X$!QЃиzцькYдaВX;j‡ЁDа6"h{}„:­"0~<'бъEщ%iЕJЅЙшb??ЕЁG>-`Ў‰ЫlМ! @оp“ЛЬŽ@J-dџЃ†"hh#АРBЅЩыkЈ!Њ„A@Гˆ йЅТ4€ЊfЩўЇ­%bР‡l€оVк"LЈД‰€к\ЁJѓXzjžD3Ше­ўN˜:Є ‚@NШ—$'tф3A +^P žџ›Шj/.YУї,8гјНЧ(ЭRош<†”Ша‚€ц €ц—Hд~KВй‹IёЭ­ ‚€ЌХ(С‡Mi‚€ "ф„Ž|&d…€плЇhߘйHгŠ€гJDH GxфCAр Ц}›HVk7ёўП-§Љ„fZ:бјёђ|гвК-šC@О š[!Hлœn њ*‰э_УЋФŽ€j@ЇЋ”г0•Bš sDˆљњBРк‰ь’§OгkЦfЋ­ йтškšN!Nˆ1œЭLš ƒgџЛря­ЈџХќ b1К‹cwZШуc?€bD„ +hбh~‰„@Э pЊn ј§5ћПfV${BМ^˜`иT$’&Y! @VЈШ{‚@V8§$ћ_VРh№=Ѕ<АЅхЯ_Uƒд I‚€&@Ы Dш?ŠџHг,иьљЩт?€XЎиЈЙ%c9МŒ3"фŒЖ>=З%=ќёbН№& qŠђвŒљЈ4дџm‰UЫвє€Х†T Оіњ ж`T>Д =ВшeŠ_Kу>|Ш`Г3ЬtDаЫRŽšпœьŽљфАї%;-ЂфК‹iь‚vz!_џtІЕ%Л­ЈdџгбJry`Вv ё?Цыˆj}“њШЂъ4nсЋdЕЌBВЌ‡1™ŠdБНDcчжїФŒIНzXзq`ўћВл+‘+•)ЖЭy-йЌKhьТїhм‚:z˜†ЎiДкЛJі?­ тtі”|?"НtЃдуŸy*c!' …YУ‘Хъx•њpTЄЩўCC@Š…†WєЏfцOЬќm•ШуО|| –ЯЦеO=Ї š~BТєвЭ.ПHў oцЇјтПУІ\U"ТF3Киё§pЛGаk§'Gw`“Œ6vQ-Вв§`ќw€ёU> ^W6.в„'žSciТ­“ЎќXўŽ "ФїрFeцoБ.уЉxѓЯмЙlHщрqэ‡z9пЅџ]{:ѓ%ђ{Œ›пJ—ЅdБиХŽБИею„:Ÿ^Л§іX oи1YыhБмљ РГЇHЖŒ?3щB€BєXzэЖ72$ПЧbƒ{юЃ>4ЇNї reў™{ВТщ‰Пd^Яfђљ^!Oв‡єFŸДЬ—Шяy@`мМЇЩ‘0žмeа‹э-V||ž}фђдЇ7ž‹-1}мќz№Ћ™м†с цi‹˜џЎшebN…p9-љs\Х 3ЭVў3Шyс[=ЏїхЫ_!!Рйџќ–^Ђњ 5э\ЬiЩZ‚tCэЅCJZP6ў)8`,уПЇџТŠ?+uNгKП>Жзhьм‘9]*ŸE"qh#pЈŸB;љ_9;оА0`Гw„ №9œ‘тKpх…ђwЎœ­Yj•њ"фŠ”6/`Рч€КЅ6 д8UЭoˆ0ОщРož%Уё](”'ЦŸyš—„ЧkєаМ3$ПGЂ‹wЮЃ1ѓwи? IэŸSЌ Ајmиo†фў#$јЗ‰уsЅ€еŽьŽ|bћ2Э]Љ0K'ЭбЅe‚Ц.lŒƒУЛ(}Пѓ^œј“Тfќ™чlі "d&КП‹@tёЮ~4fўvЖљл*фш№—}9ˆ№xŽ“п;…ќюЩєњРУ9п$ŸвCѓ#§oпˆЌ‰РиюьѓЃW}šrзБш ЊгQЦ-рrз|*П>HљCЖё‡:mіб№“ІЫ1єњ€ЗBН]ЎТУOЛ#Эќ3SЩУєˆнx(ОŽАœт• LП™Uš,ЮM8CFЙLШЏКB€…_?a}}р}ў­ЎhБJЂ1+лфoТA$1тŒ?ѓМށ"dF%*П‹ *0ч0ШCѓ[СЖ†PПќЏš'шАкЊ#!ƒq?РQ№VdK“ваWbEЖЖXЩўw.:{ƒ§N~жqy`i™рУЧИg“нњџ@ЄKTUеŸyЌь~ПdpМGH‹"D ш,‡=Ї5N—ь№W>ъ*f%bЮ‚V[S ѓщмёЏhЬм.Yвiж7-жюРG9>šУЬ›їЛŸ:K ŒН 5lќsёнџYEяуЯ—Юј9j"- XmEˆўbˆж—ФЬпю„УŸ-њЬџrJ№Ж›|^œнAн§MИmуU—™щ §ЯЗщЋH€žеЬ~яђXвЄўћ 0ЃМMaЬќЖ8tpJ^дqФ+aУJЈdоКS§.Хрїл7š&ніЖъ§K‡—! €ЫрˆвšbўЯ6|їаыwlDl{Œ оQ€єЁљ1?”рЕє†Œў>ZRѕ ~˜МюQ4ё‚С,ФkDА<_>v˜П•“ќ€љ_Qе/ЯFщF+ќxЇx=ЋЩч'ЈЯ№†~žЬєЧЭk„d7ЬЃ/, ‘ѕи љ‹вЮŠў0ЬT|Оѓјo3жџKђњ—R!ћzІ?Bctп,P›Г ;пз^ат9tsтЯњ€рѓŽDž€)й]&яч ђ†[hw1ѓЗ€љ[uШќ3Я4НєАМNЯџшѕ;WfўXѓПœUк—žX‹›№€l„“~~…й3уІЏљхS@f*ЌнВ@#рIKУ)s+ъз‹HœoЩї+Ннџ‚jcEЃ#hšˆ•8Јњ§=qтЗыђФŸVМ^аCbNЃDШЄМН/@оp ўЎ1Гсuы˜ЏЫ“VГ dєКSёKя+4ёіmY]Њ‰їЦЮ+b>нР№ћ^ф\@3V…ЪI_Ыs"x?ЊhК]Ќњљ ћф;ь‘ЯШqa-Н|/ДmуЧ[щ\Эd˜ёїРоЖŠёg†= ј|#Ё45ѓGђ{о яих~'‡мијфo+Ћ;ЕnГ їi0гwШщD/ <лmQљ|мL$3qЖC“л цХƒб^†„щG}§Т{ZбАff:Пo;іЯз0|FжCkiТИMLЎп•sїD•ЃQlЛc[ Ыј3<ю‘№ ! 36yќ]€<—ыmЃцДC™љ†dў™'Я_JЮ!рvэЧл)ећMОуtцKЂіћ˜ЙuёРОOю~8еУ‰(щ‹3_д–Р8Бf€M, АfРПвС'H(ќ9|`6`žбїщˆвдMkѕТ­VWг0ўЬ›* ј L! 34yљ]€М –л=faў™qЈQНюЭp|™МЧаЃг2_‘пљДяuv —г˜v‡уSЁKЮ|1xFGdŽвilИd&P„d˜жaЭ…FщkдбˆМж‹гtŸ?мв{ѕwуǘтФŸнЊ„ЏћADsLЫю2y?wDШЃаЎ0#ѓЯŒkвmь?“пњ?šаџ›ЬЋіћИйКз§н‰“Z%oъ$ќЊa,]€" `sѓКТd№%љмs)щЏ•єЬ3ˆЕSБѕяЄr5Џ6k$„лЮp"†FK‡с|*BrЉ+E№У1а!рv.к/"„†WЮW…кŸL іЯRА­X№€#ћ§‹Щчz‘& \—лmA}>vncђлсIиьqЅвНј9t/њй ш•‹Œ‹kНвMœ0b5|> ‡ћsz5Ь’У#'Ц‘ЅиЕјСЦoщ€ШС•=~хNbB€пћM0§Ъхям wŒ‚ЛB9љ;чуD`<‡ПрИњ*E€оуNЦ7u&ž–ЏA#Аѓъ sy‡НOзd›чp\йFФьуШс{вX#pЩD€ЧЉЧНLi>љ=3hт@$а Ёќ*ŽЌЇћbЃ,ЏЅН"\0уSVі Ь~йЃ”э'"d Mpžm‹Ё~Ж2†ѓі†l/х/iКЃр ф˜LЩоЩ4mрсlЏ|Р'!kБоxr‰а.џ„9 у@$ЏC  №КO‚q/"ŸeœЫ‘ЪЛпЇЂёзУБoЬYœ3$§Ф/Œ?Gи.}ШљšŸg|оЙєОќ’+"ф Q.Œž cОИ`ў,­KЫХЋšMЎн8ОO {ЪŒ,уЌљX(юF<„s_х$$ЖЯla•4ˆ@@шѕx’ЩтћЖъI][}Ѕ-H ЏїF„ЋŽФсЁ•0ўЫа эEРB€W„€ АЎКT˜џUѕЦ?ыЁ.}‰ –ў˜žщьЁё œtжг}<œ„‚S.в43˜з‚ТOСїмХ?Љ`>>ё3уo!Œ_Ѕ! d EВŒцoч8џвrђЯ#ˆlPђяћОiрch<ЯNЪ‰_lŸyUnг$AРЦi†bз„ж0CеЏIŠѕIT@№ћяЇ ЗПЋЯIDjђ‚ѕшљPћ“0џМ`wе=и‚ьхЬ_\ЮЧяe‡jёшП &yУ№>ga@q`•}‘EUž%"шЙŸ&н)B@ ‹8Y~ЄдзЖГЭ_NўY$o ‚€ cв5.ј^мOМcj4;<ФQiA#0zV'$хц4`rЁ 1@@ЉюiqЂЫ=wp (аХ"ЛLЬќmЮЙbѓ0ЙN".8 LІбГDШb)Ф(WНЅ0ѕГ•‡ПЋа‘7A@а.ьlщѓЛр`t НЏ]BЃO™Йa>zNў–yP§—RВЯхvН|.‚€  -!Р!Р'B@І• W§:r6WоbЕПœќЏGоA@GpўŸз…Œћh’hxхDШnџІ3Vћ—LOя+Pe•М/‚€.hќос4ёЮtAs‰Ў–И—˜ПЬ_вћf‘М'‚€.И$јL/HР•;јС™(:ЃФљ ѓПљ[Н#РI˜ЌV'’MЅQsяжћtТЁ_4™б{pnrXaѓ—“fXфwA@ ‡Р%Ÿя0š4p†сцФ„D€4Ьп"Ь?‡М ‚€ `xX№ћв`ъnF!@LМУGЭъ gјЕПсПё2AA@2№ЁіˆХ‡ъЃSiєœЛЬ†‹h˜љ[s‘-Њ„8ќ™mћЫ|A@M€п;”^Пs–Y01З0Ьп'Ьп,›]ц)‚@Ж\BРэІЬk3Їљ…љgћeA@0s€…Іг˜йЭ0us Ьќ-Ж9Аљ‹кп Л\ц(‚@0\ьгhЄё…ѓ™цЯоўЖтbѓц!з‚€ `2s€?Y`‡":`ЖQgo. РЈ™н•P?‹0Ѓnh™— a# h(žЌіщˆЛ3ьў4кy4Ьќ­vЈ§…љkt/ Y‚€ h жјќ)( 4”оИcŽЖˆ ŸshFЮъ!Ь?ќЭ"=‚€ `*X`Е$рg:œy‡бцn| €Тќс№gГ›ПбЖЏЬG(  h|аx  ИЫ0šc Ьќmьэ/Ь? _Bу"ќ–{iвэs0Qуš˜бщ…љa—ЪA@ˆ5Š9Рš@џ;4jі€X“ЃЦјЦ˜љ;œГч/j5v‰є!‚€ ‡@і `!Р !`†ю…у™„љЫзTA ’Ь^Bo˜ЩЁ"йЗБ€бszB2““$wŒє-‚€ ( t‘ќо{iт@] ЦFЮь›џ,QћЫ7SA *(šo y§ашO0†№рНЩюdц_TB§ЂВэeA@F€…Пя"љ`˜tЧ|=Ђ`дŒkЩbџ‹ Ь_O;OhAР(\МУє”'@пQ§њAєВŽЦщ_˜ПQОH2A@є†Gиљˆ,Зш‰t} zЩoy“<€зюBЋ ‚€QА€џx\iфЕLаг”є-0вGœ_Я§йэzТ]hA@0 6ќМ‹щэ;–ыiJњіg-Рсќ—†@=a/Д ‚€ шц;^їEr{^&кЏЇщƒcО9p24}„0@=a/Д ‚€ шц;>пš•pЅЬ€nf(R3ж“т@PфUA l8яПлНl–ЩaїуŒyLž6ќ,jМˆЈ]%eˆё^сA@rC@)ќуLЏ<œлЅZџм˜Ѓ~оГ5VЇh”&‚€ „‹Ћў]Ў]dёМnWZИпИРŒAЉdAŠ`ŸW iaЇ ‚€ ш.њуїOЂ7†зћT˜~cњVІшЮЏщT•oЩw т5яЪЋ ‚€ |њїЄn#‡fh7jїjуjѓgžё!N“ A`ьЉjw‹ e‚€ Ў8oЁзhт 3˜2уsХ7­$Џw‘Є6Ъ–•y‚€ e8хЏЧЕМљцGyфˆglР%шl/‘ле“ ИєЎќ"%іџёk цвk3ц7Жs^•_ё_рГєOфAР\pD™Яџ*Ннџ‚‘&nžЏѕШ&’=~”јiћšp.ІюcЏќц E“­ЬЈ-фД[Љ@œƒvJŒГS‚УFё;9mrиЌфИ"UЖ§xаŸ?i^ЅЙН”тіаE——ЮЇЙщBš‡в<оєёxL–Ёљ•2ЦМєjТ%‘)Ž$ѓЄ­ІЂў.є œЫ дLЂРŠЙќ№ *%…‚ ДƒЭ2fЖ`ЮёёN*U0JцЇRI TП—/”J'хУпёTяЮч„`Ѓ80{˜щлСєэ`жVќd%ѕ+|џЅ >rc, ў ъІЃчS•Ÿ#чSшРщ‹tј\ У{G№zшьEEP@]t • !§љ_аиУ~ŸкД—Цќy!ВzшlB їO‘Уљ j„p“\*Ф0р8œфяhQ•ЦtЊ­0z>сЧуdыцpZ‚s)n:!рЏcчiы‘3ДујyњћдкuтOAŽ7“Ц€5цzђФz™dќМ"`чгПыGrЅіЄiУ Ч8Ьѕ5Мof ВљWУ!АŠhђњћЂ†˜+З5JбS}RWМъЉ]€`p"йE;ŽЅѕћOбЦƒЇiысГЮQЊ ЯR ипІ‰K~zš аjpА/­V7Ймзг”{О6тdЭ%№ ŽјрArФНA^У sFмŸцœыуЁzЏRЊ §ЛG=КЋEŠƒJпэL l2XПя-п}”6рѕУgшk иtР&˜,Є 1G€OџnїWTlзuJHyЬ RŸѓ #'&‘ЏаrВ;HЁ ѕ7”є`€pРK‚Xл4Ўk]EнNZП7ѓ=x6…Vю:FKџ:Lыў>AлЁ!№УQ1А0ˆJањd„>у РœбbMУAБН5ш'уLьђ™˜Oрљ?№оВХЭ"ŸGŽ—яљ+VАКЇпО Ъг“НPГ ХbEILЧ=MРж#gщЋ-щчG`:8I)Ќр&Т@:ђфPNџiг›їм’Й1›9€‘_Х‘їиwpl/ІcnlнЬŠео`ўѕ+Ѕ'Рјon\&qs~-Џ\3ŽBиmР oЖЄЕаЄ\„0Рј КAš 8ЌжяO%З3MД&"chЄSѓ>iќ 7Yэ‹ЖCa<l3!у*2ьќ%Т7ЊszАc-JŠЧV”–%žИ šO7юЃЯ7яЇѕN‘Я…:_Ќ`Пi‚€Zи0ХЙfг[wTЋK­іc ЯЂМ лЛсnJ.иŒŽšJьr^њ{М €А>BјЖЊJяlG76Ќ`'ПМРЬ=VœњKˆЇŽеK*с=k—QB#97СЙф48Т„HˆL‡r Ъщпsћi(­[|$ЋKŒєžЙEч‘яЕ!П})TŠё’"иHлZЃsQ’фЕ­Q’žънzд*ЃQBѕC'"њ&‚йыvгŠнЧтЮƒЂаЯjRхєŸ6оКg˜жH‹=цбяЯ gќ]’"8лKњTШАѓW.‘DџBXп=-Ћi"‰бV‡}ю›Зš~GhЁ’[Рh”љDхєя;M^W[šUАЬЁ““АџПЕl;Нёгv:qЬŸ&IEˆЩGY"`СОё{!4М5MВ;Ыk јІh”EѕМNЫ­(TZRp—GsJъўКхжзЛ>ѕk\ Žъ"gЋН\ЌшУѕ{щK6гЄVЂPёPš 'lмюwЭФќ'y2vЫˆž€kіsR((ˆМ†„Ћœсн_a}#QА‡ % œHšъ,пy”ž§j#}П§ЧkЇ3еG‘Mƒ€Ьпч;HKšzЯAгЬ‘9Ак6яTl€{ Њh ШkP€ёлvК­y5zЌWCЊўвдGрOzщЛ?hЮ/Л) …†uПœaдкl=ruJЏwŠй˜?/Гh2oіћпь€oI€Ь Шяй"жзБщжзЋNйl/•ђŽлљЇ,џ“&ўИ•Ž‹?я@ЪW#РЇЏwЅЙ[гЛC^}Бп @цѕMѓЭ"‹{ 5! 30ђћeddёЋXМ=вЃ> n]иGšКpіПљыїаџОн$v~uЁ•о№щпч~гŒЬŸ! @`#^GМ{; Э–BA@фѕ2ж—˜ЯЉ0§GКзЇђ…%Ќя2|TњcЊ>їѕFZВѕ@zІn)ЌВвЭ%Ќ8џњм’гг†& …'ЉљšhЎ\ѓт>ЂюƒаA ] މџі еЌЭBНTPд§­Ћ71‘›њŽучшХ%аь_veВѓGnМ{fЧN9"х‘Ў?L}нЌЬŸзNЖwV;xФћНШfћŒ|~)”>fzуљбZT)AуКжЅ›UD!:љкЈНvўI?mЃcЇ9žЊйXЦѓCрsЦлЩХ‚ћzАЊXšqрЄ?^ї&8}ЗЇ7ž3ЮФB›‰<ЩВТЫяЗ E№ЇHдWДYd‚ї˜ёУЭezЧ‚ёпкДВиљ#Аь^`МрЗНєТ7щ}'гCњbЉючu‡GдixўКЦДџєEz|ёЏДїиљи %РпД]*Ёю{ш­С3L‹&.&€ЌVпbёг}яџ9Ку’ )‚ГЩ яe$ђЉˆМ§ЃQІїоЖ5Ј€”щШbЏиu”žG<џЗ[zЭŽ•GJў=кMqьєRЙbшбž h(ж=ŽГ ЂЕ­\œћl=Э…YBЉK%кИq<хєяZGЯB#N/”9‰ 'Дюя}hю‘С9dЯ˜ч žП0JЮk_‹FuЎMe$oD—ѓіПќ§4sЭNJKЭˆчх“ыnуђЬ-Њб}RUW6–fЌнIКžК№Аrх…ђЗіАи|ШѕrMdz@49mWŸџehn@Ё BЂШ (–СniYUIфг laOH›фŸО˜F“9žџ‡­Аѓƒ‰ђ ;pъЩкžЦ8с?w]тК й5vGИЇU5дt(Nc­Ѕ%›і‹I ;АДќО n]їJ*‘ИXЫdF‹ЖXЪнбšcxумїюЋ№+Z€№`дфнЬ№гЊZIzМwКЖ^yM’ЉwЂ8žбo{шПпlЂM;Ќ)ЮY4)ЦРПƒM=Ё˜yв 0>ХW•0ех‘TФКй `wЋ~]7в”!_ш†ь*€мРѕy&’; ЙьR((7Ќєђy†КПlQNфS†ЕЋ)~ZЛеЛ!oџяєЭ–+ьќ/зnйЩЧљрдљЬЕ)/кі `AЋЪ%hд‚5ДћШйиj2rД\  `ЛsЙ~ “ОDв @0;aФ{‘=ю?Ђ,_ƒА.BЬю„КџЉ>ЈJі^Я@фэDѓ‡OЂМЄ)ˆьFИянћQ(шmЉ,`Кх$uy cmХЩЏ0RљJSЌєxўMДљящЇсXлљйЧtuт˜~}m‘Ь)’ыŒњp ?›’ю ЩСЄярPPK*4Инhъа•Спhќ+EvGМ™Ÿќљ–!^Ј1ъ{—\KјдяіQУЪХше›[PзšЅcIaЧf;џѓ_џN_§q@qЊDMœњc8]eнНTОx§_Џ4ЄM rrfС(ДЕ{грY+hы~Є–e„CцЊ›!”гк|š<фvна%Bcљ5вUfјЛ§a˜G~otž&*’nКЎ –Е!aЫœњŸAFЗТљтLAЄ'МыФyzљЛЭ4cлљ]ъўHšKџPїлСxяjU1§ЈrбќЙм ўЧћЪј^пmFЈ ъJ<Њ§ў‹ˆ§ы@“‡ЎхV3\+@(ЋмoМ“ŠTјšŽ.b И(_‹/ЮшіjПдПIх(nќс;џŠ€ŸSфjРЮЯъ~}MЊ–@ п&дЛnі1§бXЁѓp|ф€SЬ5"б˜АVЧАУмчvНOSж*‰БЄ п\iA#Аѕg/5яы"ЋэI 4jбЛ]{pьXЇ,-к™:V/НБM0лљСл}NЗsVэ  ЏцЏXR<=ŽАО)ЗЗЅКЅ Х|58T№:TŽtaOЎмq$žX;CЦ•(РЇŸ…~|CщзЯŽEyt] 'yB]&ПЅOl œЁl’ыйю‹Sрp$uyљццT ^ПвTC`ЭюуємWвэќŒЕTл1§75ЏBOƒљзGˆŸ–š џПз7Ѕтљушџ>љ5НВ`Ќ#ЕPЄiсœџžдY4хо?"=”^ћ ”•:Е b}o'РP@‹ТЕ`HvвТ‘пУ|њїКOЯ:1ђƒщw‚]Лёу­tдўъG'H‰р`A‹Тu8ѕ;mšpkkбЁV4ЧgРьЇ!oџ„Я2УЮЏ…S?д§љсаy_їZє/ФєЯЏnLЄVwPыъ”ZЉЁ0ŸœEM%iPЄ“~!dСКэqMЇЉCvй# @іи\ўЩсђaяь#ЇџЫa‰щ_8 :pцЏо*АџЃ {щПШuПуљљДъаРcТ‡О8јwEСžчњ6Ѕж(рЃЗжЏI%Jpкшюї—бЉ Љ"DrY%dё#GГДœ(€œа |жqМj•џЅ{Щщ?Š^с№ї$М<{m Ѓ8†§9dДћ’+нБW}”bчsD.Cн_БDњ?˜wЗЎї%:nпl=HwОїлНь\—Ц}DѕЉ[žоЛЋнѕоЯФ‰ƒDЦ†ЮъVЏf,gђЛюХЧOgu‰йп @N;@Б§—ћЅ€{Шщ?' ЂјN†е‘шх—GћR!)ъ2№ПТБя8јБрч8z-иљy1§З4­LO#usнвкŠщшn˜ѓЫ.:s9Ѕ@ј! р‚Й4] p€ќž4mјс`n1г5ЦЏеXХšх;"ggёќWL•њ€zјЎ–е„љ‡чОSЩєкї›•џE”ьU2јiљg8љеMќi8sовД’щђ8мбЂ*OsгШЙЋШУxhС тўвьх>Ug9ЄdМ 4ОЈY:cD˜8f<Ч§'š€т?uˆ7‘Ди#€gcbBНq[k*š(Х}‚YЖѓO^ЖёчЫi МћньФЪN~Zа§сФ[ СI!žџ;лQГJХ4AV0ИЊ}MГŠ˜;œм‡u%„MэLоŸп_‰]7‡6|ŽZЭвˆ €Ф•Џ‡+ДС)Љ‡œўЏ&†У+МE­вTЙXє+МХpжyњГћ;џКнGгUЫˆAзDЫˆщяVП<=˜ў–:ŒщŽOєnDGЯЅа[ШМЈ‰TЫ‘˜d,њфœн^ ОЗ`јiБ AЋcАэЪјGB$ [€b№ŸкU- Г5NАвВE€эќџљњwњ v~пevў{јeЈћ+•HЂЧPЊwbњэ:щЯvђ№ќ_ОЉ<“LŸўК[„€<`˜§-lZЁЁдяеYДpœh2€ Ћsп;Б[Ў!шћ |Ё|#Џ\\+нИџЂrњП˜џ †R ЮЈиџy%8qЬ8‹Н •џ…dd’ œјЕpЎAЉ^Ž4шпЊbњ›PэR…bМyє9|Я:хш…šгшyЋ08h!rCŸPfP/‡н‘~]wр ‚ћЮsMy­0~^0ўЄќqє@ЇКє0тњ‹ˆЉF•]šџŽ*Х @sm%T€”›Нb\‹ооSЁG]w!@`љю~?щ"‡HЕП Z{ЕаЙ7јŒ9ƒіа‡khбК]алAqgзHLЦzєhXA‰щo^ЉИж6ŽnщYЖу0=aячэHФk.&ѕжвъП›†M…єРІѕ АЅм!6‘Иџ {ХУяlŠ‹ŽЃ†zЩ #.ВфЌо} Йт—б–}'‰”d>рJLП—*—,DO\г˜юj]љ4@Wd—"*НsyцПйHЌњ‹\.8Ўi!esTfЅA3€Ѕ*6ХˆkЂ4Њ&‡ А,>џНŠ‘йcj0€†і^С[XОушYЊWЦ<…b>кА‡†ЃPЬЩѓШ]r)“_ŒЭ PKЧ94Јcmz йыЪNдо~б!EЩ`ія <ѓKпnDyцѓ0ёАІ'рІу5з!žй’ЬТЋн Ў1Еи]йbeІOЎƒИаžиІ˜Ўn' ЦГх№iн’*сЌоAпљ‘N^Hе†ЪŸcњБ­j”І/GѕЂЩк ѓuQГЙўЛmЉЧ„ЏhЬмtш4bџYи‡зlаRсэt-Р 4dzzгmЂрЅѓлn#G\ §гњ>ігњ}'ЧУgЃБеЭ"ь§Г—Q*œы”т=Б\>|‚ё—ТIџсž GПx-9 Ц›0Чў ­Ољц ‘“ЧЭъ~јv„9/ЭпЮQ6{ВxК€жEšЇ7BŠ0ljAˆкЗJш_„v˜šнТšCрN"юНXўx5{жT_œмчОй+(еЅцЦoSК6ўё(з[KbњUй+ьЯ2хчmєквЭtьgћуцЏ ЖСuЉжЂ(РoЧѕІФ@жЎdГе8лрО61Н žа‡Pз~х.TЗ3hsСЯсбO~Ё3lѓeв#іюGBŸц(ОДxDšwoaў*эЙХїR—WПЄ/\CЧЮb9{ЃС5Z*AЇn7~6љZ:аНг*ЋлБ~z €Я?PqДQ6ƒ~ЮД”тDКp§КОaECBАqџIњљ/Aв#%nДgЩЊQЏŸjРбђЁюѕщž65Hд§ъ,ТІЇшй/7аЧыw“_:ёГ‰EZє`!зю(FОД|jє ˆ§ˆцю™\БЕD§ћ4P“~Гe?§}ђU,jМŠrœт—ЃЂо2[кЁ ‡‡q›YЂ‰я1hs&-нBo"‰гYжьиёиЕл‡%šј†5–’XЬвЦŸNЯ<ƒ/^Xд‡}ГЙ‡Е9œ…р}6вA”€Њєфщ‹4sЭzёчFkœHЇqљЂДБџQЩєЧТ‚•J$б Ж5щоv5ЉL! ыSc_y!TЭ_З›žЧЉћфp`­ŽVŠ5Љ1A#єС‚/YZвў’u№ЫF˜R(s0Џ0lЊ@ѕCх?Мˆ.”Mѓk‘p†kоЧIЕ„С’ˆwа”;лбo-ЁC'9<^сМнaJAaЊaуž65щŽ–еЈd’Й,ErЏнsŒžўl=}Гy?†р—жQž5‘Ф=фОйєыpB•шыŽ{M'DрщђФц†&з5…шcјrВ MO@8{.…ђчsRЇeєDyPД–Х Мs­2Д6уCШ Ї8ˆ…ы$Ц|GQѓ{)BFTš{іњfєв--Љ#тњѓЧЩз ЈХЩхЂПсбџдт_iдќU8ѕŸ‚Њ~ж’!1дbќ1З|ОЊzз,кКаTšy5D7Р$AbџcќхЫы№№g›ъэЭЋQ дN7ZkRЁ}?іzх~ЇќД•ŽqrŽ %2€э›ŠŠЗЁ”r§ђХЈO§ дПYUЊ_Ж0ј’иЁек7œХя=hЅ^F п§ЧЮсT‰ЕКtъWkщ'"Є›šPбsЕбПЉДц|є[@…OЏ!›Ѓy‘|Cš>@т”~­j(!j6ŸВv?Gя,лN‹р=О П+ъ{ž/3№gfЯ?œ­пУЩ3)С ‚bдЖZI…ёГoA>Ї™eўШlsvJх kv"zƒŸЈЁi‘!Iz Л-гЦвєсBНUЯзѓv5_ЛwZ{и?ПЧФЪCг|fЦHяя ъЌ„ЋfRйLф4JџќчaтДБлŽœЁCЈШ'O>Щ'т„_<•+œЊ/HЭ*ЃFхŠТЁ/dœFЅЉŽР‡Nг _m ЈашeŸ IфЃ:ЦQыаСиуў‰ЪъjІhs,ў^du:E§ЕЏWФтCяЃ‹жRгŠХ ж6vZяТљтш†Ц•”Nњ]2Шi‘љРoUтѕrњŒи^ tЬ)йќєЦащГ>ЃБЪл J^УC =єЖэ-SmЏ3§мmТЃпЇмощоџњY(Ё4РјŽNІ{g.SRgs•соцвЛIёN*’G,pє€0џШ.3 ]Гзю /}FЯ"[ущ iАѓƒљ›SYАЃн;Gƒйу ‘Уп!кCЧr<ѓlн!Џтx˜ињв>†уUдџБмyjЂ97ЗЊNs†tІ8QХЊЎщћ[єгЯ|ОžО§c_zдАhZŒЗ'lˆ„ёИОЂwюЛЦx“ЫzFЦ:ŽЗS•R5ШF­ЩjычЈідЫ ДETџYoнП ;ьЈ iТ­Xrж‹KТD`ЧБГє "1fЌњ“вR0L<ћУDTУЗѓ3УчKMэ=Pљ#љ-kщЁЁтЁб˜ЭXOЩoІќЖц8сЗЧOw0ќ:(љX@ббq§g6уiE€—NO"О§йОЭ :I™V48}бEг—oЅ K6г‘@B&*Ѓ}ŒЧKДCРбa>в7вzВјО#­ІД ПгьG’cL ЊУы[шЗР†pОъ№„j'œ.`юСьљ”oeF€ŠL_UЬЄ3-#їь -шI”Џ•&„‚€ћч#›zў‹ѕД e™9["IE(чZ.ЬkЯЏ— Ш}јуВвЯфіЎЁ Gїъ=b@РАЉqˆЧ)*}Вр”яЏ‡x~9хчkўL2„€ёз7G {$|дп.щ!dVя> Цџ}Е ЯyоC‘Дѓ_RDтeП‚\~ЭмWЩqM,ћ83:б§зB<—ЕЩ^яYМё;љ=KБ.+)С§+Н1I:єеДПЅј”_№hU„эЕ&ПЕ я рљ”o“SОО6[TЉх‡)МЖюеˆ^ИЉ…xШG|} ЦЩ•^ћnНЗтOJMqEЦЮЯ šX+ЩЏ,ЌpTхЈ ЮпPQ…Д‰Wœ:ХЏє——ЮЅКшTr].юч|lЦфАC'ПвќbРŠ5PДiМ(РŸ–с@њ–o•>ДкЭg™гІ0ьШэZИ О€pw€[Жќ$х('Ж|Э74C ?ннЁ&MКНТц`л“&d pLuњВm ѓ?Ь58zD ІŠmЇ4Ž-гЗ TА -е/S˜ърЇvщТTщЋЙœuС„8…щ;Y(РиьМЪeю‚Унш# Lџrl;|š6•y№ЛŸясо™, ђ‰I­‡вE $аЫЗЖЁСmkЊ:]щLлl…њќеo7вЌ5;ШŠx~Vї‡ћєSLL^В##c—šehpЛšдЛ~՘™š bфџОў…ЃЖQjDА@"Mcdж(Ё†ои‡kБП‡†`5]ИА‰ŽK‰ес~rІГп›љЉ€Н Y}эРє{рHпЇќ‚ŠjФ‡ Ъ_ 9хчŒЁž> 0oœzŠЂ0Mэв…Ј6lžеKЄj%’ЈtСD*^ ^Љ=Џ|07і‰J…ŠўФ…:v>…іžИ TМлyє mEС›ПO^ Wим7ŸlТUеђ€htЉGЯоа\IЅЋМ!џЃч.вB_@IDAT[?nЅЗЗџдYЈЩЦцcЗЧCёАп_‡гўƒиKэЊ•RьїZ‘ЋŽšГ‚v@шQцЋЂ„†,Ш8м№3Mq&tЇс0Мќ€‹—!Т` •?ŽR“Ярс™ц7с ЂЦЗвоRШь{kg8РtС;rЪП+ЃќЉ< геŸUсид іЮ^ѕЪQЋ*%!Ф‡х}'Е}HТВЖЮUЛјч(„ѓфCEШы 'уфжМZiš4 h“€QЖc`)0љЬZ§НђЭFкqш”z^ѓа"йрдw}ЃJєpЯ†дКjЩРšzн‹яЭрїЂ! ˆ ЉЅЩž˜Ьк%даs|t=xчR˜ЪWн@я= oUѕZјР/'’#Љ1ŽhэpBыŽ#]S9хЋЗ@šю 'wvvъ І?*ѕ^ѕЪУЋuЕ#диk{УОєХЦПiЩ–Дх№)ђЅexAчХЙ ѓќ(Іѓј5MhtЗQБзFщ6_С)ю?p№[ѕLЋќ„ЫЋ ˜ЉOE…Плз*‹§в˜zж-ŸљSMўЮхЃя~їGњ|§юШ„6jrж"JЩ;pI;р† pСOа›ўDd&мsd§^ЈaшŸђї/O{+|Й:C‡пъ§ŠrЪ7аЦЫm*ŠоB=ъ—ЇGp ъЇЂE-;}ncg|ž ЦП‰[цЏл /ш§t№DFVё†ви$„ЮuЪб‹§ZSѓJХCЙ[ЎеПь9јZ „;UNОЌ-rћЈRЩ$zДOвОVXZ­hУu)o™М„–B(сб^ЦS E;vmEѕI%Ÿ„чЬч[ ј+Шщџ•&€Э'Дœ0lj>ФS7$‹З=(рИќfHDQ(н–/—RdUА4ƒ#РC/U.Uіѓ4 e5Mи=йwр“піаЌUб*ўЬБбA­HњМђУd1š€1нъ+ц‹ n•‹bŽРvјŠL@"Ÿ™PљЇтдЋŠŸg!ТхСэjгc8ѕы5„єр™dъіъД§BбCcОКB@–d6x\№žі†v`9ёаТЏЦ:џ‰шиJsnY ~П…Н]qЅ8х[8.П+^+Сc%tСьХc?gTј)Ÿ„М~ъ‡’Л/пв IL}YcЭЭФлвфŸЖ lы~JK ‘А6}4ЈXœЦЃ аMЊ(dMSШЩ@р№й‹ЪZП§уtLN5ЦЏјˆ .сЂ/bЏыAнŸлІјљЏCtЭы_R2GDY[—mђy˜(ыЩк з ž§ЇjШк4T5œ= /Ўnџ§^M€{vl”ЭЕvƒZЁ9дќE.;хЫ1џjЭ№TNNœЦїmNџюгXЇўм`_ ЇС‰пoІХПяM?Вњ3˜dХо Ÿ‚О+гЈ% fмаŽючЌжž‰Иїзqъпs$Уг=/> W’ЭkS<ТњFvЋGџ•aЄщ5J{ђг_шљOз‰)Р( šэ§kЭ` ˜ћ]mjв˜ю ЈLвb‡@*˜ѓТuЛшeЌчцНЧё€УƒN ?žR†­П!"B^Н­тзЛ™FfdvЂmћпOiЫ~Ўr!Эр;ТЅnІыqЇbЏяТ†_J>ЫZ цПь”Я_i‚#ц_^ђмлU {в3(5№Ÿ/зг2˜”ьPQ{ЉhЁDХ<МSЊZыUrЙР‰‹ыŠМ9АнйЊ†aцЮ)ˆU(F§šWƒіЫB›іŸ"7g ц4Щ‚ЎKFrЂ•лв\ЈЂœNFvУ|Ъa@вШD–сЄ?jюJПјкyv~ЖiГNСвЏ8|њщЦеhЮАnšMшьt‚НŽSpГхфs ;ЋfАƒЪušE@4š]š†PП‡ЏmB/#.оШm% Уќ{бZБ§PК=9G2…x)!_<ѕmX‘!>М3ђСГГЄДМ#РŒтї›шsјlИIžРАTmхCiшЇЎoNєj†ЈіЊRЋzgЧQZИйsбОgEP]}u(}­WфЉѓяк JчvдUТ“МУqнZVЇќШ^ИeaSS‚д№`зK[pЮ/;‘p?q(b)h"™1/sеђ=lƒ\іч!їс*zт“ЕДЕю}Ь“љФЏ6oVTў…hЦаЎtВWF;•жM{!wТЏ€UеЊharBCH@№Жј„™/ЦЉЖ8˜зOџКžъ ЌЉ™к:c?\I+`sЮS<9‹иF ѓIqз5ЌD§›WE‘˜вŠ/…™А vЎь“ё„&ŽуџЏžHј™ ^јpАp;e`'Ѕ8UАtё:і­шўъчъ XFЫРsРР‹ђд }ѓюŽє@чz!пj„’с№jХOјіwrAЂT ubЬh2",Ш"W‚'ъSП5Ešс81аЉф4xѕяЅщ?oЅ;рŒІ8їёi_эу~ЦтБЙ?УАЏ_A)шPџ›Нq€цЯ-Ђ]аˆРМЛСBƒE`охЯ4s0МЮ`R_щCёЈvfці˜гиЙ+h—S  8o7œf†Кe‹(š.ЕЫRГJ%LЅHУўZЛчЈЮЗ№з]Д—7'hJџ5"џЧпи‚GR'iџ 0`кї4oљ6‚уЪ?oЪoІB@S-w6“Х‰•OІп=м—кз(ЭEцz›Ы™З’џК3У&—b­€т8ˆ˜sœ@Ћ —@W ъXЃ <аKQйТ‰8ˆ…9†Ц–(Lуў“PяяЃOжяЁ-Нt#ŠBбЌАWЄЏМ&!šх$s’v9“мB#fќ$€Ыa1е_"˜jЙГ™,длЮсPвўAР †§Ÿ/жг ŸџJ|‚UMUšI`fX(5(W”:!Š ђЯ7*_Tq$ф-=5ЦыиЙ‹ФUљ–CЕЯЩ—ў‚% шьXР‰”šџJ Рќ‹'%аћШcqMƒŠW~*пў>N- рх§(Э”ˆюЧ”Ыžiвјђ'р”4ІGƒLoЪЏŒŸШŸълŒ”/FЮњ%‡ЯŸJ8'•0У &9•–mнOЫИd+<Д a=jСw „и вsd–„>с8}6ЃЪм†}'‰У*7""тф…Ѕjф%Іm3|YЪ"aгМћ{PћъЂбЪn+V@AЏrpX§ћ8JiLћ”нœх§Ырr<Ьї<ЃЏC(\sиЄЅeР +)щ‡Мџ#­CU5еmІ,Аs q;­58AЏAТ!>1;”„5l6Ј?‚ъ% QХbЈ"оl:(†2Цœ Т6tъБs)єїЉѓˆП@ЛOœЃ­HЮГ ŒџрЉ t>'|kH0Vэѓ|ЂЭєKЁЄ24.ИП'ЕD^iй#Pu.Њ–HЂПТ#PI.ћЫх" €5ш)сєяˆsашЎѕƒОХЌж/W„О„ƒф}3І‘O]Бc3ЃSЛ)]т?fІŽH8сJЁ(ћ тх•(0|'˜l<Š%%ФQ9œ dС*Q  ЁŠk ђc}љš\ksV2СGГ;‚Œн Ясx)ˆOFо9ЈыЙhЬбѓ)tьl q-љ§`ђЇЁЅИ:”P=ф>HЏ6•“#^ЁЙˆyƒ0[Ѕt!Zє@/jŒŒвrFР†§P‰ЫzѓffJ№­•Х7хЪѓЄёРьG4V5KЫт`Ќs‡wЇуф§њ’щŒ8ЊS№зtOy ўЩ4шђxp:г†Црguу9лs6]>3}xЕт•х•ЬYяИШ_ЪЏЪН|?„х™Р~ИќSўГућ•ЬїХКa/WФЩџЃ{С‡B˜АЫQ­ъY`›(kьMraИђ›l˜‰ЩD‚@Яђ№ŽЖыЬй,ˆ™EьŽ–˜p{;*ї W“›™f4„€Ќfа@˜ѓ?ВСхW3WNy~Тй=‹†И/…Й+м ‹k4ќлќ‹%бТН…љ‡ИL•`JŠйў ‘VЙ\}tјmWSіеoyHџœ FZшќЋWcš<Јsz,ПrjНЈнС‚ђ“Сш'ћKЏŸG і%PЊyсˆž№c)ЎbЧцшŠMGŠ№gŽщЪ,Џ@@€+1ЭŸ85ѕЌ[žX­--o iW›f ыЏ§И еyоњ‘Лђˆ˜A`?sXW%—B{1ѕm%*ic  Ќ8І^ˆMо. #фc=,lЩ75­k*t?ўЭHѓŸюžіТпR/9ющ~bZŸL 0ЧLд ‚ЌhБђК\љœ*чЄ3МwceЪЪ+ёr_иˆ luиNNUр0еВJIЏ=’ЏA9рyPAcmŠжЭкƒ/tŠрДhХЩх%јbмжЂzшїЫ—p"U~-Dp\ЂH~‰&"Dm­Œ&еЖZ)*’еЕ4UшsЪьћzP‘ќlШкеN•Ьо G,пq}šаƒОіnpB§Ÿs6ј9њCšй0@Г­8Я1цнРАЄЉ‹@Яzхiц№tч”o•d>JЌМКCHoШWp+вV?SKСB8Р?*Х@Huе…htЕ\*‹яx"’ФД@šњpољwwM?U)Ёwъaк‘ˆЈU­ВєіРŽJцCгт тФ9нЕfёSTu%€ŽKRЁ>хtВх9ќGZDИЙYUT ыˆЌ{Лc•ЕД№@дJ9Є­}oHW˜ЎтУяOzPА ”Є™ЬЖю`H ‘)-.œ:їfУ,ѓвО§З[ВА B@Ьt 4)‰(ЁќЮ ЎJaЄLŸШЏa"РЌŸхTQ„ ЄNo@Ї —gВaѓk%оџy†/”ЧѕlDc{7EЪe8X‰" шўЙ–…'ЯнвšиЧBšКph›ЊќЂPY}є&yєБNъP‰яЙ Бг%cš:xбЫnnIћNžЇ…+ЗЧЎB^tjіTМЋs]еMЪUGbX.MO_јiІB@4fZnHћ\>Ж,RЇJ‹ьa=хЎŽŠѓЪюEgPЃŒ‚?Эj”Aэ…і(Œ('дH,k dzБЇHŒ }jДМ:jгРJ(šRQ @‘ќёєоНн'6Щ$юЈUQ‰•ІонIђU Y^.cѕ-9ўч=§п#yєП†СЯ€*Х“Єњ_№ˆЉvemдЉŸŽтA7Mќ’RX 'кьБХ>хѓўЫЗЕЅ&bЎЪ'5>к?ехI/%65еUЂаеr…I,˜Nэв…УьDnЯ+НъWЄЇonХFW9pх"ЄСъврЕsКJ>SMNащdдА+PNнu!ЋЎЛ% ƒ`еK ЃЙ5\ЦіhD§лд"BF;iY utНЪ%ш§к@ vџ,Rх­Cg’щб…Ћш†‰_Qrš;C Jзв‰Ž€ @šYА яw•b"ФrНэXƒIwДЇ?pђкКяСKrД56TџљтєЦЉќ&ЄЉ@*Д+ГWmЇџ~Бžv:•ОџФЅ>а:щQ4:YЈАЩФУ•ыж+ХjТюL:’Iљш­Л:)ЬŽcмЅe €| ^лŒ:!нЏ4ѕXКѕѕxљSњЮRк}єLzXЊ0ѕжQ"шhБТ"ŒІxR%ЂўЗДи#аЉfYzт†цR>8А8™Ж­]ŽЦѕjxG^UB`+NњwMћŽzО˜–o;€S?ћаDI$Yі€"љт)1N­,љищчэ‡шлпі h…ЌшгН™ЁЉЏноN7ћ 5dаѓ)љ ЌЭЅт| ННt3M\В‘NН˜СјБЯDщ§=ЎбХ@Ѓ Ѓ:YxbIŒC 3еA ЏC^ fzїЃќ€6ыЉ љ)ў}]kjЁёеgSвhйŸ‡№sЖ>Ѓ”|.К.ŠkнаД*Е­^:М Ёвн,˜,XЛƒž_МŽЖ)~&8эЫї^%tеф0жzf?œй.ŸФ:eŠаѓ МяНЅ8™!ёбjъ;с кєїБtС,'ЭFgЯ&г[K7EuѕЮІИш0ўіЯ/ЂЉK~'0це%аѕ`"шzљB!оBIˆБ–ІMj—)LїmЁ”Ое&…*SхѕQ%ЬљБыšЉмqјнБЪџnxЭџчу5ŠЃ_аЙ  јєЗнtђ2ыEИБтЇПэЂЮ/|DЬ^F‡‘иGб™Э„aœоНF_сРќА ЁЭзћЛдЃЖuЪСЦЬХY оРРЛЖ9•.˜OS=…ДИwL§–>\Б-t† С‘SЩєЫn˜ "ижэ9J7Nњ‚n|§KкRхФ/ёќDмИ]‹`мЕНbfЪg"ћђГзУŸёАџПpsХ.N|Ф3jƒzНmэВ4АmMMЭSтzч{њњз]щIrBЅf6|жяSŽ@лwђ<›Зœ:§їcZЬў"|кч˜~i‚@@?hђŠ!oУ:лй‰Щрэ"*›‡]”ŸьXІUчВь–Ё<тЙЮфo~K?fwЁ^пЧуЦІѕь-‰­4r<Л€^ћќW:{1-&5„vTYф=t}Ыєгc7гЋк+ЬŸЩцŸБh&} €‰оo0IŸНЖgЏњ34OhЛŸІ›пј’>Ј/5ЌPLГ;рš†•ЈkƒŠєн†=Й 8šEa`pwЖ­Ѕ)Э ЧыПЪЮ–j0йLјЛН~КŠl|AЖ%ьЃч>YK+Pc@iЙ ГAіЋ$•-ЭЊ•Ђ'olEз5ЊЌіTƒЅDЎг( б…Q,0ЮnfЄЦсVЛТ+šmўЁ7PЉю‡грР)паЗџК‰JвV,z`аZАCрO`юPцш@+Џ =ўxбЕV(Rш`ЯњнOІŸДUФзS›rk[0і Ÿ­ЃщѓЄсzщc„phaa†5}pъ+ ;џПАюA}‚Єœъ€А‚)œ1s›Ј|ЎYD йЅQ™0|Щг –`цзнЧШƒПЋlџЙAЧ;›a1уZ№`XДi ыXЋ,ѕEЅЙVCгЁжЩ07|дўћюіЮ5c’#?ЛЉАэжЊ­щЋЭќ rOЭAрdCЏГ&#m№ xр+ыˆчgfœзЦїК픈вЪїіhЇзЦTЉXR^{“ћL€€&XdeŠxШ]D’#ЕћoŽњц€OQ6urэM4 L-6Ў5Яб Ÿ#эЋ‹CЗТ™o,&ˆ}†ФбZjЌ=кДЇ5ь˜XVЖuЮ50Ž/~ОŽЖВп j…ѕБšАk›WЇЧЏoŽЂDк(Mœ4ђ–†@C‹iR’YЭhЦqж[рЮЩе­`ЈЯ!ЮКlэ5Jж$:­`УНЎi5њЬCwZ0Іn+Pc yўѓ"/йМ|№!Q_БsЎ<Ш/џѓ=ѓЩZ sŽЂnWK›Уъ~˜Ѓ сзЗЄšU…Р[š ;"фŽ‘1ЎРƒ‚3фЅ—єžчвmІсL ЙNœО@OУ kЮ}Н4yРцњCНбчыwъN `ОCвЈ%ІФЇѓпїE&]o`+BnЙLяqтŸГr;ЅђїOaќ*0h6[РДR *ў‡ћ4Ё{;еЃ‚аДHBA@›†ЯPf з€ZєекОJ‹*ЅpњСƒ0мКP/‚)рчэ4;щжеJS/xqЃ<fiМŠ0˜,V.IныUМъЃXОСšА?Ÿ -z$‚ЁŠ?эТ$”чmџмzS9гžЇ~lwv№sbЯогаŠЇњг8$єцЪЩЕD@ТЏgS[lVх>} Нїѓzaё/t‰~”l^дŸИЧцЂзжЁfYФоЋpBSgx€§—№№*yДGуUS†0ЁљдВs_5@оо8'МƒањDJнcсшWв)NфУMCмOFњооp }9"кV/УяJђŒ)Ѕ‹ŸŒ$№z%рСњ@З†HgZљвзаlЈY•ДЉъP'Њo7я%.ЕкіT-ЖЮuЪS[D,c;r„œзT›7Tд^y#|ДжіТt”rЩ!V§чїxъ˜џ%A2Ь12ьќѕ+•уoIЗДЈЎJєuaк?Z[)Ё'в @Є‡ўЕ€…Ю]t)ЙтДЮGЄBŸ†•Ј(„1-6„5зXньuЫЅYєІ/ЙAqHеBЁРPЫ™Уd рPЄSpњ1QылЄ -}М§яŽŽTЂ ъГsŠVŽЁЮЎA-МђЏCtєдФmPЦѕЦfећАIДУо *k‘4…&%!V^ЂE"1#о‹и“E $а3ЗЕЃŸžьЂIЕaЙ’Gs$р–>/G@vйхxћ/œ2ИўЗйZ!$HyєКцˆ™О†ѕlLNŽЧцИьЌЃ6ќкGВњT3янбІ&й4^Ї€ J‰Т‰дЎ†vCд‚)д•…†?Ž Œў.8їq<џSЈиW ‚€4A Zˆ-ЄЕ0ЮћNžз%1ЁЉІюJ?ќп-дЇТг2МЌЏ"цU;_ѕЖ–оhSН45уpХ`§ЂE<ьзj–ЃтIкddЌћQ ѕФв;‘эќXЗЮ *аwџО‰f яIЕЪ‰ж Щ8‚Р%$Р%(L№ €§'‘0Чф­-NЇŸЛžЏпEџA€ѕЬь9d№’кеO8љРЏЉЖ™—3Сн‰$;k•ь…9˜42пЅпЛд-Ѕ‘BЦЁ/ЭаўDЗ Г&ќSў‰|nk]K#9ŒC”Б}щфŽ a€UЛ]њ‰c Ѕ1ПЗРŽ^КжЋ@ГVlЃзПZO;Й6<‡жAи{ќЌт0™?оЁYИЎEиуѓбй pjЛ”t&†фB`ŠЫчЄVH[ЌеЦB›OрбдАZЅТ…щСюшTž,™”OЋ ]&B@L&ZlB$РaФ@{ј(MA )оЉd\ёєmєќ€TЊМыЁžнpРdЄжrЋX4IЩ‚Ј3NИ•‹Єš­ЊШkЩч\/3фh50~N-= c=Zўф­єь-m„љG {'W$`Ўш<ˆJІsHФёђвўA€Odœfu@›кєжwПгŒх[щNж%5šˆ)gGіўHЖГ)5ЁСEј_kдЁOаЂsbЦRћ ЄxИ Ы‘”XШ† бщ›ЧпдšКAгЄйi,4;q!LLfкŠр€4ВYїЪH]ћ 4CP^ЕX~m:Вe&ы”ЭЙ42ХДA Q*4Ц”ˆмїG”ѓc|ж„ =ѕу7@ „ŸзЌ&h?1Д‰iТ:9еE›8 ие­ Џl­zВgžkrњ4Ќ ГEŒЭ:8экс/QЏ|бЬфiђwKЄ <зП"D ѓзфЂ2Рl[СыЇэ‡N™mж†ž/';тф;1mP#ЪO55ЮЦіјˆ'й ЭQв­# €жWHmњ ИMЕQiэj–Ѕђ\ukГ†БЋBэ]I—Є)цAа<’@ѓKЄ2?эSpјЇƒC=•H€HyFЊпHmUёŒВZяWлпV­ЃЇGњр(Ж1ю Э8єhP1ЖхСPЋk8ќ/ѓJчYDb>ЉЕЬ‹™'&П !" @ˆ€щўr{Ÿ#Ѕ$Њю'# Рf€тВДЈ76яŠl†аxKї`ћ|ˆ8ё#ˆ†к"ъ"т>ЧPШ3жKёАќ}уc€eNIq‹€qОУЪLJ&%RЧZHСЫ'дЈ€PЗВœDIуOџqь ЧюСрФёќ./ЕЈ^†Оw#jIt'XбrП_у8\"/ фšріŠq РЅo‚‰~ёzщзнGM4aуO•н9КеЋˆ‰цYСHЬTKвGzлxЎ™[c”щ-l‹Q@jщу§ЉwЃЪJЊ~ЮKŒ€€dЈ0Т*†:<С6ю;ъ]rНЦh[Г Х#zЕЛ( иOЬTѕRЪ6ŽХ 0‹eюŽbA iкЋ ЛІ)U€hйнј\^=! QzZ-ЕhЕYшO„В#`R‚S­^ЅŸ#PНdaЅЌьяЛdЊlЂР9!QМN2о)&€KњџLјp2%ф шлВІ’ХЏ—[ЮВБ”Р?йЕм>ЯюОXНЏ7zc…“ёЦ €ёж4ї!Tkчб3Ды•г~цЖм'$W0|ВхдРПџu0Ъ€OДи%љтЎЈ№Шљ`ыoZ­ =~c+КU"%D6€–МŒМК9ЬЭцІu8)АH:ќЈuѕв4AQqƒј(Z &ФЁŠr cm­гЪњ|Tы+ƒ †cЏiFїvЎO%‘QШЪ­zC@Н­˜ZєтдГzЧAдЁЎZ=J?@ iх’”&–ЌфyˆC#Э:œdG-)Зт Д юоˆЙЖЙRЦXД ‚€šˆ &šzъ '  {Q <Е\ОUOjVіZЏWЖ­§ѓ€›špОJ­•ѓ6Ÿђ{ЕЈNућЕЅVеJч­ƒм%Іƒ,dЇ!a€yNїЗйlДхРIј ŒЌ4У РIjкд(ЃиДЃ9Љ|:qdLњЕЈIŸ§ы&г3Цт,Jƒ'УЇўSšЩАЪТ›lХгХї=хЂ‹жь<xG^ ‚›83_ŽŽъЊЮеONі;аI+”'йќ2жъЛЭSJ„K3!њьЈ –B6g‚туч,b"ЭРѓљ‡?ібнэХРHынАBqŠƒЇ{Ь;Qё„Н%SД@Ф\NЩР5Ф‹ДбпnмѓOVCг+фЉ€kzЌпUЏчЂќ–ЖфNэ€7ЏGї-ШfOќGрsкщ*$]ФфX Рй‹iт§ЛUP}ф*% !yMкq№ТUя>‹§щіВјФ˜oхЦсѕёЬќћФ9ZѕзЁŒ"Rњ й˜ћ)’ГУ0}?‡ЛzN‘пЗ‚МжЏр ЛЦNѓF Qк@§њНI–ЕЩŸжwм€›š‘Э‘Cрй џпоuРGU-§й’„Ž иPQ)*vќTА Ё |Яі|>ЛЯЎHQШ‹EDх‰ЛЈ”в Ё™в‘"а‹є’RvїюїŸЛYXBЪжЛЗЬљ§6йНхœ9џsю93sfb9LqЉітмћi5o3R)rСyб@vg-Б­™фЄV0фrДG‡6~О‰&Ѕ tЌўѓѓь!Ц"\Ј­2ІЯZ9ќ;МЪvђxgз3о9єSoxћЪБЗCZєџi+qx%ѕ=”ЖЖ$wI{ЄР‚fРv•~ШЬѕ_AЂ“ЉЫ6Nxwт**vг+]ўе3F z­fГb6ŒвJhЧк бyьUЂU?Ѕ№pc№јХyсW wъ ^хsКJVяЛaћS”ѕфУ'яtRœs)эЈO,?ЕiїB ъЇЧшџ‘mы%PДЧo6Д‚0Ÿб S”йЋЗ яIkМФГ(Х>юA#Гш—пзвѓЏЂнt іЂKXcџ„l‰ЖФŸт.ц‹sЋ™ќ(їџzDЭY‹ˆ‘zц‹vŒ(їЋі9тьљЄИW‘л5ЙfQнњ‹hи“PяT]*яё Ыqh9н˜2„зО„\%TЭ€Ўa ,~Ч `ЩЦ]Ф/„‹',АšyЮiЇѕ;а пLЇofЏ ž]ЏІ{ЏЛP<М1 [b,kТ№H {xЧ\ ПŒЄq‹ђЈ№pдџеГУtЪ „њWљЌнѓИђБџnzSШCПQƒњЋРєЁѓО„6њ™ЉьVМL§м˜ђ>Uы2HМl&ИТ@ б О.ЎФ{ћШ‘bš { ‘€SYэЯЋ;д^шУђ;щС&б3џ онЎЁNWœЏ xуEФ™ ъЈщy7ќU?€3g0џYIЦkЈCnЗў3cфтЙ‰ёМ™2ЙсD0Fў•ОХМтй ЕоoPэЇƒёgб…)5ъј№Jh@`>a` -f`0Qы ь&€0рИ_vTрDВ“ 9§}їкhв’ є\‡V†Y=ГрHtТЬХѓ IšГr3хЌлF]Z5Ѓоw\пХБ`сшŽ,аmиОOлФ@Фкh]ц ‹9cЄm::МЯд… 8и:Яf|Іу5—•~ЅНŠС‹N _lп' ,ТЁEєФявСќ+H)MЦ‹Т] a I4€щь;МEy; ›.<ГЁЮˆЋ˜jСЦ]Pˆn`.4„аh}„sm|rmкЙя˜sќ NtаžќBzkє™Гš^ь|5§ i_kY`ѕsюЉѕЩŽў+ќ|ёЫ&fЅК%qЬŽCХўОVЅЈъ\Hhr vЭьB по§в@В‰Отє3}оEЇ (GЩ!…эљюпˆšЏ%ŸгНf}Ž­и Ÿ00‡цQђ‡oSНУџi'‚@Wk Эі,1(" ТІйwЈЮ—Ќп… ЛЈЕьцьuЮžю‹з`+Su…0|о1№мWгшЛпў ^нZг]зД0LћъКXбљsА‚М)сg*†cсd›Ѕ]#РЮ?хќЉjќtMЈЉˆѓ3}АZ…ƒђx”G™^—N eSZЏ-ёьЎv@`/}ЊЪЁч>@{]W“ f›ъ3p!9! А Т@ j1џюТ–БŸАJ6‚Р`\rv#Ь‘V1ˆ|Шe1ќю2žnПќ<ъг§ZКЙх9ъqГ§9§ЄкtRэt„“НФTР bЉa0лШФЇ?YnЃE˜ћтќcќљY№oзsCъђz6л“ЧОTь™KуњТ3W%>@`п?Rэй8”M;€Šі^Ѓ ЊЁэее[ЬˆХю;VqЏЇ”Лла)ukЦЎ(е|ёYЇaoо *ѓ~žО$2‰cєКуZЇD‰2}TSъџѓ`иБ‡Э$њ IЈˆќˆ|1s9|ГБ“лц/ЭQV™>МіљП”ЧГL ЖьЭЂƒіE4НgЁц4б`ќ€@"П„=КВдOїџKівkЩ­tЊ]рЈaњL1"няX!oоy&/YOпˆр:/—œ}2еЏS“Р‘C.p,u{шЧYаєљ‰лЎ ;]GСк!WЅЧАЛумFѕ)'-‰;"4EŒРŠ­{(!П%юФPЋРПЪїхБy‚?MA\œйTуJ6,Є <Ч*жю›О€Р~ЇН‚З:ЂёЇKJ*еЋu-4 auСjІЙ* ЈX‚WKЉLцяВVб§m[ъ>&РйАqЗ€Їћя‡ \ћS\VбЕ OБФŽбўУХєЮ/94zюŸєR—Ћщб›/#оKoєrVУ:x<фљ0њ8FJ?Џў`ŽЫъ?$YЕПkX+эAPEЩУŸ Ж,jYВ!’ <‘PюНЦxУMJх˜ЦГеЯƒƒџC.Ѕ5y! (aРж Т€]нќЭкТ ОћeЮš­Дp§_t}‹Ц‘еуЛyЏћ-ŸCПЏ‚MЄлšXE•љ†]ь(˜AУГVЊŽ‚нЏnnш=юж-гŽФRрКcYŒ'RШеWзпъЮ‡м`D7lм}ˆFAАѕ=#VЇˆ`ѓнЬ пПвw—z1Э7#O2ю ЩŽN|ЏE-(OЈ Й cнњQЕЅЬФЁ™єррўАГ\ьG№ъŒjzLазCиНwСiьKЈХѕ.0Ž7Е<›о…€‡WЙќАFZЪ QЪнќJQ№ѕ;Џ3lDС3рЈ:}ёуx*„—E•mХЌ№9Ј%УАњпЗП@VџЁ€ЮЋ|PЏ;[іАЪЯёв|DтƒS9Šё€@м}ТРtšN=>юGі#з#Q ˆ…›O3 Т@ lе~‡эxb…3уkvкIе^Я Ўmv& ;їІПD7ц}™Ѓрdь›žНb3=аюbz 9š†р:*ЇжЋ ўя€œ •e %Km4аыdџw™HьЪ‚-г-%l дћ Аmo"ГЈfб2њ>•§гLWŒ-GкГ‡ёЊ|ў5ш *АЕA(ХNАВ@p>„lƒЌиCQЋј;VsF†Н?шэПпPё5:9ZПVumе”>š€xSeЋїЈ’VQ№ЫєХ4qQ.=ƒ| џюp%5„ѓЁЪЩuk (ЂНL0ХBc46c9э„ @lџAЂЪ+ЗgЙ•|Іч o4цelљ4_љІWl3щ4Њзѓ”PtЅ*И]ŸРaу`TдФцCьФiќЙ’ў:ЈЫн+ЧбЋІЎ‘;сŽM pм ЁЈџЯПQ›~?!˜а uСq„ш№ЧЩTx7€ы!Аu_LyЫ#їБtЌі'8ѕY€љѓАšSœАœDatЏ)ј< шчd7в#А›QџŽ˜л‘ш{VъМД† Э…gГф[J9к4kЖэЅG>žLЗМ9’в—ck•ŽKFѕjХP8вqп­Nк'г–јVџЅ1ИЉР‚ОтvСН?=ИŒ•љ€уЦH™Š .бJх? RgЂ=МЯ^Ч…бАZ^MЁЉ…‰‡M аф`їA—AcЈЂ .йИK—169QьПКž˜•с§+uѕ/кŸ AцеПЂЌ'*]є=ПаZ@Ђs15фЊо8MШѓиИm?} _Н—ЎW5ЃЖиˆРQк‘ŠИTЄл§eЮ*К9uНќУ,bЇ+="АјОшiTbOЫћ“РѓnQВ;#xАUѕ?<§гRйŸЬХZ€КkР‹HMмmП+ЏќЏ , >О”vчs(§–$xэПyO[JТ–@Ггh\mhёђгqѓшњз ї'. У_Х Рљb‰:Ћ СЬ-Эˆ9ЁЁEvвp6н!ІG•ЯЖМ№СWХэХF™Щ'jтж|9]*f€ g5V›wьЃO3–yCќ.Лљ’sш)xщS)ЬxZ^iСQp+R­Оњ§ j—ђЃ|Х­&Двš˜ул€уё0ѓ/Т>;— 9ъ_4тb˜ЌРОБŸ„тоL%Ъ‚РУfџn= fёшmWЊZ€`yЙFЕ­žБ”6яAq—џєhKm.=BoљБZјEЏћeыwвп‡ŒЃŽoЇQцŸ[уŠZ}о!Сљb‰E\{ЈqуСтЈ1Yмм8lUˆьЌгёЃ\Чў№^{–ž2ѕi1}Ќ'ј:ŒБТˆЈM 0Е]`ўƒ'bЏНЮЫIˆ №ЭгщќЦ'# D˜Ли‘Y<#кUР[я€л dь0`§ѓ“ЩДz{|В€жЎБ,РKЭШЫ6tR7ћT0Oгc9tЄ„ў3:БЯ4єƒб#саЄ†‘Ї‰смjф{Ќ'№hйœШSzXœC˜Кpxћnі Z ћЂо '§Rw:‡Ѓ†*@e_3)RяП‰nНђ|п§‘dгУJЌТФї3–б§~Є~ЃцhюOQ§‰)зGt[€щфэ<@ЉiйфцэІАјш­|>ѕr нЧ>ŸB]ŽІпWoёmЉSїд‹ЁvtџœГŠЦЭ_yѕ,}rŠ•Њўwm#[ёэFL?-YW_ДrV!]|ѓyHt­Ъє3&њІ/СНћђсFaЇлило…ЗZ7oLЛў": ќPQ)БZзu?;Ш]ијъїЗvдяю6x‡V,sКлV0+tПІро›wcз~„[И-kЗьІ‘ѓжЈA„Zžеˆ№О§(ŽR8q!Д6•є-тІАэvэtхyЇE\UМ+(ј~6m)˜џdЪ\О‘<М 7ŒiЬЕ‹Я>%цнйŽ`?~4Т<б\Œ9Ѕ:m€M&^eЅѕЎS cJ–еуџˆЗјcPжєJ‰)жцЉ+ЪЇ.ЂюP‹Зn~ІaњеИa]ъйѕZzЙѓеєзB:ˆр=uБEю O ђ%оЉ'бзOu"N@діќ…kЗљDИ‚ЋцaЃчРAŸС†;ц•ч;]MOн~%qІУhUЈЭpЕPЮZЕ™њУI3џU“‰е§х;бotтtьзЅiЂ<­њћ‡BєjГ§Ђ?кДЁ(‚х‹6ЦЖ•‹1ВЩЦR Z’Op`5\xИ„zў8› Kтt'ТIсГ>ыфКtЩйЈIЃњA3џРf;\~>Эш?Н§[}Љ#;Р^zv ƒ[яяfЈŽ‚œБ”Е еќуЙ­ZD"Є3^ЗoA№ІЇ†ЅSЇЗFQЮ 0џВ-œН4шЬипзб№YгЭЊџЏ)ыgч?ХГ‰kЊџyŠZ[HЛ—пЎ_Ыъ?ФЗПl№вЩ†ƒда)‹BМй<—зЋ™H}К_GYo>DїД͘lXLD;€ЁaMU›їаУC'аmџAS—mа7hрўЎ(*Zw’…ЋЯрЊнУщ ЬcuW†jGзš’аллеЯс31нјсQJшАрUІ і?T(ж,жxЬK8Cрй>ЦЕљ;ПЮЅyБs` ƒ*Эoa[oк+wв/=яЂЫс' F!dчТH |,иі<BVзitЯћПЧxзka™а(…ЕО™pьЛ}РHzŽ~[іђЉа ВS‚ƒ.Н:|mи†РRЌ­:<ж ‹?ЫЊџ4~J…G›§{ њ3Ф™ЦђБЗ§…oЇSыЌ^ю‚#м,l№а-дА.R№ЊfY#Дcq№–”ŸшйЏ3hуюƒ!Aэт­‹’Rƒ:ПxЦЪЭдё­б”Е|vc€щџЁ—n}…н#ВVЊš"Наd8:де?BТлїы?Мi шѾИFлЕKМhӘi`PѓWmЁcrТИй|ЗpънзяОžцќї!КяцЫА-+Hеу\вXШњ™лО>œоBТ—`Зbй>‹Вћ`!qЖFƒЈћ1XЖiѕ§љ7Ÿж_4џа„і›mЅ Сі ыxьЧіл UаHеаКs!ќžуEњ?фŸИIHЄЈД<ыdњљ…;hBŸtM‹Це" 7h\Xиq €о€ ИЭ?аW3—Љ[ЋК] г+RРQˆxKЇъхєˆ1ОpЌџЇПœц јюЎct5ЖT2ѓw—цCЕ6>Ж щПvŽŽ‘§Kф€9€АОT>AcЬJHчyЈЇ7Г=UЪQ:]й”fЄмOя=z;о vY!іˆ`~1ƒаЕfыzќгIt#R]Ая3ЎѓФRъцЬˆЖYНЈлчŒvbлњ<іUсpєœышіОяЯ™аЖm.г\ФК&Ў_i&}б“Ќ]D№џ˜оЋHЁqdЗzh? !ў‡3в&ьG~њЋiTФЖo)GЈ‹нЏtЙ–ц ј=вЁx7Лhфрz€ћ‚?ЗбнƒЧPчЃш7fхŠ:1жX%`9h5ћљЭь?шѓtФњ7 йB3‚jТГКїпћCP—›ќ"XQ>C` "#ЊЛЗяPOO‹›Š/IDATХJTќ*fЇ7PSO}§>jwIŸo@$ЙќЭ0S€ŸР4ф‚чдУї8žиVь/GJЋ§ЄљыЖгЋˆ СzЧ‡M'^їŸф­?#ТšLqЛУјЋkTЅВ%0”ПУЋz№Иy42‡.JЉ[/mBгњ§>~В#@DQй-Р С!“е§#АZМ ™ыxwЦV„m.uЉЌЃ"RЂs к…ЪЬбiРКЕАПЧЃиЊx рˆ„њЦ4`ћПЧ6œвžEц$)"7%аFBET*’іqРџ+M ѓќ–шxпz№ŠЭ•5шпЎ‚Yр!zІы5”ФЋјRuaгk$ЅlЧ;Œ ?ŸЎ‡ЃрЏаЪФZu,@$ƒVёНGeѓqd\ЭЯoW”ЊѓŸk/Дб‘UdžЛE(?–оцY9SТ˜p§€ fл‹He<‰Жя/(Аќ@ Щ)ѕщ“G;аЌўаэ­šњRЛБb{џ},@#А YэжmХ;_~ўsБја'C|ў:ЃЄpщ§ѓo4хwор—фЏ[ў‡…ђн;‘ЦІn0Фе€HЪƒЬс/kмЂ(NПБЂ]‰З6EMtТ–Мєњ ЯЂI}яЅЏŸэJMЯhPЖ[€піЖyjш†§ЊЄD!H ѕb>ШЪ?J˜В@ЌxŠ!H}ЅMQ у)[faВќ&ОТ1е)pНќ§L„YQr‰Pѓўы–Ы(ыПџ —юjC5`&ˆЪnъŽТy—“> I”6 ѕ§qlў`ZЬИЄDŽЇ§ѕxf“cйќШ+3O "T4–У†qŒшї @ Рˆо-l   ј2}Бь ЈhžUrьЬ†uшƒ‡oЅйџy€кЋfјЈ VЯѓА’Юшіp,Б ПгYШQ№œўŠе\O,щДJн€QСv›чSJKУУ$Х€~$ЪџwДРОї4б”&Фп,@9l”:*™з–„xГЕ/oнтLšГР—џюBчvRєЬж†UЗН_Оy7=8t<э?TЈ‰йFЗ@D›0^§+žпЉАўєhWmєњDЈlйРnŒ‰S"jИЪ@ ђ8д˜ь%ўЪ74JЖ šя26 Єщ=aW‘Rб”GЄќяq}чA=ŽlВ7*ўHи’ЖяаКяƒ_){ЭжђhЫя Ј[#‘zuП)‡џAwЖiЩБЭVŸxљtfC%в ЇjЦrl§ї GsWn‚к_о/QŸПМяпSК„j%ЅU3–=-@0CЏ(я“Ч}X|‚Ћškјх !`зОКїНБ4w-V>RТBртГб/=яІ^ш'С(Цƒ‰h*ѓ‡œЙlƒЌќCƒ.„ЋUеи{єЃЌў+M€Ъ <>Ўџb,ГFљ|ДZ>˜М8Г§Е7ŸюœFМѕIJxис`ља—вo)ќO„ц­уXŽуO<цOx}ˆЯ]БЦЇђ^БкŸ…пйKСќБJЫXХКџёЎП /aћŒ”Ъ 2dЪїBЯ*žNхQ‰р7^NЛ*єќ Э\Б)‚КфжsNЉGпbЛршWяІgL„8ђZЮWIммЁїnЬїЌх~цм}rUЈр§Т2ˆЭі н™rjЈw[хz‚щ;оМў’їС0˜ЋхšP€&`7VD{o MD:[)‘!pwы iж›бЃЎFў<оќд ИЂ‘жX:уйDю_ћЉћ;Ѓi;Р"JІ”#рХћксl џ­Їbм’aЋ ˜Ёsи_"gBЭоІСаdІk ьЫ?B љ•†gЎ0SЯтв—Ц ывWOwІ7яЛЩч *Єб@–nмI]ŽЂЅЙтэˆKЬПГƒЌžЁn)чЦМ-6 @uƒvgъе˜@їР АК+х|$`ЕZPTB2‘>šВ0’šфо2J8`+sјИЕhЧ #;{хfКуэQДvѓqјгz Н‰Ї‘нљЂжMЁ=Њ%иœ иp­‘.ЕZzL|т”`;л‹_OЃО?ЯVЃšИЗ1э3џё зСrЅЭ#юёШѓ8 ьœЩedіjК{аhкЖч˜?;ќIб6нкmPЗ7/еМm7ЈЭлAч TJ^ЗЏƒч7Y§WŠPєOР2; •EBPPT§6,PуŠ-Лi%>œ‡!іc&ВЧ`V!ївЇщ‹шŸCЧбУХої:кSЕ ѕрtмSћЦѕнЂ•и{^%‡Ѓ†Ќў+)&gxѕ”рЄяЇ/ЅЛоMЃMМz’щKз“‹…ЇВ•hH7‡qБ8Э†н-~ЩІчћ‚5ZїBJœPMИіtWj›8SЂЋцefV6нSo ›НГ/ keЩё˜"Oщ‹ѓ(љЭŸiюšm1mЪL•sbžqЊњ_‹еП9ЏhŽN!†bљ†и4цЏf8zJОФ  ŠУŽХœ­ЅЄп+ЂТ щЕСёяUиO“  LљФиMзB•нѕ­4ќ7й!Pсt-wpёњПР€ўђ1цm)=Їh№<ћПFјЫћ)ˆ1ucŽкэi…#й8s5Ж”ŠPОноМ{GлЫъП"pтp лїб#Їз~˜IEœGJЅŒ™З†мХкЉџ+%DNКBB‚нžюО”ќa’ЎH‹1"”ОGдv=Ё.JлypтјvTVSƒгц ˜Ъ(ЪлЙ?ŽФшЗщ§‡‹ш—љBнЉЁЧ9, Ѕ ’ЅпY!”E€}ьŽыЉЦЁGYј‹хп}йэ˜ ЗШъП<0:јЭm№ ШX˜KЗџчGšМX"–•ЉKђh3"ЮiэxЦšn)‚€!АсEbѓОF=zе7Н1$R€@poLqТ–жSUЩОџ@dєѕBРІПbuНўгl1 ”{т9cYЦЪF.hФ0аK“Ё#Рq‰—’ЛЮ?BПй\wˆ8ž  :НQі§‚ЂгяиjUoїЗGeRч#hйІ]:%T;Вц ЋbіъЭкЊџЫКЇ(ЂаnЄЅЅˆPUVо—Ќž(HџLъ‘›ПъљЏЁёдпИќ ВxГ—­Їі0 В+QькА`a№јч­19фŽгъŸ)ї… ќQ”;5GР"ј4JP^вМm4‡х‚NzHFлP]KWрQљnD8hзУаLЁБи7рў[ЈэEgБ'Aг<"{ЭBФD€тЂ‡‡юп…фC†20Б"Џ=ЧL{!o єкІЎ)_бФд?LлЯJ:&+ол_ЉПЏрЫi%Ѓ"‡#C LЙluL§‘^§n:эйфчLЄ•ж‡мtмД”qF@MфМŽ \œ(hxœЉбЄyk>=R’л>Ž-ˆ#BIБ6ў 1l‰ д[Нх Кук ЈašКТхЫщKЈч7tшVАlваKѓП№œFДф§'ЉІ§ўиМ‹>O_DЃА]r?ЖˆЊл%Х  —йЃ/:ьў=ž•”д–вzвqбЇЦšЗэaиў[HдПшO(CжXІ@0[Ъўc#eУ7 YуSшЮжвпл]B—59 <#~ wћўњЯˆйєеєЅ€вJiЉl|=XўыЩ Р;$rжlЅ/2г„kЉ ПŒЏ; (•a*Чу€€š(Шy ЙŠџ‰ж?Œš6i= @rŸFTГЏў›Ъъ_гЙfЌЦ8Ї>IЕ“Јu‹ГЈл5PчЋšSГгТTЈЭcsИИ”FЬYIƒЦfгњ­{‰рП ›џqƒ‰Њч—•>MЕ’Ž;ЅѕќЂJGJфЏf.ЃйцмРPЗИi ŽДжыйH%оыiJъЮрn2цUжг$еxЋџІВњ7ц„еŒj^eуS‚И™АЛѓЇнštMѓЦtг%чR‡+šRГ3ЦФLР+ўqpRќzЦRZšЗнЧєuОrхlI‰WЩлy€FCХ?bЮ ZЩ™!9Ю;ћrшЗxс%эV€/D№y”рzWЅTqЅсOiГ”б L=RNЇRл|lїhB<ШRPрэˆЛЯХІЇ7 +Ю;Z5=ƒЎiv&5…@а v ЊW+)”Zщ`a1"в\ьFШXО2WnЂ{ђ}Žj:vH<кIЈлЯ<Й.­§єYЊSƒ“jjS)ЁЬU›рдЗ’2–цб`HЌ1fк@$­„‹ke7В\]G“S7„[ояГ– „Ѓ„„&ВњзћДд)}eщ‡™:^ёцnлKЙp0KcЏrxфзЋUƒЮiTŸ7ЌKЇ7ЈCЇT›NЉW‹ъеLЂX‰ВДЭЖrVэя?\D;АвпОЏ€6ь>HА‚-).С2иЛŸе§*Z­ў‹сЌљVјcц§I“ЎЃU[љ‘…2YэhЖ€T^ :N%*с@qЯ€тАHДŽ sпЦфLњмЦВњkЎШMе!Рі“Wƒ АРР+вђ{ѕљ:ў№u|ŽЭ ]W]{z9~œ\ПmќтyЊ a'к…Г:ўЙmMB№ЃЩHzДh§rСЖ/Ћ§hC-ѕ" њ(фВЕЃЩ§—ž2ЫwыhIOBЂk,џЬ2uuиfцЂ~ŽЪРфCНПjыšВ8—fТ™oёњПЈЧTIVћQСX*ЉŸ/@]R\Нpхј@b7WБ†а%хВC§/!Э5{Ѕ7КA€M".6_„Yи4В}>§Оv;M‡ФXOШЧGћDј_KђМVЇв5DАуv*НMдКљXЖчвbйF|ыіРƒгщ&GтПeѕ_Э,г‚@4PUљxЅА_вэEQЉCOpЂ ХГ њЎЖ4>ѕ žH —ˆх&-wЄДФKшAёќ7щјJЗє‡;эЉЋ|sЏ+єМPЄ М‹ЬюИіЎG4iOƒFЬ+xщExўзеПГHšAР АG+йŸЇ}Я0CwЭ)t~ѓrЌDў.Ћ3LQщƒ :A@ ь<ŽЋЯш„ЂˆШ0Їр№МDvg]Y§G47фfA@ђ№vWЛ§IJўoгђЇŒіл|@ї~W‘ЭоCўm* Н‚€ _ˆрF”шсDA†.№к1Yiqг{ˆљпŠМІ иdВA’ю‚€ `dд Uб…7NЅЕ™;кsiКіmеџ]Вњ7ъtКA@0ЊРY[9QaЗ͘H№bœЏ Œh &m2Р!$ ‚€ `!8DА ‰‚КєЛСЈН6  ›в{4Ђ˜?МБQg›а-‚€™p8"X9ƒ.?u­^mИфцRRьДУ;ћў/Ц`˜izI_A@єŠЧА9Ю%WУeД.k­^ЩЌŒ.s˜–Иo"›3YM#ZYOхИ ‚€ MT‰‚lœ.ј§šбЌZ‹КŒ/№ъŸЏ"№OЂиўЕ˜2в† ‚РQји­Љ8џoGф‹ё€EЎлaћПMVџ™qBІ fC@Еў{{Rчо Œд5cћ$?—DЮкŸУбLі§iк ­‚€ ˜ јž9œ …о_€yFщ™Б5‰u/EHЦХёЯ(гMшAРЌАРі§дp6џђ7ЏР=]—5‡ZДs!QУ->!@Tх'€ќA@Јfў`—ї ЊЙц JKѓTw‡Ю›[р`! Y[ЏL0ТА‚€ њ@РoѓwПK5зОnцЯиš_р^цЮаЎ>"0RA@‚@ ŒљЛ]ƒЉжКОfbўмykмгмЌlј”Р'рVё `@Є‚€ TŽРqЬПй˜?їл:їvн)‚€ U PЦќЯ`Ј§MЩќЙѓжИЧ,ДhW Ч@hxO‡A@AР€ŸљЛпЃ—љЇšТсЯпЛРџжИїыВr' ށbœ ђ]K#PЦќ=ЎїЉцхН)э^г2fk мѓ\кУ1P4Œ‡A@,@ѓwЛ? УЮо4хYS3jы мћм99дМ]в7ŠРxHAРЊ№>ЬПРб Б§нV€Ск№КЌЙдЂэЗс—М,Ш _њ(‚€^љƒz\CЈРiцЯ}€QX7g.т!;„q dDЄ‚€ `ŽЊ§џG…ЮзЌВђїЌ~$r!4ПЁ€~LфП ІEРЯќ]RaBOЋ1V'wnж”–ŒЅљCŠЪ]Eдс ›ђœŽКаˆ pHrРАјПЧНМќh ?ЅЩrЋьOчзoТљW№NщЁСЁ>"”AVЦќнЎ/I)xN˜•3ЉТ“"TKE8žЊъЧOg‰*йœi4)ѕШБ *љ–ќЦ•ф№ОŒф=xщееO%8Щac  2~ЈњїRМ?‘]љ„& \ё6ъи7ОF/уžл№uБFРЪE˜4F_€h XОŽŽНŸ‚кю#ЫjŽ1ў? чмE™Љ‡ЫУTэя.ЏЗ… єUЩcЉњв5hЕИЩњ@@uюГц?й~‚ ћ9Ѕ\6q7І8ЉŽЋ+ю‡pьh йК‚€ъ;QњyN–в_›˜“J€0Ћі6UpТ@жqф9ЪјнЋрёЛGвŒAHЄQћялў/!і"29ь"D„ЇмkŽ1ўнАa§Hnћ0š6`mдšM~.‰ьѕю‚`ќ24dџЇжk%€Ъќ]_гс„gБА(ŽЎЌH€XzЇ>OB]ЧšJ,[Šoн*учеˆ{ ьќŸ€QџD“п9UЂzєpPQѓNаМ„zoVЈD#UˆЅВHРќgеS‡j7јйМ/нyЉъ$hjAћТћ~C…‰џц_хьъЄAССE^ƒ!Р„š~сёjЧэЪХjф*Цjgц;ћ"@Ћњ[[іHЄѓЮяL^ЧѓPо$‚@ѕЩБDРЯјб†тўО&‡2‚& оЫVЋЛл '‘ЛцƒxŸC ъbC1с‚ƒWўn—0џу?В"D†_pw'ї~’ыЧИиц€ЃŒпНлї>УJч{и6їF”ЎКъ‰:­A'"чsXн‚M5 ШіС(,еT‰Р1~цДѓРtПІФЄ_i|*2†ЦЉ$їiгл?бњ3ˆ!p.ДqP ˜@`.eчx ЎoБђFVўб›_"DЫЊkъичqЌ–?СJйИBЏіљуqmЦџЯБвљ–&мUuЧc|–Ѓj–Д‡њѕiДд/ОDbŒЙ•ЋWэћиУяqBЌŠ)`Впао§™Дx˜~ђЬwш{„bМolO’гyІсецяѓOцхgO€(Zeuћ>†‡ђSУ *уЧKOqmУ’т *uC34TqV jРЩdьА{Ÿ‚”rВ8&XtQОЦ^эл0џе Vю\˜љGƒŠ‘4yрЪ8P|“]zCb“ Р–УйШ‚Бoхџšј4}/С~pWŠNбЛЊc/Nж$ъ>кКкQWќUJ_’зѓЅП !@чЅsЏЫIqў ц‰А‰žс{q[}пДЮЧLwфсеш7uyмPЅgс™E№яєЈ;ИЦКяЩЏ7нџ§C@ЄMƒиR™Пћ{ЊSє4ЅU8,жИY ~т1Шњ<ŠЗ kє)јПл ѕОї+Јњ‡бЄA[тUDmњV@ї`љі^цWјMbЙЙRŽЊј]8сХ п6†мžq”1hyЅїхD—>’bGЄMя§ŒыСiбЇеа#§О•?˜Б0џŽ1ЗЪЊ“!ий`‡ 5ЕŸё{мЛБbј–JKП щяoЌВF8йх‰ZфjР[„аuVAЇЈZЦ]œ0‚1ЄЏ@еЁš.^+Ъчщx&Ч’sп<š4ЌњШ•1Є.&Uћ4d/тYИЮЩЕtЇ`цяv§@u‹Ÿ’•LfРбJE8 EОшEP_€Њ—э^ №=)ЅŸQњ{БлПЈ6йБw Мё;тїНxё#фpBMUaр(Dжљ‚зŸWн/ЗЂй'Лhe Aф> ”NНЏ…АѓžƒюŽ“trлЗђŽ•џ“Тќc?Eˆ=ЦUЗмї_p*њ,.š€c[™8TipЩIЊюqЮr`Ё#MЏРЦ њМ/Уј„уєB(›Э Іп‹J=УЂБ2Jт{oЇ^7 ТЮ+№ѓщ @YТЁ8$Ь_sаEађ lпћrк>зL8ЦјyЯ2B•"ˆOFHЩI*ш„Б0PаДL2_`%tЅЊ5pw„є рg@ёьЁћХ”­q ‹ Ш‹Ы%^ы€нŽчРWC+B|ЬџGrьв”f­p Бьm‘wжg/ЃѓлlƒMЎ„оo’јЅЧ{j/і0+п‘BOвд?ањlV§[ЗЌ^эЅМьдДmMМ“U“€uбАFЯ™сx•)4ыя­бс zЙ>g=]qЦrДя†3aiЂю„ˆЕŸŒЪќK"G 0џЬчsєёКD€x!_Онѕ9>!РћДM};•П"ќпЧ?ogŽ0КOбдЗПгг6z_ј=ацЮІзqў‚‡aHвІAi%n№3AЪ ЪЭцŒ•RќА0œ›Г–š]9Q6WТDr„тЦx'ХЦaV ђЃ2џ'‚JюЇSўG~ Єш фоиЋkџ6љЄˆWЂќ’c…‚т)ФЊ? ѕ ЅєAKѕд]бbЃфо“Щ™аQн"Ѕ3т„œ(!РЯ…WйOЅо+aћ7оіж(СT5ЗПR›’zАё.Й\Н'Z ‡Tцяњ™ьI ѓj4Ђ~‘hЂi„цe/ЇІзm…ъ-|MРБ^tЃ‘A+ўŸQ^ЮЮЉ3џэЭл&ћnВ=аФCЭ 6}2о -ЅJ6ЬsQюœeдЄ 4aIiŠфfЇР\™F€™ПЛtдўТќЋ€иž Жј†W{^Юrjv=ЏLи'€•AжƒЇ’,ЏRŒм/XХ>CS ЅМ9кe& ’Rн^vЮ {Шюљ„€ZКЅQ‹ U­Ми’Ш*ВанrJ€з"КрšQфЁнx%5УЛІ`пOexЉЬп={§G1БљЧqБ'EЏtъ§L_BЈкР+~ŽUЎИKёeVЏџУŠ?GЏнв=]њРйљОшžT!0D|ъџ|<+­Lы"DHТКМгЋЇcЕёlŒOтY9+ш„C*ѓwѓ AЦ Уj[nŠ"D ЪU”мчAФ јZ Ќьo„_flуїx‰ ™ЩМю!”>8ѓј‹фWШ$їКщGЧoy>BOч7ЈЯ‹{:ЅПг^ч”ƒМіЏŸMї“Xx<† ZЇUUŸ$VfКна ”>*Ь_У+&}ŒCхTфСKЙiЄп 40уWU§ЈњЇлћіёЄМЙ›+ЏHЮРщэv!iѓ§0доќtэra<`Р­ќ[_Ф“ гДН~N>|‹fQ“Жc‘| *3лаР|VЮ,Ря+0НЛz‘ŠшЩЫё ьрHp"+Ÿ•л4ЁlЖ%њ+"D@€”л-Œ€bƒМ!^ŠАЉЏГЙ4mа&=#4zG@вы}„„>#Pc6yŽl'Лѓ,:Іг"Єyyџ?MАHoЅ›‚@Фˆ bЅЫ"‘Š 6ЩЈ‡ рSџтѕПЦCh0"b˜„Hн"`ГџJ–џX}J‰ЌўЗбšљ~nќˆ–c! &c—PЋ7з<$B†@ч…bˆурА€”‰Ф'#ŽУ M бkМ„ZН!0§§BђzЧJ†Р8 3ЗЋёџgЦ‘ iZ0"nШ„`§!`3@Љ˜т42>яџ%tв–?уD4+ 9lBДЎиwp)ДKE ЇQQеџž‰”–ц‰вЌ `HD0фА бКB€3ЅITР8ŒŠЊўw— /Ує8Д.M †F@CŸЏлDЁ“ šПТ”•ДuгJЭ›–ƒ# €СPШз 3­ G2j<ЊњŸ&въ4ј`HP ДфZA RА§Ь3€ФЈЁшŸрШn Яˆ~нRЃ `~D0џKЕBРыс ћХ@#РyѕяѕЎЁт=K5jQšL…€ІNщL\Шј`+8вtЩЈб(има$ЪќОXЃЅAРTˆ`Њс”ЮФХў‹dдbX§яr“ІiбšД!˜Ь8ЊвЇј! Ю&ХГ]Ь1Vџ+о фQХИ%Љ^0-"˜vhЅcqA`ц'ћШ ЏtŸz:.$XЂQ_лdтPЬRA ,D 6ЙIЈ› н’!А ˆ";…е?g`TD§ŽrЗеРъ3@њ}МžЙpD†@0*)бGРЇўпBЮЂyбЏ\jЌƒ€жkщЉVЈщW1ФpЛњкšJщхЧЈЉVА"Xb˜Ѕ“š#рU8C rˆ ъи{<^"ћдЈз+ C@‹ ИtW#Жзс Ы$C`”ёцдП^e)‡af‘"‘  FвˆЄЙW*@`OІ‡ЮЛО>9эI? ”ш РоџŠg Эјpdt*”Zы" ыŽНє<жx•™0‹3`іBћoГЭŒbR• `YўˆŒцys6PWIENDЎB`‚UEFITool-0.28.0/uefitool.ico000066400000000000000000000764461363631100000155230ustar00rootroot00000000000000 hF ЈЎ00 Ј%V@@ (Bў:(  @џџџџџџџџџџџџџџџџџџvGovGџvGџvG“џџџџџџџџџџџџџџџџџџџџџџџџzJyJWzJ zJyIЫyIџyIџyIуzJ zJyIUyJ џџџџџџџџџ}M|LŸ{Lџ{Lх{Lх{Lџ{Lџ{Lџ{Lџ{Lщ{Lй{Lџ{LС|L џџџџџџNYNџNџNџNџNџNџƒSџOџNџNџNџNџNџџџџџџQ ‚Qч‚Qџ‚QџЋ‹TџщрвџНЄyџЕ™iџцнЭџ„Tџ‚Qџ‚Qџ‚QѕQџџџџџџˆW†Uс†UџЏXџїѓюџмЭДџМЁqџЙœkџљїѓџД•`џЎUџ†Uџ†Uы‡VџџџŒYk‹XХ‹Xџ‹YџёысџсдОџяшнџоаЗџЛžjџ§§ќџлЫАџђэуџŒYџ‹Xџ‹YНŒYi\џ\џ\џ›nџџџџџчмШџїѓэџРЄpџЏ‹IџїєяџЃz.џужОџ’aџ\џ\џ\џ“`џ“`џ“`џoџўўўџ§ќћџысЯџ—f џ”aџфзПџ№щмџысаџ•cџ“`џ“`џ“`џ–c—cй˜dџ˜dџШЌvџзТ›џсвЖџљіёџѕёшџвМ‘џлШЅџмЪЇџ˜dџ˜dџ—cз–cџџџ›fœgпœgџœgџдНџџџџџвЙ‹џЩЋqџљі№џєюфџЅtџœgџœgы›f џџџџџџЁl kб kџ kџЇvџкХ›џў§ќџэфбџЛ–JџК”Fџ kџ kџ kэЁkџџџџџџЃnQЃnџЃnџЃnџЃnџЃnџ­}џЏ€џЃnџЃnџЃnџЃnџЃnџЃn{џџџџџџІp ІpНЇpџІpѓІpчЇpџЇpџЇpџЇpџІpчІpѓЇpџІpзІpџџџџџџџџџЉr Љr{ЉrЉrЉrЙЉsџЉsџЉrбЉrЉrЉrЉrџџџџџџџџџџџџџџџџџџџџџџџџЋtgЌuџЌuџЋt‹џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ( @ €џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџvGЏvGџvGџvGџvGџvGѓvG џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџwHwHљwHџwHџwHџwHџwHџwHQџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџxIexIџxIџxIџxIџxIџxIџxIЃџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџzJ yJЕyJЁzJ#џџџџџџzJyJЫyJџyJџyJџyJџyJџyJџyJяzJџџџџџџzJ yJ{yJйyJ-џџџџџџџџџџџџџџџџџџџџџџџџџџџ{L {KЙ{Kџ{Kџ{Kћ{K›{K™{Kљ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kћ{K­{Ky{Kщ{Kџ{Kџ{Kщ{K-џџџџџџџџџџџџџџџџџџџџџ}M |LЛ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lщ|L-џџџџџџџџџџџџџџџџџџ~NЙ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nщ~Nџџџџџџџџџџџџџџџ€OЇ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€OџcџPџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oх€O џџџџџџџџџџџџџџџQ)QћQџQџQџQџQџQџaџФЎˆџтзХџЩЕ’џ‚RџQџщрвџѓюцџМЂwџQџQџQџQџQџQџQџQџQeџџџџџџџџџџџџџџџџџџџџџƒRЁƒRџƒRџƒRџƒRџƒRџЈ†LџіѓэџџџџџџџџџџџџџЋŠRџƒRџщрвџџџџџэхйџƒRџ‹]џƒRџƒRџƒRџƒRџƒRџƒRеƒRџџџџџџџџџџџџџџџџџџџџџ…T‘…Tџ…Tџ…Tџ…TџЌŠQџ§ќћџџџџџџџџџќњјџУЌ‚џaџ…Tџъсгџџџџџэцйџ…TџЪЕџЉ†Jџ…Tџ…Tџ…Tџ…Tџ…TГџџџџџџџџџџџџџџџџџџџџџˆW‡Vѕ‡Vџ‡Vџ‡Vџ•jџљіђџџџџџрдОџЯМ™џЅ@џбОžџЬЗ“џ‡VџэцйџџџџџњјѕџВ“\џЭЙ–џїєюџ”hџ‡Vџ‡Vџ‡Vџ‡V§‡Vџџџџџџџџџџџџ‹X ‹XYŠXЛŠXџŠXџŠXџŠXџЯ˘џџџџџ§§ќџ—kџХ­ƒџњјєџџџџџЧЏ†џŠXџэциџџџџџџџџџъсвџЮК–џџџџџбН›џŠXџŠXџŠXџŠXџŠXБ‹XG‹XџџџŒYЋŒYѕŒYџŒYџŒYџŒYџŒYџŽ[џјєяџџџџџћњјџѕ№шџџџџџџџџџџџџџЕ”[џŒYџъсвџџџџџњјєџфиФџЯК–џџџџџњјѕџ]џŒYџŒYџŒYџŒYџŒYџŒYёŒYЉŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џЁv*џџџџџџџџџџџџџќћјџђэтџџџџџў§ќџ™kџŽ[џрвЙџџџџџьфдџ“b џПЂnџў§ќџџџџџ•eџŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џ]џ]џ]џ]џ]џ]џ]џЏŠGџџџџџџџџџ§§ќџЅ|0џьфдџџџџџлЪЌџ]џ]џОЁjџџџџџѕёщџ‘_џЋ…>џЋ„=џхиТџ—gџ]џ]џ]џ]џ]џ]џ]џ’_џ’_џ’_џ’_џ’_џ’_џ’_џА‹FџџџџџџџџџџџџџљіђџџџџџњјєџЂv$џ’_џ’_џ•dџячйџџџџџзХЂџытаџѓюфџКš^џ“`џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ”aџ”aџ”aџ”aџ”aџ”aџ”aџ rџўў§џџџџџџџџџџџџџ§ќњџЖ“Qџ”aџ”aџ”aџ”aџЉ€1џњїѓџџџџџџџџџџџџџџџџџ›lџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ–cѓ–cџ–cџ–cџ–cџ–cџ–cџ–cџжУџџџџџџџџџячзџЛ™YџЯИ‹џэфгџћљѕџїєэџсгИџЕ’LџЎ†9џєюфџџџџџџџџџѓютџ–dџ–cџ–cџ–cџ–cџ–cџ–cџ–cѓ™d™dC™d™dз™dџ™dџ™dџ™dџšeџБ‰=џА‡9џО]џћљѕџџџџџџџџџџџџџџџџџџџџџџџџџчкУџГŒBџЦЈoџбЙŒџЌ/џ™dџ™dџ™dџ™dџ™dз™d‰™dA™dџџџџџџџџџ›f ›fё›fџ›fџ›fџ›fџ›fџЇyџ§ќњџџџџџџџџџЯЖ„џЋ)џдН‘џ§ќћџџџџџџџџџў§ќџиУœџЄtџ›fџ›fџ›fџ›fџ›f§›fџџџџџџџџџџџџџџџџџџџџџh‘hџhџhџhџhџ­€(џўў§џџџџџџџџџќћљџаЗ…џЂo џЏ„.џщнЦџџџџџџџџџќћјџЗCџhџhџhџhџhБџџџџџџџџџџџџџџџџџџџџџџџџŸjcŸjџŸjџŸjџŸjџŸjџŸjџМ˜Mџѕяуџџџџџџџџџџџџџѕ№цџУЂ`џ lџХЅeџ№шзџИ‘BџŸjџŸjџŸjџŸjџŸjџŸjЙџџџџџџџџџџџџџџџџџџџџџЁlЁkпЁkџЁkџЁkџЁkџЁkџЁkџЁkџЄq џаЖџћљіџџџџџџџџџџџџџфеЗџЄq џЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁk?џџџџџџџџџџџџџџџџџџЂmqЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџ­~џОšMџПšOџДˆ/џЃoџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmУџџџџџџџџџџџџџџџџџџЄnгЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄn§Єn'џџџџџџџџџџџџџџџІp)ІpчІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІp§ІpeџџџџџџџџџџџџџџџџџџџџџЇq)ЇqчЇqџЇqџЇqџЇqЯЇqЃЇqљЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqљЇqЇЇqбЇqџЇqџЇqџЇq§ЇqeџџџџџџџџџџџџџџџџџџџџџџџџџџџЉr'ЉrхЉrсЉr]ЉrџџџЉrЉrЇЉrџЉrџЉrџЉrџЉrџЉrџЉrЩЉrџџџџџџЉrQЉrЯЉr§ЉreџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊsЊs џџџџџџџџџџџџЊsCЊsџЊsџЊsџЊsџЊsџЊsџЊs}џџџџџџџџџџџџџџџЊs+џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋtЋtяЋtџЋtџЋtџЋtџЋtџЋt;џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌuЇЌuџЌuџЌuџЌuџЌuёЌuџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(0` €%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџuG%vGбvGџvGџvGџvGџvGџvGџvGїuGuvGџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџvG]vGэvGџvGџvGџvGџvGџvGџvGџvGЇvGџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџwH vG•wH§wHџvHџwHџvHџwHџwHџvHџwHлwH/џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџxI/xHйxIџxIџxIџxIџxIџxIџxIџxIџxIџxIeџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџyI'yIUyIџџџџџџџџџџџџџџџџџџxIayIћyIџyIџyIџyIџyIџxIџyIџyIџxIџxIБџџџџџџџџџџџџџџџџџџxJ yI?yIYxJџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџzJyIУyIщyJСzJKzJџџџџџџzJzJ9yIЩyIџyIџyIџyIџyIџyJџyJџyJџyJџyJџyJѓzJ=yJџџџџџџџџџzJyJ™yIпyJыyJczJџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ{L{K/{KХ{Kџ{Kџ{KџzKћzKЧzK[zKW{KХ{KћzKџ{Kџ{KџzKџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{K§{Kе{Ko{K;{K{Kя{Kџ{Kџ{Kџ{Kя{Kw{K џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ{L/{KЛ{Kћ{Lџ{Lџ{Lџ{Lџ{Lџ{Lї{Kѕ{Lџ{Kџ{Lџ{Lџ{Kџ{Lџ{Lџ{Kџ{Kџ{Lџ{Kџ{Kџ{Lџ{Kџ{Kџ{Lџ{K§{Lч{Lџ{Kџ{Kџ{Lџ{Kџ{Kџ{Lщ{Lw|Lџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ}M|LХ|Lћ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lя|Laџџџџџџџџџџџџџџџџџџџџџџџџџџџ~M+~NХ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nя~Nq~N џџџџџџџџџџџџџџџџџџџџџN]NэNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNЇNџџџџџџџџџџџџџџџџџџџџџ€O€OЧ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ‹^џ–m+џ‚Rџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oы€O_€OџџџџџџџџџџџџџџџџџџџџџџџџQSQ§QџQџQџQџQџQџQџQџQџQџŠ]џЄGџФЎ‰џжЦЋџПІ}џfџQџQџЊ‰RџѓяшџєяшџЫИ–џx9џQџQџQџQџQџQџQџQџQџQџQџQџQЇQ џџџџџџџџџџџџџџџџџџџџџџџџџџџ‚Q ‚QЭ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ„Sџœu5џЭКšџјіђџџџџџўўўџќћљџЪЖ”џŠ\џ‚QџЊ‰RџѓяшџџџџџџџџџпгПџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qї‚Q7џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџƒR_ƒRљƒRџƒRџƒRџƒRџƒRџƒRџƒRџІ„Iџшпаџќќњџџџџџўўўџџџџџџџџџюшнџšr.џƒRџЋŠRџєяшџџџџџџџџџфиЦџƒRџˆY џeџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRЋƒQџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ…TO…Tѓ…Tџ…Tџ…Tџ…Tџ…Tџ‡WџЉ‡MџћљїџџџџџџџџџџџџџџџџџќћњџсеСџ­ŒTџŠ[ џ…TџЌŒSџє№шџџџџџўўўџфйЦџ…TџcџргОџІ‚Eџ†Vџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tyџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ†UЕ†Uџ†Uџ†Uџ†Uџ†Uџ†UџЃ}<џыуеџџџџџџџџџўўўџўўўџ№ърџШВ‹џŸx4џЖ™fџЁ{9џ†UџЏWџѕёъџџџџџџџџџътгџ‹\ џdџцмЪџшпЯџ y6џ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uй‡Vџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‡W!‡Vї‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‘dџдУЄџ§ќћџџџџџјѕёџСЈ{џЙžkџЇƒBџЈ…GџргНџїѓюџД•_џ‡VџБ’ZџіђьџџџџџџџџџћљїџвРЁџЃ}:џцмЪџ§ќњџбПŸџ‘cџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡V§‡V7џџџџџџџџџџџџџџџџџџџџџ‹X ‹X'ŠWO‰XГ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰XџБVџќњјџџџџџўўўџЯК—џ“fџЇƒBџйЩЌџћљїџџџџџўўўџЎŒOџ‰XџВ’Yџіђьџџџџџўўўџџџџџє№шџЙœhџцмЪџџџџџћњїџВ’Yџ‰Xџ‰Wџ‰Xџ‰Xџ‰Wџ‰Xџ‰XЋŠX=‹W‹XџџџџџџŒY!ŒYW‹X‹XЭ‹Xѕ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹XџкЫЏџџџџџўўўџќћњџгР џША†џєяцџџџџџўўўџџџџџљіђџІ:џ‹XџВUџѕёщџџџџџџџџџўўўџє№шџКœhџчмЪџџџџџџџџџргМџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xы‹XН‹XƒŒYQŒY!ŒYЯŒYыŒY§ŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџ^џљіђџўўўџџџџџў§§џјѕ№џќћљџџџџџўўўџџџџџџџџџытгџŸu*џŒYџБRџє№шџџџџџџџџџѓяцџцмЩџЛiџчмЪџџџџџџџџџћњїџ“cџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYћŒYчŒYЯŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џІ7џџџџџџџџџџџџџџџџџџџџџќћњџѕёщџџџџџџџџџўўўџеТ џ•fџŽ[џЊ„@џёъпџџџџџўўўџужРџ‘`џp!џмЬБџў§§џўўўџџџџџ˜iџŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џЖ•YџџџџџџџџџўўўџўўўџђьсџЬЖŒџдСžџџџџџўўўџљі№џМžfџ]џ\џ›mџчнЪџџџџџџџџџхкХџ\џ‘^џЅ|2џгРœџѕ№шџўўўџ™jџ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џРЃmџџџџџўўўџџџџџў§§џЯЙџqџьфеџўўўџџџџџшоЫџžqџ]џ]џ‘_џЬД‰џџџџџџџџџє№чџ“aџškџУЈuџЃy+џБŽLџужПџškџ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џРЃlџџџџџџџџџџџџџџџџџјѕ№џљѕ№џџџџџџџџџћњїџДNџ’_џ’_џ’_џ’_џ™iџёъоџџџџџџџџџгП™џЙ™]џёъоџѕёщџЧ­}џЃy)џ“aџ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џЖ“QџџџџџўўўџџџџџџџџџўўўџџџџџџџџџќќњџеР›џ”bџ“`џ“`џ“`џ“`џ“`џМbџїѓьџџџџџ§ќћџњјѓџў§ќџўўўџ§ќћџшнШџšjџ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ sџ§§ќџџџџџџџџџџџџџџџџџџџџџ§ќћџлЪЉџЁuџ”aџ”aџ”aџ”aџ”aџ”aџšjџЮЖ‰џћљѕџџџџџўўўџўўўџџџџџўўўџўўўџžpџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ–cї–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџвН“џќћјџџџџџџџџџў§ќџѓюуџЪА~џ­…8џРЁfџпбГџёыоџљїђџѕёшџчмХџЭЕ†џЉ.џžnџЦЋuџіђъџџўўџџџџџџџџџџџџџєяхџ–dџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cѕ—dq—d—dЫ—dљ˜dџ˜dџ˜dџ˜dџ—cџ˜dџ˜dџ—cџЅx!џбЙџцкУџцйРџдН“џНœ]џЬГ‚џэхдџ§§ћџџџџџўўўџџџџџўўўџџџџџџџџџ№щлџЯЗŠџ­ƒ4џО^џуеЛџћјєџўўўџіђъџЧЋuџ—cџ˜dџ˜dџ—cџ˜dџ˜dџ—cџ˜dџ—dї—dЩ—d›—dq™c™d™d'™dK™d‰™dг™dџ™dџ™dџ™dџ™dџ™dџ™eџžl џЇy!џІx џЈ{%џйЦ џќњїџџџџџџџџџџџџџџџџџџџџџџџџџўўўџўўўџџџџџќћјџячиџФЅkџЊ~*џДŒAџЛ™VџБ‰<џŸm џ™dџ™dџ™dџ™dџ™dџ™dџ™dе™dƒ™dG™d%˜d™dџџџџџџџџџџџџџџџ›f›fѕ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџŸl џвЛџ§§ћџџџџџџџџџџџџџсбГџЎƒ0џИ’IџраАџїђъџўў§џџџџџџџџџџџџџўў§џысЭџО›YџЁpџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›f§›f=џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџœgЏœgџœgџœgџœgџœgџœgџœgџœgџЇxџудЗџџџџџџџџџўўўџџџџџэуаџНšUџ n џЂpџРž\џциНџўўўџџџџџџџџџўўўџџџџџњјѓџЩ­uџžjџœgџœgџœgџœgџœgџœgџœgйœgџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџhOhѓhџhџhџhџhџhџhџЃqџйФœџўў§џџџџџџџџџџџџџўў§џѕ№чџлЧ џЈyџžjџЊ{ џгМџљі№џўўўџџџџџўўўџљѕяџЕ>џžiџhџhџhџhџhџhџhuџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŸj-ŸjгŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџЈxџеПџїѓъџўўўџџџџџџџџџџџџџџџџџїѓыџЯЖ‚џЎ‚*џ kџБ‡1џоЬЈџїєыџъоЧџЖ=џŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸj}џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ kw kћ jџ kџ kџ jџ kџ jџ kџ kџ jџЂnџУ \џфеЖџћљіџџџџџўўўџџџџџўўўџ§§ќџырЪџУЁ^џЈvџЅr џСŸYџБ….џЁlџ jџ kџ kџ jџ kџ kџ jџ kџ kуЁjџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЁlЁkхЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁmџЋ{џЧЇgџїѓъџџџџџџџџџџџџџўўўџўўўџђынџУЁ]џЃpџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkwџџџџџџџџџџџџџџџџџџџџџџџџџџџЃnЂm‹ЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЊzџХЄ_џЭБwџЮВyџЦІdџЙ‘?џЅrџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmйЂm3џџџџџџџџџџџџџџџџџџџџџџџџЃm7ЃnйЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnљЃn‡Єm џџџџџџџџџџџџџџџџџџџџџЄnQЄnщЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄn§ЄnЃЄnџџџџџџџџџџџџџџџџџџџџџџџџІp[ІpэІpџІpџІpџІpџІpџІpџІpџІoџІpџІpџІoџІpџІpџІoџІpџІpџІoџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІp§ІpЉІpџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџІpoІpчЇpџІpџЇpџЇpџЇpџЇpџЇpџІpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇp§ЇpЙЇpџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЇq ЇqoЇqэЇqџЇqџЇqџЇqџЇqѓЇqЇqcЇqХЇqћЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqћЇqЩЇqiЇqЃЇqѓЇqџЇqџЇqџЇqџЇq§ЇqЙЇq-џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЉrYЉrэЉrџЉrщЉrЁЉr/ЉrџџџЉrЈq+ЉrŸЉr§ЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrУЉr'џџџџџџџџџЉr'Јr‘ЉrпЉrћЉr§ЉrЉЉqџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЉriЉrЃЉsYЉs џџџџџџџџџџџџџџџЉs5ЉsчЉsџЉrџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsoџџџџџџџџџџџџџџџЉsЉrAЉsЉsЇЉsџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊsЊrЊsџџџџџџџџџџџџџџџџџџЊsЊsЗЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsяЊs?џџџџџџџџџџџџџџџџџџџџџЊr ЊsџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋtЋtЋtћЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtСЋt!џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌuOЌtчЌuџЋuџЌuџЌuџЋtџЌuџЌuџЌu—Ћu џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌuЌuЯЌuџЌuџЌuџЌuџЌuџЌuџЌuѕЌuoЋtџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ(@€ BџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџvG1vGџvGџvGџvGџvGџvGџvGџvGџvGџvGџvGгџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџvG‹vGџvGџvGџvGџvGџvGџvGџvGџvGџvGџvGџvG%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџwHwHхwHџwHџwHџwHџwHџwHџwHџwHџwHџwHџwHџwHyџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџwHCwHџwHџwHџwHџwHџwHџwHџwHџwHџwHџwHџwHџwHЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџxIxIџxIџxIџxIџxIџxIџxIџxIџxIџxIџxIџxIџxIџxIџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџyI yIёyIџyIџyIџyIџyIџyIџyIџyIџyIџyIџyIџyIџyIџyIoџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџyIџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџyJ#yJгyJƒyJџџџџџџџџџџџџџџџџџџџџџџџџyJUyJџyJџyJџyJџyJџyJџyJџyJџyJџyJџyJџyJџyJџyJџyJСџџџџџџџџџџџџџџџџџџџџџџџџџџџyJ;yJНyJ­yJџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџzJ#zJуzJџzJџzJяzJzJџџџџџџџџџџџџzJ zJazJзzJџzJџzJџzJџzJџzJџzJџzJџzJџzJџzJџzJџzJџzJџzJ§zJgzJџџџџџџџџџџџџџџџzJ1zJГzJџzJџzJџzJЋzJџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ{K#{Kу{Kџ{Kџ{Kџ{Kџ{Kџ{Kэ{Ky{K {K {Kw{Kы{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{Kѓ{K›{K!џџџ{K){KЋ{Kџ{Kџ{Kџ{Kџ{Kџ{Kџ{KЋ{Kџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ{L%{Lу{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lы{Lч{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lћ{LП{L§{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{Lџ{LЋ{Lџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ|L%|Lу|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|Lџ|LЋ|Lџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ}M%}Mх}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}Mџ}MЋ}Mџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ~N'~Nх~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~Nџ~NЋ~NџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџNNнNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџNџN{џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ€O“€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oџ€Oћ€O'џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџPPѕPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџžx;џžx:џ„TџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџPџP™џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџQ‹QџQџQџQџQџQџQџQџQџQџQџQџQџQџQџQџ‚RџЂCџСЉџЫЗ•џИoџ‡Y џQџQџQџQџгТЅџџџџџњїєџдФЈџЁ}@џQџQџQџQџQџQџQџQџQџQџQџQџQџQџQџQџQџQѕQџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‚Q‚Qѓ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚RџБ’_џюшнџџџџџџџџџџџџџџџџџшпаџ†Vџ‚Qџ‚Qџ‚QџгТЅџџџџџџџџџџџџџџџџџаО џ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Qџ‚Q…џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџƒR…ƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџdџнаЙџџџџџџџџџџџџџџџџџџџџџџџџџџџџџОЄyџƒRџƒRџƒRџгТЅџџџџџџџџџџџџџџџџџлЬДџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRџƒRыƒRџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ„S„Sё„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџœu2џѓячџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџщсвџ„Sџ„Sџ„SџдТЅџџџџџџџџџџџџџџџџџлЬДџ„Sџ„Sџ‘eџ—n(џ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Sџ„Soџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ…T…Tп…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…TџŸy7џљіђџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџмЮЖџ•j!џ…Tџ…Tџ…TџдУЅџџџџџџџџџџџџџџџџџлЭДџ…Tџ…Tџ›r-џѕёъџšr,џ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…Tџ…T1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ†Ua†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ–l"џіђьџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓюхџЎŽUџ†Uџ”iџŽ`џ†Uџ†UџжЦЉџџџџџџџџџџџџџџџџџлЭДџ†Uџ†Uџ›s-џџџџџђьуџ“gџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†Uџ†UЁџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‡Vй‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‰YџчнЬџџџџџџџџџџџџџџџџџџџџџџџџџ§ќћџЧБ‰џ^ џ^ џХ­„џћљіџœt-џ‡Vџ‡VџмЭДџџџџџџџџџџџџџџџџџьфзџ•jџ‡Vџœt-џџџџџџџџџрдОџˆWџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vџ‡Vї‡VџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџˆW;ˆWџˆWџˆWџˆWџˆWџˆWџˆWџˆWџˆWџНЂrџџџџџџџџџџџџџџџџџючкџ—m!џЅ?џu-џŠZџЙjџѕёщџџџџџџџџџœs*џˆWџˆWџмЮДџџџџџџџџџџџџџџџџџџџџџњјѕџЕ–`џu-џџџџџџџџџџџџџЙjџˆWџˆWџˆWџˆWџˆWџˆWџˆWџˆWџˆWџˆW]џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ‰X1‰XЛ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ` џїѓэџџџџџџџџџџџџџџџџџЈƒBџ‰Xџ‰Xџ­‹Nџьуеџџџџџџџџџџџџџџџџџ–jџ‰Xџ‰XџмЮДџџџџџџџџџџџџџџџџџџџџџџџџџеФЅџžu-џџџџџџџџџџџџџіђыџaџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰Xџ‰XЛ‰Xџџџџџџџџџџџџџџџџџџџџџџџџџџџ‹X/‹X…‹Xл‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹XџКœhџџџџџџџџџџџџџџџџџњјѕџŒYџЁx1џтеПџџџџџџџџџџџџџџџџџџџџџќћљџ[џ‹Xџ‹XџмЬБџџџџџџџџџџџџџџџџџџџџџџџџџжФЅџŸu-џџџџџџџџџџџџџџџџџРЄtџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xџ‹Xљ‹XЗ‹Xg‹XџџџџџџџџџŒY-ŒYƒŒYйŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџфиУџџџџџџџџџџџџџџџџџђэуџзХІџџўўџџџџџџџџџџџџџџџџџџџџџџџџџытгџŒYџŒYџŒYџжФЅџџџџџџџџџџџџџџџџџџџџџџџџџжФЅџ v-џџџџџџџџџџџџџџџџџэциџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYџŒYЩŒY{ŒY+ZџZџZџZџZџZџZџZџZџZџZџZџZџZџZџ“cџў§ќџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџвОšџZџZџZџзХЅџџџџџџџџџџџџџџџџџыугџцлЧџзХЅџЁw-џџџџџџџџџџџџџџџџџџџџџ™kџZџZџZџZџZџZџZџZџZџZџZџZџZџZџZџŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џЊ„@џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџД’VџŽ[џŽ[џŽ[џЭЖŽџџџџџџџџџџџџџџџџџкЪЌџŽ\џ u)џЂx-џџџџџџџџџџџџџџџџџџџџџ›nџŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џŽ[џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џО jџџџџџџџџџџџџџџџџџџџџџџџџџџџџџєяцџЭЗŽџџџџџџџџџџџџџџџџџћљѕџ•d џ\џ\џ\џЗ–ZџџџџџџџџџџџџџџџџџйШЉџ\џ\џ“aџЩА„џћљіџџџџџџџџџџџџџœoџ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ\џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џЪВ†џџџџџџџџџџџџџџџџџџџџџў§ќџЭЗџ•e џРЄoџџџџџџџџџџџџџџџџџгО™џ]џ]џ]џ]џ˜hџќћљџџџџџџџџџџџџџсдЛџ]џ]џ’`џ]џpџиЦІџџўўџџџџџpџ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ]џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џвН–џџџџџџџџџџџџџџџџџџџџџћљіџ”cџžrџєяцџџџџџџџџџџџџџќћљџŸr џ‘^џ‘^џ‘^џ‘^џ‘^џжУ џџџџџџџџџџџџџїѓьџ•d џ‘^џЉ7џузПџІ}1џ‘^џЌ†?џърЮџžqџ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ‘^џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џвН–џџџџџџџџџџџџџџџџџџџџџџџџџюцзџњїѓџџџџџџџџџџџџџџџџџЭЕ‰џ’_џ’_џ’_џ’_џ’_џ’_џŸrџњјєџџџџџџџџџџџџџзФЁџ”bџИ—Yџџџџџўў§џвМ•џ›lџ“`џ”bџ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ’_џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џЪБ‚џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџюхжџ–eџ“`џ“`џ“`џ“`џ“`џ“`џ“`џФЈtџџџџџџџџџџџџџџџџџєяхџїѓыџџџџџџџџџџџџџјѕяџФЈsџ”bџ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ“`џ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџЙ—WџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїєэџЃw$џ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”bџрвЖџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅz(џ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ”aџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџžpџ§ќњџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџїѓыџЋ‚4џ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ›kџысЯџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЁsџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ•bџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџгО•џџџџџџџџџџџџџџџџџџџџџџџџџџџџџшмЦџЄx"џ—eџГŽGџаКŽџцкТџѕ№цџљіёџѕ№чџьтаџдП–џЕ‘Lџ—dџ–cџŸpџшнЧџџџџџџџџџџџџџџџџџџџџџџџџџџџџџњјѓџ—eџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ–cџ—dЯ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџžoџысЮџџџџџџџџџџџџџџџџџјєэџЦЋtџ›j џХЉrџєяфџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџячзџЛšYџ˜eџšhџгО”џџўўџџџџџџџџџџџџџџџџџџџџџжТšџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—dџ—d§—dЯџџџ™d™da™dЉ™dё™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџœiџПž`џдО”џгМџМ™Wџžk џ­‚2џячиџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџысЮџГ‹@џ™dџЏ†8џтгЗџў§ќџџџџџўў§џпЯАџŸmџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dџ™dы™dЅ™d[™dџџџџџџџџџџџџџџџšeše?še‡šeйšeџšeџšeџšeџšeџšeџšeџšeџšeџšeџšeџšeџšeџšeџДBџћњіџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџпЮЎџЇz"џšeџŸl џЇz"џЁpџšeџšeџšeџšeџšeџšeџšeџšeџšeџšeџšeсše}še5šeџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ›f+›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџЃrџјѕюџџџџџџџџџџџџџџџџџџџџџђынџЖDџЏ…4џШЊrџ№шиџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћњіџЯЕ„џ m џ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›fџ›feџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџœgЧœgџœgџœgџœgџœgџœgџœgџœgџœgџœgџœgџœgџХІiџџџџџџџџџџџџџџџџџџџџџџџџџ№шиџІvџœgџœgџžiџЦЇkџљѕяџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџѓэсџПœYџœhџœgџœgџœgџœgџœgџœgџœgџœgџœgџœgљœgџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџh_hџhџhџhџhџhџhџhџhџhџhџhџЫЏxџџџџџџџџџџџџџџџџџџџџџџџџџџџџџіёшџОœWџhџhџhџІvџмЩЃџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџёълџЃqџhџhџhџhџhџhџhџhџhџhџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџžižiпžiџžiџžiџžiџžiџžiџžiџžiџžiџžiџžiџЎ‚*џ§ћљџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџчйПџБ†1џžiџžiџžiџЗBџ№шиџџџџџџџџџџџџџџџџџџџџџѕяфџЌ~$џžiџžiџžiџžiџžiџžiџžiџžiџžiџžiџži+џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџŸjŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџЗ?џіёчџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџўў§џкЦџЈyџŸjџŸjџЁnџЮГ}џќњіџџџџџџџџџ№шзџЌ~#џŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸjџŸj1џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ k kэ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџІuџиС•џў§ћџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџћјѓџЬАwџЂnџ kџ kџ­#џфеЖџпЭЉџІtџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kџ kЕџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЁk‰ЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџАƒ*џфеЗџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђълџПšQџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁkџЁk;џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЁlЁlїЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџОšOџђълџџџџџџџџџџџџџџџџџџџџџџџџџџџџџў§ћџфдЕџА„*џЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlџЁlСџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЂmЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЄpџЪЌnџзС’џоЫЄџпЬІџйУ–џЭАuџН—IџЇvџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmџЂmEџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЃn+Ѓn§ЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnџЃnЩџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЄnЏЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnџЄnQџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЅoŸЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoџЅoљЅoIџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџІpІpŸІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpџІpљІpKџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЇpЇpЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpџЇpљЇpMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЇqЇqЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇq§ЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqџЇqљЇqMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЈrЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrсЈr[ЈrЈr{ЈrчЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrщЈrƒЈrЈreЈrпЈrџЈrџЈrџЈrџЈrџЈrџЈrџЈrљЈrMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЉrЉrџЉrџЉrџЉrџЉrяЉrsЉr џџџџџџџџџЉrЉrIЉrЉЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrџЉrЩЉrEЉrџџџџџџџџџЉrЉrcЉrпЉrџЉrџЉrџЉrџЉrљЉrMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЉs›ЉsџЉsљЉsЉsџџџџџџџџџџџџџџџџџџџџџЉsЉsёЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉsџЉs_џџџџџџџџџџџџџџџџџџџџџЉsЉsaЉsлЉsџЉsљЉsOџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊsoЊs#џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЊsЋЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџЊsџџџџџџџџџџџџџџџџџџџџџџџџџџџЊsЊs_ЊsMџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋt]ЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtзџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЋtЋt§ЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋtџЋt•џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌuУЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuSџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЌuuЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌuџЌu§Ќuџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ­v)­vџ­vџ­vџ­vџ­vџ­vџ­vџ­vџ­vџ­vџ­vЫџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџUEFITool-0.28.0/uefitool.pro000066400000000000000000000017501363631100000155330ustar00rootroot00000000000000QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = UEFITool TEMPLATE = app SOURCES += uefitool_main.cpp \ uefitool.cpp \ searchdialog.cpp \ types.cpp \ descriptor.cpp \ ffs.cpp \ peimage.cpp \ ffsengine.cpp \ treeitem.cpp \ treemodel.cpp \ messagelistitem.cpp \ guidlineedit.cpp \ LZMA/LzmaCompress.c \ LZMA/LzmaDecompress.c \ LZMA/SDK/C/LzFind.c \ LZMA/SDK/C/LzmaDec.c \ LZMA/SDK/C/LzmaEnc.c \ LZMA/SDK/C/Bra86.c \ Tiano/EfiTianoDecompress.c \ Tiano/EfiTianoCompress.c \ Tiano/EfiTianoCompressLegacy.c HEADERS += uefitool.h \ searchdialog.h \ basetypes.h \ descriptor.h \ gbe.h \ me.h \ ffs.h \ peimage.h \ types.h \ ffsengine.h \ treeitem.h \ treemodel.h \ messagelistitem.h \ guidlineedit.h \ version.h \ LZMA/LzmaCompress.h \ LZMA/LzmaDecompress.h \ LZMA/x86Convert.h \ Tiano/EfiTianoDecompress.h \ Tiano/EfiTianoCompress.h FORMS += uefitool.ui \ searchdialog.ui RC_FILE = uefitool.rc ICON = uefitool.icns UEFITool-0.28.0/uefitool.rc000066400000000000000000000000511363631100000153300ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "uefitool.ico"UEFITool-0.28.0/uefitool.ui000066400000000000000000000377201363631100000153560ustar00rootroot00000000000000 UEFITool 0 0 800 600 0 0 true UEFITool 0 0 0 0 0 0 0 Qt::Vertical Qt::Horizontal Structure 0 5 5 5 5 0 0 10 false true 200 true Information 0 5 5 5 5 false false true false Messages 0 5 5 5 5 true 0 0 800 31 &File H&elp &Action &Capsule &Image &Region &Padding &Volume &File &Section &Messages false Insert &after... Insert an object from file after selected object Ctrl+Shift+I false Insert b&efore... Insert object from file before selected object Ctrl+Alt+I false Rep&lace as is... Replace selected object as is with an object from file Ctrl+R false E&xtract as is... Extract selected object as is to file Ctrl+E false Extract &body... Extract body of selected object to file Ctrl+Shift+E false Re&move Remove selected object Ctrl+Del &Open image file... Open image file Ctrl+O false Insert &into... Insert object from file into selected object Ctrl+I false &Save image file... Save modified image file Ctrl+S false &Rebuild Rebuild selected object Ctrl+Space &About UEFITool F1 QAction::AboutRole About &Qt QAction::AboutQtRole &Quit QAction::QuitRole false Sear&ch... Ctrl+F Cl&ear Clear messages Ctrl+Backspace false Replace b&ody... Replace body of selected object with a data from file Ctrl+Shift+R &Copy Ctrl+Shift+C C&opy all Ctrl+Alt+C &Open image file in new window... Open image file in new window Ctrl+Shift+O false &Do not rebuild Ctrl+Shift+Space UEFITool-0.28.0/uefitool_main.cpp000066400000000000000000000016521363631100000165220ustar00rootroot00000000000000/* uefitool_main.cpp Copyright (c) 2014, Nikolaj Schlej. All rights reserved. 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. */ #include #include #include "uefitool.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); a.setOrganizationName("CodeRush"); a.setOrganizationDomain("coderush.me"); a.setApplicationName("UEFITool"); UEFITool w; w.setProgramPath(a.arguments().at(0)); if (a.arguments().length() > 1) w.openImageFile(a.arguments().at(1)); w.show(); return a.exec(); }UEFITool-0.28.0/unixbuild.sh000077500000000000000000000071601363631100000155260ustar00rootroot00000000000000#!/bin/bash UTARGET=$(uname) BINSUFFIX="" if [ "$UTARGET" = "Darwin" ]; then export UPLATFORM="mac" elif [ "$UTARGET" = "Linux" ]; then export UPLATFORM="linux_$(uname -m)" elif [ "${UTARGET/MINGW32/}" != "$UTARGET" ]; then export UPLATFORM="win32" export BINSUFFIX=".exe" else # Fallback to something... export UPLATFORM="$UTARGET" fi if [ "$UPLATFORM" = "mac" ]; then if [ ! -d /opt/qt56sm ]; then curl -L -o /tmp/qt-5.6.3-static-mac.zip https://github.com/distdb/qtbuilds/blob/master/qt-5.6.3-static-mac.zip?raw=true || exit 1 qtsum=$(shasum -a 256 /tmp/qt-5.6.3-static-mac.zip | cut -f1 -d' ') qtexpsum="214d22d8572ea6162753c8dd251d79275f3b22d49204718c637d722409e0cfcb" if [ "$qtsum" != "$qtexpsum" ]; then echo "Qt hash $qtsum does not match $qtexpsum" exit 1 fi sudo mkdir -p /opt || exit 1 cd /opt || exit 1 sudo unzip -q /tmp/qt-5.6.3-static-mac.zip || exit 1 cd - || exit 1 fi export PATH="/opt/qt56sm/bin:$PATH" elif [ "$UPLATFORM" = "win32" ]; then # Install missing dependencies pacman -S --noconfirm --needed zip unzip curl perl mingw-w64-i686-toolchain mingw-w64-i686-cmake || exit 1 # Fix PATH to support running shasum. export PATH="/usr/bin/core_perl:$PATH" if [ ! -d "/c/Qt/5.6/mingw49_32_release_static/" ]; then curl -L -o /tmp/qt-5.6.3-static-win32.zip https://github.com/distdb/qtbuilds/blob/master/qt-5.6.3-static-win32.zip?raw=true || exit 1 qtsum=$(shasum -a 256 /tmp/qt-5.6.3-static-win32.zip | cut -f1 -d' ') qtexpsum="bcd85145d6fed00da37498c08c49d763c6fa883337f754880b5c786899e6bb1d" if [ "$qtsum" != "$qtexpsum" ]; then echo "Qt hash $qtsum does not match $qtexpsum" exit 1 fi mkdir -p /c/Qt/5.6 || exit 1 cd /c/Qt/5.6 || exit 1 unzip -q /tmp/qt-5.6.3-static-win32.zip || exit 1 cd - || exit 1 fi export PATH="/c/Qt/5.6/mingw49_32_release_static/bin:$PATH" fi echo "Attempting to build UEFITool for ${UPLATFORM}..." UEFITOOL_VER=$(cat version.h | grep PROGRAM_VERSION | cut -d'"' -f2) build_tool() { echo "Building $1 $2" # Check version if [ "$(echo "$2" | grep '^[0-9]*\.[0-9]*\.[0-9]*$')" != "$2" ]; then echo "Invalid $1 version!" exit 1 fi # Tools are in subdirectories if [ "$1" != "UEFITool" ]; then cd "$1" || exit 1 fi # Build # -flto is flawed on CI atm if [ "$UPLATFORM" = "mac" ]; then qmake $3 QMAKE_CXXFLAGS+=-flto QMAKE_LFLAGS+=-flto CONFIG+=optimize_size || exit 1 elif [ "$UPLATFORM" = "win32" ]; then qmake $3 QMAKE_CXXFLAGS="-static -flto -Os" QMAKE_LFLAGS="-static -flto -Os" CONFIG+=optimize_size CONFIG+=staticlib CONFIG+=static || exit 1 else qmake $3 CONFIG+=optimize_size || exit 1 fi make || exit 1 # Move the binary out of the dir if [ "$UPLATFORM" = "win32" ]; then mv "release/${1}${BINSUFFIX}" "${1}${BINSUFFIX}" || exit 1 fi # Archive if [ "$1" = "UEFITool" ]; then if [ "$UPLATFORM" = "mac" ]; then strip -x UEFITool.app/Contents/MacOS/UEFITool || exit 1 zip -qry dist/"${1}_${2}_${UPLATFORM}.zip" UEFITool.app ${4} || exit 1 else strip -x "${1}${BINSUFFIX}" || exit 1 zip -qry dist/"${1}_${2}_${UPLATFORM}.zip" "${1}${BINSUFFIX}" ${4} || exit 1 fi else strip -x "${1}${BINSUFFIX}" || exit 1 zip -qry ../dist/"${1}_${2}_${UPLATFORM}.zip" "${1}${BINSUFFIX}" ${4} || exit 1 fi # Return to parent if [ "$1" != "UEFITool" ]; then cd - || exit 1 fi } rm -rf dist mkdir -p dist || exit 1 build_tool UEFITool "$UEFITOOL_VER" uefitool.pro build_tool UEFIPatch "$UEFITOOL_VER" uefipatch.pro patches*.txt build_tool UEFIReplace "$UEFITOOL_VER" uefireplace.pro exit 0 UEFITool-0.28.0/version.h000066400000000000000000000010721363631100000150160ustar00rootroot00000000000000/* types.h Copyright (c) 2015, Nikolaj Schlej. All rights reserved. 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. */ #ifndef VERSION_H #define VERSION_H #define PROGRAM_VERSION "0.28.0" #endif // VERSION_H