From 4afe74850db36279f86782e13ff73577a10b8a9b Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Tue, 8 Oct 2013 09:07:03 +0200 Subject: [PATCH] Version 0.2.0 Initial public commit --- .gitattributes | 22 + .gitignore | 215 ++++ LZMA/._LzmaCompress.c | Bin 0 -> 4096 bytes LZMA/._LzmaCompress.h | Bin 0 -> 4096 bytes LZMA/LzmaCompress.c | 110 ++ LZMA/LzmaCompress.h | 39 + LZMA/LzmaDecompress.c | 159 +++ LZMA/LzmaDecompress.h | 99 ++ LZMA/SDK/C/7zVersion.h | 7 + LZMA/SDK/C/Bra.h | 68 ++ LZMA/SDK/C/Bra86.c | 85 ++ LZMA/SDK/C/CpuArch.h | 155 +++ LZMA/SDK/C/LzFind.c | 761 ++++++++++++ LZMA/SDK/C/LzFind.h | 115 ++ LZMA/SDK/C/LzHash.h | 54 + LZMA/SDK/C/LzmaDec.c | 999 ++++++++++++++++ LZMA/SDK/C/LzmaDec.h | 231 ++++ LZMA/SDK/C/LzmaEnc.c | 2268 ++++++++++++++++++++++++++++++++++++ LZMA/SDK/C/LzmaEnc.h | 80 ++ LZMA/SDK/C/Types.h | 256 ++++ LZMA/UefiLzma.h | 31 + Tiano/EfiCompress.c | 1598 +++++++++++++++++++++++++ Tiano/EfiTianoCompress.h | 101 ++ Tiano/EfiTianoDecompress.c | 994 ++++++++++++++++ Tiano/EfiTianoDecompress.h | 185 +++ Tiano/TianoCompress.c | 1753 ++++++++++++++++++++++++++++ basetypes.h | 86 ++ descriptor.cpp | 34 + descriptor.h | 163 +++ ffs.cpp | 151 +++ ffs.h | 415 +++++++ main.cpp | 28 + parse.cpp | 520 +++++++++ parse.h | 28 + treeitem.cpp | 155 +++ treeitem.h | 68 ++ treeitemtypes.h | 80 ++ treemodel.cpp | 354 ++++++ treemodel.h | 60 + uefitool.cpp | 960 +++++++++++++++ uefitool.h | 84 ++ uefitool.icns | Bin 0 -> 213068 bytes uefitool.ico | Bin 0 -> 32038 bytes uefitool.pro | 34 + uefitool.rc | 1 + uefitool.ui | 211 ++++ 46 files changed, 13817 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LZMA/._LzmaCompress.c create mode 100644 LZMA/._LzmaCompress.h create mode 100644 LZMA/LzmaCompress.c create mode 100644 LZMA/LzmaCompress.h create mode 100644 LZMA/LzmaDecompress.c create mode 100644 LZMA/LzmaDecompress.h create mode 100644 LZMA/SDK/C/7zVersion.h create mode 100644 LZMA/SDK/C/Bra.h create mode 100644 LZMA/SDK/C/Bra86.c create mode 100644 LZMA/SDK/C/CpuArch.h create mode 100644 LZMA/SDK/C/LzFind.c create mode 100644 LZMA/SDK/C/LzFind.h create mode 100644 LZMA/SDK/C/LzHash.h create mode 100644 LZMA/SDK/C/LzmaDec.c create mode 100644 LZMA/SDK/C/LzmaDec.h create mode 100644 LZMA/SDK/C/LzmaEnc.c create mode 100644 LZMA/SDK/C/LzmaEnc.h create mode 100644 LZMA/SDK/C/Types.h create mode 100644 LZMA/UefiLzma.h create mode 100644 Tiano/EfiCompress.c create mode 100644 Tiano/EfiTianoCompress.h create mode 100644 Tiano/EfiTianoDecompress.c create mode 100644 Tiano/EfiTianoDecompress.h create mode 100644 Tiano/TianoCompress.c create mode 100644 basetypes.h create mode 100644 descriptor.cpp create mode 100644 descriptor.h create mode 100644 ffs.cpp create mode 100644 ffs.h create mode 100644 main.cpp create mode 100644 parse.cpp create mode 100644 parse.h create mode 100644 treeitem.cpp create mode 100644 treeitem.h create mode 100644 treeitemtypes.h create mode 100644 treemodel.cpp create mode 100644 treemodel.h create mode 100644 uefitool.cpp create mode 100644 uefitool.h create mode 100644 uefitool.icns create mode 100644 uefitool.ico create mode 100644 uefitool.pro create mode 100644 uefitool.rc create mode 100644 uefitool.ui diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9d6bd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,215 @@ +################# +## 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 diff --git a/LZMA/._LzmaCompress.c b/LZMA/._LzmaCompress.c new file mode 100644 index 0000000000000000000000000000000000000000..c8938291eb09c366b177a71a0c9181906bb7d51f GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDsI z=wO%wWb>nG0m{L|#gp@M^%4sTa#Hm|QY%Va^OEyZGV{`b3Q9}TbS$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103v0xDsI z=wO%wWb>nG0m{L|#gp@M^%4sTa#Hm|QY%Va^OEyZGV{`b3Q9}TbS + +#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) + +static void * AllocForLzma(void *p, size_t size) { return malloc(size); } +static void FreeForLzma(void *p, void *address) { free(address); } +static ISzAlloc SzAllocForLzma = { &AllocForLzma, &FreeForLzma }; + +SRes OnProgress(void *p, UInt64 inSize, UInt64 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; + } +} + +INT32 + EFIAPI + LzmaCompress ( + CONST VOID *Source, + SizeT SourceSize, + VOID *Destination, + SizeT *DestinationSize + ) +{ + SRes LzmaResult; + CLzmaEncProps props; + SizeT propsSize = LZMA_PROPS_SIZE; + SizeT destLen = SourceSize + SourceSize / 3 + 128; + + if (*DestinationSize < destLen) + { + *DestinationSize = destLen; + return ERR_BUFFER_TOO_SMALL; + } + + LzmaEncProps_Init(&props); + props.dictSize = LZMA_DICTIONARY_SIZE; + props.level = 9; + props.fb = 273; + + LzmaResult = LzmaEncode( + (Byte*)((UINT8*)Destination + LZMA_HEADER_SIZE), + &destLen, + Source, + SourceSize, + &props, + (UINT8*)Destination, + &propsSize, + props.writeEndMark, + &g_ProgressCallback, + &SzAllocForLzma, + &SzAllocForLzma); + + *DestinationSize = destLen + LZMA_HEADER_SIZE; + + SetEncodedSizeOfBuf((UINT64)SourceSize, Destination); + + if (LzmaResult == SZ_OK) { + return ERR_SUCCESS; + } else { + return ERR_INVALID_PARAMETER; + } +} + + + diff --git a/LZMA/LzmaCompress.h b/LZMA/LzmaCompress.h new file mode 100644 index 0000000..061ac22 --- /dev/null +++ b/LZMA/LzmaCompress.h @@ -0,0 +1,39 @@ +/* LZMA Compress Header + + 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. + +*/ + +#ifndef __LZMACOMPRESS_H__ +#define __LZMACOMPRESS_H__ + +#include "Sdk/C/Types.h" +#include "../basetypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LZMA_DICTIONARY_SIZE 0x800000 +#define _LZMA_SIZE_OPT + +INT32 +EFIAPI +LzmaCompress ( + const VOID *Source, + SizeT SourceSize, + VOID *Destination, + SizeT *DestinationSize + ); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/LZMA/LzmaDecompress.c b/LZMA/LzmaDecompress.c new file mode 100644 index 0000000..43b671c --- /dev/null +++ b/LZMA/LzmaDecompress.c @@ -0,0 +1,159 @@ +/* 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) { return malloc(size); } +static void FreeForLzma(void *p, void *address) { 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. + +*/ +INT32 + EFIAPI + LzmaGetInfo ( + CONST VOID *Source, + UINT32 SourceSize, + UINT32 *DestinationSize + ) +{ + UInt64 DecodedSize; + + ASSERT(SourceSize >= LZMA_HEADER_SIZE); + + DecodedSize = GetDecodedSizeOfBuf((UINT8*)Source); + + *DestinationSize = (UINT32)DecodedSize; + return ERR_SUCCESS; +} + +/* +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). +*/ +INT32 + 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( + Destination, + &DecodedBufSize, + (Byte*)((UINT8*)Source + LZMA_HEADER_SIZE), + &EncodedDataSize, + Source, + LZMA_PROPS_SIZE, + LZMA_FINISH_END, + &Status, + &SzAllocForLzma + ); + + if (LzmaResult == SZ_OK) { + return ERR_SUCCESS; + } else { + return ERR_INVALID_PARAMETER; + } +} + diff --git a/LZMA/LzmaDecompress.h b/LZMA/LzmaDecompress.h new file mode 100644 index 0000000..b22da39 --- /dev/null +++ b/LZMA/LzmaDecompress.h @@ -0,0 +1,99 @@ +/* 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" + +#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) + +#ifdef __cplusplus +extern "C" { +#endif + +UINT64 +EFIAPI +LShiftU64 ( + UINT64 Operand, + UINT32 Count + ); + +/* + 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. + +*/ +INT32 +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). +*/ +INT32 +EFIAPI +LzmaDecompress ( + const VOID *Source, + UINT32 SourceSize, + VOID *Destination + ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/LZMA/SDK/C/7zVersion.h b/LZMA/SDK/C/7zVersion.h new file mode 100644 index 0000000..9d99c5d --- /dev/null +++ b/LZMA/SDK/C/7zVersion.h @@ -0,0 +1,7 @@ +#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 diff --git a/LZMA/SDK/C/Bra.h b/LZMA/SDK/C/Bra.h new file mode 100644 index 0000000..5748c1c --- /dev/null +++ b/LZMA/SDK/C/Bra.h @@ -0,0 +1,68 @@ +/* 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 diff --git a/LZMA/SDK/C/Bra86.c b/LZMA/SDK/C/Bra86.c new file mode 100644 index 0000000..1ee0e70 --- /dev/null +++ b/LZMA/SDK/C/Bra86.c @@ -0,0 +1,85 @@ +/* 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; +} diff --git a/LZMA/SDK/C/CpuArch.h b/LZMA/SDK/C/CpuArch.h new file mode 100644 index 0000000..01930c7 --- /dev/null +++ b/LZMA/SDK/C/CpuArch.h @@ -0,0 +1,155 @@ +/* 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 diff --git a/LZMA/SDK/C/LzFind.c b/LZMA/SDK/C/LzFind.c new file mode 100644 index 0000000..ea1c210 --- /dev/null +++ b/LZMA/SDK/C/LzFind.c @@ -0,0 +1,761 @@ +/* LzFind.c -- Match finder for LZ algorithms +2009-04-22 : Igor Pavlov : Public doma*/ + +#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; + } +} diff --git a/LZMA/SDK/C/LzFind.h b/LZMA/SDK/C/LzFind.h new file mode 100644 index 0000000..010c4b9 --- /dev/null +++ b/LZMA/SDK/C/LzFind.h @@ -0,0 +1,115 @@ +/* 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 diff --git a/LZMA/SDK/C/LzHash.h b/LZMA/SDK/C/LzHash.h new file mode 100644 index 0000000..f3e8996 --- /dev/null +++ b/LZMA/SDK/C/LzHash.h @@ -0,0 +1,54 @@ +/* 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 diff --git a/LZMA/SDK/C/LzmaDec.c b/LZMA/SDK/C/LzmaDec.c new file mode 100644 index 0000000..54bd103 --- /dev/null +++ b/LZMA/SDK/C/LzmaDec.c @@ -0,0 +1,999 @@ +/* LzmaDec.c -- LZMA Decoder +2009-09-20 : Igor Pavlov : Public doma*/ + +#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 withlast 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; +} diff --git a/LZMA/SDK/C/LzmaDec.h b/LZMA/SDK/C/LzmaDec.h new file mode 100644 index 0000000..bf7f084 --- /dev/null +++ b/LZMA/SDK/C/LzmaDec.h @@ -0,0 +1,231 @@ +/* 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 diff --git a/LZMA/SDK/C/LzmaEnc.c b/LZMA/SDK/C/LzmaEnc.c new file mode 100644 index 0000000..ef7f4ac --- /dev/null +++ b/LZMA/SDK/C/LzmaEnc.c @@ -0,0 +1,2268 @@ +/* LzmaEnc.c -- LZMA Encoder +2010-04-16 : Igor Pavlov : Public doma*/ + +#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; + Bool btMode; + if (!RangeEnc_Alloc(&p->rc, alloc)) + return SZ_ERROR_MEM; + btMode = (p->matchFinderBase.btMode != 0); + #ifndef _7ZIP_ST + 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 + pp = 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; +} diff --git a/LZMA/SDK/C/LzmaEnc.h b/LZMA/SDK/C/LzmaEnc.h new file mode 100644 index 0000000..200d60e --- /dev/null +++ b/LZMA/SDK/C/LzmaEnc.h @@ -0,0 +1,80 @@ +/* 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 diff --git a/LZMA/SDK/C/Types.h b/LZMA/SDK/C/Types.h new file mode 100644 index 0000000..90f6752 --- /dev/null +++ b/LZMA/SDK/C/Types.h @@ -0,0 +1,256 @@ +/* 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 diff --git a/LZMA/UefiLzma.h b/LZMA/UefiLzma.h new file mode 100644 index 0000000..2ef4b0e --- /dev/null +++ b/LZMA/UefiLzma.h @@ -0,0 +1,31 @@ +/* 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__ + diff --git a/Tiano/EfiCompress.c b/Tiano/EfiCompress.c new file mode 100644 index 0000000..dda0436 --- /dev/null +++ b/Tiano/EfiCompress.c @@ -0,0 +1,1598 @@ +/* + +Copyright (c) 2006, 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: + + EfiCompress.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. + +--*/ + +#include +#include +#include "EfiTianoCompress.h" + +// +// Macro Definitions +// + +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 +#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 +UINT32 +AllocateMemory ( + ); + +STATIC +VOID +FreeMemory ( + ); + +STATIC +VOID +InitSlide ( + ); + +STATIC +NODE +Child ( + NODE q, + UINT8 c + ); + +STATIC +VOID +MakeChild ( + NODE q, + UINT8 c, + NODE r + ); + +STATIC +VOID +Split ( + NODE Old + ); + +STATIC +VOID +InsertNode ( + ); + +STATIC +VOID +DeleteNode ( + ); + +STATIC +VOID +GetNextMatch ( + ); + +STATIC +UINT32 +Encode ( + ); + +STATIC +VOID +CountTFreq ( + ); + +STATIC +VOID +WritePTLen ( + INT32 n, + INT32 nbit, + INT32 Special + ); + +STATIC +VOID +WriteCLen ( + ); + +STATIC +VOID +EncodeC ( + INT32 c + ); + +STATIC +VOID +EncodeP ( + UINT32 p + ); + +STATIC +VOID +SendBlock ( + ); + +STATIC +VOID +Output ( + UINT32 c, + UINT32 p + ); + +STATIC +VOID +HufEncodeStart ( + ); + +STATIC +VOID +HufEncodeEnd ( + ); + +STATIC +VOID +MakeCrcTable ( + ); + +STATIC +VOID +PutBits ( + INT32 n, + UINT32 x + ); + +STATIC +INT32 +FreadCrc ( + UINT8 *p, + INT32 n + ); + +STATIC +VOID +InitPutBits ( + ); + +STATIC +VOID +CountLen ( + INT32 i + ); + +STATIC +VOID +MakeLen ( + INT32 Root + ); + +STATIC +VOID +DownHeap ( + INT32 i + ); + +STATIC +VOID +MakeCode ( + INT32 n, + 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], mCTable[4096], mCCode[NC], + mPFreq[2 * NP - 1], mPTCode[NPT], mTFreq[2 * NT - 1]; + +STATIC NODE mPos, mMatchPos, mAvail, *mPosition, *mParent, *mPrev, *mNext = NULL; + + +// +// functions +// + +UINT32 +EfiCompress ( + UINT8 *SrcBuffer, + UINT32 SrcSize, + UINT8 *DstBuffer, + 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.this case, + DstSize contains the size needed. + EFI_SUCCESS - Compression is successful. + +--*/ +{ + UINT32 Status = ERR_SUCCESS; + + // + // Initializations + // + mBufSiz = 0; + mBuf = NULL; + mText = NULL; + mLevel = NULL; + mChildCount = NULL; + mPosition = NULL; + mParent = NULL; + mPrev = NULL; + mNext = NULL; + + + mSrc = 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 ERR_OUT_OF_RESOURCES; + } + + // + // Null terminate the compressed data + // + if (mDst < mDstUpperLimit) { + *mDst++ = 0; + } + + // + // Fillcompressed size and original size + // + mDst = DstBuffer; + PutDword(mCompSize+1); + PutDword(mOrigSize); + + // + // Return + // + + if (mCompSize + 1 + 8 > *DstSize) { + *DstSize = mCompSize + 1 + 8; + return ERR_BUFFER_TOO_SMALL; + } else { + *DstSize = mCompSize + 1 + 8; + return ERR_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 +UINT32 +AllocateMemory () +/*++ + +Routine Description: + + Allocate memory spaces for data structures usedcompression process + +Argements: (VOID) + +Returns: + + EFI_SUCCESS - Memory is allocated successfully + EFI_OUT_OF_RESOURCES - Allocation fails + +--*/ +{ + UINT32 i; + + mText = malloc (WNDSIZ * 2 + MAXMATCH); + for (i = 0 ; i < WNDSIZ * 2 + MAXMATCH; i ++) { + mText[i] = 0; + } + + mLevel = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof(*mLevel)); + mChildCount = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof(*mChildCount)); + mPosition = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof(*mPosition)); + mParent = malloc (WNDSIZ * 2 * sizeof(*mParent)); + mPrev = malloc (WNDSIZ * 2 * sizeof(*mPrev)); + mNext = malloc ((MAX_HASH_VAL + 1) * sizeof(*mNext)); + + mBufSiz = 16 * 1024U; + while ((mBuf = malloc(mBufSiz)) == NULL) { + mBufSiz = (mBufSiz / 10U) * 9U; + if (mBufSiz < 4 * 1024U) { + return ERR_OUT_OF_RESOURCES; + } + } + mBuf[0] = 0; + + return ERR_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 <= WNDSIZ + UINT8_MAX; i++) { + mLevel[i] = 1; + mPosition[i] = NIL; /* sentinel */ + } + for (i = WNDSIZ; i < WNDSIZ * 2; i++) { + mParent[i] = NIL; + } + mAvail = 1; + for (i = 1; i < WNDSIZ - 1; i++) { + mNext[i] = (NODE)(i + 1); + } + + mNext[WNDSIZ - 1] = NIL; + for (i = WNDSIZ * 2; i <= MAX_HASH_VAL; i++) { + mNext[i] = NIL; + } +} + + +STATIC +NODE +Child ( + NODE q, + 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 ( + NODE q, + UINT8 c, + 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 + //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 < 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 >= 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 >= 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 < 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 (readnew 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 +UINT32 +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 + +--*/ +{ + UINT32 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 { + + // + // 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 ERR_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 ( + INT32 n, + INT32 nbit, + 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 ( + INT32 c + ) +{ + PutBits(mCLen[c], mCCode[c]); +} + +STATIC +VOID +EncodeP ( + 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, PBIT, -1); + } else { + PutBits(PBIT, 0); + PutBits(PBIT, 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 ( + UINT32 c, + 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 ( + INT32 n, + 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 ( + UINT8 *p, + INT32 n + ) +/*++ + +Routine Description: + + Readsource 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 ( + 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 ( + 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 ( + 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 ( + INT32 n, + UINT8 Len[], + 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 ( + 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 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; +} + diff --git a/Tiano/EfiTianoCompress.h b/Tiano/EfiTianoCompress.h new file mode 100644 index 0000000..edc860a --- /dev/null +++ b/Tiano/EfiTianoCompress.h @@ -0,0 +1,101 @@ +/* EFI/Tiano Compress Header + +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, +WITHWARRANTIES 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. + +--*/ +INT32 +TianoCompress ( + UINT8 *SrcBuffer, + UINT32 SrcSize, + UINT8 *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. + +--*/ +INT32 +EfiCompress ( + UINT8 *SrcBuffer, + UINT32 SrcSize, + UINT8 *DstBuffer, + UINT32 *DstSize + ) +; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Tiano/EfiTianoDecompress.c b/Tiano/EfiTianoDecompress.c new file mode 100644 index 0000000..0851374 --- /dev/null +++ b/Tiano/EfiTianoDecompress.c @@ -0,0 +1,994 @@ +/*++ + +Copyright (c) 2004 - 2006, 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 +//#define UINT8_MAX 0xff +#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'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 ( + SCRATCH_DATA *Sd, + UINT16 NumOfBits + ) +/*++ + +Routine Description: + + Shift mBitBuf NumOfBits left. ReadNumOfBits of bits from source. + +Arguments: + + Sd - The global scratch data + NumOfBits - The number of bits to shift and read. + +Returns: (VOID) + +--*/ +{ + Sd->mBitBuf = (UINT32) (Sd->mBitBuf << NumOfBits); + + while (NumOfBits > Sd->mBitCount) { + + Sd->mBitBuf |= (UINT32) (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 ( + SCRATCH_DATA *Sd, + UINT16 NumOfBits + ) +/*++ + +Routine Description: + + Get NumOfBits of bits from mBitBuf. Fill mBitBuf with subsequent + NumOfBits of bits from source. Returns NumOfBits of bits that are + popped. + +Arguments: + + Sd - The global scratch data. + NumOfBits - The number of bits to pop and read. + +Returns: + + The bits that are popped. + +--*/ +{ + UINT32 Bits; + + Bits = (UINT32) (Sd->mBitBuf >> (BITBUFSIZ - NumOfBits)); + + FillBuf (Sd, NumOfBits); + + return Bits; +} + +STATIC +UINT16 +MakeTable ( + SCRATCH_DATA *Sd, + UINT16 NumOfChar, + UINT8 *BitLen, + UINT16 TableBits, + UINT16 *Table + ) +/*++ + +Routine Description: + + Creates Huffman Code mapping table according to code length array. + +Arguments: + + Sd - The global scratch data + NumOfChar - Number of symbolsthe 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; + + for (Index = 1; Index <= 16; Index++) { + Count[Index] = 0; + } + + for (Index = 0; Index < NumOfChar; Index++) { + Count[BitLen[Index]]++; + } + + 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 = (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) { + continue; + } + + NextCode = (UINT16) (Start[Len] + Weight[Len]); + + if (Len <= TableBits) { + + for (Index = Start[Len]; Index < NextCode; Index++) { + Table[Index] = Char; + } + + } else { + + Index3 = Start[Len]; + Pointer = &Table[Index3 >> JuBits]; + Index = (UINT16) (Len - TableBits); + + while (Index != 0) { + if (*Pointer == 0) { + Sd->mRight[Avail] = Sd->mLeft[Avail] = 0; + *Pointer = Avail++; + } + + if (Index3 & Mask) { + Pointer = &Sd->mRight[*Pointer]; + } else { + Pointer = &Sd->mLeft[*Pointer]; + } + + Index3 <<= 1; + Index--; + } + + *Pointer = Char; + + } + + Start[Len] = NextCode; + } + // + // Succeeds + // + return 0; +} + +STATIC +UINT32 +DecodeP ( + 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 ( + SCRATCH_DATA *Sd, + UINT16 nn, + UINT16 nbit, + 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 == 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) { + 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; + + BytesRemain--; + while ((INT16) (BytesRemain) >= 0) { + Sd->mDstBase[Sd->mOutBuf++] = Sd->mDstBase[DataIdx++]; + if (Sd->mOutBuf >= Sd->mOrigSize) { + return ; + } + + BytesRemain--; + } + } + } + + return ; +} + +UINT32 +GetInfo ( + VOID *Source, + UINT32 SrcSize, + UINT32 *DstSize, + 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 + +--*/ +{ + UINT8 *Src; + + *ScratchSize = sizeof (SCRATCH_DATA); + + Src = Source; + if (SrcSize < 8) { + return ERR_INVALID_PARAMETER; + } + + *DstSize = Src[4] + (Src[5] << 8) + (Src[6] << 16) + (Src[7] << 24); + return ERR_SUCCESS; +} + +UINT32 +Decompress ( + VOID *Source, + UINT32 SrcSize, + VOID *Destination, + UINT32 DstSize, + VOID *Scratch, + UINT32 ScratchSize, + 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 successfull + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + UINT32 Index; + UINT32 CompSize; + UINT32 OrigSize; + UINT32 Status; + SCRATCH_DATA *Sd; + UINT8 *Src; + UINT8 *Dst; + + Status = ERR_SUCCESS; + Src = Source; + Dst = Destination; + + if (ScratchSize < sizeof (SCRATCH_DATA)) { + return ERR_INVALID_PARAMETER; + } + + Sd = (SCRATCH_DATA *) Scratch; + + if (SrcSize < 8) { + return ERR_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 ERR_INVALID_PARAMETER; + } + + if (DstSize != OrigSize) { + return ERR_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'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 ERR_INVALID_PARAMETER; + } + + Sd->mSrcBase = 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 = ERR_INVALID_PARAMETER; + } + + return Status; +} + +UINT32 +EFIAPI +EfiGetInfo ( + VOID *Source, + UINT32 SrcSize, + UINT32 *DstSize, + UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The implementation is same as that 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 successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + return GetInfo ( + Source, + SrcSize, + DstSize, + ScratchSize + ); +} + +UINT32 +EFIAPI +EfiDecompress ( + 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: + + 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 successfull + 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 + ); +} + +UINT32 +EFIAPI +TianoGetInfo ( + VOID *Source, + UINT32 SrcSize, + UINT32 *DstSize, + UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The implementation is same as that of EFI_TIANO_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 successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +{ + return GetInfo ( + Source, + SrcSize, + DstSize, + ScratchSize + ); +} + +UINT32 +EFIAPI +TianoDecompress ( + 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: + + 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 successfull + 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 + ); +} + diff --git a/Tiano/EfiTianoDecompress.h b/Tiano/EfiTianoDecompress.h new file mode 100644 index 0000000..5d4eac6 --- /dev/null +++ b/Tiano/EfiTianoDecompress.h @@ -0,0 +1,185 @@ +/*++ + +Copyright (c) 2004 - 2006, 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 + +UINT32 +EFIAPI +EfiGetInfo ( + VOID *Source, + UINT32 SrcSize, + UINT32 *DstSize, + UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The implementation is same as that 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 successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +; + +UINT32 +EFIAPI +EfiDecompress ( + 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: + + 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 successfull + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +; + +UINT32 +EFIAPI +TianoGetInfo ( + VOID *Source, + UINT32 SrcSize, + UINT32 *DstSize, + UINT32 *ScratchSize + ) +/*++ + +Routine Description: + + The implementation is same as that of EFI_TIANO_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 successull retrieved. + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +; + +UINT32 +EFIAPI +TianoDecompress ( + 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: + + 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 successfull + EFI_INVALID_PARAMETER - The source data is corrupted + +--*/ +; + +typedef +UINT32 +(*GETINFO_FUNCTION) ( + VOID *Source, + UINT32 SrcSize, + UINT32 *DstSize, + UINT32 *ScratchSize + ); + +typedef +UINT32 +(*DECOMPRESS_FUNCTION) ( + VOID *Source, + UINT32 SrcSize, + VOID *Destination, + UINT32 DstSize, + VOID *Scratch, + UINT32 ScratchSize + ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Tiano/TianoCompress.c b/Tiano/TianoCompress.c new file mode 100644 index 0000000..4d50af1 --- /dev/null +++ b/Tiano/TianoCompress.c @@ -0,0 +1,1753 @@ +/* Tiano Compress Implementation + +Copyright (c) 2006 - 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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +Module Name: + +TianoCompress.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. + +*/ + +#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 NODE mPos, mMatchPos, mAvail, *mPosition, *mParent, *mPrev, *mNext = NULL; + +// +// functions +// +INT32 + TianoCompress ( + UINT8 *SrcBuffer, + UINT32 SrcSize, + UINT8 *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 + // + mBufSiz = 0; + mBuf = NULL; + mText = NULL; + mLevel = NULL; + mChildCount = NULL; + mPosition = NULL; + mParent = NULL; + mPrev = NULL; + mNext = NULL; + + mSrc = 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 ERR_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 ERR_BUFFER_TOO_SMALL; + } else { + *DstSize = mCompSize + 1 + 8; + return ERR_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 + + Argements: + VOID + + Returns: + + EFI_SUCCESS - Memory is allocated successfully + EFI_OUT_OF_RESOURCES - Allocation fails + + --*/ +{ + UINT32 Index; + + mText = malloc (WNDSIZ * 2 + MAXMATCH); + for (Index = 0; Index < WNDSIZ * 2 + MAXMATCH; Index++) { + mText[Index] = 0; + } + + mLevel = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mLevel)); + mChildCount = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mChildCount)); + mPosition = malloc ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mPosition)); + mParent = malloc (WNDSIZ * 2 * sizeof (*mParent)); + mPrev = malloc (WNDSIZ * 2 * sizeof (*mPrev)); + mNext = malloc ((MAX_HASH_VAL + 1) * sizeof (*mNext)); + + mBufSiz = BLKSIZ; + mBuf = malloc (mBufSiz); + while (mBuf == NULL) { + mBufSiz = (mBufSiz / 10U) * 9U; + if (mBufSiz < 4 * 1024U) { + return ERR_OUT_OF_RESOURCES; + } + + mBuf = malloc (mBufSiz); + } + + mBuf[0] = 0; + + return ERR_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 = WNDSIZ; Index <= WNDSIZ + UINT8_MAX; Index++) { + mLevel[Index] = 1; + mPosition[Index] = NIL; // sentinel + } + + for (Index = WNDSIZ; Index < WNDSIZ * 2; Index++) { + mParent[Index] = NIL; + } + + mAvail = 1; + for (Index = 1; Index < WNDSIZ - 1; Index++) { + mNext[Index] = (NODE) (Index + 1); + } + + mNext[WNDSIZ - 1] = NIL; + for (Index = WNDSIZ * 2; Index <= 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. Travese 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 < 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 >= 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 >= 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 < 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 macontrolling 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 ERR_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, PBIT, -1); + } else { + PutBits (PBIT, 0); + PutBits (PBIT, 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 outputing 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; +} diff --git a/basetypes.h b/basetypes.h new file mode 100644 index 0000000..cb11504 --- /dev/null +++ b/basetypes.h @@ -0,0 +1,86 @@ +/* basetypes.h + +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. + +*/ + +#ifndef __BASETYPES_H__ +#define __BASETYPES_H__ + +#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; + +#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 + +#ifndef NULL +#define NULL ((VOID *) 0) +#endif + +#if _MSC_EXTENSIONS + // + // Microsoft* compiler requires _EFIAPI useage, __cdecl is Microsoft* specific C. + // + #define EFIAPI __cdecl +#endif + +#if __GNUC__ + #define EFIAPI __attribute__((cdecl)) +#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_INVALID_FLASH_DESCRIPTOR 8 +#define ERR_BIOS_REGION_NOT_FOUND 9 +#define ERR_VOLUMES_NOT_FOUND 10 +#define ERR_INVALID_VOLUME 11 +#define ERR_VOLUME_REVISION_NOT_SUPPORTED 12 +#define ERR_UNKNOWN_FFS 13 + + +// EFI GUID +typedef struct{ + UINT8 Data[16]; +} EFI_GUID; + +#define ALIGN4(Value) (((Value)+3) & ~3) +#define ALIGN8(Value) (((Value)+7) & ~7) + +#include +#define ASSERT(x) assert(x) + +#endif diff --git a/descriptor.cpp b/descriptor.cpp new file mode 100644 index 0000000..266b16f --- /dev/null +++ b/descriptor.cpp @@ -0,0 +1,34 @@ +/* descriptor.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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#include "descriptor.h" + +// Calculate address of data structure addressed by descriptor address format +// 8 bit base or limit +UINT8* calculateAddress8(UINT8* baseAddress, const UINT8 baseOrLimit) +{ + return baseAddress + baseOrLimit * 0x10; +} + +// 16 bit base or limit +UINT8* calculateAddress16(UINT8* baseAddress, const UINT16 baseOrLimit) +{ + return baseAddress + baseOrLimit * 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; +} \ No newline at end of file diff --git a/descriptor.h b/descriptor.h new file mode 100644 index 0000000..e6bb88f --- /dev/null +++ b/descriptor.h @@ -0,0 +1,163 @@ +/* descriptor.h + +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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#ifndef __DESCRIPTOR_H__ +#define __DESCRIPTOR_H__ + +#include "basetypes.h" + +// Make sure we use right packing rules +#pragma pack(push,1) + +// Flash descriptor header +typedef struct { + 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 map +// Base fields are storing bits [11:4] of actual base addresses, all other bits are 0 +typedef struct { + UINT8 ComponentBase; // 0x03 on most machines + UINT8 NumberOfFlashChips; // Zero-based number of flash chips installed on board + UINT8 RegionBase; // 0x04 on most machines + UINT8 NumberOfRegions; // Zero-based number of flash regions (descriptor is always included) + UINT8 MasterBase; // 0x06 on most machines + UINT8 NumberOfMasters; // Zero-based number of flash masters + UINT8 PchStrapsBase; // 0x10 on most machines + UINT8 NumberOfPchStraps; // One-based number of UINT32s to read as PCH Straps, min=0, max=255 (1 Kb) + UINT8 ProcStrapsBase; // 0x20 on most machines + UINT8 NumberOfProcStraps; // Number of PROC staps to be read, can be 0 or 1 + UINT8 IccTableBase; // 0x21 on most machines + UINT8 NumberOfIccTableEntries; // 0x00 on most machines + UINT8 DmiTableBase; // 0x25 on most machines + UINT8 NumberOfDmiTableEntries; // 0x00 on most machines + UINT16 ReservedZero; // Still unknown, zeroes in all descriptors I have seen +} FLASH_DESCRIPTOR_MAP; + + +// Component section +// Flash parameters dword structure +typedef struct { + UINT8 FirstChipDensity : 3; + UINT8 SecondFlashDensity : 3; + UINT8 ReservedZero0 : 2; // Still unknown, zeroes in all descriptors I have seen + UINT8 ReservedZero1 : 8; // Still unknown, zeroes in all descriptors I have seen + UINT8 ReservedZero2 : 4; // Still unknown, zeroes in all descriptors I have seen + UINT8 FastReadEnabled : 1; + UINT8 FastReadFreqency : 3; + UINT8 FlashReadStatusFrequency : 3; + UINT8 FlashWriteFrequency : 3; + UINT8 DualOutputFastReadSupported : 1; + UINT8 ReservedZero3 : 1; // Still unknown, zero in all descriptors I have seen +} 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 + +// Flash frequences +#define FLASH_FREQUENCY_20MHZ 0x00 +#define FLASH_FREQUENCY_33MHZ 0x01 +#define FLASH_FREQUENCY_50MHZ 0x04 + +// Component section structure +typedef struct { + FLASH_PARAMETERS FlashParameters; + UINT8 InvalidInstruction0; // Instructions for SPI chip, that must not be executed, like FLASH ERASE + UINT8 InvalidInstruction1; // + UINT8 InvalidInstruction2; // + UINT8 InvalidInstruction3; // + UINT16 PartitionBoundary; // Upper 16 bit of partition boundary address. Default is 0x0000, which makes the boundary to be 0x00001000 + UINT16 RezervedZero; // Still unknown, zero in all descriptors I have seen +} FLASH_DESCRIPTOR_COMPONENT_SECTION; + +// 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 { + UINT16 RezervedZero; // Still unknown, zero in all descriptors I have seen + UINT16 FlashBlockEraseSize; // Size of block erased by single BLOCK ERASE command + UINT16 BiosBase; + UINT16 BiosLimit; + UINT16 MeBase; + UINT16 MeLimit; + UINT16 GbeBase; + UINT16 GbeLimit; + UINT16 PdrBase; + UINT16 PdrLimit; +} FLASH_DESCRIPTOR_REGION_SECTION; + +// Flash block erase sizes +#define FLASH_BLOCK_ERASE_SIZE_4KB 0x0000 +#define FLASH_BLOCK_ERASE_SIZE_8KB 0x0001 +#define FLASH_BLOCK_ERASE_SIZE_64KB 0x000F + +// Master section +typedef struct { + UINT16 BiosId; + UINT8 BiosRead; + UINT8 BiosWrite; + UINT16 MeId; + UINT8 MeRead; + UINT8 MeWrite; + UINT16 GbeId; + UINT8 GbeRead; + UINT8 GbeWrite; +} FLASH_DESCRIPTOR_MASTER_SECTION; + +//!TODO: Describe PCH and PROC straps sections, as well as ICC and DMI tables + +// Base address of descriptor upper map +#define FLASH_DESCRIPTOR_UPPER_MAP_BASE 0x0EFC + +// Descriptor upper map structure +typedef struct { + 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 { + UINT8 VendorId; // JEDEC VendorID byte + UINT8 DeviceId0; // JEDEC DeviceID first byte + UINT8 DeviceId1; // JEDEC DeviceID second byte + UINT8 ReservedZero; // Reserved, must be zero + UINT32 VsccId; // VSCC ID, normally it is 0x20052005 or 0x20152015 +} VSCC_TABLE_ENTRY; + +// Base address and size of OEM section +#define FLASH_DESCRIPTOR_OEM_SECTION_BASE 0x0F00 +#define FLASH_DESCRIPTOR_OEM_SECTION_SIZE 0xFF + +// Restore previous packing rules +#pragma pack(pop) + +// Calculate address of data structure addressed by descriptor address format +// 8 bit base or limit +UINT8* calculateAddress8(UINT8* baseAddress, const UINT8 baseOrLimit); +// 16 bit base or limit +UINT8* calculateAddress16(UINT8* baseAddress, const UINT16 baseOrLimit); +//Calculate size of region using its base and limit +size_t calculateRegionSize(const UINT16 base, const UINT16 limit); +#endif \ No newline at end of file diff --git a/ffs.cpp b/ffs.cpp new file mode 100644 index 0000000..c76a8d8 --- /dev/null +++ b/ffs.cpp @@ -0,0 +1,151 @@ +/* ffs.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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#include "ffs.h" + +const UINT8 ffsAlignmentTable[] = +{0, 4, 7, 9, 10, 12, 15, 16}; + +UINT8 calculateChecksum8(UINT8* buffer, UINT32 bufferSize) +{ + UINT8 counter = 0; + while(bufferSize--) + counter += buffer[bufferSize]; + return ~counter + 1; +} + +UINT16 calculateChecksum16(UINT8* buffer, UINT32 bufferSize) +{ + UINT16 counter = 0; + while(bufferSize--) + counter += buffer[bufferSize]; + return ~counter + 1; +} + +VOID uint32ToUint24(UINT32 size, UINT8* ffsSize) +{ + ffsSize[2] = (UINT8) ((size) >> 16); + ffsSize[1] = (UINT8) ((size) >> 8); + ffsSize[0] = (UINT8) ((size) ); +} + +UINT32 uint24ToUint32(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)); + UINT32 i32 = *(UINT32*)baGuid.left(4).constData(); + UINT16 i16_0 = *(UINT16*)baGuid.mid(4, 2).constData(); + UINT16 i16_1 = *(UINT16*)baGuid.mid(6, 2).constData(); + UINT8 i8_0 = *(UINT8*)baGuid.mid(8, 1).constData(); + UINT8 i8_1 = *(UINT8*)baGuid.mid(9, 1).constData(); + UINT8 i8_2 = *(UINT8*)baGuid.mid(10, 1).constData(); + UINT8 i8_3 = *(UINT8*)baGuid.mid(11, 1).constData(); + UINT8 i8_4 = *(UINT8*)baGuid.mid(12, 1).constData(); + UINT8 i8_5 = *(UINT8*)baGuid.mid(13, 1).constData(); + UINT8 i8_6 = *(UINT8*)baGuid.mid(14, 1).constData(); + UINT8 i8_7 = *(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("Security 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_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"); + default: + return QObject::tr("Unknown"); + } +} \ No newline at end of file diff --git a/ffs.h b/ffs.h new file mode 100644 index 0000000..684c1b8 --- /dev/null +++ b/ffs.h @@ -0,0 +1,415 @@ +/* ffs.h + +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, +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); + +#ifdef __cplusplus +extern "C" { +#endif +// Make sure we use right packing rules +#pragma pack(push,1) + +//***************************************************************************** +// EFI Capsule +//***************************************************************************** +// Capsule header +typedef struct { + 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); + +// AMI Aptio extended capsule header +typedef struct { + 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 extended capsule GUID +const QByteArray APTIO_CAPSULE_GUID("\x8B\xA6\x3C\x4A\x23\x77\xFB\x48\x80\x3D\x57\x8C\xC1\xFE\xC4\x4D", 16); + +//***************************************************************************** +// EFI Firmware Volume +//***************************************************************************** +// Firmware block map entry +// FvBlockMap ends with an entry {0x00000000, 0x00000000} +typedef struct { + UINT32 NumBlocks; + UINT32 BlockLength; +} EFI_FV_BLOCK_MAP_ENTRY; + +// Volume header +typedef struct { + UINT8 ZeroVector[16]; + EFI_GUID FileSystemGuid; + UINT64 FvLength; + UINT32 Signature; + UINT32 Attributes; + UINT16 HeaderLength; + UINT16 Checksum; + UINT16 ExtHeaderOffset; //Reserved in Revision 1 + UINT8 Reserved[1]; + UINT8 Revision; + //EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[1]; +} EFI_FIRMWARE_VOLUME_HEADER; + +// Filesystem 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); + +// 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_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 0x00 +typedef struct { + UINT16 ExtEntrySize; + UINT16 ExtEntryType; +} EFI_FIRMWARE_VOLUME_EXT_ENTRY; + +// GUID that maps OEM file types to GUIDs +#define EFI_FV_EXT_TYPE_OEM_TYPE 0x01 +typedef struct { + 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 0x02 +typedef struct { + EFI_FIRMWARE_VOLUME_EXT_ENTRY Header; + EFI_GUID FormatType; + //UINT8 Data[]; +} EFI_FIRMWARE_VOLUME_EXT_ENTRY_GUID_TYPE; + + +// Volume header 16bit checksum calculation routine +extern UINT16 calculateChecksum16(UINT8* 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_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_GUID Name; +// EFI_FFS_INTEGRITY_CHECK IntegrityCheck; +// UINT8 Type; +// UINT8 Attributes; +// UINT8 Size[3]; +// UINT8 State; +// UINT12 ExtendedSize; +//} EFI_FFS_FILE_HEADER2; + +// Standart 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_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 +// Revision 1 +#define FFS_ATTRIB_TAIL_PRESENT 0x01 +#define FFS_ATTRIB_RECOVERY 0x02 +//#define FFS_ATTRIB_HEADER_EXTENSION 0x04 //Must be set to zero in Revision 1 +#define FFS_ATTRIB_DATA_ALIGNMENT 0x38 +#define FFS_ATTRIB_CHECKSUM 0x40 +// Revision 2 +#define FFS_ATTRIB_LARGE_FILE 0x01 +#define FFS_ATTRIB_FIXED 0x04 + +// FFS alignment table +extern const UINT8 ffsAlignmentTable[]; + +// 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 + +// 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); + +// FFS size convertion routines +extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize); +extern UINT32 uint24ToUint32(UINT8* ffsSize); +// FFS file 8bit checksum calculation routine +extern UINT8 calculateChecksum8(UINT8* buffer, UINT32 bufferSize); + +//***************************************************************************** +// EFI FFS File Section +//***************************************************************************** +// Common section header +typedef struct { + UINT8 Size[3]; + UINT8 Type; +} EFI_COMMON_SECTION_HEADER; + +// Large file common section header +//typedef struct { +// UINT8 Size[3]; //Must be 0xFFFFFF for this header to be used +// UINT8 Type; +// UINT32 ExtendedSize; +//} EFI_COMMON_SECTION_HEADER2; + +// File section types +#define EFI_SECTION_ALL 0x00 // Imposible 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 + +// Compression section +typedef struct { + UINT8 Size[3]; + UINT8 Type; + UINT32 UncompressedLength; + UINT8 CompressionType; +} EFI_COMPRESSION_SECTION; + +// Compression types +#define EFI_NOT_COMPRESSED 0x00 +#define EFI_STANDARD_COMPRESSION 0x01 //Tiano for Revision 2, EFI for Revision 1 +#define EFI_CUSTOMIZED_COMPRESSION 0x02 //LZMA for Revision 2 Tiano for Revision 1 + +//GUID defined section +typedef struct { + UINT8 Size[3]; + UINT8 Type; + EFI_GUID SectionDefinitionGuid; + UINT16 DataOffset; + UINT16 Attributes; +} EFI_GUID_DEFINED_SECTION; + +// Attributes for GUID defined section +#define EFI_GUIDED_SECTION_PROCESSING_REQUIRED 0x01 +#define EFI_GUIDED_SECTION_AUTH_STATUS_VALID 0x02 + +// Version section +typedef struct { + UINT8 Size[3]; + UINT8 Type; + UINT16 BuildNumber; + //CHAR16 VersionString[1]; +} EFI_VERSION_SECTION; + +// Freeform subtype GUID section +typedef struct { + UINT8 Size[3]; + UINT8 Type; + EFI_GUID SubTypeGuid; +} EFI_FREEFORM_SUBTYPE_GUID_SECTION; + +// User interface section +typedef struct { + UINT8 Size[3]; + UINT8 Type; + //CHAR16 FileNameString[1]; +} EFI_USER_INTERFACE_SECTION; + +// Other sections +typedef EFI_COMMON_SECTION_HEADER EFI_DISPOSABLE_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_RAW_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_DXE_DEPEX_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_PEI_DEPEX_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_SMM_DEPEX_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_PE32_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_PIC_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_TE_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_COMPATIBILITY16_SECTION; +typedef EFI_COMMON_SECTION_HEADER EFI_FIRMWARE_VOLUME_IMAGE_SECTION; + +// Restore previous packing rules +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0526d5e --- /dev/null +++ b/main.cpp @@ -0,0 +1,28 @@ +/* main.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 +#include +#include "uefitool.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + UEFITool w; + + if (a.arguments().length() > 1) + w.openImageFile(a.arguments().at(1)); + w.show(); + + return a.exec(); +} diff --git a/parse.cpp b/parse.cpp new file mode 100644 index 0000000..56b249a --- /dev/null +++ b/parse.cpp @@ -0,0 +1,520 @@ +/* parse.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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#include "parse.h" + +// Implementation of GNU memmem function using Boyer-Moore-Horspool algorithm +UINT8* find_pattern(UINT8* string, UINT32 slen, CONST UINT8* pattern, UINT32 plen) +{ + UINT32 scan = 0; + UINT32 bad_char_skip[256]; + UINT32 last; + + if (plen == 0 || !string || !pattern) + return NULL; + + for (scan = 0; scan <= 255; scan++) + bad_char_skip[scan] = plen; + + last = plen - 1; + + for (scan = 0; scan < last; scan++) + bad_char_skip[pattern[scan]] = last - scan; + + while (slen >= plen) + { + for (scan = last; string[scan] == pattern[scan]; scan--) + if (scan == 0) + return string; + + slen -= bad_char_skip[string[last]]; + string += bad_char_skip[string[last]]; + } + + return NULL; +} + +UINT8 write_to_file(UINT8* buffer, CONST size_t size, const char* name, const char* extension) +{ + /*char str[_MAX_PATH]; // Buffer for file name construction + FILE* file; + + sprintf(str, "%s.%s", name, extension); + file = fopen((const char*) str, "wb"); + if (!file) { + printf("Can't create %s file\n", str); + return ERR_FILE_OPEN; + } + if (fwrite(buffer, sizeof(UINT8), size, file) != size) { + printf("Can't write %s file\n", str); + return ERR_FILE_WRITE; + } + fclose(file);*/ + return ERR_SUCCESS; +} + +UINT8* parse_region(UINT8* buffer, size_t size, CONST BOOLEAN two_flash_chips, const char* region_name, CONST UINT16 region_base, CONST UINT16 region_limit) +{ + UINT8 result = ERR_SUCCESS; + UINT8* region = NULL; + size_t region_size = calculate_region_size(region_base, region_limit); + + if (!buffer || !size || !region_name || !region_size) + return NULL; + + // Check region base to be in buffer + if (region_base >= size) + { + printf("Warning: %s region stored in descriptor is not found\n", region_name); + if (two_flash_chips) + printf("Two flash chips installed, so it could be in another flash chip\n" + "Make a dump from another flash chip and open it to view information about %s region\n", region_name); + else + printf("Error: one flash chip installed, so it is an error caused by damaged or incomplete dump\n"); + printf("Warning: absence of %s region assumed\n", region_name); + return NULL; + } + // Check region to be fully present in buffer + else if (region_base + region_size > size) + { + printf("Warning: %s region stored in descriptor overlaps the end of opened file\n", region_name); + if (two_flash_chips) + printf("Two flash chips installed, so it could be in another flash chip\n" + "Make a dump from another flash chip and open it to view information about %s region\n", region_name); + else + printf("Error: One flash chip installed, so it is an error caused by damaged or incomplete dump\n"); + printf("Warning: absence of %s region assumed\n", region_name); + return NULL; + } + + // Calculate region address + region = calculate_address_16(buffer, region_base); + + // Display information about region + printf("%s region found, size: %d (0x%08X)\n", region_name, region_size, region_size); + + // Write all regions that has non-zero size to files + result = write_to_file(region, region_size, region_name, "region"); + if (result){ + printf("Error: Can't write %s region to file\n", region_name); + return NULL; + } + printf("%s region written to file\n", region_name); + + return region; +} + +UINT8* find_next_volume(UINT8* buffer, size_t buffer_size) +{ + UINT8* pointer; + if (!buffer || !buffer_size) + return NULL; + + pointer = find_pattern(buffer, buffer_size, FV_SIGNATURE_STR, FV_SIGNATURE_LENGTH); + if (pointer && pointer - FV_SIGNATURE_OFFSET >= buffer) + return pointer - FV_SIGNATURE_OFFSET; + + return NULL; +} + +UINT8 calculate_checksum_8(UINT8* buffer, size_t buffer_size) +{ + UINT8 counter = 0; + while(buffer_size--) + counter += buffer[buffer_size]; + return ~counter + 1; +} + +UINT16 calculate_checksum_16(UINT8* buffer, size_t buffer_size) +{ + UINT16 counter = 0; + while(buffer_size--) + counter += buffer[buffer_size]; + return ~counter + 1; +} + +// Format GUID to standard readable form +// Assumes str to have at least 37 bytes free +UINT8 format_guid(EFI_GUID* guid, char* str) +{ + UINT8* guid1 = (UINT8*) guid; + UINT8* guid2 = guid1 + 4; + UINT8* guid3 = guid1 + 6; + UINT32* data1 = (UINT32*) guid1; + UINT16* data2 = (UINT16*) guid2; + UINT16* data3 = (UINT16*) guid3; + UINT8* data41 = guid1 + 8; + UINT8* data42 = guid1 + 9; + UINT8* data51 = guid1 + 10; + UINT8* data52 = guid1 + 11; + UINT8* data53 = guid1 + 12; + UINT8* data54 = guid1 + 13; + UINT8* data55 = guid1 + 14; + UINT8* data56 = guid1 + 15; + + sprintf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", *data1, *data2, *data3, *data41, *data42, + *data51, *data52, *data53, *data54, *data55, *data56); + + return ERR_SUCCESS; +} + +VOID size_to_filesize(size_t size, UINT8* module_size) +{ + module_size[2] = (UINT8) ((size) >> 16); + module_size[1] = (UINT8) ((size) >> 8); + module_size[0] = (UINT8) ((size) ); +} + +size_t filesize_to_size(UINT8* module_size) +{ + return (module_size[2] << 16) + + (module_size[1] << 8) + + module_size[0]; +} + +UINT8 parse_volume(UINT8* buffer, size_t buffer_size, const char* prefix) +{ + EFI_FIRMWARE_VOLUME_HEADER* volume_header; + UINT8* current_file; + char str[_MAX_PATH]; + char guid[37]; // FD44820B-F1AB-41C0-AE4E-0C55556EB9BD for example + UINT8 empty_byte; + UINT8 result; + size_t header_size; + size_t file_size; + size_t files_counter = 0; + + volume_header = (EFI_FIRMWARE_VOLUME_HEADER*) buffer; + if (volume_header->Signature != FV_SIGNATURE_INT) + return ERR_INVALID_VOLUME; + + //!TODO: Check zero vector for code, so it could be a boot volume + + // Check filesystem GUID to be known + // Check for FFS1 + if (find_pattern((UINT8*)&volume_header->FileSystemGuid, sizeof(EFI_GUID), EFI_FIRMWARE_FILE_SYSTEM_GUID, sizeof(EFI_GUID)) == (UINT8*)&volume_header->FileSystemGuid) { + printf("File system: PI FFS v1\n"); + // Check for FFS2 + } else if (find_pattern((UINT8*)&volume_header->FileSystemGuid, sizeof(EFI_GUID), EFI_FIRMWARE_FILE_SYSTEM2_GUID, sizeof(EFI_GUID)) == (UINT8*)&volume_header->FileSystemGuid) { + printf("File system: PI FFS v2\n"); + // Unknown FFS + } else { + printf("File system: unknown\nWarning: volume can't be parsed\n"); + return ERR_UNKNOWN_FFS; + } + + // Check attributes + // Determine erase polarity + if (volume_header->Attributes & EFI_FVB_ERASE_POLARITY) + empty_byte = 0xFF; + else + empty_byte = 0x00; + printf("Empty byte: 0x%02X\n", empty_byte); + + // Check header checksum by recalculating it + if (!calculate_checksum_16(buffer, volume_header->HeaderLength)) { + printf("Warning: volume header checksum is invalid\nVolume can be inacessible for UEFI routines\n"); + } + + // Check for presence of extended header, only if header revision is not 1 + if (volume_header->Revision > 1 && volume_header->ExtHeaderOffset) { + EFI_FIRMWARE_VOLUME_EXT_HEADER* extended_header; + extended_header = (EFI_FIRMWARE_VOLUME_EXT_HEADER*) (buffer + volume_header->ExtHeaderOffset); + result = format_guid(&extended_header->FvName, guid); + if (result) + return result; + printf("Extended header: present\n" + "Volume GUID: %s", guid); + header_size = volume_header->ExtHeaderOffset + extended_header->ExtHeaderSize; + } else { + printf("Extended header: none\n"); + header_size = volume_header->HeaderLength; + } + + //!TODO: Check block map to make enough space for the whole volume + + // Find and parse all files in current volume + for(current_file = buffer + header_size; current_file && current_file < buffer + volume_header->FvLength - header_size;) { + EFI_FFS_FILE_HEADER* file_header = (EFI_FFS_FILE_HEADER*) current_file; + UINT8* byte; + size_t empty_bytes_counter = 0; + // Check if no more files left + for (byte = current_file; *byte == empty_byte; byte++) { + empty_bytes_counter++; + if(empty_bytes_counter >= sizeof(EFI_FFS_FILE_HEADER)) + { + printf("No more files left in current volume\n"); + return ERR_SUCCESS; + } + } + + result = format_guid(&file_header->Name, guid); + if (result) + return result; + printf("FFS file %s found\n", guid); + + // Constructing file name + sprintf(str, "%s.%d.%s", prefix, files_counter, guid); + + // Getting file size + file_size = filesize_to_size(file_header->Size); + + // Store current volume to file + result = write_to_file(current_file, file_size, + str, "file"); + if (result){ + printf("Error: Can't write FFS file to file\n"); + return result; + } + printf("FFS file %s stored to file\n", guid); + + //!TODO: Parse current file + + // Parsed + printf("FFS file %s parsed\n", guid); + current_file += ALIGN_8(file_size); + files_counter++; + } + + return ERR_SUCCESS; +} + +UINT8 parse_bios_space(UINT8* buffer, size_t buffer_size) +{ + UINT8* current_pointer = NULL; + UINT8* previous_pointer = NULL; + size_t previous_volume_size = 0; + EFI_FIRMWARE_VOLUME_HEADER* volume_header = NULL; + UINT32 files_counter = 0; + size_t rest_size = 0; + UINT8 result = 0; + char str[_MAX_PATH]; + + // Search for all firmware volume headers + for (current_pointer = buffer, previous_pointer = NULL; current_pointer && current_pointer - buffer <= buffer_size;) { + rest_size = buffer_size - (current_pointer - buffer); + // Search for next firmware volume + //!TODO: add checks for incomplete file + previous_pointer = current_pointer; + current_pointer = find_next_volume(current_pointer, rest_size); + if (current_pointer) { + // Check for padding between previous and current volumes + if (previous_pointer + previous_volume_size < current_pointer) { + sprintf(str, "%d", files_counter); + result = write_to_file(previous_pointer + previous_volume_size, + current_pointer - previous_pointer - previous_volume_size, + str, "padding"); + if (result){ + printf("Error: Can't write padding to file\n"); + return result; + } + printf("Padding between firmware volumes stored to file\n\n"); + files_counter++; + } + + volume_header = (EFI_FIRMWARE_VOLUME_HEADER*) current_pointer; + printf("Firmware volume found\n"); + + // Check if volume overlaps the end if buffer + if (current_pointer + volume_header->FvLength > buffer + buffer_size) { + printf("Error: Volume overlaps the end of input buffer.\n"); + return ERR_INVALID_VOLUME; + } + + // Constructing volume name + sprintf(str, "%d", files_counter); + + // Store current volume to file + result = write_to_file(current_pointer, volume_header->FvLength, + str, "volume"); + if (result){ + printf("Error: Can't write volume to file\n"); + return result; + } + printf("Volume stored to file\n"); + + // Parse volume + result = parse_volume(current_pointer, volume_header->FvLength, str); + if (result) + return result; + printf("Firmware volume parsed\n\n"); + + // Move to next volume + current_pointer += volume_header->FvLength; + files_counter++; + } + else {// Previous_pointer was the last one (or there was none at all) + // Check for padding between previous volume and end of buffer + if (previous_pointer + previous_volume_size < buffer + buffer_size) { + char str[256]; + sprintf(str, "%d", files_counter); + result = write_to_file(previous_pointer + previous_volume_size, + buffer_size + buffer - previous_pointer, + str, "padding"); + if (result){ + printf("Error: Can't write padding to file\n"); + return result; + } + printf("Padding between last firmware volume and end of buffer stored to file\n\n"); + files_counter++; + } + } + } + + return ERR_SUCCESS; +} + +UINT8 parse_buffer(UINT8* buffer, size_t buffer_size) +{ + UINT8* original_buffer = buffer; + size_t original_size = buffer_size; + size_t capsule_header_size = 0; + size_t flash_image_size = 0; + FLASH_DESCRIPTOR_HEADER* descriptor_header = NULL; + UINT8 result = ERR_SUCCESS; + + // Check buffer to be valid + if (!buffer || !buffer_size) + return ERR_INVALID_PARAMETER; + + // Check buffer for being normal EFI capsule header + if (find_pattern(buffer, sizeof(EFI_GUID), EFI_CAPSULE_GUID, sizeof(EFI_GUID)) == buffer) { + EFI_CAPSULE_HEADER* capsule_header = (EFI_CAPSULE_HEADER*) buffer; + printf("EFI Capsule header found\n" + "Header size: %d (0x%08X)\n" + "Header flags: %08X\n" + "Image size: %d (0x%08X)\n\n", + capsule_header->HeaderSize, + capsule_header->HeaderSize, + capsule_header->Flags, + capsule_header->CapsuleImageSize, + capsule_header->CapsuleImageSize); + capsule_header_size = capsule_header->HeaderSize; + } + + // Check buffer for being extended Aptio capsule header + else if (find_pattern(buffer, sizeof(EFI_GUID), APTIO_CAPSULE_GUID, sizeof(EFI_GUID)) == buffer) { + APTIO_CAPSULE_HEADER* aptio_capsule_header = (APTIO_CAPSULE_HEADER*) buffer; + printf("AMI Aptio extended capsule header found\n" + "Header size: %d (0x%08X)\n" + "Header flags: %08X\n" + "Image size: %d (0x%08X)\n\n", + aptio_capsule_header->RomImageOffset, + aptio_capsule_header->RomImageOffset, + aptio_capsule_header->CapsuleHeader.Flags, + aptio_capsule_header->CapsuleHeader.CapsuleImageSize - aptio_capsule_header->RomImageOffset, + aptio_capsule_header->CapsuleHeader.CapsuleImageSize - aptio_capsule_header->RomImageOffset); + //!TODO: add more information about extended headed here + capsule_header_size = aptio_capsule_header->RomImageOffset; + } + + // Skip capsule header + buffer += capsule_header_size; + flash_image_size = buffer_size - capsule_header_size; + + // Consider buffer to be normal flash image without any headers now + + // Check buffer for being Intel flash descriptor + descriptor_header = (FLASH_DESCRIPTOR_HEADER*) buffer; + // Check descriptor signature + if (descriptor_header->Signature == FLASH_DESCRIPTOR_SIGNATURE) { + FLASH_DESCRIPTOR_MAP* descriptor_map; + FLASH_DESCRIPTOR_COMPONENT_SECTION* component_section; + FLASH_DESCRIPTOR_REGION_SECTION* region_section; + FLASH_DESCRIPTOR_MASTER_SECTION* master_section; + // Vector of 16 0xFF for sanity check + CONST UINT8 FFVector[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + // Store the beginning of descriptor as descriptor base address + UINT8* descriptor = buffer; + UINT8* bios_region = NULL; + + printf("Intel flash descriptor found\n"); + // Check for buffer size to be greater or equal to descriptor region size + if (flash_image_size < FLASH_DESCRIPTOR_SIZE) { + printf("Error: Input file is smaller then mininum descriptor size of 4KB\nAborting\n"); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + //Write descriptor to desc.bin + result = write_to_file(descriptor, FLASH_DESCRIPTOR_SIZE, "DESC", "region"); + if (result){ + printf("Error: Can't write descriptor region to file\nAborting\n"); + return result; + } + printf("Descriptor region written to file\n"); + + // Check FFVector to consist 16 0xFF, as sanity check + if (find_pattern(buffer, 16, FFVector, 16) != buffer) + printf("Warning: Descriptor start vector has something but 0xFF bytes. That's strange\n"); + + // Skip descriptor header + buffer += sizeof(FLASH_DESCRIPTOR_HEADER); + + // Parse descriptor map + descriptor_map = (FLASH_DESCRIPTOR_MAP*) buffer; + component_section = (FLASH_DESCRIPTOR_COMPONENT_SECTION*) calculate_address_8(descriptor, descriptor_map->ComponentBase); + region_section = (FLASH_DESCRIPTOR_REGION_SECTION*) calculate_address_8(descriptor, descriptor_map->RegionBase); + master_section = (FLASH_DESCRIPTOR_MASTER_SECTION*) calculate_address_8(descriptor, descriptor_map->MasterBase); + printf("\nParsing descriptor map\n" + "Flash chips: %d\n" + "Flash regions: %d\n" + "Flash masters: %d\n" + "PCH straps: %d\n" + "PROC straps: %d\n" + "ICC table entries: %d\n", + descriptor_map->NumberOfFlashChips + 1, + descriptor_map->NumberOfRegions + 1, + descriptor_map->NumberOfMasters + 1, + descriptor_map->NumberOfPchStraps, + descriptor_map->NumberOfProcStraps, + descriptor_map->NumberOfIccTableEntries); + printf("Descriptor map parsed\n\n"); + + // Parse component section + //!TODO: add parsing code + // Parse master section + //!TODO: add parsing code + + // Parse region section + printf("Parsing region section\n"); + parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "GBE", region_section->GbeBase, region_section->GbeLimit); + parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "ME", region_section->MeBase, region_section->MeLimit); + bios_region = parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "BIOS", region_section->BiosBase, region_section->BiosLimit); + parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "PDR", region_section->PdrBase, region_section->PdrLimit); + printf("Region section parsed\n\n"); + + // Parsing complete + printf("Descriptor parsing complete\n\n"); + + // Exiting if no bios region found + if (!bios_region) { + printf("BIOS region not found\nAborting\n"); + return ERR_BIOS_REGION_NOT_FOUND; + } + + // BIOS region is our buffer now + buffer = bios_region; + buffer_size = calculate_region_size(region_section->BiosBase, region_section->BiosLimit); + } + else { + printf("Intel flash descriptor not found, assuming that input file is BIOS region image\n\n"); + } + + // We are in the beginning of BIOS space, where firmware volumes are + // Parse BIOS space + + result = parse_bios_space(buffer, buffer_size); + if (result) + return result; + + return ERR_SUCCESS; +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..6011688 --- /dev/null +++ b/parse.h @@ -0,0 +1,28 @@ +/* parse.h + +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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +*/ + +#ifndef __PARSE_H__ +#define __PARSE_H__ + +#include +#include +#include "basetypes.h" +#include "ffs.h" +#include "descriptor.h" +//#include "Tiano/TianoCompress.h" +//#include "Tiano/TianoDecompress.h" +//#include "LZMA/LzmaCompress.h" +//#include "LZMA/LzmaDecompress.h" + +UINT8 parse_buffer(UINT8* buffer, size_t size); + +#endif \ No newline at end of file diff --git a/treeitem.cpp b/treeitem.cpp new file mode 100644 index 0000000..e4288c5 --- /dev/null +++ b/treeitem.cpp @@ -0,0 +1,155 @@ +/* treeitem.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 "treeitem.h" +#include "treeitemtypes.h" + +TreeItem::TreeItem(const UINT8 type, const UINT8 subtype, const QString & name, const QString & typeName, const QString & subtypeName, + const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, TreeItem *parent) +{ + + itemType = type; + itemSubtype = subtype; + itemName = name; + itemTypeName = typeName; + itemSubtypeName = subtypeName; + itemText = text; + itemInfo = info; + itemHeader = header; + itemBody = body; + parentItem = parent; +} + +TreeItem::~TreeItem() +{ + qDeleteAll(childItems); +} + +void TreeItem::appendChild(TreeItem *item) +{ + childItems.append(item); +} + +void TreeItem::removeChild(TreeItem *item) +{ + childItems.removeAll(item); +} + +TreeItem *TreeItem::child(int row) +{ + return childItems.value(row); +} + +int TreeItem::childCount() const +{ + return childItems.count(); +} + +int TreeItem::columnCount() const +{ + return 4; +} + +QString TreeItem::data(int column) const +{ + switch(column) + { + case 0: //Object + return itemName; + break; + case 1: //Type + return itemTypeName; + break; + case 2: //Subtype + return itemSubtypeName; + break; + case 3: //Text + return itemText; + break; + default: + return ""; + } +} + +TreeItem *TreeItem::parent() +{ + return parentItem; +} + +void TreeItem::setName(const QString &text) +{ + itemName = text; +} + +void TreeItem::setText(const QString &text) +{ + itemText = text; +} + +void TreeItem::setTypeName(const QString &text) +{ + itemTypeName = text; +} + +void TreeItem::setSubtypeName(const QString &text) +{ + itemSubtypeName = text; +} + +QString TreeItem::info() +{ + return itemInfo; +} + +void TreeItem::setInfo(const QString &text) +{ + itemInfo = text; +} + +int TreeItem::row() const +{ + if (parentItem) + return parentItem->childItems.indexOf(const_cast(this)); + + return 0; +} + +UINT8 TreeItem::type() +{ + return itemType; +} + +UINT8 TreeItem::subtype() +{ + return itemSubtype; +} + +QByteArray TreeItem::header() +{ + return itemHeader; +} + +QByteArray TreeItem::body() +{ + return itemBody; +} + +bool TreeItem::hasEmptyHeader() +{ + return itemHeader.isEmpty(); +} + +bool TreeItem::hasEmptyBody() +{ + return itemBody.isEmpty(); +} \ No newline at end of file diff --git a/treeitem.h b/treeitem.h new file mode 100644 index 0000000..2a1bec5 --- /dev/null +++ b/treeitem.h @@ -0,0 +1,68 @@ +/* treeitem.h + +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. + +*/ + +#ifndef __TREEITEM_H__ +#define __TREEITEM_H__ + +#include +#include +#include + +#include "basetypes.h" + +class TreeItem +{ +public: + TreeItem(const UINT8 type, const UINT8 subtype = 0, const QString & name = QString(), const QString & typeName = QString(), const QString & subtypeName = QString(), + const QString & text = QString(), const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), TreeItem *parent = 0); + ~TreeItem(); + + void appendChild(TreeItem *item); + void removeChild(TreeItem *item); + + TreeItem *child(int row); + int childCount() const; + int columnCount() const; + QString data(int column) const; + int row() const; + TreeItem *parent(); + + UINT8 type(); + UINT8 subtype(); + QByteArray header(); + QByteArray body(); + QString info(); + bool hasEmptyHeader(); + bool hasEmptyBody(); + + void setName(const QString &text); + void setText(const QString &text); + void setTypeName(const QString &text); + void setSubtypeName(const QString &text); + void setInfo(const QString &text); + +private: + QList childItems; + UINT8 itemType; + UINT8 itemSubtype; + QByteArray itemHeader; + QByteArray itemBody; + QString itemName; + QString itemTypeName; + QString itemSubtypeName; + QString itemText; + QString itemInfo; + TreeItem *parentItem; +}; + +#endif diff --git a/treeitemtypes.h b/treeitemtypes.h new file mode 100644 index 0000000..4cb8fbd --- /dev/null +++ b/treeitemtypes.h @@ -0,0 +1,80 @@ +/* treeitemtypes.h + +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. + +*/ + +#ifndef __TREEITEMTYPES_H__ +#define __TREEITEMTYPES_H__ + +// TreeItem types +enum ItemTypes { + RootItem, + CapsuleItem, + DescriptorItem, + RegionItem, + PaddingItem, + VolumeItem, + FileItem, + SectionItem +}; + +// Capsule subtypes +enum CapsuleSubtypes { + AptioCapsule, + UefiCapsule +}; + +// Region subtypes +enum RegionSubtypes { + GbeRegion, + MeRegion, + BiosRegion, + PdrRegion +}; + +// File subtypes +enum FileSubtypes { + RawFile, + FreeformFile, + SecurityCoreFile, + PeiCoreFile, + DxeCoreFile, + PeiModuleFile, + DxeDriverFile, + CombinedPeiDxeFile, + ApplicationFile, + SmmModuleFile, + VolumeImageFile, + CombinedSmmDxeFile, + SmmCoreFile, + PadFile = 0xF0 +}; + +enum SectionSubtypes { + CompressionSection = 0x01, + GuidDefinedSection, + DisposableSection, + Pe32ImageSection = 0x10, + PicImageSection, + TeImageSection, + DxeDepexSection, + VersionSection, + UserInterfaceSection, + Compatibility16Section, + VolumeImageSection, + FreeformSubtypeGuidSection, + RawSection, + PeiDepexSection = 0x1B, + SmmDepexSection +}; + + +#endif \ No newline at end of file diff --git a/treemodel.cpp b/treemodel.cpp new file mode 100644 index 0000000..ab551bf --- /dev/null +++ b/treemodel.cpp @@ -0,0 +1,354 @@ +/* treemodel.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 "treeitem.h" +#include "treemodel.h" +#include "ffs.h" +#include "descriptor.h" + +TreeModel::TreeModel(QObject *parent) + : QAbstractItemModel(parent) +{ + rootItem = new TreeItem(ItemTypes::RootItem, 0, tr("Object"), tr("Type"), tr("Subtype"), tr("Text")); +} + +TreeModel::~TreeModel() +{ + delete rootItem; +} + +bool TreeModel::hasEmptyHeader(const QModelIndex& index) +{ + if (!index.isValid()) + return true; + + TreeItem *item = static_cast(index.internalPointer()); + + return item->hasEmptyHeader(); +} + +bool TreeModel::hasEmptyBody(const QModelIndex& index) +{ + if (!index.isValid()) + return true; + + TreeItem *item = static_cast(index.internalPointer()); + + return item->hasEmptyBody(); +} + + +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(); + + return QVariant(); +} + +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) + return rootItem->data(section); + + 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()); + 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(); +} + +QModelIndex TreeModel::addItem(UINT8 type, UINT8 subtype, const QByteArray &header, const QByteArray &body, const QModelIndex &parent) +{ + TreeItem *parentItem; + int parentColumn = 0; + + if (!parent.isValid()) + parentItem = rootItem; + else + { + parentItem = static_cast(parent.internalPointer()); + parentColumn = parent.column(); + } + + // All information extraction must be here + QString name, typeName, subtypeName, info; + EFI_CAPSULE_HEADER* capsuleHeader; + APTIO_CAPSULE_HEADER* aptioCapsuleHeader; + FLASH_DESCRIPTOR_MAP* descriptorMap; + //FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection; + //FLASH_DESCRIPTOR_REGION_SECTION* regionSection; + //FLASH_DESCRIPTOR_MASTER_SECTION* masterSection; + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader; + EFI_FFS_FILE_HEADER* fileHeader; + //EFI_COMMON_SECTION_HEADER* sectionHeader; + + switch (type) + { + case ItemTypes::RootItem: + // Do not allow to add another root item + return QModelIndex(); + break; + case ItemTypes::CapsuleItem: + //typeName = tr("Capsule"); + switch (subtype) + { + case CapsuleSubtypes::AptioCapsule: + name = tr("AMI Aptio capsule"); + aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) header.constData(); + info = tr("GUID: %1\nHeader size: %2\nFlags: %3\nImage size: %4") + .arg(guidToQString(aptioCapsuleHeader->CapsuleHeader.CapsuleGuid)) + .arg(aptioCapsuleHeader->CapsuleHeader.Flags, 8, 16, QChar('0')) + .arg(aptioCapsuleHeader->RomImageOffset, 4, 16, QChar('0')) + .arg(aptioCapsuleHeader->CapsuleHeader.CapsuleImageSize - aptioCapsuleHeader->RomImageOffset, 8, 16, QChar('0')); + //!TODO: more info about Aptio capsule + break; + case CapsuleSubtypes::UefiCapsule: + name = tr("UEFI capsule"); + capsuleHeader = (EFI_CAPSULE_HEADER*) header.constData(); + info = tr("GUID: %1\nHeader size: %2\nFlags: %3\nImage size: %4") + .arg(guidToQString(capsuleHeader->CapsuleGuid)) + .arg(capsuleHeader->Flags, 8, 16, QChar('0')) + .arg(capsuleHeader->HeaderSize, 8, 16, QChar('0')) + .arg(capsuleHeader->CapsuleImageSize, 8, 16, QChar('0')); + break; + default: + name = tr("Unknown capsule"); + info = tr("GUID: %1\n").arg(guidToQString(*(EFI_GUID*)header.constData())); + break; + } + break; + case ItemTypes::DescriptorItem: + name = tr("Descriptor"); + descriptorMap = (FLASH_DESCRIPTOR_MAP*) body.constData(); + info = tr("Flash chips: %1\nRegions: %2\nMasters: %3\nPCH straps:%4\nPROC straps: %5\nICC table entries: %6") + .arg(descriptorMap->NumberOfFlashChips + 1) // + .arg(descriptorMap->NumberOfRegions + 1) // Zero-based numbers in storage + .arg(descriptorMap->NumberOfMasters + 1) // + .arg(descriptorMap->NumberOfPchStraps) + .arg(descriptorMap->NumberOfProcStraps) + .arg(descriptorMap->NumberOfIccTableEntries); + //!TODO: more info about descriptor + break; + case ItemTypes::RegionItem: + typeName = tr("Region"); + info = tr("Size: %1").arg(body.size(), 8, 16, QChar('0')); + //!TODO: more info about GbE and ME regions + switch (subtype) + { + case RegionSubtypes::GbeRegion: + name = tr("GbE region"); + break; + case RegionSubtypes::MeRegion: + name = tr("ME region"); + break; + case RegionSubtypes::BiosRegion: + name = tr("BIOS region"); + break; + case RegionSubtypes::PdrRegion: + name = tr("PDR region"); + break; + default: + name = tr("Unknown region"); + break; + } + break; + case ItemTypes::PaddingItem: + name = tr("Padding"); + info = tr("Size: %1").arg(body.size(), 8, 16, QChar('0')); + break; + case ItemTypes::VolumeItem: + typeName = tr("Volume"); + // Parse volume header to determine its revision and file system + volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) header.constData(); + name = guidToQString(volumeHeader->FileSystemGuid); + subtypeName = tr("Revision %1").arg(volumeHeader->Revision); + info = tr("Size: %1\nSignature: %2\nAttributes: %3\nHeader size: %4") + .arg(volumeHeader->FvLength, 8, 16, QChar('0')) + .arg(volumeHeader->Signature, 8, 16, QChar('0')) + .arg(volumeHeader->Attributes, 8, 16, QChar('0')) + .arg(volumeHeader->HeaderLength, 4, 16, QChar('0')); + break; + case ItemTypes::FileItem: + typeName = tr("File"); + // Parse file header to determine its GUID and type + fileHeader = (EFI_FFS_FILE_HEADER*) header.constData(); + name = guidToQString(fileHeader->Name); + subtypeName = fileTypeToQString(subtype); + info = tr("Type: %1\nAttributes: %2\nSize: %3\nState: %4") + .arg(fileHeader->Type, 2, 16, QChar('0')) + .arg(fileHeader->Attributes, 2, 16, QChar('0')) + .arg(uint24ToUint32(fileHeader->Size), 6, 16, QChar('0')) + .arg(fileHeader->State, 2, 16, QChar('0')); + break; + case ItemTypes::SectionItem: + typeName = tr("Section"); + name = sectionTypeToQString(subtype) + tr(" section"); + info = tr("Size: %1").arg(body.size(), 8, 16, QChar('0')); + //!TODO: add more specific info for all section types with uncommon headers + // Set name of file + if (subtype == SectionSubtypes::UserInterfaceSection) + { + QString text = QString::fromUtf16((const ushort*)body.constData()); + setItemText(text, findParentOfType(ItemTypes::FileItem, parent)); + } + break; + default: + name = tr("Unknown"); + break; + } + + emit layoutAboutToBeChanged(); + TreeItem *item = new TreeItem(type, subtype, name, typeName, subtypeName, "", info, header, body, parentItem); + parentItem->appendChild(item); + emit layoutChanged(); + return createIndex(parentItem->childCount() - 1, parentColumn, item); +} + +bool TreeModel::setItemName(const QString &data, const QModelIndex &index) +{ + if(!index.isValid()) + return false; + + TreeItem *item = static_cast(index.internalPointer()); + item->setName(data); + emit dataChanged(index, index); + return true; +} + +bool TreeModel::setItemText(const QString &data, const QModelIndex &index) +{ + if(!index.isValid()) + return false; + + TreeItem *item = static_cast(index.internalPointer()); + item->setText(data); + emit dataChanged(index, index); + return true; +} + +bool TreeModel::removeItem(const QModelIndex &index) +{ + TreeItem *item = static_cast(index.internalPointer()); + item->parent()->removeChild(item); + delete item; + return true; +} + +QModelIndex TreeModel::findParentOfType(UINT8 type, const QModelIndex& index) +{ + 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(); +} + +QByteArray TreeModel::header(const QModelIndex& index) +{ + if (!index.isValid()) + return QByteArray(); + + TreeItem *item = static_cast(index.internalPointer()); + return item->header(); +} + +QByteArray TreeModel::body(const QModelIndex& index) +{ + if (!index.isValid()) + return QByteArray(); + + TreeItem *item = static_cast(index.internalPointer()); + return item->body(); +} \ No newline at end of file diff --git a/treemodel.h b/treemodel.h new file mode 100644 index 0000000..4eba0a3 --- /dev/null +++ b/treemodel.h @@ -0,0 +1,60 @@ +/* treemodel.h + +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. + +*/ + +#ifndef __TREEMODEL_H__ +#define __TREEMODEL_H__ + +#include +#include +#include +#include + +#include "basetypes.h" +#include "treeitemtypes.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; + + QModelIndex addItem(UINT8 type, UINT8 subtype = 0, const QByteArray &header = QByteArray(), const QByteArray &body = QByteArray(), const QModelIndex &parent = QModelIndex()); + bool removeItem(const QModelIndex &index); + + QByteArray header(const QModelIndex& index); + bool hasEmptyHeader(const QModelIndex& index); + QByteArray body(const QModelIndex& index); + bool hasEmptyBody(const QModelIndex& index); + +private: + QModelIndex findParentOfType(UINT8 type, const QModelIndex& index); + bool setItemName(const QString &data, const QModelIndex &index); + bool setItemText(const QString &data, const QModelIndex &index); + TreeItem *rootItem; +}; + +#endif diff --git a/uefitool.cpp b/uefitool.cpp new file mode 100644 index 0000000..f751073 --- /dev/null +++ b/uefitool.cpp @@ -0,0 +1,960 @@ +/* uefitool.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 "uefitool.h" +#include "ui_uefitool.h" + +UEFITool::UEFITool(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::UEFITool) +{ + ui->setupUi(this); + treeModel = NULL; + + // Signal-slot connections + connect(ui->fromFileButton, SIGNAL(clicked()), this, SLOT(openImageFile())); + connect(ui->exportAllButton, SIGNAL(clicked()), this, SLOT(saveAll())); + connect(ui->exportBodyButton, SIGNAL(clicked()), this, SLOT(saveBody())); + + // Enable Drag-and-Drop actions + this->setAcceptDrops(true); + + // Initialise non-persistent data + init(); +} + +UEFITool::~UEFITool() +{ + delete ui; + delete treeModel; +} + +void UEFITool::init() +{ + // Clear UI components + ui->debugEdit->clear(); + ui->infoEdit->clear(); + ui->exportAllButton->setDisabled(true); + ui->exportBodyButton->setDisabled(true); + // Make new tree model + TreeModel * newModel = new TreeModel(this); + ui->structureTreeView->setModel(newModel); + if (treeModel) + delete treeModel; + treeModel = newModel; + // Show info after selection the item in tree view + connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(populateUi(const QModelIndex &))); + //connect(ui->structureTreeView, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void))); + connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void))); + resizeTreeViewColums(); +} + +void UEFITool::populateUi(const QModelIndex ¤t/*, const QModelIndex &previous*/) +{ + //!TODO: make widget + currentIndex = current; + ui->infoEdit->setPlainText(current.data(Qt::UserRole).toString()); + ui->exportAllButton->setDisabled(treeModel->hasEmptyBody(current) && treeModel->hasEmptyHeader(current)); + ui->exportBodyButton->setDisabled(treeModel->hasEmptyHeader(current)); +} + +void UEFITool::resizeTreeViewColums(/*const QModelIndex &index*/) +{ + int count = treeModel->columnCount(); + for(int i = 0; i < count; i++) + ui->structureTreeView->resizeColumnToContents(i); +} + +void UEFITool::openImageFile() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"),".","BIOS image file (*.rom *.bin *.cap *.bio *.fd *.wph *.efi);;All files (*.*)"); + openImageFile(path); +} + +void UEFITool::openImageFile(QString path) +{ + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) + { + ui->statusBar->showMessage(tr("Please select existing BIOS image file.")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for reading. Check file permissions.")); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + init(); + UINT8 result = parseInputFile(buffer); + if (result) + debug(tr("Opened file can't be parsed as UEFI image (%1)").arg(result)); + else + ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); + resizeTreeViewColums(); +} + +void UEFITool::saveAll() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save header to binary file"),".","Binary files (*.bin);;All files (*.*)"); + + QFile outputFile; + outputFile.setFileName(path); + if (!outputFile.open(QFile::WriteOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for writing. Check file permissions.")); + return; + } + + outputFile.write(treeModel->header(currentIndex) + treeModel->body(currentIndex)); + outputFile.close(); +} + +void UEFITool::saveBody() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save body to binary file"),".","Binary files (*.bin);;All files (*.*)"); + + QFile outputFile; + outputFile.setFileName(path); + if (!outputFile.open(QFile::WriteOnly)) + { + ui->statusBar->showMessage(tr("Can't open file for writing. Check file permissions.")); + return; + } + + outputFile.write(treeModel->body(currentIndex)); + outputFile.close(); +} + +/*void UEFITool::saveImageFile() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"),".","BIOS image file (*.rom *.bin *.cap *.fd *.fwh);;All files (*.*)"); + + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) + { + ui->statusBar->showMessage(tr("Please select existing BIOS image file.")); + return; + } + + QFile outputFile; + outputFile.setFileName(path); + if (!outputFile.open(QFile::ReadWrite)) + { + ui->statusBar->showMessage(tr("Can't open file for writing. Check file permissions.")); + return; + } +}*/ + +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::debug(const QString & text) +{ + //!TODO: log to separate debug window + ui->debugEdit->appendPlainText(text); +} + +QModelIndex UEFITool::addTreeItem(UINT8 type, UINT8 subtype, + const QByteArray & header, const QByteArray & body, const QModelIndex & parent) +{ + return treeModel->addItem(type, subtype, header, body, parent); +} + +UINT8 UEFITool::parseInputFile(const QByteArray & buffer) +{ + UINT32 capsuleHeaderSize = 0; + FLASH_DESCRIPTOR_HEADER* descriptorHeader = NULL; + QByteArray flashImage; + QByteArray bios; + QModelIndex index; + + // Check buffer for being normal EFI capsule header + if (buffer.startsWith(EFI_CAPSULE_GUID)) { + EFI_CAPSULE_HEADER* capsuleHeader = (EFI_CAPSULE_HEADER*) buffer.constData(); + capsuleHeaderSize = capsuleHeader->HeaderSize; + QByteArray header = buffer.left(capsuleHeaderSize); + QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); + index = addTreeItem(ItemTypes::CapsuleItem, CapsuleSubtypes::UefiCapsule, header, body); + } + + // Check buffer for being extended Aptio capsule header + else if (buffer.startsWith(APTIO_CAPSULE_GUID)) { + APTIO_CAPSULE_HEADER* aptioCapsuleHeader = (APTIO_CAPSULE_HEADER*) buffer.constData(); + capsuleHeaderSize = aptioCapsuleHeader->RomImageOffset; + QByteArray header = buffer.left(capsuleHeaderSize); + QByteArray body = buffer.right(buffer.size() - capsuleHeaderSize); + index = addTreeItem(ItemTypes::CapsuleItem, CapsuleSubtypes::AptioCapsule, header, body); + } + + // Skip capsule header to have flash chip image + flashImage = buffer.right(buffer.size() - capsuleHeaderSize); + + // Check buffer for being Intel flash descriptor + descriptorHeader = (FLASH_DESCRIPTOR_HEADER*) flashImage.constData(); + // Check descriptor signature + if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) { + FLASH_DESCRIPTOR_MAP* descriptorMap; + FLASH_DESCRIPTOR_REGION_SECTION* regionSection; + + // Store the beginning of descriptor as descriptor base address + UINT8* descriptor = (UINT8*) flashImage.constData(); + UINT8* gbeRegion = NULL; + UINT8* meRegion = NULL; + UINT8* biosRegion = NULL; + UINT8* pdrRegion = NULL; + + // Check for buffer size to be greater or equal to descriptor region size + if (flashImage.size() < FLASH_DESCRIPTOR_SIZE) { + debug(tr("Input file is smaller then mininum descriptor size of 4KB")); + return ERR_INVALID_FLASH_DESCRIPTOR; + } + + // Parse descriptor map + descriptorMap = (FLASH_DESCRIPTOR_MAP*) (flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER)); + regionSection = (FLASH_DESCRIPTOR_REGION_SECTION*) calculateAddress8(descriptor, descriptorMap->RegionBase); + + // Add tree item + QByteArray header = flashImage.left(sizeof(FLASH_DESCRIPTOR_HEADER)); + QByteArray body = flashImage.mid(sizeof(FLASH_DESCRIPTOR_HEADER), FLASH_DESCRIPTOR_SIZE - sizeof(FLASH_DESCRIPTOR_HEADER)); + index = addTreeItem(ItemTypes::DescriptorItem, 0, header, body, index); + + // Parse region section + QModelIndex gbeIndex(index); + QModelIndex meIndex(index); + QModelIndex biosIndex(index); + QModelIndex pdrIndex(index); + gbeRegion = parseRegion(flashImage, RegionSubtypes::GbeRegion, regionSection->GbeBase, regionSection->GbeLimit, gbeIndex); + meRegion = parseRegion(flashImage, RegionSubtypes::MeRegion, regionSection->MeBase, regionSection->MeLimit, meIndex); + biosRegion = parseRegion(flashImage, RegionSubtypes::BiosRegion, regionSection->BiosBase, regionSection->BiosLimit, biosIndex); + pdrRegion = parseRegion(flashImage, RegionSubtypes::PdrRegion, regionSection->PdrBase, regionSection->PdrLimit, pdrIndex); + + // Parse complete + //!TODO: show some info about GbE, ME and PDR regions if found + + // Exit if no bios region found + if (!biosRegion) { + debug(tr("BIOS region not found")); + return ERR_BIOS_REGION_NOT_FOUND; + } + + index = biosIndex; + bios = QByteArray::fromRawData((const char*) biosRegion, calculateRegionSize(regionSection->BiosBase, regionSection->BiosLimit)); + } + else { + bios = buffer; + } + + // We are in the beginning of BIOS space, where firmware volumes are + // Parse BIOS space + + return parseBios(bios, index); +} + +UINT8* UEFITool::parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index) +{ + // Check for empty region + if (!regionLimit) + return NULL; + + // Calculate region size + UINT32 regionSize = calculateRegionSize(regionBase, regionLimit); + + // Populate descriptor map + FLASH_DESCRIPTOR_MAP* descriptor_map = (FLASH_DESCRIPTOR_MAP*) flashImage.constData() + sizeof(FLASH_DESCRIPTOR_HEADER); + + // Determine presence of 2 flash chips + bool twoChips = descriptor_map->NumberOfFlashChips; + + // construct region name + //!TODO: make this to regionTypeToQString(const UINT8 type) in descriptor.cpp + QString regionName; + switch (regionSubtype) + { + case RegionSubtypes::GbeRegion: + regionName = "GbE"; + break; + case RegionSubtypes::MeRegion: + regionName = "ME"; + break; + case RegionSubtypes::BiosRegion: + regionName = "Bios"; + break; + case RegionSubtypes::PdrRegion: + regionName = "PDR"; + break; + default: + regionName = "Unknown"; + debug(tr("Unknown region type")); + }; + + // Check region base to be in buffer + if (regionBase * 0x1000 >= flashImage.size()) + { + debug(tr("%1 region stored in descriptor not found").arg(regionName)); + if (twoChips) + debug(tr("Two flash chips installed, so it could be in another flash chip\n" + "Make a dump from another flash chip and open it to view information about %1 region").arg(regionName)); + else + debug(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump")); + debug(tr("Absence of %1 region assumed").arg(regionName)); + return NULL; + } + + // Check region to be fully present in buffer + else if (regionBase * 0x1000 + regionSize > flashImage.size()) + { + debug(tr("%s region stored in descriptor overlaps the end of opened file").arg(regionName)); + if (twoChips) + debug(tr("Two flash chips installed, so it could be in another flash chip\n" + "Make a dump from another flash chip and open it to view information about %1 region").arg(regionName)); + else + debug(tr("One flash chip installed, so it is an error caused by damaged or incomplete dump")); + debug(tr("Absence of %1 region assumed\n").arg(regionName)); + return NULL; + } + + // Calculate region address + UINT8* region = calculateAddress16((UINT8*) flashImage.constData(), regionBase); + + // Add tree item + QByteArray body = flashImage.mid(regionBase * 0x1000, regionSize); + index = addTreeItem(ItemTypes::RegionItem, regionSubtype, QByteArray(), body, index); + + return region; +} + +UINT8 UEFITool::parseBios(const QByteArray & bios, QModelIndex & parent) +{ + // Search for first volume + INT32 prevVolumeIndex = getNextVolumeIndex(bios); + + // No volumes found + if (prevVolumeIndex < 0) { + return ERR_VOLUMES_NOT_FOUND; + } + + // First volume is not at the beginning of bios space + if (prevVolumeIndex > 0) { + QByteArray padding = bios.left(prevVolumeIndex); + addTreeItem(ItemTypes::PaddingItem, 0, QByteArray(), padding, parent); + } + + // Search for and parse all volumes + INT32 volumeIndex; + UINT32 prevVolumeSize; + for (volumeIndex = prevVolumeIndex, prevVolumeSize = 0; + volumeIndex >= 0; + prevVolumeIndex = volumeIndex, prevVolumeSize = getVolumeSize(bios, volumeIndex), volumeIndex = getNextVolumeIndex(bios, volumeIndex + prevVolumeSize)) + { + // Padding between volumes + if (volumeIndex > prevVolumeIndex + prevVolumeSize) { + UINT32 size = volumeIndex - prevVolumeIndex - prevVolumeSize; + QByteArray padding = bios.mid(prevVolumeIndex + prevVolumeSize, size); + addTreeItem(ItemTypes::PaddingItem, 0, QByteArray(), padding, parent); + } + + // Populate volume header + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeIndex); + + //Check that volume is fully present in input + if (volumeIndex + volumeHeader->FvLength > bios.size()) { + debug(tr("Volume overlaps the end of input buffer")); + return ERR_INVALID_VOLUME; + } + + // Check volume revision and alignment + UINT32 alignment; + if (volumeHeader->Revision == 1) { + // Aquire alignment bits + bool alignmentCap = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_CAP; + bool alignment2 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_2; + bool alignment4 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_4; + bool alignment8 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_8; + bool alignment16 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_16; + bool alignment32 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_32; + bool alignment64 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_64; + bool alignment128 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_128; + bool alignment256 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_256; + bool alignment512 = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_512; + bool alignment1k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_1K; + bool alignment2k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_2K; + bool alignment4k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_4K; + bool alignment8k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_8K; + bool alignment16k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_16K; + bool alignment32k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_32K; + bool alignment64k = volumeHeader->Attributes && EFI_FVB_ALIGNMENT_64K; + + // Check alignment setup + if (!alignmentCap && + ( alignment2 || alignment4 || alignment8 || alignment16 + || alignment32 || alignment64 || alignment128 || alignment256 + || alignment512 || alignment1k || alignment2k || alignment4k + || alignment8k || alignment16k || alignment32k || alignment64k)) + debug("Incompatible revision 1 volume alignment setup"); + + // Assume that smaller alignment value consumes greater + alignment = 0x01; + if (alignment2) + alignment = 0x02; + else if (alignment4) + alignment = 0x04; + else if (alignment8) + alignment = 0x08; + else if (alignment16) + alignment = 0x10; + else if (alignment32) + alignment = 0x20; + else if (alignment64) + alignment = 0x40; + else if (alignment128) + alignment = 0x80; + else if (alignment256) + alignment = 0x100; + else if (alignment512) + alignment = 0x200; + else if (alignment1k) + alignment = 0x400; + else if (alignment2k) + alignment = 0x800; + else if (alignment4k) + alignment = 0x1000; + else if (alignment8k) + alignment = 0x2000; + else if (alignment16k) + alignment = 0x4000; + else if (alignment32k) + alignment = 0x8000; + else if (alignment64k) + alignment = 0x10000; + + // Check alignment + if (volumeIndex % alignment) { + debug(tr("Unaligned revision 1 volume")); + } + } + else if (volumeHeader->Revision == 2) { + // Aquire alignment + alignment = pow(2, (volumeHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); + + // Check alignment + if (volumeIndex % alignment) { + debug(tr("Unaligned revision 2 volume")); + } + } + else + debug(tr("Unknown volume revision (%1)").arg(volumeHeader->Revision)); + + // Check filesystem GUID to be known + // Do not parse volume with unknown FFS, because parsing will fail + bool parseCurrentVolume = true; + // FFS GUID v1 + if (QByteArray((const char*) &volumeHeader->FileSystemGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_FILE_SYSTEM_GUID) { + // Code can be added here + } + // FFS GUID v2 + else if (QByteArray((const char*) &volumeHeader->FileSystemGuid, sizeof(EFI_GUID)) == EFI_FIRMWARE_FILE_SYSTEM2_GUID) { + // Code can be added here + } + // Other GUID + else { + //info = info.append(tr("File system: unknown\n")); + debug(tr("Unknown file system (%1)").arg(guidToQString(volumeHeader->FileSystemGuid))); + parseCurrentVolume = false; + } + + // Check attributes + // Determine erase polarity + bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY; + + // Check header checksum by recalculating it + if (!calculateChecksum16((UINT8*) volumeHeader, volumeHeader->HeaderLength)) { + debug(tr("Volume header checksum is invalid")); + } + + // Check for presence of extended header, only if header revision is not 1 + UINT32 headerSize; + if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) { + EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER*) ((UINT8*) volumeHeader + volumeHeader->ExtHeaderOffset); + headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize; + } else { + headerSize = volumeHeader->HeaderLength; + } + + // Adding tree item + QByteArray header = bios.mid(volumeIndex, headerSize); + QByteArray body = bios.mid(volumeIndex + headerSize, volumeHeader->FvLength - headerSize); + QModelIndex index = addTreeItem(ItemTypes::VolumeItem, 0, header, body, parent); + + // Parse volume + if (parseCurrentVolume) { + UINT32 result = parseVolume(bios.mid(volumeIndex + headerSize, volumeHeader->FvLength - headerSize), headerSize, volumeHeader->Revision, erasePolarity, index); + if (result) + debug(tr("Volume parsing failed (%1)").arg(result)); + } + } + + return ERR_SUCCESS; +} + +INT32 UEFITool::getNextVolumeIndex(const QByteArray & bios, INT32 volumeIndex) +{ + if (volumeIndex < 0) + return -1; + + INT32 nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeIndex); + if (nextIndex < EFI_FV_SIGNATURE_OFFSET) { + return -1; + } + + return nextIndex - EFI_FV_SIGNATURE_OFFSET; +} + +UINT32 UEFITool::getVolumeSize(const QByteArray & bios, INT32 volumeIndex) +{ + // Populate volume header + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (bios.constData() + volumeIndex); + + // Check volume signature + if (QByteArray((const char*) &volumeHeader->Signature, sizeof(volumeHeader->Signature)) != EFI_FV_SIGNATURE) + return 0; + return volumeHeader->FvLength; +} + +UINT8 UEFITool::parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, QModelIndex & parent) +{ + // Construct empty byte based on erasePolarity value + // Native char type is used because QByteArray.count() takes it + char empty = erasePolarity ? '\xFF' : '\x00'; + + // Search for and parse all files + INT32 fileIndex = 0; + while (fileIndex >= 0) { + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) (volume.constData() + fileIndex); + QByteArray file = volume.mid(fileIndex, uint24ToUint32(fileHeader->Size)); + QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER)); + + // We are at empty space in the end of volume + if (header.count(empty) == header.size()) { + QByteArray body = volume.right(volume.size() - fileIndex); + addTreeItem(ItemTypes::PaddingItem, 0, QByteArray(), body, parent); + break; + } + + // Check header checksum + QByteArray tempHeader = header; + EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*) (tempHeader.data()); + tempFileHeader->IntegrityCheck.Checksum.Header = 0; + tempFileHeader->IntegrityCheck.Checksum.File = 0; + UINT8 calculated = calculateChecksum8((UINT8*) tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); + if (fileHeader->IntegrityCheck.Checksum.Header != calculated) + { + debug(tr("%1: stored header checksum %2 differs from calculated %3") + .arg(guidToQString(fileHeader->Name)) + .arg(fileHeader->IntegrityCheck.Checksum.Header, 2, 16, QChar('0')) + .arg(calculated, 2, 16, QChar('0'))); + } + + // Check data checksum, if no tail was found + // Data checksum must be calculated + if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { + UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER); + // Exclude file tail from data checksum calculation + if(revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) + bufferSize -= sizeof(UINT16); + calculated = calculateChecksum8((UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize); + if (fileHeader->IntegrityCheck.Checksum.File != calculated) { + debug(tr("%1: stored data checksum %2 differs from calculated %3") + .arg(guidToQString(fileHeader->Name)) + .arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0')) + .arg(calculated, 2, 16, QChar('0'))); + } + } + // Data checksum must be one of predefined values + else { + if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM &&fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2) { + debug(tr("%1: stored data checksum %2 differs from standard value") + .arg(guidToQString(fileHeader->Name)) + .arg(fileHeader->IntegrityCheck.Checksum.File, 2, 16, QChar('0'))); + } + } + + // Check file alignment + UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3]; + UINT32 alignment = pow(2, alignmentPower); + if ((volumeBase + fileIndex + sizeof(EFI_FFS_FILE_HEADER)) % alignment) { + debug(tr("%1: unaligned file").arg(guidToQString(fileHeader->Name))); + } + + // Get file body + QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER)); + // For files in Revision 1 volumes, check for file tail presence + if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) + { + //Check file tail; + UINT16* tail = (UINT16*) body.right(sizeof(UINT16)).constData(); + if (!fileHeader->IntegrityCheck.TailReference == *tail) + debug(tr("%1: file tail value %2 is not a bitwise not of %3 stored in file header") + .arg(guidToQString(fileHeader->Name)) + .arg(*tail, 4, 16, QChar('0')) + .arg(fileHeader->IntegrityCheck.TailReference, 4, 16, QChar('0'))); + + // Remove tail from file body + body = body.left(body.size() - sizeof(UINT16)); + } + + // Parse current file by default + bool parseCurrentFile = true; + // Raw files can hide volumes inside them + // So try to parse them as bios space + bool parseAsBios = false; + + // Check file type + //!TODO: add more file specific checks + switch (fileHeader->Type) + { + case EFI_FV_FILETYPE_ALL: + parseAsBios = true; + break; + case EFI_FV_FILETYPE_RAW: + parseAsBios = true; + break; + case EFI_FV_FILETYPE_FREEFORM: + break; + case EFI_FV_FILETYPE_SECURITY_CORE: + break; + case EFI_FV_FILETYPE_PEI_CORE: + break; + case EFI_FV_FILETYPE_DXE_CORE: + break; + case EFI_FV_FILETYPE_PEIM: + break; + case EFI_FV_FILETYPE_DRIVER: + break; + case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER: + break; + case EFI_FV_FILETYPE_APPLICATION: + break; + case EFI_FV_FILETYPE_SMM: + break; + case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: + break; + case EFI_FV_FILETYPE_COMBINED_SMM_DXE: + break; + case EFI_FV_FILETYPE_SMM_CORE: + break; + case EFI_FV_FILETYPE_PAD: + parseCurrentFile = false; + break; + default: + parseCurrentFile = false; + debug(tr("Unknown file type (%1)").arg(fileHeader->Type, 2, 16, QChar('0'))); + }; + + // Check for empty file + if (body.count(empty) == body.size()) + { + // No need to parse empty files + parseCurrentFile = false; + } + + // Add tree item + QModelIndex index = addTreeItem(ItemTypes::FileItem, fileHeader->Type, header, body, parent); + + // Parse file + if (parseCurrentFile) { + if (parseAsBios) { + UINT32 result = parseBios(body, index); + if (result && result != ERR_VOLUMES_NOT_FOUND) + debug(tr("Parse file as BIOS failed (%1)").arg(result)); + } + else { + UINT32 result = parseFile(body, revision, erasePolarity, index); + if (result) + debug(tr("Parse file as FFS failed (%1)").arg(result)); + } + } + + // Move to next file + fileIndex += file.size(); + fileIndex = ALIGN8(fileIndex); + + // Exit from loop if no files left + if (fileIndex >= volume.size()) + fileIndex = -1; + } + + return ERR_SUCCESS; +} + +UINT8 UEFITool::parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, QModelIndex & parent) +{ + // Search for and parse all sections + INT32 sectionIndex = 0; + while(sectionIndex >= 0) + { + EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) (file.constData() + sectionIndex); + UINT32 sectionSize = uint24ToUint32(sectionHeader->Size); + + // This declarations must be here because of the nature of switch statement + EFI_COMPRESSION_SECTION* compressedSectionHeader; + EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader; + QByteArray header; + QByteArray body; + UINT32 decompressedSize; + UINT32 scratchSize; + UINT8* decompressed; + UINT8* scratch; + VOID* data; + UINT32 dataSize; + QModelIndex index; + UINT32 result; + bool lzmaHeaderFound; + + switch (sectionHeader->Type) + { + // Encapsulated sections + case EFI_SECTION_COMPRESSION: + compressedSectionHeader = (EFI_COMPRESSION_SECTION*) sectionHeader; + header = file.mid(sectionIndex, sizeof(EFI_COMPRESSION_SECTION)); + + // Try to decompress this section + switch (compressedSectionHeader->CompressionType) + { + case EFI_NOT_COMPRESSED: + body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), compressedSectionHeader->UncompressedLength); + index = addTreeItem(ItemTypes::SectionItem, SectionSubtypes::CompressionSection, header, body, parent); + // Parse stored file + result = parseFile(body, revision, erasePolarity, index); + if (result) + debug(tr("Stored section can not be parsed as file (%1)").arg(result)); + break; + case EFI_STANDARD_COMPRESSION: + //Must be Tiano for all revisions, needs checking + body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + + // Get buffer sizes + data = (VOID*) (file.constData() + sectionIndex + sizeof(EFI_COMPRESSION_SECTION)); + dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION); + if (TianoGetInfo(data, dataSize, &decompressedSize, &scratchSize) != ERR_SUCCESS + || decompressedSize != compressedSectionHeader->UncompressedLength) + debug(tr("TianoGetInfo failed")); + else { + decompressed = new UINT8[decompressedSize]; + scratch = new UINT8[scratchSize]; + // Decompress section data + if (TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize) != ERR_SUCCESS) + debug(tr("TianoDecompress failed")); + else + { + body = QByteArray::fromRawData((const char*) decompressed, decompressedSize); + // Parse stored file + result = parseFile(body, revision, erasePolarity, index); + if (result) + debug(tr("Compressed section with Tiano compression can not be parsed as file (%1)").arg(result)); + } + + delete[] decompressed; + delete[] scratch; + } + break; + case EFI_CUSTOMIZED_COMPRESSION: + body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + + // Get buffer sizes + data = (VOID*) (file.constData() + sectionIndex + sizeof(EFI_COMPRESSION_SECTION)); + dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION); + if (LzmaGetInfo(data, dataSize, &decompressedSize) != ERR_SUCCESS + || decompressedSize != compressedSectionHeader->UncompressedLength) + { + // Shitty file with some data between COMPRESSED_SECTION_HEADER and LZMA_HEADER + // Search for LZMA header in body + // Header structure: UINT8 LzmaProperties | UINT32 DictionarySize | UINT64 DecompressedSize + // We can't determine first two fiels here, but Decompressed size is known, so search for it + INT32 lzmaHeaderIndex = body.indexOf(QByteArray((const char*)&compressedSectionHeader->UncompressedLength, + sizeof(compressedSectionHeader->UncompressedLength)).append('\x00').append('\x00').append('\x00').append('\x00')); + lzmaHeaderIndex -= LZMA_PROPS_SIZE; + // LZMA header not found + if (lzmaHeaderIndex < 0) + { + lzmaHeaderFound = false; + debug(tr("Lzma header not found")); + } + // LZMA header found + else if (lzmaHeaderIndex) { + data = (VOID*) (file.constData() + sectionIndex + sizeof(EFI_COMPRESSION_SECTION) + lzmaHeaderIndex); + dataSize = uint24ToUint32(sectionHeader->Size) - sizeof(EFI_COMPRESSION_SECTION) - lzmaHeaderIndex; + // Get buffer sizes again + if (LzmaGetInfo(data, dataSize, &decompressedSize) != ERR_SUCCESS + || decompressedSize != compressedSectionHeader->UncompressedLength) + { + debug(tr("LzmaGetInfo failed on LZMA header")); + lzmaHeaderFound = false; + } + else + debug(tr("Padding of size %1 between CompressedSectionHeader and LzmaHeader") + .arg(lzmaHeaderIndex)); + } + lzmaHeaderFound = true; + } + else + lzmaHeaderFound = true; + // Decompress if header is found + if (lzmaHeaderFound) { + decompressed = new UINT8[decompressedSize]; + // Decompress section data + if (LzmaDecompress(data, dataSize, decompressed) != ERR_SUCCESS) + debug(tr("LzmaDecompress failed")); + else + { + body = QByteArray::fromRawData((const char*) decompressed, decompressedSize); + // Parse stored file + result = parseFile(body, revision, erasePolarity, index); + if (result) + debug(tr("Compressed section with LZMA compression can not be parsed as file (%1)").arg(result)); + } + + delete[] decompressed; + } + break; + default: + body = file.mid(sectionIndex + sizeof(EFI_COMPRESSION_SECTION), sectionSize - sizeof(EFI_COMPRESSION_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + debug(tr("Compressed section with unknown compression type found (%1)").arg(compressedSectionHeader->CompressionType)); + } + + break; + case EFI_SECTION_GUID_DEFINED: + header = file.mid(sectionIndex, sizeof(EFI_GUID_DEFINED_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_GUID_DEFINED_SECTION), sectionSize - sizeof(EFI_GUID_DEFINED_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + // Parse section body as file + guidDefinedSectionHeader = (EFI_GUID_DEFINED_SECTION*) (header.constData()); + body = file.mid(sectionIndex + guidDefinedSectionHeader->DataOffset, sectionSize - guidDefinedSectionHeader->DataOffset); + result = parseFile(body, revision, erasePolarity, index); + if (result) + debug(tr("GUID defined section body can not be parsed as file (%1)").arg(result)); + break; + case EFI_SECTION_DISPOSABLE: + header = file.mid(sectionIndex, sizeof(EFI_DISPOSABLE_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_DISPOSABLE_SECTION), sectionSize - sizeof(EFI_DISPOSABLE_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + // Leaf sections + case EFI_SECTION_PE32: + header = file.mid(sectionIndex, sizeof(EFI_PE32_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_PE32_SECTION), sectionSize - sizeof(EFI_PE32_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_PIC: + header = file.mid(sectionIndex, sizeof(EFI_PIC_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_PIC_SECTION), sectionSize - sizeof(EFI_PIC_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_TE: + header = file.mid(sectionIndex, sizeof(EFI_TE_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_TE_SECTION), sectionSize - sizeof(EFI_TE_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_VERSION: + header = file.mid(sectionIndex, sizeof(EFI_VERSION_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_VERSION_SECTION), sectionSize - sizeof(EFI_VERSION_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_USER_INTERFACE: + header = file.mid(sectionIndex, sizeof(EFI_USER_INTERFACE_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_USER_INTERFACE_SECTION), sectionSize - sizeof(EFI_USER_INTERFACE_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_COMPATIBILITY16: + header = file.mid(sectionIndex, sizeof(EFI_COMPATIBILITY16_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_COMPATIBILITY16_SECTION), sectionSize - sizeof(EFI_COMPATIBILITY16_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_FIRMWARE_VOLUME_IMAGE: + header = file.mid(sectionIndex, sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION), sectionSize - sizeof(EFI_FIRMWARE_VOLUME_IMAGE_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + // Parse section body as BIOS space + result = parseBios(body, index); + if (result && result != ERR_VOLUMES_NOT_FOUND) + debug(tr("Firmware volume image can not be parsed (%1)").arg(result)); + break; + case EFI_SECTION_FREEFORM_SUBTYPE_GUID: + header = file.mid(sectionIndex, sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION), sectionSize - sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_RAW: + header = file.mid(sectionIndex, sizeof(EFI_RAW_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_RAW_SECTION), sectionSize - sizeof(EFI_RAW_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + // Parse section body as BIOS space + result = parseBios(body, index); + if (result && result != ERR_VOLUMES_NOT_FOUND) + debug(tr("Raw section can not be parsed as BIOS (%1)").arg(result)); + break; + break; + case EFI_SECTION_DXE_DEPEX: + header = file.mid(sectionIndex, sizeof(EFI_DXE_DEPEX_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_DXE_DEPEX_SECTION), sectionSize - sizeof(EFI_DXE_DEPEX_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_PEI_DEPEX: + header = file.mid(sectionIndex, sizeof(EFI_PEI_DEPEX_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_PEI_DEPEX_SECTION), sectionSize - sizeof(EFI_PEI_DEPEX_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + case EFI_SECTION_SMM_DEPEX: + header = file.mid(sectionIndex, sizeof(EFI_SMM_DEPEX_SECTION)); + body = file.mid(sectionIndex + sizeof(EFI_SMM_DEPEX_SECTION), sectionSize - sizeof(EFI_SMM_DEPEX_SECTION)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + break; + default: + debug(tr("Section with unknown type (%1)").arg(sectionHeader->Type, 2, 16, QChar('0'))); + header = file.mid(sectionIndex, sizeof(EFI_COMMON_SECTION_HEADER)); + body = file.mid(sectionIndex + sizeof(EFI_COMMON_SECTION_HEADER), sectionSize - sizeof(EFI_COMMON_SECTION_HEADER)); + index = addTreeItem(ItemTypes::SectionItem, sectionHeader->Type, header, body, parent); + } + + // Move to next section + sectionIndex += uint24ToUint32(sectionHeader->Size); + sectionIndex = ALIGN4(sectionIndex); + + // Exit from loop if no sections left + if (sectionIndex >= file.size()) + sectionIndex = -1; + } + + return ERR_SUCCESS; +} + diff --git a/uefitool.h b/uefitool.h new file mode 100644 index 0000000..2b17033 --- /dev/null +++ b/uefitool.h @@ -0,0 +1,84 @@ +/* uefitool.h + + 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. + +*/ + +#ifndef __UEFITOOL_H__ +#define __UEFITOOL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "treemodel.h" + +#include "basetypes.h" +#include "descriptor.h" +#include "ffs.h" +#include "Tiano\EfiTianoCompress.h" +#include "Tiano\EfiTianoDecompress.h" +#include "LZMA\LzmaCompress.h" +#include "LZMA\LzmaDecompress.h" + +namespace Ui { +class UEFITool; +} + +class UEFITool : public QMainWindow +{ + Q_OBJECT + +public: + explicit UEFITool(QWidget *parent = 0); + ~UEFITool(); + + void openImageFile(QString path); + +private slots: + void init(); + void openImageFile(); + //void saveImageFile(); + void populateUi(const QModelIndex ¤t/*, const QModelIndex &previous*/); + void resizeTreeViewColums(/*const QModelIndex &index*/); + void saveAll(); + void saveBody(); + +private: + Ui::UEFITool * ui; + TreeModel * treeModel; + QModelIndex currentIndex; + + void debug(const QString & text); + void dragEnterEvent(QDragEnterEvent* event); + void dropEvent(QDropEvent* event); + QModelIndex addTreeItem(UINT8 type, UINT8 subtype, const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), + const QModelIndex & parent = QModelIndex()); + + UINT8 parseInputFile(const QByteArray & buffer); + UINT8* parseRegion(const QByteArray & flashImage, UINT8 regionSubtype, const UINT16 regionBase, const UINT16 regionLimit, QModelIndex & index); + UINT8 parseBios(const QByteArray & bios, QModelIndex & parent = QModelIndex()); + INT32 getNextVolumeIndex(const QByteArray & bios, INT32 volumeIndex = 0); + UINT32 getVolumeSize(const QByteArray & bios, INT32 volumeIndex); + UINT8 parseVolume(const QByteArray & volume, UINT32 volumeBase, UINT8 revision, bool erasePolarity, QModelIndex & parent = QModelIndex()); + UINT8 parseFile(const QByteArray & file, UINT8 revision, bool erasePolarity, QModelIndex & parent = QModelIndex()); +}; + +#endif diff --git a/uefitool.icns b/uefitool.icns new file mode 100644 index 0000000000000000000000000000000000000000..466379999ee35dc9b7add1b266fd39f3ad79b8e5 GIT binary patch literal 213068 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 + + UEFITool + + + + 0 + 0 + 900 + 600 + + + + + 0 + 0 + + + + true + + + UEFITool 0.2.0 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Structure + + + + + + + Consolas + 10 + + + + 10 + + + false + + + true + + + 200 + + + true + + + + + + + + + + + 0 + 0 + + + + + 216 + 16777215 + + + + Information + + + + + + + 0 + 0 + + + + + 200 + 16777215 + + + + + Consolas + 10 + + + + false + + + true + + + false + + + + + + + false + + + Export... + + + + + + + false + + + Export without header... + + + + + + + + + + + 0 + 0 + + + + Debug + + + + + + + 0 + 0 + + + + + 16777215 + 100 + + + + + 0 + 50 + + + + + Consolas + 10 + + + + false + + + true + + + false + + + + + + + + + + Open BIOS image file... + + + + + + + + + + +