From 839151eaaad24771892afaae6bac690e31e58384 Mon Sep 17 00:00:00 2001 From: Igor Pavlov <87184205+ip7z@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:00:00 +0000 Subject: 26.00 --- C/7zVersion.h | 10 +- C/7zWindows.h | 10 +- C/7zip_gcc_c.mak | 2 +- C/CpuArch.c | 4 +- C/CpuArch.h | 19 +- C/HuffEnc.c | 9 +- C/HuffEnc.h | 2 +- C/Xxh64.c | 98 ++- CPP/7zip/7zip_gcc.mak | 4 +- CPP/7zip/Archive/7z/7zUpdate.cpp | 2 +- CPP/7zip/Archive/ComHandler.cpp | 1324 ++++++++++++++++++++--------- CPP/7zip/Archive/CpioHandler.cpp | 2 +- CPP/7zip/Archive/QcowHandler.cpp | 5 +- CPP/7zip/Archive/Rar/Rar5Handler.cpp | 11 +- CPP/7zip/Archive/Rar/RarHandler.cpp | 22 +- CPP/7zip/Archive/Tar/TarHandler.cpp | 35 +- CPP/7zip/Archive/Tar/TarIn.cpp | 147 ++-- CPP/7zip/Archive/Tar/TarItem.h | 1 + CPP/7zip/Archive/Udf/UdfIn.cpp | 18 +- CPP/7zip/Archive/Udf/UdfIn.h | 7 +- CPP/7zip/Archive/Zip/ZipIn.cpp | 90 +- CPP/7zip/Archive/Zip/ZipOut.cpp | 44 +- CPP/7zip/Bundles/SFXCon/SfxCon.cpp | 2 +- CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp | 50 +- CPP/7zip/Common/FileStreams.cpp | 2 +- CPP/7zip/Common/FileStreams.h | 4 +- CPP/7zip/UI/Common/ArchiveCommandLine.cpp | 2 +- CPP/7zip/UI/Common/ArchiveName.cpp | 4 +- CPP/7zip/UI/Common/Bench.cpp | 9 +- CPP/7zip/UI/Console/List.cpp | 4 +- CPP/7zip/UI/FileManager/BrowseDialog2.cpp | 94 +- CPP/7zip/UI/FileManager/FSFolder.cpp | 8 +- CPP/7zip/UI/FileManager/LangPage.cpp | 3 +- CPP/7zip/UI/FileManager/MenuPage.cpp | 3 +- CPP/7zip/UI/FileManager/PanelItemOpen.cpp | 25 +- CPP/7zip/UI/FileManager/PanelMenu.cpp | 4 +- CPP/7zip/UI/FileManager/PanelSort.cpp | 55 +- CPP/7zip/UI/GUI/BenchmarkDialog.cpp | 55 +- CPP/7zip/UI/GUI/CompressDialog.cpp | 129 ++- CPP/7zip/UI/GUI/CompressDialog.rc | 4 +- CPP/7zip/UI/GUI/ExtractDialog.cpp | 3 +- CPP/Common/Common0.h | 5 +- CPP/Common/MyBuffer.h | 48 +- CPP/Windows/Control/ComboBox.cpp | 9 + CPP/Windows/Control/ComboBox.h | 2 + CPP/Windows/FileFind.cpp | 13 +- CPP/Windows/FileFind.h | 6 +- CPP/Windows/SecurityUtils.h | 4 + CPP/Windows/System.cpp | 10 +- CPP/Windows/System.h | 23 +- CPP/Windows/SystemInfo.cpp | 4 +- CPP/Windows/TimeUtils.h | 8 + DOC/7zip.wxs | 4 +- DOC/License.txt | 6 +- DOC/readme.txt | 50 +- DOC/src-history.txt | 182 ++-- 56 files changed, 1740 insertions(+), 960 deletions(-) diff --git a/C/7zVersion.h b/C/7zVersion.h index b6142e9..770370a 100644 --- a/C/7zVersion.h +++ b/C/7zVersion.h @@ -1,7 +1,7 @@ -#define MY_VER_MAJOR 25 -#define MY_VER_MINOR 1 +#define MY_VER_MAJOR 26 +#define MY_VER_MINOR 0 #define MY_VER_BUILD 0 -#define MY_VERSION_NUMBERS "25.01" +#define MY_VERSION_NUMBERS "26.00" #define MY_VERSION MY_VERSION_NUMBERS #ifdef MY_CPU_NAME @@ -10,12 +10,12 @@ #define MY_VERSION_CPU MY_VERSION #endif -#define MY_DATE "2025-08-03" +#define MY_DATE "2026-02-12" #undef MY_COPYRIGHT #undef MY_VERSION_COPYRIGHT_DATE #define MY_AUTHOR_NAME "Igor Pavlov" #define MY_COPYRIGHT_PD "Igor Pavlov : Public domain" -#define MY_COPYRIGHT_CR "Copyright (c) 1999-2025 Igor Pavlov" +#define MY_COPYRIGHT_CR "Copyright (c) 1999-2026 Igor Pavlov" #ifdef USE_COPYRIGHT_CR #define MY_COPYRIGHT MY_COPYRIGHT_CR diff --git a/C/7zWindows.h b/C/7zWindows.h index 42c6db8..381159e 100644 --- a/C/7zWindows.h +++ b/C/7zWindows.h @@ -1,11 +1,17 @@ -/* 7zWindows.h -- StdAfx -2023-04-02 : Igor Pavlov : Public domain */ +/* 7zWindows.h -- Windows.h and related code +Igor Pavlov : Public domain */ #ifndef ZIP7_INC_7Z_WINDOWS_H #define ZIP7_INC_7Z_WINDOWS_H #ifdef _WIN32 +#if defined(_MSC_VER) && _MSC_VER >= 1950 && !defined(__clang__) // VS2026 +// and some another windows files need that option +// VS2026: wtypesbase.h: warning C4865: 'tagCLSCTX': the underlying type will change from 'int' to 'unsigned int' when '/Zc:enumTypes' is specified on the command line +#pragma warning(disable : 4865) +#endif + #if defined(__clang__) # pragma clang diagnostic push #endif diff --git a/C/7zip_gcc_c.mak b/C/7zip_gcc_c.mak index 195d23d..006cfe0 100644 --- a/C/7zip_gcc_c.mak +++ b/C/7zip_gcc_c.mak @@ -106,7 +106,7 @@ DEL_OBJ_EXE = -$(RM) $(O)\*.o $(O)\$(PROG).exe $(O)\$(PROG).dll endif -LIB2 = -lOle32 -loleaut32 -luuid -ladvapi32 -lUser32 -lShell32 +LIB2 = -lole32 -loleaut32 -luuid -ladvapi32 -luser32 -lshell32 CFLAGS_EXTRA = -DUNICODE -D_UNICODE # -Wno-delete-non-virtual-dtor diff --git a/C/CpuArch.c b/C/CpuArch.c index 6e02551..342280d 100644 --- a/C/CpuArch.c +++ b/C/CpuArch.c @@ -859,7 +859,7 @@ BoolInt CPU_IsSupported_AES (void) { return APPLE_CRYPTO_SUPPORT_VAL; } #if defined(__GLIBC__) && (__GLIBC__ * 100 + __GLIBC_MINOR__ >= 216) #define Z7_GETAUXV_AVAILABLE -#else +#elif !defined(__QNXNTO__) // #pragma message("=== is not NEW GLIBC === ") #if defined __has_include #if __has_include () @@ -877,7 +877,7 @@ BoolInt CPU_IsSupported_AES (void) { return APPLE_CRYPTO_SUPPORT_VAL; } #ifdef USE_HWCAP -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) static unsigned long MY_getauxval(int aux) { unsigned long val; diff --git a/C/CpuArch.h b/C/CpuArch.h index 1690a5b..c682720 100644 --- a/C/CpuArch.h +++ b/C/CpuArch.h @@ -31,7 +31,12 @@ MY_CPU_64BIT means that processor can work with 64-bit registers. #define MY_CPU_NAME "x32" #define MY_CPU_SIZEOF_POINTER 4 #else - #define MY_CPU_NAME "x64" + #if defined(__APX_EGPR__) || defined(__EGPR__) + #define MY_CPU_NAME "x64-apx" + #define MY_CPU_AMD64_APX + #else + #define MY_CPU_NAME "x64" + #endif #define MY_CPU_SIZEOF_POINTER 8 #endif #define MY_CPU_64BIT @@ -596,8 +601,20 @@ problem-4 : performace: #define SetBe32a(p, v) { *(UInt32 *)(void *)(p) = (v); } #define SetBe16a(p, v) { *(UInt16 *)(void *)(p) = (v); } +// gcc and clang for powerpc can transform load byte access to load reverse word access. +// sp we can use byte access instead of word access. Z7_BSWAP64 cab be slow +#if 1 && defined(Z7_CPU_FAST_BSWAP_SUPPORTED) && defined(MY_CPU_64BIT) +#define GetUi64a(p) Z7_BSWAP64 (*(const UInt64 *)(const void *)(p)) +#else #define GetUi64a(p) GetUi64(p) +#endif + +#if 1 && defined(Z7_CPU_FAST_BSWAP_SUPPORTED) +#define GetUi32a(p) Z7_BSWAP32 (*(const UInt32 *)(const void *)(p)) +#else #define GetUi32a(p) GetUi32(p) +#endif + #define GetUi16a(p) GetUi16(p) #define SetUi32a(p, v) SetUi32(p, v) #define SetUi16a(p, v) SetUi16(p, v) diff --git a/C/HuffEnc.c b/C/HuffEnc.c index cbf8c22..297b41a 100644 --- a/C/HuffEnc.c +++ b/C/HuffEnc.c @@ -13,7 +13,7 @@ Igor Pavlov : Public domain */ #define NUM_BITS 10 #define MASK ((1u << NUM_BITS) - 1) #define FREQ_MASK (~(UInt32)MASK) -#define NUM_COUNTERS (48 * 2) +#define NUM_COUNTERS (104 * 2) // (80 * 2) or (128 * 2) : ((prime_number + 1) * 2) for smaller code. #if 1 && (defined(MY_CPU_LE) || defined(MY_CPU_BE)) #if defined(MY_CPU_LE) @@ -95,9 +95,10 @@ void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, unsigned numSy counters[1] = 0; for (i = 2; i != NUM_COUNTERS; i += 2) { - unsigned c; - c = (counters )[i]; (counters )[i] = num; num += c; - c = (counters + 1)[i]; (counters + 1)[i] = num; num += c; + const unsigned c0 = (counters )[i]; + const unsigned c1 = (counters + 1)[i]; + (counters )[i] = num; num += c0; + (counters + 1)[i] = num; num += c1; } counters[0] = num; // we want to write (freq==0) symbols to the end of (p) array { diff --git a/C/HuffEnc.h b/C/HuffEnc.h index 2217f55..45567d0 100644 --- a/C/HuffEnc.h +++ b/C/HuffEnc.h @@ -16,7 +16,7 @@ Conditions: 1 <= maxLen <= 16 = Z7_HUFFMAN_LEN_MAX Num_Items(p) >= HUFFMAN_TEMP_SIZE(num) */ -void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, UInt32 num, UInt32 maxLen); +void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, unsigned num, unsigned maxLen); EXTERN_C_END diff --git a/C/Xxh64.c b/C/Xxh64.c index dc02a02..660e0be 100644 --- a/C/Xxh64.c +++ b/C/Xxh64.c @@ -1,6 +1,6 @@ /* Xxh64.c -- XXH64 hash calculation original code: Copyright (c) Yann Collet. -2023-08-18 : modified by Igor Pavlov. +modified by Igor Pavlov. This source code is licensed under BSD 2-Clause License. */ @@ -27,6 +27,14 @@ void Xxh64State_Init(CXxh64State *p) #if !defined(MY_CPU_64BIT) && defined(MY_CPU_X86) && defined(_MSC_VER) #define Z7_XXH64_USE_ASM +#elif !defined(MY_CPU_LE_UNALIGN_64) // && defined (MY_CPU_LE) + #define Z7_XXH64_USE_ALIGNED +#endif + +#ifdef Z7_XXH64_USE_ALIGNED + #define Xxh64State_UpdateBlocks_Unaligned_Select Xxh64State_UpdateBlocks_Unaligned +#else + #define Xxh64State_UpdateBlocks_Unaligned_Select Xxh64State_UpdateBlocks #endif #if !defined(MY_CPU_64BIT) && defined(MY_CPU_X86) \ @@ -188,32 +196,76 @@ Xxh64State_UpdateBlocks(CXxh64State *p, const void *data, const void *end) #else +#ifdef Z7_XXH64_USE_ALIGNED +static +#endif void Z7_NO_INLINE Z7_FASTCALL -Xxh64State_UpdateBlocks(CXxh64State *p, const void *_data, const void *end) +Xxh64State_UpdateBlocks_Unaligned_Select(CXxh64State *p, const void *_data, const void *end) { const Byte *data = (const Byte *)_data; - UInt64 v[4]; - v[0] = p->v[0]; - v[1] = p->v[1]; - v[2] = p->v[2]; - v[3] = p->v[3]; + UInt64 v0, v1, v2, v3; + v0 = p->v[0]; + v1 = p->v[1]; + v2 = p->v[2]; + v3 = p->v[3]; do { - v[0] = Xxh64_Round(v[0], GetUi64(data)); data += 8; - v[1] = Xxh64_Round(v[1], GetUi64(data)); data += 8; - v[2] = Xxh64_Round(v[2], GetUi64(data)); data += 8; - v[3] = Xxh64_Round(v[3], GetUi64(data)); data += 8; + v0 = Xxh64_Round(v0, GetUi64(data)); data += 8; + v1 = Xxh64_Round(v1, GetUi64(data)); data += 8; + v2 = Xxh64_Round(v2, GetUi64(data)); data += 8; + v3 = Xxh64_Round(v3, GetUi64(data)); data += 8; } while (data != end); - p->v[0] = v[0]; - p->v[1] = v[1]; - p->v[2] = v[2]; - p->v[3] = v[3]; + p->v[0] = v0; + p->v[1] = v1; + p->v[2] = v2; + p->v[3] = v3; } -#endif + +#ifdef Z7_XXH64_USE_ALIGNED + +static +void +Z7_NO_INLINE +Z7_FASTCALL +Xxh64State_UpdateBlocks_Aligned(CXxh64State *p, const void *_data, const void *end) +{ + const Byte *data = (const Byte *)_data; + UInt64 v0, v1, v2, v3; + v0 = p->v[0]; + v1 = p->v[1]; + v2 = p->v[2]; + v3 = p->v[3]; + do + { + v0 = Xxh64_Round(v0, GetUi64a(data)); data += 8; + v1 = Xxh64_Round(v1, GetUi64a(data)); data += 8; + v2 = Xxh64_Round(v2, GetUi64a(data)); data += 8; + v3 = Xxh64_Round(v3, GetUi64a(data)); data += 8; + } + while (data != end); + p->v[0] = v0; + p->v[1] = v1; + p->v[2] = v2; + p->v[3] = v3; +} + +void +Z7_NO_INLINE +Z7_FASTCALL +Xxh64State_UpdateBlocks(CXxh64State *p, const void *data, const void *end) +{ + if (((unsigned)(ptrdiff_t)data & 7) == 0) + Xxh64State_UpdateBlocks_Aligned(p, data, end); + else + Xxh64State_UpdateBlocks_Unaligned(p, data, end); +} + +#endif // Z7_XXH64_USE_ALIGNED +#endif // Z7_XXH64_USE_ASM UInt64 Xxh64State_Digest(const CXxh64State *p, const void *_data, UInt64 count) { @@ -306,12 +358,22 @@ void Xxh64_Update(CXxh64 *p, const void *_data, size_t size) while (--rem); if (cnt != 32) return; - Xxh64State_UpdateBlocks(&p->state, p->buf64, &p->buf64[4]); +#ifdef Z7_XXH64_USE_ALIGNED + Xxh64State_UpdateBlocks_Aligned +#else + Xxh64State_UpdateBlocks_Unaligned_Select +#endif + (&p->state, p->buf64, &p->buf64[4]); } if (size &= ~(size_t)31) { - Xxh64State_UpdateBlocks(&p->state, data, data + size); +#ifdef Z7_XXH64_USE_ALIGNED + if (((unsigned)(ptrdiff_t)data & 7) == 0) + Xxh64State_UpdateBlocks_Aligned(&p->state, data, data + size); + else +#endif + Xxh64State_UpdateBlocks_Unaligned_Select(&p->state, data, data + size); data += size; } diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak index 12f1ef2..a78c0fa 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -142,8 +142,8 @@ MY_MKDIR=mkdir DEL_OBJ_EXE = -$(RM) $(O)\*.o $(O)\$(PROG).exe $(O)\$(PROG).dll endif -LIB2_GUI = -lOle32 -lGdi32 -lComctl32 -lComdlg32 -lShell32 $(LIB_HTMLHELP) -LIB2 = -loleaut32 -luuid -ladvapi32 -lUser32 $(LIB2_GUI) +LIB2_GUI = -lole32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 $(LIB_HTMLHELP) +LIB2 = -loleaut32 -luuid -ladvapi32 -luser32 $(LIB2_GUI) # v24.00: -DUNICODE and -D_UNICODE are defined in precompilation header files # CXXFLAGS_EXTRA = -DUNICODE -D_UNICODE diff --git a/CPP/7zip/Archive/7z/7zUpdate.cpp b/CPP/7zip/Archive/7z/7zUpdate.cpp index c8c5d26..6ba04a2 100644 --- a/CPP/7zip/Archive/7z/7zUpdate.cpp +++ b/CPP/7zip/Archive/7z/7zUpdate.cpp @@ -721,7 +721,7 @@ static int CompareEmptyItems(const unsigned *p1, const unsigned *p2, void *param return (u1.IsDir && u1.IsAnti) ? -n : n; } -static const char *g_Exts = +static const char * const g_Exts = " 7z xz lzma ace arc arj bz tbz bz2 tbz2 cab deb gz tgz ha lha lzh lzo lzx pak rar rpm sit zoo" " zip jar ear war msi" " 3gp avi mov mpeg mpg mpe wmv" diff --git a/CPP/7zip/Archive/ComHandler.cpp b/CPP/7zip/Archive/ComHandler.cpp index 144369e..40a5349 100644 --- a/CPP/7zip/Archive/ComHandler.cpp +++ b/CPP/7zip/Archive/ComHandler.cpp @@ -2,41 +2,62 @@ #include "StdAfx.h" -#include "../../../C/Alloc.h" #include "../../../C/CpuArch.h" -#include "../../Common/IntToString.h" #include "../../Common/ComTry.h" -#include "../../Common/MyCom.h" -#include "../../Common/MyBuffer.h" -#include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" -#define Get16(p) GetUi16(p) -#define Get32(p) GetUi32(p) +#include "Common/ItemNameUtils.h" + +#define Get16(p) GetUi16a(p) +#define Get32(p) GetUi32a(p) + +// we don't expect to get deleted files in real files +// define Z7_COMPOUND_SHOW_DELETED for debug +// #define Z7_COMPOUND_SHOW_DELETED namespace NArchive { namespace NCom { +static const unsigned k_Long_path_level_limit = 256; + static const Byte kSignature[] = - { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; + { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; + +// encoded "[!]MsiPatchSequence" name in "msp" file +static const Byte k_Sequence_msp[] = + { 0x40, 0x48, 0x96, 0x45, 0x6c, 0x3e, 0xe4, 0x45, + 0xe6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41, + 0x37, 0x41, 0, 0 }; + +// encoded "MergeModule.CABinet" name in "msm" file +static const Byte k_Sequence_msm[] = + { 0x16, 0x42, 0xb5, 0x42, 0xa8, 0x3d, 0xf2, 0x41, + 0xf8, 0x43, 0xa8, 0x47, 0x8c, 0x3a, 0x0b, 0x43, + 0x31, 0x42, 0x37, 0x48, 0, 0 }; + +// static const Byte k_CLSID_AAF_V3[] = { 0x41, 0x41, 0x46, 0x42, 0x0d, 0x00, 0x4f, 0x4d, 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0xff }; +// static const Byte k_CLSID_AAF_V4[] = { 0x01, 0x02, 0x01, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x03, 0x02, 0x01, 0x01 }; -enum EType +enum EArcType { k_Type_Common, k_Type_Msi, k_Type_Msp, + k_Type_Msm, k_Type_Doc, k_Type_Ppt, - k_Type_Xls + k_Type_Xls, + k_Type_Aaf }; static const char * const kExtensions[] = @@ -44,35 +65,63 @@ static const char * const kExtensions[] = "compound" , "msi" , "msp" + , "msm" , "doc" , "ppt" , "xls" + , "aaf" }; namespace NFatID { - // static const UInt32 kFree = 0xFFFFFFFF; - static const UInt32 kEndOfChain = 0xFFFFFFFE; - // static const UInt32 kFatSector = 0xFFFFFFFD; - // static const UInt32 kMatSector = 0xFFFFFFFC; - static const UInt32 kMaxValue = 0xFFFFFFFA; + static const UInt32 kFree = 0xffffffff; + static const UInt32 kEndOfChain = 0xfffffffe; + static const UInt32 kFatSector = 0xfffffffd; + static const UInt32 k_DIF_SECT = 0xfffffffc; // double-indirect file allocation table (DIFAT) + static const UInt32 kMaxValue = 0xfffffffa; } namespace NItemType { - static const Byte kEmpty = 0; - static const Byte kStorage = 1; - // static const Byte kStream = 2; - // static const Byte kLockBytes = 3; - // static const Byte kProperty = 4; - static const Byte kRootStorage = 5; + static const unsigned kEmpty = 0; + static const unsigned kStorage = 1; + static const unsigned kStream = 2; + // static const unsigned kLockBytes = 3; + // static const unsigned kProperty = 4; + static const unsigned kRootStorage = 5; +} + +static const unsigned k_MiniSectorSizeBits = 6; +static const UInt32 k_LongStreamMinSize = 1 << 12; + +static const unsigned k_Msi_NumBits = 6; +static const unsigned k_Msi_NumChars = 1 << k_Msi_NumBits; +static const unsigned k_Msi_CharMask = k_Msi_NumChars - 1; +static const unsigned k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1); +static const unsigned k_Msi_StartUnicodeChar = 0x3800; +static const unsigned k_Msi_SpecUnicodeChar = k_Msi_StartUnicodeChar + k_Msi_UnicodeRange; +// (k_Msi_SpecUnicodeChar == 0x4840) is used as special symbol that is used +// as first character in some names in dir entries +/* +static bool IsMsiName(const Byte *p) +{ + unsigned c = Get16(p); + c -= k_Msi_StartUnicodeChar; + return c <= k_Msi_UnicodeRange; +} +*/ + +Z7_FORCE_INLINE static bool IsLargeStream(UInt64 size) +{ + return size >= k_LongStreamMinSize; } static const unsigned kNameSizeMax = 64; +static const UInt32 k_Item_Level_Unused = (UInt32)0 - 1; struct CItem { - Byte Name[kNameSizeMax]; + Byte Name[kNameSizeMax]; // must be aligned for 2-bytes // UInt16 NameSize; // UInt32 Flags; FILETIME CTime; @@ -82,484 +131,792 @@ struct CItem UInt32 RightDid; UInt32 SonDid; UInt32 Sid; - Byte Type; + unsigned Type; // Byte : we use unsigned instead of Byte for alignment + + UInt32 Level; - bool IsEmpty() const { return Type == NItemType::kEmpty; } + bool IsEmptyType() const { return Type == NItemType::kEmpty; } bool IsDir() const { return Type == NItemType::kStorage || Type == NItemType::kRootStorage; } + bool IsStorage() const { return Type == NItemType::kStorage; } + + bool IsLevel_Unused() const { return Level == k_Item_Level_Unused; } - void Parse(const Byte *p, bool mode64bit); + // bool IsSpecMsiName() const { return Get16(Name) == k_Msi_SpecUnicodeChar; } + bool AreMsiChars() const + { + for (unsigned i = 0; i < kNameSizeMax; i += 2) + { + unsigned c = Get16(Name + i); + if (c == 0) + break; + c -= k_Msi_StartUnicodeChar; + if (c <= k_Msi_UnicodeRange) + return true; + } + return false; + } + bool Parse(const Byte *p, bool mode64bit); }; + +static const UInt32 k_Ref_Parent_Root = 0xffffffff; + struct CRef { - int Parent; - UInt32 Did; + UInt32 Parent; // index in Refs[] + UInt32 Did; // index in Items[] }; + class CDatabase { - CObjArray MiniSids; - - HRESULT AddNode(int parent, UInt32 did); - public: + CRecordVector Refs; + CObjectVector Items; CObjArray Fat; CObjArray Mat; - CObjectVector Items; - CRecordVector Refs; -private: - UInt32 NumSectorsInMiniStream; -public: - UInt32 MatSize; + CObjArray MiniSids; + UInt32 FatSize; + UInt32 MatSize; + UInt32 NumSectors_in_MiniStream; - UInt32 LongStreamMinSize; + // UInt32 LongStreamMinSize; unsigned SectorSizeBits; - unsigned MiniSectorSizeBits; Int32 MainSubfile; - EType Type; + EArcType Type; + + bool IsArc; + bool HeadersError; + // bool IsMsi; UInt64 PhySize; - UInt64 PhySize_Aligned; + UInt64 PhySize_Unaligned; + // UInt64 FreeSize; - bool IsNotArcType() const + IArchiveOpenCallback *OpenCallback; + UInt32 Callback_Cur; + +private: + /* + HRESULT IncreaseOpenTotal(UInt32 numSects) { - return - Type != k_Type_Msi && - Type != k_Type_Msp; + if (!OpenCallback) + return S_OK; + const UInt64 total = (UInt64)(Callback_Cur + numSects) << SectorSizeBits; + return OpenCallback->SetTotal(NULL, &total); } + */ + HRESULT AddNodes(); + HRESULT ReadSector(IInStream *inStream, Byte *buf, UInt32 sid); + HRESULT ReadIDs(IInStream *inStream, Byte *buf, UInt32 sid, UInt32 *dest); + HRESULT Check_Item(unsigned index); - void UpdatePhySize(UInt64 val, UInt64 val_Aligned) +public: + bool IsNotArcType() const { - if (PhySize < val) - PhySize = val; - if (PhySize_Aligned < val_Aligned) - PhySize_Aligned = val_Aligned; + return + Type != k_Type_Msi && + Type != k_Type_Msp && + Type != k_Type_Msm; } - HRESULT ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid); - HRESULT ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest); - - HRESULT Update_PhySize_WithItem(unsigned index); void Clear(); - bool IsLargeStream(UInt64 size) const { return size >= LongStreamMinSize; } UString GetItemPath(UInt32 index) const; UInt64 GetItemPackSize(UInt64 size) const { - const UInt64 mask = ((UInt32)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1; - return (size + mask) & ~mask; - } - - bool GetMiniCluster(UInt32 sid, UInt64 &res) const - { - const unsigned subBits = SectorSizeBits - MiniSectorSizeBits; - const UInt32 fid = sid >> subBits; - if (fid >= NumSectorsInMiniStream) - return false; - res = (((UInt64)MiniSids[fid] + 1) << subBits) + (sid & ((1 << subBits) - 1)); - return true; + const UInt64 mask = ((UInt32)1 << (IsLargeStream(size) ? SectorSizeBits : k_MiniSectorSizeBits)) - 1; + return (size + mask) & ~(UInt64)mask; } HRESULT Open(IInStream *inStream); }; -HRESULT CDatabase::ReadSector(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid) +HRESULT CDatabase::ReadSector(IInStream *inStream, Byte *buf, UInt32 sid) { - const UInt64 end = ((UInt64)sid + 2) << sectorSizeBits; - UpdatePhySize(end, end); - RINOK(InStream_SeekSet(inStream, (((UInt64)sid + 1) << sectorSizeBits))) - return ReadStream_FALSE(inStream, buf, (size_t)1 << sectorSizeBits); + const unsigned sb = SectorSizeBits; + RINOK(InStream_SeekSet(inStream, ((UInt64)sid + 1) << sb)) + RINOK(ReadStream_FALSE(inStream, buf, (size_t)1 << sb)) + if (OpenCallback) + { + if ((++Callback_Cur & 0xfff) == 0) + { + const UInt64 processed = (UInt64)Callback_Cur << sb; + const UInt64 numFiles = Items.Size(); + RINOK(OpenCallback->SetCompleted(&numFiles, &processed)) + } + } + return S_OK; } -HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest) +HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, UInt32 sid, UInt32 *dest) { - RINOK(ReadSector(inStream, buf, sectorSizeBits, sid)) - const UInt32 sectorSize = (UInt32)1 << sectorSizeBits; - for (UInt32 t = 0; t < sectorSize; t += 4) + RINOK(ReadSector(inStream, buf, sid)) + const size_t sectorSize = (size_t)1 << SectorSizeBits; + for (size_t t = 0; t < sectorSize; t += 4) *dest++ = Get32(buf + t); return S_OK; } + +Z7_FORCE_INLINE static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) { ft->dwLowDateTime = Get32(p); ft->dwHighDateTime = Get32(p + 4); } -void CItem::Parse(const Byte *p, bool mode64bit) +bool CItem::Parse(const Byte *p, bool mode64bit) { memcpy(Name, p, kNameSizeMax); - // NameSize = Get16(p + 64); + unsigned i; + for (i = 0; i < kNameSizeMax; i += 2) + if (*(const UInt16 *)(const void *)(p + i) == 0) + break; +#if 0 // 1 : for debug : for more strict field check + { + for (unsigned k = i; k < kNameSizeMax; k += 2) + if (*(const UInt16 *)(const void *)(p + k) != 0) + return false; + } +#endif Type = p[66]; + // DOC: names are limited to 32 UTF-16 code points, including the terminating null character. + if (!IsEmptyType()) + if (i == kNameSizeMax || i + 2 != Get16(p + 64)) // NameLength + return false; + if (p[67] >= 2) // Color: 0 (red) or 1 (black) + return false; LeftDid = Get32(p + 68); RightDid = Get32(p + 72); SonDid = Get32(p + 76); - // Flags = Get32(p + 96); + // if (Get32(p + 96) == 0) return false; // State / Flags GetFileTimeFromMem(p + 100, &CTime); GetFileTimeFromMem(p + 108, &MTime); Sid = Get32(p + 116); Size = Get32(p + 120); + /* MS DOC: it is recommended that parsers ignore the most + significant 32 bits of this field in version 3 compound files */ if (mode64bit) Size |= ((UInt64)Get32(p + 124) << 32); + return true; } + void CDatabase::Clear() { + Type = k_Type_Common; + MainSubfile = -1; + IsArc = false; + HeadersError = false; + // IsMsi = false; PhySize = 0; - PhySize_Aligned = 0; - + PhySize_Unaligned = 0; + // FreeSize = 0; + Callback_Cur = 0; + // OpenCallback = NULL; + + FatSize = 0; + MatSize = 0; + NumSectors_in_MiniStream = 0; + Fat.Free(); - MiniSids.Free(); Mat.Free(); + MiniSids.Free(); Items.Clear(); Refs.Clear(); } -static const UInt32 kNoDid = 0xFFFFFFFF; -HRESULT CDatabase::AddNode(int parent, UInt32 did) +static const UInt32 kNoDid = 0xffffffff; + +HRESULT CDatabase::AddNodes() { - if (did == kNoDid) + UInt32 index = Items[0].SonDid; // Items[0] is root item + if (index == kNoDid) // no files case return S_OK; - if (did >= (UInt32)Items.Size()) - return S_FALSE; - const CItem &item = Items[did]; - if (item.IsEmpty()) - return S_FALSE; - CRef ref; - ref.Parent = parent; - ref.Did = did; - const unsigned index = Refs.Add(ref); - if (Refs.Size() > Items.Size()) + if (index == 0 || index >= Items.Size()) return S_FALSE; - RINOK(AddNode(parent, item.LeftDid)) - RINOK(AddNode(parent, item.RightDid)) - if (item.IsDir()) + + CObjArray itemParents(Items.Size()); + CByteArr states(Items.Size()); + memset(itemParents, 0, (size_t)Items.Size() * sizeof(itemParents[0])); // optional + memset(states, 0, Items.Size()); + +#if 1 // 0 : for debug + const UInt32 k_exitParent = 0; + const UInt32 k_startLevel = 1; + // we don't show "Root Entry" dir + states[0] = 3; // we mark root node as processed, also we block any cycle links to root node + // itemParents[0] = 0xffffffff; // optional / unused value +#else + // we show item[0] "Root Entry" dir + const UInt32 k_exitParent = 0xffffffff; + const UInt32 k_startLevel = 0; + index = 0; +#endif + + UInt32 level = k_startLevel; // directory level + unsigned state = 0; + UInt32 parent = k_exitParent; // in Items[], itemParents[], states[] + UInt32 refParent = k_Ref_Parent_Root; // in Refs[] + + for (;;) { - RINOK(AddNode((int)index, item.SonDid)) - } - return S_OK; -} + if (state >= 3) + { + // we return to parent node + if (state != 3) + return E_FAIL; + index = parent; + if (index == k_exitParent) + break; + if (index >= Items.Size()) + return E_FAIL; // (index) was checked already + parent = itemParents[index]; + state = states[index]; + if (state == 0) + return E_FAIL; + if (state == 2) + { + // we return to parent Dir node + if (refParent >= Refs.Size()) + return E_FAIL; + refParent = Refs[refParent].Parent; + level--; + } + continue; + } -static UString CompoundNameToFileName(const UString &s) -{ - UString res; - for (unsigned i = 0; i < s.Len(); i++) + if (index >= Items.Size()) + return S_FALSE; + CItem &item = Items[index]; + if (item.IsEmptyType()) + return S_FALSE; + item.Level = level; + state++; + states[index] = (Byte)state; // we mark current (index) node as used node + + UInt32 newIndex; + if (state != 2) + newIndex = (state < 2) ? item.LeftDid : item.RightDid; + else + { + CRef ref; + ref.Parent = refParent; + ref.Did = index; + const unsigned refIndex = Refs.Add(ref); + if (!item.IsDir()) + continue; + newIndex = item.SonDid; + if (newIndex != kNoDid) + { + level++; + refParent = refIndex; + } + } + + if (newIndex != kNoDid) + { + itemParents[index] = parent; + state = 0; + parent = index; + index = newIndex; + if (index >= Items.Size() || states[index]) + return S_FALSE; + } + } + + if (level != k_startLevel || refParent != k_Ref_Parent_Root) + return E_FAIL; +#if 1 // 1 : optional + // we check that all non-empty items were processed correctly + FOR_VECTOR(i, Items) { - const wchar_t c = s[i]; - if ((unsigned)(int)c < 0x20) + const unsigned st = states[i]; + if (Items[i].IsEmptyType()) { - res.Add_Char('['); - res.Add_UInt32((UInt32)(unsigned)(int)c); - res.Add_Char(']'); + if (st) + return E_FAIL; } + else if (st == 3) + continue; + else if (st) + return E_FAIL; else - res += c; + return S_FALSE; // there is unused directory item } - return res; +#endif + return S_OK; } + static const char k_Msi_Chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._"; - -// static const char * const k_Msi_ID = ""; // "{msi}"; -static const char k_Msi_SpecChar = '!'; - -static const unsigned k_Msi_NumBits = 6; -static const unsigned k_Msi_NumChars = 1 << k_Msi_NumBits; -static const unsigned k_Msi_CharMask = k_Msi_NumChars - 1; -static const unsigned k_Msi_StartUnicodeChar = 0x3800; -static const unsigned k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1); - - -static bool IsMsiName(const Byte *p) -{ - UInt32 c = Get16(p); - return - c >= k_Msi_StartUnicodeChar && - c <= k_Msi_StartUnicodeChar + k_Msi_UnicodeRange; -} +static const char k_Msi_SpecChar_Replace = '!'; static bool AreEqualNames(const Byte *rawName, const char *asciiName) { - for (unsigned i = 0; i < kNameSizeMax / 2; i++) + for (;;) { - wchar_t c = Get16(rawName + i * 2); - wchar_t c2 = (Byte)asciiName[i]; + const unsigned c = Get16(rawName); + rawName += 2; + const unsigned c2 = (Byte)*asciiName; + asciiName++; if (c != c2) return false; - if (c == 0) + if (c2 == 0) return true; } - return false; } -static bool CompoundMsiNameToFileName(const UString &name, UString &res) -{ - res.Empty(); - for (unsigned i = 0; i < name.Len(); i++) - { - wchar_t c = name[i]; - if (c < (wchar_t)k_Msi_StartUnicodeChar || c > (wchar_t)(k_Msi_StartUnicodeChar + k_Msi_UnicodeRange)) - return false; - /* - if (i == 0) - res += k_Msi_ID; - */ - c -= (wchar_t)k_Msi_StartUnicodeChar; - - const unsigned c0 = (unsigned)c & k_Msi_CharMask; - const unsigned c1 = (unsigned)c >> k_Msi_NumBits; - if (c1 <= k_Msi_NumChars) - { - res.Add_Char(k_Msi_Chars[c0]); - if (c1 == k_Msi_NumChars) - break; - res.Add_Char(k_Msi_Chars[c1]); - } - else - res.Add_Char(k_Msi_SpecChar); - } - return true; -} - -static UString ConvertName(const Byte *p, bool &isMsi) +static void MsiName_To_FileName(const Byte *p, UString &res) { - isMsi = false; - UString s; - + res.Empty(); for (unsigned i = 0; i < kNameSizeMax; i += 2) { - wchar_t c = Get16(p + i); + unsigned c = Get16(p + i); if (c == 0) break; - s += c; - } - - UString msiName; - if (CompoundMsiNameToFileName(s, msiName)) - { - isMsi = true; - return msiName; + if (c <= k_Msi_SpecUnicodeChar) + { + if (c < k_Msi_StartUnicodeChar) + { + if (c < 0x20) + { + res.Add_Char('['); + res.Add_UInt32((UInt32)c); + c = ']'; + } + } + else + { +#if 0 // 1 : for debug + if (i == 0) res += "{msi}"; +#endif + c -= k_Msi_StartUnicodeChar; + const unsigned c1 = (unsigned)c >> k_Msi_NumBits; + if (c1 <= k_Msi_NumChars) + { + res.Add_Char(k_Msi_Chars[(unsigned)c & k_Msi_CharMask]); + if (c1 == k_Msi_NumChars) + continue; + c = (Byte)k_Msi_Chars[c1]; + } + else + c = k_Msi_SpecChar_Replace; + } + } + res += (wchar_t)c; } - return CompoundNameToFileName(s); } -static UString ConvertName(const Byte *p) -{ - bool isMsi; - return ConvertName(p, isMsi); -} UString CDatabase::GetItemPath(UInt32 index) const { UString s; - while (index != kNoDid) + UString name; + unsigned level = 0; + while (index != k_Ref_Parent_Root) { const CRef &ref = Refs[index]; const CItem &item = Items[ref.Did]; if (!s.IsEmpty()) s.InsertAtFront(WCHAR_PATH_SEPARATOR); - s.Insert(0, ConvertName(item.Name)); - index = (unsigned)ref.Parent; + // if (IsMsi) + MsiName_To_FileName(item.Name, name); + // else NonMsiName_To_FileName(item.Name, name); + NItemName::NormalizeSlashes_in_FileName_for_OsPath(name); + if (name.IsEmpty()) + name = "[]"; + s.Insert(0, name); + index = ref.Parent; +#ifdef Z7_COMPOUND_SHOW_DELETED + if (item.IsLevel_Unused()) + { + s.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); + break; + } +#endif + if (item.Level >= k_Long_path_level_limit && level) + { + s.Insert(0, L"[LONG_PATH]" WSTRING_PATH_SEPARATOR); + break; + } + level = 1; // level++; } return s; } -HRESULT CDatabase::Update_PhySize_WithItem(unsigned index) + +HRESULT CDatabase::Check_Item(unsigned index) { const CItem &item = Items[index]; - const bool isLargeStream = (index == 0 || IsLargeStream(item.Size)); - if (!isLargeStream) + if (item.IsEmptyType() || item.IsStorage()) return S_OK; - const unsigned bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits; - // streamSpec->Size = item.Size; - - const UInt32 clusterSize = (UInt32)1 << bsLog; - const UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog; - if (numClusters64 >= ((UInt32)1 << 31)) - return S_FALSE; - UInt32 sid = item.Sid; UInt64 size = item.Size; - - if (size != 0) + const bool isLargeStream = (index == 0 || IsLargeStream(size)); + if (!isLargeStream) { - for (;; size -= clusterSize) + const unsigned bsLog = k_MiniSectorSizeBits; + const UInt32 clusterSize = (UInt32)1 << bsLog; + const UInt64 numClusters = (size + clusterSize - 1) >> bsLog; + if (numClusters > MatSize) + return S_FALSE; + UInt32 sid = item.Sid; + if (size != 0) { - // if (isLargeStream) + for (;; size -= clusterSize) + { + if (sid >= MatSize) + return S_FALSE; + const unsigned subBits = SectorSizeBits - k_MiniSectorSizeBits; + const UInt32 fid = sid >> subBits; + if (fid >= NumSectors_in_MiniStream) + return false; + sid = Mat[sid]; + if (size <= clusterSize) + break; + } + } + if (sid != NFatID::kEndOfChain) + return S_FALSE; + } + else + { + const unsigned bsLog = SectorSizeBits; + const UInt32 clusterSize = (UInt32)1 << bsLog; + const UInt64 numClusters = (size + clusterSize - 1) >> bsLog; + if (numClusters > FatSize) + return S_FALSE; + UInt32 sid = item.Sid; + if (size != 0) + { + for (;; size -= clusterSize) { if (sid >= FatSize) return S_FALSE; - UInt64 end = ((UInt64)sid + 1) << bsLog; - const UInt64 end_Aligned = end + clusterSize; - if (size < clusterSize) - end += size; - else - end = end_Aligned; - UpdatePhySize(end, end_Aligned); + const UInt32 sidPrev = sid; sid = Fat[sid]; + if (size <= clusterSize) + { + const UInt64 phySize = (((UInt64)sidPrev + 1) << SectorSizeBits) + size; + if (PhySize_Unaligned < phySize) + PhySize_Unaligned = phySize; + break; + } } - if (size <= clusterSize) - break; } + if (sid != NFatID::kEndOfChain) + return S_FALSE; } - if (sid != NFatID::kEndOfChain) - return S_FALSE; return S_OK; } -// There is name "[!]MsiPatchSequence" in msp files -static const unsigned kMspSequence_Size = 18; -static const Byte kMspSequence[kMspSequence_Size] = - { 0x40, 0x48, 0x96, 0x45, 0x6C, 0x3E, 0xE4, 0x45, - 0xE6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41, - 0x37, 0x41 }; HRESULT CDatabase::Open(IInStream *inStream) { - MainSubfile = -1; - Type = k_Type_Common; - const UInt32 kHeaderSize = 512; - Byte p[kHeaderSize]; - PhySize = kHeaderSize; - RINOK(ReadStream_FALSE(inStream, p, kHeaderSize)) - if (memcmp(p, kSignature, Z7_ARRAY_SIZE(kSignature)) != 0) + const unsigned kHeaderSize = 512; + UInt32 p32[kHeaderSize / 4]; + RINOK(ReadStream_FALSE(inStream, p32, kHeaderSize)) + const Byte *p = (const Byte *)(const void *)p32; + if (memcmp(p, kSignature, Z7_ARRAY_SIZE(kSignature))) + return S_FALSE; + /* + if (memcmp(p + 8, k_CLSID_AAF_V3, Z7_ARRAY_SIZE(k_CLSID_AAF_V3)) == 0 || + memcmp(p + 8, k_CLSID_AAF_V4, Z7_ARRAY_SIZE(k_CLSID_AAF_V4)) == 0) + */ + if (Get32(p32 + 4) == 0x342b0e06) // simplified AAF signature check + Type = k_Type_Aaf; + if (Get16(p + 0x18) != 0x3e) // minorVer return S_FALSE; - if (Get16(p + 0x1A) > 4) // majorVer + const unsigned ver = Get16(p + 0x1a); // majorVer + if (ver < 3 || ver > 4) return S_FALSE; - if (Get16(p + 0x1C) != 0xFFFE) // Little-endian + if (Get16(p + 0x1c) != 0xfffe) // Little-endian + return S_FALSE; + const unsigned sectorSizeBits = Get16(p + 0x1e); + if (sectorSizeBits != ver * 3) // (ver == 3 ? 9 : 12) return S_FALSE; - unsigned sectorSizeBits = Get16(p + 0x1E); - bool mode64bit = (sectorSizeBits >= 12); - unsigned miniSectorSizeBits = Get16(p + 0x20); SectorSizeBits = sectorSizeBits; - MiniSectorSizeBits = miniSectorSizeBits; + if (Get16(p + 0x20) != k_MiniSectorSizeBits) + return S_FALSE; + + IsArc = true; + HeadersError = true; - if (sectorSizeBits > 24 || - sectorSizeBits < 7 || - miniSectorSizeBits > 24 || - miniSectorSizeBits < 2 || - miniSectorSizeBits > sectorSizeBits) + const bool mode64bit = (sectorSizeBits >= 12); // (ver == 4) + if (Get16(p + 0x22) || p32[9]) // reserved + return S_FALSE; + + const UInt32 numDirSectors = Get32(p32 + 10); + // If (ver==3), the Number of Directory Sectors MUST be zero. + if (ver != 3 + (unsigned)(numDirSectors != 0)) + return S_FALSE; + if (numDirSectors > ((1u << (32 - 2)) >> (sectorSizeBits - (7 + 2)))) + return S_FALSE; + + const UInt32 numSectorsForFAT = Get32(p32 + 11); // SAT + + // MSDOC: A 512-byte sector compound file MUST be no greater than 2 GB in size for compatibility reasons. + // but actual restriction for windows compond creation code can be more strict: + // (numSectorsForFAT < (1 << 15)) : actual restriction in win10 for compound creation code + // (numSectorsForFAT <= (1 << 15)) : relaxed restriction to allow 2 GB files. + if (sectorSizeBits == 9 && + numSectorsForFAT >= (1u << (31 - (9 + 9 - 2)))) // we use most strict check return S_FALSE; - UInt32 numSectorsForFAT = Get32(p + 0x2C); // SAT - LongStreamMinSize = Get32(p + 0x38); - - UInt32 sectSize = (UInt32)1 << sectorSizeBits; - CByteBuffer sect(sectSize); + // const UInt32 TransactionSignatureNumber = Get32(p32 + 13); + if (Get32(p32 + 14) != k_LongStreamMinSize) + return S_FALSE; - unsigned ssb2 = sectorSizeBits - 2; - UInt32 numSidsInSec = (UInt32)1 << ssb2; - UInt32 numFatItems = numSectorsForFAT << ssb2; - if ((numFatItems >> ssb2) != numSectorsForFAT) + const unsigned ssb2 = sectorSizeBits - 2; + const UInt32 numSidsInSec = (UInt32)1 << ssb2; + const UInt32 numFatItems = numSectorsForFAT << ssb2; + if (numFatItems == 0 || (numFatItems >> ssb2) != numSectorsForFAT) return S_FALSE; - FatSize = numFatItems; + const size_t sectSize = (size_t)1 << sectorSizeBits; + CByteArr sect(sectSize); + CByteArr used(numFatItems); + // don't change these const values. These values use same order as (0xffffffff - NFatID::const) + // const Byte k_Used_Free = 0; + const Byte k_Used_ChainTo = 1; + const Byte k_Used_FAT = 2; + const Byte k_Used_DIFAT = 3; + memset(used, 0, numFatItems); + UInt32 *fat; { - UInt32 numSectorsForBat = Get32(p + 0x48); // master sector allocation table - const UInt32 kNumHeaderBatItems = 109; - UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2); - if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat) + // ========== READ FAT ========== + const UInt32 numSectorsForBat = Get32(p32 + 18); // master sector allocation table + const unsigned ssb2_m1 = ssb2 - 1; + if (numSectorsForBat > ((1u << 30) >> ssb2_m1 >> ssb2_m1)) return S_FALSE; + const unsigned kNumHeaderBatItems = 109; + UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2); // real size can be smaller CObjArray bat(numBatItems); - UInt32 i; + size_t i; for (i = 0; i < kNumHeaderBatItems; i++) - bat[i] = Get32(p + 0x4c + i * 4); - UInt32 sid = Get32(p + 0x44); - for (UInt32 s = 0; s < numSectorsForBat; s++) + bat[i] = Get32(p32 + 19 + i); { - RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i)) - i += numSidsInSec - 1; - sid = bat[i]; + UInt32 sid = Get32(p32 + 17); + for (UInt32 s = 0; s < numSectorsForBat; s++) + { + if (sid >= numFatItems || used[sid]) + return S_FALSE; + used[sid] = k_Used_DIFAT; + RINOK(ReadIDs(inStream, sect, sid, bat + i)) + i += numSidsInSec - 1; + sid = bat[i]; + } + if (sid != NFatID::kEndOfChain // NFatID::kEndOfChain is expected value for most files + && sid != NFatID::kFree) // NFatID::kFree is used in some AAF files + return S_FALSE; } - numBatItems = i; - + numBatItems = (UInt32)i; // corrected value + if (numSectorsForFAT > numBatItems) + return S_FALSE; + for (i = numSectorsForFAT; i < numBatItems; i++) + if (bat[i] != NFatID::kFree) + return S_FALSE; + + // RINOK(IncreaseOpenTotal(numSectorsForFAT + numDirSectors)) + Fat.Alloc(numFatItems); - UInt32 j = 0; - - for (i = 0; i < numFatItems; j++, i += numSidsInSec) + fat = Fat; + for (i = 0; i < numSectorsForFAT; i++) { - if (j >= numBatItems) + const UInt32 sectorIndex = bat[i]; + if (sectorIndex >= numFatItems) return S_FALSE; - RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i)) + if (used[sectorIndex]) + return S_FALSE; + used[sectorIndex] = k_Used_FAT; + UInt32 *fat2 = fat + ((size_t)i << ssb2); + RINOK(ReadIDs(inStream, sect, sectorIndex, fat2)) + for (size_t k = 0; k < numSidsInSec; k++) + { + const UInt32 sid = fat2[k]; + if (sid > NFatID::kMaxValue) + { + if (sid == NFatID::k_DIF_SECT + && used[((size_t)i << ssb2) + k] != k_Used_DIFAT) + return S_FALSE; + continue; + } + if (sid >= numFatItems || used[sid]) + return S_FALSE; // strict error check + used[sid] = k_Used_ChainTo; + } } - FatSize = numFatItems = i; + { + for (i = 0; i < numSectorsForFAT; i++) + if (fat[bat[i]] != NFatID::kFatSector) + return S_FALSE; + } + FatSize = numFatItems; + } + + { + size_t i = numFatItems; + do + if (fat[i - 1] != NFatID::kFree) + break; + while (--i); + PhySize = ((UInt64)i + 1) << sectorSizeBits; + /* + if (i) + { + const UInt32 *lim = fat + i; + UInt32 num = 0; + do + if (*fat++ == NFatID::kFree) + num++; + while (fat != lim); + FreeSize = num << sectorSizeBits; + } + */ } UInt32 numMatItems; { - UInt32 numSectorsForMat = Get32(p + 0x40); + // ========== READ MAT ========== + const UInt32 numSectorsForMat = Get32(p32 + 16); numMatItems = (UInt32)numSectorsForMat << ssb2; if ((numMatItems >> ssb2) != numSectorsForMat) return S_FALSE; Mat.Alloc(numMatItems); - UInt32 i; - UInt32 sid = Get32(p + 0x3C); // short-sector table SID - for (i = 0; i < numMatItems; i += numSidsInSec) + UInt32 sid = Get32(p32 + 15); // short-sector table SID + if (numMatItems) + { + if (sid >= numFatItems || used[sid]) + return S_FALSE; + used[sid] = k_Used_ChainTo; + } + for (UInt32 i = 0; i < numMatItems; i += numSidsInSec) { - RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i)) if (sid >= numFatItems) return S_FALSE; - sid = Fat[sid]; + RINOK(ReadIDs(inStream, sect, sid, Mat + i)) + sid = fat[sid]; } if (sid != NFatID::kEndOfChain) return S_FALSE; } { - CByteBuffer used(numFatItems); - for (UInt32 i = 0; i < numFatItems; i++) - used[i] = 0; - UInt32 sid = Get32(p + 0x30); // directory stream SID - for (;;) + // ========== READ DIR ITEMS ========== + UInt32 sid = Get32(p32 + 12); // directory stream SID + UInt32 numDirSectors_Processed = 0; + if (sid >= numFatItems || used[sid]) + return S_FALSE; + used[sid] = k_Used_ChainTo; + do { + // we need to check sid here becase kEndOfChain sid < numFatItems is required if (sid >= numFatItems) return S_FALSE; - if (used[sid]) + if (numDirSectors && numDirSectors_Processed >= numDirSectors) return S_FALSE; - used[sid] = 1; - RINOK(ReadSector(inStream, sect, sectorSizeBits, sid)) - for (UInt32 i = 0; i < sectSize; i += 128) + numDirSectors_Processed++; + RINOK(ReadSector(inStream, sect, sid)) + for (size_t i = 0; i < sectSize; i += (1 << 7)) { CItem item; - item.Parse(sect + i, mode64bit); + item.Level = k_Item_Level_Unused; + if (!item.Parse(sect + i, mode64bit)) + return S_FALSE; // we use (item.Size) check here. // so we don't need additional overflow checks for (item.Size +) in another code - if (item.Size >= ((UInt64)1 << 63)) + if ((UInt32)(item.Size >> 32) >= sectSize) // it's because FAT size is limited by (1 << 32) items. return S_FALSE; + + if (Items.IsEmpty()) + { + if (item.Type != NItemType::kRootStorage + || item.LeftDid != kNoDid + || item.RightDid != kNoDid + || item.SonDid == 0) + return S_FALSE; + if (item.Sid != NFatID::kEndOfChain) + { + if (item.Sid >= numFatItems || used[item.Sid]) + return S_FALSE; + used[item.Sid] = k_Used_ChainTo; + } + } + else if (item.IsStorage()) + { + if (item.Size != 0) // by specification + return S_FALSE; + if (item.Sid != 0 // by specification + && item.Sid != NFatID::kFree) // NFatID::kFree is used in some AAF files + return S_FALSE; + } + // else if (item.Type == NItemType::kRootStorage) return S_FALSE; + else if (item.IsEmptyType()) + { + // kNoDid is expected in *Did fileds, but rare case MSI contains zero in all fields + if ((item.Sid != 0 // expected value + && item.Sid != NFatID::kFree // NFatID::kFree is used in some AAF files + && item.Sid != NFatID::kEndOfChain) // used by some MSI file + || (item.LeftDid != kNoDid && item.LeftDid) + || (item.RightDid != kNoDid && item.RightDid) + || (item.SonDid != kNoDid && item.SonDid) + // || item.Size != 0 // the check is disabled because some MSI file contains non zero + // || Get16(item.Name) != 0 // the check is disabled because some MSI file contains some name + ) + return S_FALSE; + } + else + { + if (item.Type != NItemType::kStream) + return S_FALSE; + // NItemType::kStream case + if (item.SonDid != kNoDid) // optional check + return S_FALSE; + if (item.Size == 0) + { + if (item.Sid != NFatID::kEndOfChain) + return S_FALSE; + } + else if (IsLargeStream(item.Size)) + { + if (item.Sid >= numFatItems || used[item.Sid]) + return S_FALSE; + used[item.Sid] = k_Used_ChainTo; + } + } + Items.Add(item); } - sid = Fat[sid]; - if (sid == NFatID::kEndOfChain) - break; + sid = fat[sid]; } + while (sid != NFatID::kEndOfChain); } - const CItem &root = Items[0]; - { + // root stream contains all data that stored with mini Sectors + const CItem &root = Items[0]; UInt32 numSectorsInMiniStream; { - UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits; - if (numSatSects64 > NFatID::kMaxValue) + const UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits; + if (numSatSects64 > NFatID::kMaxValue + 1) return S_FALSE; numSectorsInMiniStream = (UInt32)numSatSects64; } - NumSectorsInMiniStream = numSectorsInMiniStream; - MiniSids.Alloc(numSectorsInMiniStream); { - UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits; - if (matSize64 > NFatID::kMaxValue) + const UInt64 matSize64 = (root.Size + (1 << k_MiniSectorSizeBits) - 1) >> k_MiniSectorSizeBits; + if (matSize64 > numMatItems) return S_FALSE; MatSize = (UInt32)matSize64; - if (numMatItems < MatSize) - return S_FALSE; } - + MiniSids.Alloc(numSectorsInMiniStream); + UInt32 * const miniSids = MiniSids; UInt32 sid = root.Sid; for (UInt32 i = 0; ; i++) { @@ -571,95 +928,186 @@ HRESULT CDatabase::Open(IInStream *inStream) } if (i >= numSectorsInMiniStream) return S_FALSE; - MiniSids[i] = sid; if (sid >= numFatItems) return S_FALSE; - sid = Fat[sid]; + miniSids[i] = sid; + sid = fat[sid]; } + NumSectors_in_MiniStream = numSectorsInMiniStream; } - RINOK(AddNode(-1, root.SonDid)) - - unsigned numCabs = 0; - - FOR_VECTOR (i, Refs) + { - const CItem &item = Items[Refs[i].Did]; - if (item.IsDir() || numCabs > 1) - continue; - bool isMsiName; - const UString msiName = ConvertName(item.Name, isMsiName); - if (isMsiName && !msiName.IsEmpty()) - { - // bool isThereExt = (msiName.Find(L'.') >= 0); - bool isMsiSpec = (msiName[0] == k_Msi_SpecChar); - if ((msiName.Len() >= 4 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(4), ".cab")) - || (!isMsiSpec && msiName.Len() >= 3 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(3), "exe")) - // || (!isMsiSpec && !isThereExt) - ) +/* + MS DOCs: + The range lock sector covers file offsets 0x7FFFFF00-0x7FFFFFFF. + These offsets are reserved for byte-range locking to support + concurrency, transactions, and other compound file features. + The range lock sector MUST be allocated in the FAT and marked with + ENDOFCHAIN (0xFFFFFFFE), when the compound file grows beyond 2 GB. + If the compound file is greater than 2 GB and then shrinks to below 2 GB, + the range lock sector SHOULD be marked as FREESECT (0xFFFFFFFF) in the FAT. +*/ + { + const UInt32 lockSector = (0x7fffffff >> sectorSizeBits) - 1; + if (lockSector < numFatItems) { - numCabs++; - MainSubfile = (int)i; + if (used[lockSector]) + return S_FALSE; + const UInt32 f = fat[lockSector]; + if (f == NFatID::kEndOfChain) + used[lockSector] = k_Used_ChainTo; // we use fake state to pass the check in loop below + else if (f != NFatID::kFree) + return S_FALSE; } } + for (size_t i = 0; i < numFatItems; i++) + { + UInt32 f = fat[i]; + const UInt32 u = ~(UInt32)used[i]; // (0xffffffff - used[i]) + if (f < NFatID::kMaxValue + 1) + f = NFatID::kEndOfChain; + if (f != u) + return S_FALSE; + } } - - if (numCabs > 1) - MainSubfile = -1; { - FOR_VECTOR (t, Items) + // Don't move that code up, becase Check_Item uses Mat[] array. + FOR_VECTOR(t, Items) { - Update_PhySize_WithItem(t); + RINOK(Check_Item(t)) } } + + RINOK(AddNodes()) + + { + // some msi (in rare cases) have unaligned size of archive, + // unaligned size of compond files is also possible if we create just one stream + // where there is no padding data after payload data in last cluster of archive + UInt64 fileSize; + RINOK(InStream_GetSize_SeekToEnd(inStream, fileSize)) + if ( fileSize < PhySize + && fileSize > PhySize - sectSize + && fileSize >= PhySize_Unaligned + && PhySize_Unaligned > PhySize - sectSize) + PhySize = PhySize_Unaligned; + } + + bool isMsi = false; { - if (PhySize != PhySize_Aligned) + FOR_VECTOR (i, Refs) { - /* some msi (in rare cases) have unaligned size of archive, - where there is no padding data after payload data in last cluster of archive */ - UInt64 fileSize; - RINOK(InStream_GetSize_SeekToEnd(inStream, fileSize)) - if (PhySize != fileSize) - PhySize = PhySize_Aligned; + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir()) + continue; + if (item.AreMsiChars()) + // if (item.IsSpecMsiName()) + { + isMsi = true; + break; + } } } + + // IsMsi = isMsi; + if (isMsi) { - FOR_VECTOR (t, Items) + unsigned numCabs = 0; + UString name; + FOR_VECTOR (i, Refs) { - const CItem &item = Items[t]; - - if (IsMsiName(item.Name)) + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir() /* || item.IsSpecMsiName() */) + continue; + MsiName_To_FileName(item.Name, name); + if ( (name.Len() >= 4 && StringsAreEqualNoCase_Ascii(name.RightPtr(4), ".cab")) + || (name.Len() >= 3 && StringsAreEqualNoCase_Ascii(name.RightPtr(3), "exe")) + ) { - Type = k_Type_Msi; - if (memcmp(item.Name, kMspSequence, kMspSequence_Size) == 0) + numCabs++; + if (numCabs > 1) { - Type = k_Type_Msp; + MainSubfile = -1; break; } - continue; - } - if (AreEqualNames(item.Name, "WordDocument")) - { - Type = k_Type_Doc; - break; + MainSubfile = (int)i; } - if (AreEqualNames(item.Name, "PowerPoint Document")) + } + } + + if (isMsi) // we provide msi priority over AAF + Type = k_Type_Msi; + if (Type != k_Type_Aaf) + { + FOR_VECTOR (i, Refs) + { + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir()) + continue; + const Byte *name = item.Name; + // if (IsMsiName(name)) + if (isMsi) { - Type = k_Type_Ppt; - break; + if (memcmp(name, k_Sequence_msp, sizeof(k_Sequence_msp)) == 0) + { + Type = k_Type_Msp; + break; + } + if (memcmp(name, k_Sequence_msm, sizeof(k_Sequence_msm)) == 0) + { + Type = k_Type_Msm; + break; + } } - if (AreEqualNames(item.Name, "Workbook")) + else { - Type = k_Type_Xls; - break; + if (AreEqualNames(name, "WordDocument")) + { + Type = k_Type_Doc; + break; + } + if (AreEqualNames(name, "PowerPoint Document")) + { + Type = k_Type_Ppt; + break; + } + if (AreEqualNames(name, "Workbook")) + { + Type = k_Type_Xls; + break; + } } } } +#ifdef Z7_COMPOUND_SHOW_DELETED + { + // we skip Items[0] that is root item + for (unsigned t = 1; t < Items.Size(); t++) + { + const CItem &item = Items[t]; + if ( +#if 1 // 0 for debug to show empty files + item.IsEmptyType() || +#endif + !item.IsLevel_Unused()) + continue; + CRef ref; + ref.Parent = k_Ref_Parent_Root; + ref.Did = t; + Refs.Add(ref); + } + } +#endif + + HeadersError = false; return S_OK; } + + Z7_CLASS_IMP_CHandler_IInArchive_1( IInArchiveGetStream ) @@ -674,13 +1122,15 @@ static const Byte kProps[] = kpidPackSize, kpidCTime, kpidMTime + // , kpidCharacts // for debug }; static const Byte kArcProps[] = { kpidExtension, - kpidClusterSize, - kpidSectorSize + kpidClusterSize + // , kpidSectorSize + // , kpidFreeSpace }; IMP_IInArchive_Props @@ -695,9 +1145,20 @@ Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) case kpidExtension: prop = kExtensions[(unsigned)_db.Type]; break; case kpidPhySize: prop = _db.PhySize; break; case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break; - case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break; + // case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break; case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; break; + // case kpidFreeSpace: prop = _db.FreeSize; break; case kpidIsNotArcType: if (_db.IsNotArcType()) prop = true; break; + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_db.IsArc) + v |= kpv_ErrorFlags_IsNotArc; + if (_db.HeadersError) + v |= kpv_ErrorFlags_HeadersError; + prop = v; + break; + } } prop.Detach(value); return S_OK; @@ -719,6 +1180,7 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidMTime: prop = item.MTime; break; case kpidPackSize: if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break; case kpidSize: if (!item.IsDir()) prop = item.Size; break; + // case kpidCharacts: prop = item.Level; break; } prop.Detach(value); return S_OK; @@ -727,17 +1189,17 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 * /* maxCheckStartPosition */, - IArchiveOpenCallback * /* openArchiveCallback */)) + IArchiveOpenCallback *openArchiveCallback)) { COM_TRY_BEGIN Close(); - try + _db.OpenCallback = openArchiveCallback; + // try { - if (_db.Open(inStream) != S_OK) - return S_FALSE; + RINOK(_db.Open(inStream)) _stream = inStream; } - catch(...) { return S_FALSE; } + // catch(...) { return S_FALSE; } return S_OK; COM_TRY_END } @@ -775,52 +1237,57 @@ Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, CMyComPtr2_Create lps; lps->Init(extractCallback, false); - for (i = 0; i < numItems; i++) + for (i = 0;; i++) { lps->InSize = totalPackSize; lps->OutSize = totalSize; RINOK(lps->SetCur()) + if (i >= numItems) + break; + const UInt32 index = allFilesMode ? i : indices[i]; const CItem &item = _db.Items[_db.Refs[index].Did]; Int32 res; - { - CMyComPtr outStream; - const Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - RINOK(extractCallback->GetStream(index, &outStream, askMode)) - - if (item.IsDir()) { + CMyComPtr outStream; + const Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(index, &outStream, askMode)) + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)) + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) + continue; + } + + totalPackSize += _db.GetItemPackSize(item.Size); + totalSize += item.Size; + + if (!testMode && !outStream) + continue; RINOK(extractCallback->PrepareOperation(askMode)) - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) - continue; - } - - totalPackSize += _db.GetItemPackSize(item.Size); - totalSize += item.Size; - - if (!testMode && !outStream) - continue; - RINOK(extractCallback->PrepareOperation(askMode)) - res = NExtract::NOperationResult::kDataError; - CMyComPtr inStream; - HRESULT hres = GetStream(index, &inStream); - if (hres == S_FALSE) res = NExtract::NOperationResult::kDataError; - else if (hres == E_NOTIMPL) - res = NExtract::NOperationResult::kUnsupportedMethod; - else - { - RINOK(hres) - if (inStream) + CMyComPtr inStream; + const HRESULT hres = GetStream(index, &inStream); + if (hres == S_FALSE) + res = NExtract::NOperationResult::kDataError; + /* + else if (hres == E_NOTIMPL) + res = NExtract::NOperationResult::kUnsupportedMethod; + */ + else { - RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps)) - if (copyCoder->TotalSize == item.Size) - res = NExtract::NOperationResult::kOK; + RINOK(hres) + if (inStream) + { + RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps)) + if (copyCoder->TotalSize == item.Size) + res = NExtract::NOperationResult::kOK; + } } } - } RINOK(extractCallback->SetOperationResult(res)) } return S_OK; @@ -839,20 +1306,64 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) *stream = NULL; const UInt32 itemIndex = _db.Refs[index].Did; const CItem &item = _db.Items[itemIndex]; + if (item.IsDir()) + return S_FALSE; + const bool isLargeStream = (itemIndex == 0 || IsLargeStream(item.Size)); + if (!isLargeStream) + { + CBufferInStream *streamSpec = new CBufferInStream; + CMyComPtr streamTemp = streamSpec; + + UInt32 size = (UInt32)item.Size; + streamSpec->Buf.Alloc(size); + streamSpec->Init(); + + UInt32 sid = item.Sid; + Byte *dest = streamSpec->Buf; + + UInt64 phyPos = 0; + while (size) + { + if (sid >= _db.MatSize) + return S_FALSE; + const unsigned subBits = _db.SectorSizeBits - k_MiniSectorSizeBits; + const UInt32 fid = sid >> subBits; + if (fid >= _db.NumSectors_in_MiniStream) + return false; + const UInt64 offset = (((UInt64)_db.MiniSids[fid] + 1) << _db.SectorSizeBits) + + ((sid & ((1u << subBits) - 1)) << k_MiniSectorSizeBits); + if (phyPos != offset) + { + RINOK(InStream_SeekSet(_stream, offset)) + phyPos = offset; + } + UInt32 readSize = (UInt32)1 << k_MiniSectorSizeBits; + if (readSize > size) + readSize = size; + RINOK(ReadStream_FALSE(_stream, dest, readSize)) + phyPos += readSize; + dest += readSize; + sid = _db.Mat[sid]; + size -= readSize; + } + if (sid != NFatID::kEndOfChain) + return S_FALSE; + *stream = streamTemp.Detach(); + return S_OK; + } + CClusterInStream *streamSpec = new CClusterInStream; CMyComPtr streamTemp = streamSpec; streamSpec->Stream = _stream; streamSpec->StartOffset = 0; - - const bool isLargeStream = (itemIndex == 0 || _db.IsLargeStream(item.Size)); - const unsigned bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits; + const unsigned bsLog = _db.SectorSizeBits; streamSpec->BlockSizeLog = bsLog; streamSpec->Size = item.Size; const UInt32 clusterSize = (UInt32)1 << bsLog; const UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog; - if (numClusters64 >= ((UInt32)1 << 31)) - return E_NOTIMPL; + if (numClusters64 > _db.FatSize) + return S_FALSE; streamSpec->Vector.ClearAndReserve((unsigned)numClusters64); UInt32 sid = item.Sid; UInt64 size = item.Size; @@ -861,21 +1372,10 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) { for (;; size -= clusterSize) { - if (isLargeStream) - { - if (sid >= _db.FatSize) - return S_FALSE; - streamSpec->Vector.AddInReserved(sid + 1); - sid = _db.Fat[sid]; - } - else - { - UInt64 val = 0; - if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32) - return S_FALSE; - streamSpec->Vector.AddInReserved((UInt32)val); - sid = _db.Mat[sid]; - } + if (sid >= _db.FatSize) + return S_FALSE; + streamSpec->Vector.AddInReserved(sid + 1); + sid = _db.Fat[sid]; if (size <= clusterSize) break; } @@ -889,7 +1389,7 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) } REGISTER_ARC_I( - "Compound", "msi msp doc xls ppt", NULL, 0xE5, + "Compound", "msi msp msm doc xls ppt aaf", NULL, 0xe5, kSignature, 0, 0, diff --git a/CPP/7zip/Archive/CpioHandler.cpp b/CPP/7zip/Archive/CpioHandler.cpp index 62184f0..e1d6d81 100644 --- a/CPP/7zip/Archive/CpioHandler.cpp +++ b/CPP/7zip/Archive/CpioHandler.cpp @@ -927,7 +927,7 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val { #ifdef _WIN32 UString u; - ConvertUTF8ToUnicode(item.Name, u); + ConvertUTF8ToUnicode(s, u); #else const UString u = MultiByteToUnicodeString(s, CP_OEMCP); #endif diff --git a/CPP/7zip/Archive/QcowHandler.cpp b/CPP/7zip/Archive/QcowHandler.cpp index b072880..6edf86d 100644 --- a/CPP/7zip/Archive/QcowHandler.cpp +++ b/CPP/7zip/Archive/QcowHandler.cpp @@ -482,6 +482,10 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) if (_phySize < headerSize) _phySize = headerSize; + // we use 32 MiB limit for L1 size, as QEMU with QCOW_MAX_L1_SIZE limit. + if (l1Size > (1u << 22)) // if (l1Size > (1u << (sizeof(size_t) * 8 - 4))) + return S_FALSE; + _isArc = true; { const UInt64 backOffset = Get64((const Byte *)(const void *)buf64 + 8); @@ -519,7 +523,6 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) } CObjArray table64(l1Size); { - // if ((t1SizeBytes >> 3) != l1Size) return S_FALSE; RINOK(InStream_SeekSet(stream, l1Offset)) RINOK(ReadStream_FALSE(stream, table64, t1SizeBytes)) } diff --git a/CPP/7zip/Archive/Rar/Rar5Handler.cpp b/CPP/7zip/Archive/Rar/Rar5Handler.cpp index a639d8b..c15ff52 100644 --- a/CPP/7zip/Archive/Rar/Rar5Handler.cpp +++ b/CPP/7zip/Archive/Rar/Rar5Handler.cpp @@ -8,6 +8,7 @@ #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/MyBuffer2.h" +#include "../../../Common/MyLinux.h" #include "../../../Common/UTFConvert.h" #include "../../../Windows/PropVariantUtils.h" @@ -1184,7 +1185,15 @@ HRESULT CUnpacker::Code(const CItem &item, const CItem &lastItem, UInt64 packSiz const UInt64 processedSize = outStream->GetPos(); if (res == S_OK && !lastItem.Is_UnknownSize() && processedSize != lastItem.Size) - res = S_FALSE; + { + // rar_v7.13-: linux archive contains symLink with (packSize == 0 && lastItem.Size != 0) + // v25.02: we ignore such record in rar headers: + if (packSize != 0 + || method != 0 + || lastItem.HostOS != kHost_Unix + || !MY_LIN_S_ISLNK(lastItem.Attrib)) + res = S_FALSE; + } // if (res == S_OK) { diff --git a/CPP/7zip/Archive/Rar/RarHandler.cpp b/CPP/7zip/Archive/Rar/RarHandler.cpp index dfbad33..6c53847 100644 --- a/CPP/7zip/Archive/Rar/RarHandler.cpp +++ b/CPP/7zip/Archive/Rar/RarHandler.cpp @@ -7,6 +7,7 @@ #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/MyBuffer2.h" +#include "../../../Common/MyLinux.h" #include "../../../Common/UTFConvert.h" #include "../../../Windows/PropVariantUtils.h" @@ -70,8 +71,14 @@ bool CItem::IsDir() const case NHeader::NFile::kHostMSDOS: case NHeader::NFile::kHostOS2: case NHeader::NFile::kHostWin32: - if ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0) + if (Attrib & FILE_ATTRIBUTE_DIRECTORY) return true; + break; + case NHeader::NFile::kHostUnix: + case NHeader::NFile::kHostBeOS: + if (MY_LIN_S_ISDIR(Attrib)) + return true; + break; } return false; } @@ -86,11 +93,20 @@ UInt32 CItem::GetWinAttrib() const case NHeader::NFile::kHostWin32: a = Attrib; break; + case NHeader::NFile::kHostUnix: + case NHeader::NFile::kHostBeOS: + a = Attrib << 16; + a |= 0x8000; // add posix mode marker + break; + // case NHeader::NFile::kHostMacOS: + // kHostMacOS was used only by some very old rare case rar. + // New rar4-rar7 for macos probably uses kHostUnix. + // So we process kHostMacOS without attribute parsing: default: - a = 0; // must be converted from unix value; + a = 0; } if (IsDir()) - a |= NHeader::NFile::kWinFileDirectoryAttributeMask; + a |= FILE_ATTRIBUTE_DIRECTORY; return a; } diff --git a/CPP/7zip/Archive/Tar/TarHandler.cpp b/CPP/7zip/Archive/Tar/TarHandler.cpp index 29f28e8..5761ea3 100644 --- a/CPP/7zip/Archive/Tar/TarHandler.cpp +++ b/CPP/7zip/Archive/Tar/TarHandler.cpp @@ -65,7 +65,7 @@ static const Byte kArcProps[] = kpidComment }; -static const char *k_Characts_Prefix = "PREFIX"; +static const char * const k_Characts_Prefix = "PREFIX"; IMP_IInArchive_Props IMP_IInArchive_ArcProps @@ -684,10 +684,14 @@ Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val s.Add_OptSpaced("SCHILY.fflags="); s += item->SCHILY_fflags; } + if (item->Is_Sparse()) + s.Add_OptSpaced("SPARSE"); if (item->IsThereWarning()) s.Add_OptSpaced("WARNING"); if (item->HeaderError) s.Add_OptSpaced("ERROR"); + if (item->Method_Error) + s.Add_OptSpaced("METHOD_ERROR"); if (item->Pax_Error) s.Add_OptSpaced("PAX_error"); if (!item->PaxExtra.RawLines.IsEmpty()) @@ -812,11 +816,16 @@ Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, inStream2 = inStream; else { - GetStream(index, &inStream2); - if (!inStream2) - return E_FAIL; + const HRESULT hres = GetStream(index, &inStream2); + if (hres == E_NOTIMPL) + opRes = NExtract::NOperationResult::kHeadersError; // kUnsupportedMethod + else if (!inStream2) + { + opRes = NExtract::NOperationResult::kDataError; + // return E_FAIL; + } } - + if (opRes == NExtract::NOperationResult::kOK) { if (item->Is_SymLink()) { @@ -855,9 +864,9 @@ Z7_CLASS_IMP_IInStream( bool _needStartSeek; public: + unsigned ItemIndex; CHandler *Handler; CMyComPtr HandlerRef; - unsigned ItemIndex; CRecordVector PhyOffsets; void Init() @@ -879,7 +888,7 @@ Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) if (_virtPos >= item.Size) return S_OK; { - UInt64 rem = item.Size - _virtPos; + const UInt64 rem = item.Size - _virtPos; if (size > rem) size = (UInt32)rem; } @@ -903,17 +912,17 @@ Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) } const CSparseBlock &sb = item.SparseBlocks[left]; - UInt64 relat = _virtPos - sb.Offset; + const UInt64 relat = _virtPos - sb.Offset; if (_virtPos >= sb.Offset && relat < sb.Size) { - UInt64 rem = sb.Size - relat; + const UInt64 rem = sb.Size - relat; if (size > rem) size = (UInt32)rem; - UInt64 phyPos = PhyOffsets[left] + relat; + const UInt64 phyPos = PhyOffsets[left] + relat; if (_needStartSeek || _phyPos != phyPos) { - RINOK(InStream_SeekSet(Handler->_stream, (item.Get_DataPos() + phyPos))) + RINOK(InStream_SeekSet(Handler->_stream, item.Get_DataPos() + phyPos)) _needStartSeek = false; _phyPos = phyPos; } @@ -927,7 +936,7 @@ Z7_COM7F_IMF(CSparseStream::Read(void *data, UInt32 size, UInt32 *processedSize) next = sb.Offset; else if (left + 1 < item.SparseBlocks.Size()) next = item.SparseBlocks[left + 1].Offset; - UInt64 rem = next - _virtPos; + const UInt64 rem = next - _virtPos; if (size > rem) size = (UInt32)rem; memset(data, 0, size); @@ -965,6 +974,8 @@ Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) if (item.Is_Sparse()) { + if (item.Method_Error) + return E_NOTIMPL; // S_FALSE CSparseStream *streamSpec = new CSparseStream; CMyComPtr streamTemp = streamSpec; streamSpec->Init(); diff --git a/CPP/7zip/Archive/Tar/TarIn.cpp b/CPP/7zip/Archive/Tar/TarIn.cpp index 22b8902..e702b68 100644 --- a/CPP/7zip/Archive/Tar/TarIn.cpp +++ b/CPP/7zip/Archive/Tar/TarIn.cpp @@ -181,6 +181,7 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) { char buf[NFileHeader::kRecordSize]; + item.Method_Error = false; error = k_ErrorType_OK; filled = false; @@ -218,10 +219,7 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) break; item.HeaderSize += NFileHeader::kRecordSize; thereAreEmptyRecords = true; - if (OpenCallback) - { - RINOK(Progress(item, 0)) - } + RINOK(Progress(item, 0)) } if (thereAreEmptyRecords) { @@ -335,84 +333,83 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) if (item.LinkFlag == NFileHeader::NLinkFlag::kSparse) { - Byte isExtended = (Byte)buf[482]; - if (isExtended != 0 && isExtended != 1) - return S_OK; + // OLD GNU format: parse sparse file information: + // PackSize = cumulative size of all non-empty blocks of the file. + // We read actual file size from 'realsize' member of oldgnu_header: RIF(ParseSize(buf + 483, item.Size, item.Size_IsBin)) - UInt64 min = 0; - for (unsigned i = 0; i < 4; i++) - { - p = buf + 386 + 24 * i; - if (GetBe32(p) == 0) - { - if (isExtended != 0) - return S_OK; - break; - } - CSparseBlock sb; - RIF(ParseSize(p, sb.Offset)) - RIF(ParseSize(p + 12, sb.Size)) - item.SparseBlocks.Add(sb); - if (sb.Offset < min || sb.Offset > item.Size) - return S_OK; - if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0) - return S_OK; - min = sb.Offset + sb.Size; - if (min < sb.Offset) - return S_OK; - } - if (min > item.Size) + if (item.Size < item.PackSize) // additional check return S_OK; - while (isExtended != 0) - { - size_t processedSize = NFileHeader::kRecordSize; - RINOK(ReadStream(SeqStream, buf, &processedSize)) - if (processedSize != NFileHeader::kRecordSize) - { - error = k_ErrorType_UnexpectedEnd; - return S_OK; - } - - item.HeaderSize += NFileHeader::kRecordSize; - - if (OpenCallback) - { - RINOK(Progress(item, 0)) - } + p = buf + 386; + + UInt64 end = 0, packSum = 0; + unsigned numRecords = 4; + unsigned isExtended = (Byte)p[4 * 24]; // (Byte)p[numRecords * 24]; + // the list of blocks contains non-empty blocks. All another data is empty. - isExtended = (Byte)buf[21 * 24]; - if (isExtended != 0 && isExtended != 1) + for (;;) + { + // const unsigned isExtended = (Byte)p[numRecords * 24]; + if (isExtended > 1) return S_OK; - for (unsigned i = 0; i < 21; i++) + do { - p = buf + 24 * i; if (GetBe32(p) == 0) { - if (isExtended != 0) + if (isExtended) return S_OK; break; } CSparseBlock sb; RIF(ParseSize(p, sb.Offset)) RIF(ParseSize(p + 12, sb.Size)) - item.SparseBlocks.Add(sb); - if (sb.Offset < min || sb.Offset > item.Size) - return S_OK; - if ((sb.Offset & 0x1FF) != 0 || (sb.Size & 0x1FF) != 0) - return S_OK; - min = sb.Offset + sb.Size; - if (min < sb.Offset) + p += 24; + /* for all non-last blocks we expect : + ((sb.Size & 0x1ff) == 0) && ((sb.Offset & 0x1ff) == 0) + for last block : (sb.Size == 0) is possible. + */ + if (sb.Offset < end + || item.Size < sb.Offset + || item.Size - sb.Offset < sb.Size) return S_OK; + // optional check: + if (sb.Size && ((end & 0x1ff) || (sb.Offset & 0x1ff))) + { + item.Method_Error = true; // relaxed check + // return S_OK; + } + end = sb.Offset + sb.Size; + packSum += sb.Size; + item.SparseBlocks.Add(sb); + } + while (--numRecords); + + if (!isExtended) + break; + + size_t processedSize = NFileHeader::kRecordSize; + RINOK(ReadStream(SeqStream, buf, &processedSize)) + if (processedSize != NFileHeader::kRecordSize) + { + error = k_ErrorType_UnexpectedEnd; + return S_OK; } + item.HeaderSize += NFileHeader::kRecordSize; + RINOK(Progress(item, 0)) + p = buf; + numRecords = 21; + isExtended = (Byte)p[21 * 24]; // (Byte)p[numRecords * 24]; + } + // optional checks for strict size consistency: + if (end != item.Size || packSum != item.PackSize) + { + item.Method_Error = true; // relaxed check + // return S_OK; } - if (min > item.Size) - return S_OK; } - if (item.PackSize >= (UInt64)1 << 63) + if (item.PackSize >= (UInt64)1 << 63) // optional check. It was checked in ParseSize() already return S_OK; - filled = true; error = k_ErrorType_OK; return S_OK; @@ -421,6 +418,8 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) HRESULT CArchive::Progress(const CItemEx &item, UInt64 posOffset) { + if (!OpenCallback) + return S_OK; const UInt64 pos = item.Get_DataPos() + posOffset; if (NumFiles - NumFiles_Prev < (1 << 16) // && NumRecords - NumRecords_Prev < (1 << 16) @@ -500,10 +499,7 @@ HRESULT CArchive::ReadDataToBuffer(const CItemEx &item, do { - if (OpenCallback) - { - RINOK(Progress(item, pos)) - } + RINOK(Progress(item, pos)) unsigned size = kBufSize; if (size > packSize) @@ -813,6 +809,7 @@ HRESULT CArchive::ReadItem2(CItemEx &item) item.LongLink_WasUsed_2 = false; item.HeaderError = false; + item.Method_Error = false; item.IsSignedChecksum = false; item.Prefix_WasUsed = false; @@ -838,13 +835,8 @@ HRESULT CArchive::ReadItem2(CItemEx &item) for (;;) { - if (OpenCallback) - { - RINOK(Progress(item, 0)) - } - + RINOK(Progress(item, 0)) RINOK(GetNextItemReal(item)) - // NumRecords++; if (!filled) @@ -1064,9 +1056,14 @@ HRESULT CArchive::ReadItem2(CItemEx &item) // GNU TAR ignores (item.Size) in that case if (item.Size != 0 && item.Size != piSize) item.Pax_Error = true; - item.Size = piSize; - item.PackSize = piSize; - item.pax_size_WasUsed = true; + if (piSize >= ((UInt64)1 << 63)) + item.Pax_Error = true; + else + { + item.Size = piSize; + item.PackSize = piSize; + item.pax_size_WasUsed = true; + } } item.PaxTimes = paxInfo; diff --git a/CPP/7zip/Archive/Tar/TarItem.h b/CPP/7zip/Archive/Tar/TarItem.h index 112f38d..d4e2ea5 100644 --- a/CPP/7zip/Archive/Tar/TarItem.h +++ b/CPP/7zip/Archive/Tar/TarItem.h @@ -322,6 +322,7 @@ struct CPaxExtra struct CItemEx: public CItem { bool HeaderError; + bool Method_Error; bool IsSignedChecksum; bool Prefix_WasUsed; diff --git a/CPP/7zip/Archive/Udf/UdfIn.cpp b/CPP/7zip/Archive/Udf/UdfIn.cpp index ce87c54..a9e4ebf 100644 --- a/CPP/7zip/Archive/Udf/UdfIn.cpp +++ b/CPP/7zip/Archive/Udf/UdfIn.cpp @@ -500,10 +500,15 @@ size_t CFileId::Parse(const Byte *p, size_t size) processed += impLen; Id.Parse(p + processed, idLen); processed += idLen; + // const size_t processed2 = processed; for (;(processed & 3) != 0; processed++) if (p[processed] != 0) return 0; - if ((size_t)tag.CrcLen + 16 != processed) return 0; + // some program can create non-standard UDF file where CrcLen doesn't include Padding data + if ((size_t)tag.CrcLen + 16 != processed + // && (size_t)tag.CrcLen + 16 != processed2 // we can enable this check to support non-standard UDF + ) + return 0; return (processed <= size) ? processed : 0; } @@ -577,15 +582,20 @@ HRESULT CInArchive::ReadItem(unsigned volIndex, int fsIndex, const CLongAllocDes item.IcbTag.Parse(p + 16); + // maybe another FileType values are possible in rare cases. + // Shoud we ignore FileType here? if (fsIndex < 0) { + // if (item.IcbTag.FileType == ICB_FILE_TYPE_DIR) return S_FALSE; if (item.IcbTag.FileType != ICB_FILE_TYPE_METADATA && - item.IcbTag.FileType != ICB_FILE_TYPE_METADATA_MIRROR) + item.IcbTag.FileType != ICB_FILE_TYPE_METADATA_MIRROR && + item.IcbTag.FileType != ICB_FILE_TYPE_METADATA_BITMAP) return S_FALSE; } else if ( item.IcbTag.FileType != ICB_FILE_TYPE_DIR && - item.IcbTag.FileType != ICB_FILE_TYPE_FILE) + item.IcbTag.FileType != ICB_FILE_TYPE_FILE && + item.IcbTag.FileType != ICB_FILE_TYPE_REAL_TIME_FILE) // M2TS files in /BDMV/STREAM/ in Blu-ray movie return S_FALSE; item.Parse(p); @@ -1210,7 +1220,7 @@ HRESULT CInArchive::Open2() if (tag.Id != DESC_TYPE_FileSet) return S_FALSE; - PRF(printf("\n FileSet", volIndex)); + PRF(printf("\n FileSet")); CFileSet fs; fs.RecordingTime.Parse(p + 16); // fs.InterchangeLevel = Get16(p + 18); diff --git a/CPP/7zip/Archive/Udf/UdfIn.h b/CPP/7zip/Archive/Udf/UdfIn.h index 9ccbf74..cbe1a27 100644 --- a/CPP/7zip/Archive/Udf/UdfIn.h +++ b/CPP/7zip/Archive/Udf/UdfIn.h @@ -250,9 +250,10 @@ enum EIcbFileType { ICB_FILE_TYPE_DIR = 4, ICB_FILE_TYPE_FILE = 5, - - ICB_FILE_TYPE_METADATA = 250, // 2.2.13.1 Metadata File - ICB_FILE_TYPE_METADATA_MIRROR = 251 + ICB_FILE_TYPE_REAL_TIME_FILE = 249, // 2.3.5.2.1 + ICB_FILE_TYPE_METADATA = 250, // 2.2.13.1 + ICB_FILE_TYPE_METADATA_MIRROR = 251, // 2.2.13.1 + ICB_FILE_TYPE_METADATA_BITMAP = 252 // 2.2.13.2 }; enum EIcbDescriptorType diff --git a/CPP/7zip/Archive/Zip/ZipIn.cpp b/CPP/7zip/Archive/Zip/ZipIn.cpp index 788810f..9d77e87 100644 --- a/CPP/7zip/Archive/Zip/ZipIn.cpp +++ b/CPP/7zip/Archive/Zip/ZipIn.cpp @@ -1718,61 +1718,49 @@ HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) HRESULT CInArchive::FindCd(bool checkOffsetMode) { - CCdInfo &cdInfo = Vols.ecd; - - UInt64 endPos; - // There are no useful data in cache in most cases here. - // So here we don't use cache data from previous operations . - + // So here we don't use cache data from previous operations. InitBuf(); + UInt64 endPos; RINOK(InStream_GetSize_SeekToEnd(Stream, endPos)) _streamPos = endPos; - - // const UInt32 kBufSizeMax2 = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; - const size_t kBufSizeMax = ((size_t)1 << 17); // must be larger than kBufSizeMax2 - + const size_t kBufSizeMax = (size_t)1 << 17; // must be larger than + // (1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize const size_t bufSize = (endPos < kBufSizeMax) ? (size_t)endPos : kBufSizeMax; if (bufSize < kEcdSize) return S_FALSE; - // CByteArr byteBuffer(bufSize); - RINOK(AllocateBuffer(kBufSizeMax)) + { + RINOK(Seek_SavePos(endPos - bufSize)) + size_t processed = bufSize; + const HRESULT res = ReadStream(Stream, Buffer, &processed); + _streamPos += processed; + _bufCached = processed; + _bufPos = 0; + _cnt += processed; + if (res != S_OK) + return res; + if (processed != bufSize) + return S_FALSE; + } - RINOK(Seek_SavePos(endPos - bufSize)) - - size_t processed = bufSize; - HRESULT res = ReadStream(Stream, Buffer, &processed); - _streamPos += processed; - _bufCached = processed; - _bufPos = 0; - _cnt += processed; - if (res != S_OK) - return res; - if (processed != bufSize) - return S_FALSE; - + CCdInfo &cdInfo = Vols.ecd; for (size_t i = bufSize - kEcdSize + 1;;) { - if (i == 0) - return S_FALSE; - const Byte *buf = Buffer; - - for (;;) { - i--; - if (buf[i] == 0x50) - break; - if (i == 0) - return S_FALSE; - } - - if (Get32(buf + i) != NSignature::kEcd) - continue; + const Byte *p = buf + i; + do + if (p == buf) + return S_FALSE; + while (*(--p) != 0x50); - cdInfo.ParseEcd32(buf + i); + i = (size_t)(p - buf); + if (Get32(p) != NSignature::kEcd) + continue; + cdInfo.ParseEcd32(p); + } if (i >= kEcd64Locator_Size) { @@ -1793,29 +1781,24 @@ HRESULT CInArchive::FindCd(bool checkOffsetMode) // Most of the zip64 use fixed size Zip64 ECD // we try relative backward reading. - - UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); + const UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); if (locatorIndex >= kEcd64_FullSize) if (checkOffsetMode || absEcd64 == locator.Ecd64Offset) { const Byte *ecd64 = buf + locatorIndex - kEcd64_FullSize; - if (Get32(ecd64) == NSignature::kEcd64) + if (Get32(ecd64) == NSignature::kEcd64 && + Get64(ecd64 + 4) == kEcd64_MainSize) { - UInt64 mainEcd64Size = Get64(ecd64 + 4); - if (mainEcd64Size == kEcd64_MainSize) - { - cdInfo.ParseEcd64e(ecd64 + 12); - ArcInfo.Base = (Int64)(absEcd64 - locator.Ecd64Offset); - // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; - return S_OK; - } + cdInfo.ParseEcd64e(ecd64 + 12); + ArcInfo.Base = (Int64)(absEcd64 - locator.Ecd64Offset); + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; + return S_OK; } } // some zip64 use variable size Zip64 ECD. // we try to use absolute offset from locator. - if (absEcd64 != locator.Ecd64Offset) { if (TryEcd64(locator.Ecd64Offset, cdInfo) == S_OK) @@ -1881,6 +1864,9 @@ HRESULT CInArchive::TryReadCd(CObjectVector &items, const CCdInfo &cdIn items.Clear(); IsCdUnsorted = false; + if ((Int64)cdOffset < 0) + return S_FALSE; + // _startLocalFromCd_Disk = (UInt32)(Int32)-1; // _startLocalFromCd_Offset = (UInt64)(Int64)-1; diff --git a/CPP/7zip/Archive/Zip/ZipOut.cpp b/CPP/7zip/Archive/Zip/ZipOut.cpp index 63f1a71..e8a21c2 100644 --- a/CPP/7zip/Archive/Zip/ZipOut.cpp +++ b/CPP/7zip/Archive/Zip/ZipOut.cpp @@ -49,42 +49,54 @@ void COutArchive::SeekToCurPos() // #define DOES_NEED_ZIP64(v) (v >= 0) +Z7_NO_INLINE void COutArchive::WriteBytes(const void *data, size_t size) { m_OutBuffer.WriteBytes(data, size); m_CurPos += size; } +Z7_NO_INLINE void COutArchive::Write8(Byte b) { m_OutBuffer.WriteByte(b); m_CurPos++; } +Z7_NO_INLINE void COutArchive::Write16(UInt16 val) { Write8((Byte)val); Write8((Byte)(val >> 8)); } +Z7_NO_INLINE void COutArchive::Write32(UInt32 val) { for (int i = 0; i < 4; i++) { - Write8((Byte)val); + // Write8((Byte)val); + m_OutBuffer.WriteByte((Byte)val); val >>= 8; } + m_CurPos += 4; } +#define WRITE_CONST_PAIR_16_16(a, b) { Write32((a) | ((UInt32)(b) << 16)); } + +Z7_NO_INLINE void COutArchive::Write64(UInt64 val) { for (int i = 0; i < 8; i++) { - Write8((Byte)val); + // Write8((Byte)val); + m_OutBuffer.WriteByte((Byte)val); val >>= 8; } + m_CurPos += 8; } +Z7_NO_INLINE void COutArchive::WriteExtra(const CExtraBlock &extra) { FOR_VECTOR (i, extra.SubBlocks) @@ -134,11 +146,9 @@ void COutArchive::WriteTimeExtra(const CItemOut &item, bool writeNtfs) if (writeNtfs) { // windows explorer ignores that extra - Write16(NFileHeader::NExtraID::kNTFS); - Write16(k_Ntfs_ExtraSize); + WRITE_CONST_PAIR_16_16(NFileHeader::NExtraID::kNTFS, k_Ntfs_ExtraSize) Write32(0); // reserved - Write16(NFileHeader::NNtfsExtra::kTagTime); - Write16(8 * 3); + WRITE_CONST_PAIR_16_16(NFileHeader::NNtfsExtra::kTagTime, 8 * 3) WriteNtfsTime(item.Ntfs_MTime); WriteNtfsTime(item.Ntfs_ATime); WriteNtfsTime(item.Ntfs_CTime); @@ -148,8 +158,7 @@ void COutArchive::WriteTimeExtra(const CItemOut &item, bool writeNtfs) { // windows explorer ignores that extra // by specification : should we write to local header also? - Write16(NFileHeader::NExtraID::kUnixTime); - Write16(k_UnixTime_ExtraSize); + WRITE_CONST_PAIR_16_16(NFileHeader::NExtraID::kUnixTime, k_UnixTime_ExtraSize) const Byte flags = (Byte)((unsigned)1 << NFileHeader::NUnixTime::kMTime); Write8(flags); UInt32 unixTime; @@ -217,8 +226,7 @@ void COutArchive::WriteLocalHeader(CItemOut &item, bool needCheck) if (isZip64) { - Write16(NFileHeader::NExtraID::kZip64); - Write16(8 + 8); + WRITE_CONST_PAIR_16_16(NFileHeader::NExtraID::kZip64, 8 + 8) Write64(size); Write64(packSize); } @@ -357,8 +365,9 @@ HRESULT COutArchive::WriteCentralDir(const CObjectVector &items, const const UInt64 cdSize = cd64EndOffset - cdOffset; const bool cdOffset64 = DOES_NEED_ZIP64(cdOffset); const bool cdSize64 = DOES_NEED_ZIP64(cdSize); - const bool items64 = items.Size() >= 0xFFFF; - const bool isZip64 = (cdOffset64 || cdSize64 || items64); + const bool need_Items_64 = items.Size() >= 0xFFFF; + const unsigned items16 = (UInt16)(need_Items_64 ? 0xFFFF: items.Size()); + const bool isZip64 = (cdOffset64 || cdSize64 || need_Items_64); // isZip64 = true; // to test Zip64 @@ -371,8 +380,8 @@ HRESULT COutArchive::WriteCentralDir(const CObjectVector &items, const // const UInt32 extraSize = 1 << 26; // Write64(kEcd64_MainSize + extraSize); - Write16(45); // made by version - Write16(45); // extract version + WRITE_CONST_PAIR_16_16(45, // made by version + 45) // extract version Write32(0); // ThisDiskNumber Write32(0); // StartCentralDirectoryDiskNumber Write64((UInt64)items.Size()); @@ -389,10 +398,9 @@ HRESULT COutArchive::WriteCentralDir(const CObjectVector &items, const } Write32(NSignature::kEcd); - Write16(0); // ThisDiskNumber - Write16(0); // StartCentralDirectoryDiskNumber - Write16((UInt16)(items64 ? 0xFFFF: items.Size())); - Write16((UInt16)(items64 ? 0xFFFF: items.Size())); + WRITE_CONST_PAIR_16_16(0, 0) // ThisDiskNumber, StartCentralDirectoryDiskNumber + Write16((UInt16)items16); + Write16((UInt16)items16); WRITE_32_VAL_SPEC(cdSize, cdSize64) WRITE_32_VAL_SPEC(cdOffset, cdOffset64) diff --git a/CPP/7zip/Bundles/SFXCon/SfxCon.cpp b/CPP/7zip/Bundles/SFXCon/SfxCon.cpp index aac4e28..9e2d13d 100644 --- a/CPP/7zip/Bundles/SFXCon/SfxCon.cpp +++ b/CPP/7zip/Bundles/SFXCon/SfxCon.cpp @@ -153,7 +153,7 @@ namespace NCommandType }; } -static const char *g_Commands = "txl"; +static const char * const g_Commands = "txl"; struct CArchiveCommand { diff --git a/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp b/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp index 0c09807..d4240d9 100644 --- a/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp +++ b/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp @@ -48,72 +48,60 @@ static bool ReadDataString(CFSTR fileName, LPCSTR startID, NIO::CInFile inFile; if (!inFile.Open(fileName)) return false; - const size_t kBufferSize = (1 << 12); + const size_t kBufferSize = 1 << 12; Byte buffer[kBufferSize]; - const unsigned signatureStartSize = MyStringLen(startID); - const unsigned signatureEndSize = MyStringLen(endID); + const size_t signatureStartSize = MyStringLen(startID + 1); + const size_t signatureEndSize = MyStringLen(endID + 1); size_t numBytesPrev = 0; bool writeMode = false; - UInt64 posTotal = 0; + UInt32 posTotal = 0; for (;;) { - if (posTotal > (1 << 20)) - return (stringResult.IsEmpty()); const size_t numReadBytes = kBufferSize - numBytesPrev; size_t processedSize; if (!inFile.ReadFull(buffer + numBytesPrev, numReadBytes, processedSize)) return false; if (processedSize == 0) return true; - const size_t numBytesInBuffer = numBytesPrev + processedSize; - UInt32 pos = 0; + numBytesPrev += processedSize; + size_t pos = 0; for (;;) { if (writeMode) { - if (pos + signatureEndSize > numBytesInBuffer) + if (pos + signatureEndSize > numBytesPrev) break; - if (memcmp(buffer + pos, endID, signatureEndSize) == 0) - return true; - const Byte b = buffer[pos]; + const Byte b = buffer[pos++]; if (b == 0) return false; + if (b == ';' && memcmp(buffer + pos, endID + 1, signatureEndSize) == 0) + return true; stringResult += (char)b; - pos++; } else { - if (pos + signatureStartSize > numBytesInBuffer) + if (pos + signatureStartSize > numBytesPrev) break; - if (memcmp(buffer + pos, startID, signatureStartSize) == 0) + const Byte b = buffer[pos++]; + if (b == ';' && memcmp(buffer + pos, startID + 1, signatureStartSize) == 0) { writeMode = true; pos += signatureStartSize; } - else - pos++; } } - numBytesPrev = numBytesInBuffer - pos; - posTotal += pos; + posTotal += (UInt32)pos; + if (posTotal > (1 << 21)) + return stringResult.IsEmpty(); + numBytesPrev -= pos; memmove(buffer, buffer + pos, numBytesPrev); } } -static char kStartID[] = { ',','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 }; -static char kEndID[] = { ',','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 }; - -static struct CInstallIDInit -{ - CInstallIDInit() - { - kStartID[0] = ';'; - kEndID[0] = ';'; - } -} g_CInstallIDInit; - +static const char * const kStartID = ",!@Install@!UTF-8!"; +static const char * const kEndID = ",!@InstallEnd@!"; #if defined(_WIN32) && defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE) #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1; diff --git a/CPP/7zip/Common/FileStreams.cpp b/CPP/7zip/Common/FileStreams.cpp index f90e280..b7e4fbe 100644 --- a/CPP/7zip/Common/FileStreams.cpp +++ b/CPP/7zip/Common/FileStreams.cpp @@ -753,7 +753,7 @@ Z7_COM7F_IMF(CInFileStream::GetProperty(PROPID propID, PROPVARIANT *value)) { if (StoreOwnerName) { - const uid_t gid = st.st_gid; + const gid_t gid = st.st_gid; { if (!OwnerGroup.IsEmpty() && _gid == gid) prop = OwnerGroup; diff --git a/CPP/7zip/Common/FileStreams.h b/CPP/7zip/Common/FileStreams.h index 212d4f0..7f465cf 100644 --- a/CPP/7zip/Common/FileStreams.h +++ b/CPP/7zip/Common/FileStreams.h @@ -84,8 +84,8 @@ public: BY_HANDLE_FILE_INFORMATION _info; #else struct stat _info; - UInt32 _uid; - UInt32 _gid; + uid_t _uid; // uid_t can be unsigned or signed int + gid_t _gid; UString OwnerName; UString OwnerGroup; bool StoreOwnerId; diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp index 7fe18fb..73974e6 100644 --- a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -426,7 +426,7 @@ static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) } } -static const char *g_Commands = "audtexlbih"; +static const char * const g_Commands = "audtexlbih"; static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command) { diff --git a/CPP/7zip/UI/Common/ArchiveName.cpp b/CPP/7zip/UI/Common/ArchiveName.cpp index 3c0976d..f859d94 100644 --- a/CPP/7zip/UI/Common/ArchiveName.cpp +++ b/CPP/7zip/UI/Common/ArchiveName.cpp @@ -17,14 +17,14 @@ using namespace NWindows; using namespace NFile; -static const char *g_ArcExts = +static const char * const g_ArcExts = "7z" "\0" "zip" "\0" "tar" "\0" "wim" "\0"; -static const char *g_HashExts = +static const char * const g_HashExts = "sha256" "\0"; diff --git a/CPP/7zip/UI/Common/Bench.cpp b/CPP/7zip/UI/Common/Bench.cpp index eb24e7f..316c980 100644 --- a/CPP/7zip/UI/Common/Bench.cpp +++ b/CPP/7zip/UI/Common/Bench.cpp @@ -3038,7 +3038,7 @@ AString GetProcessThreadsInfo(const NSystem::CProcessAffinity &ti) FOR_VECTOR (i, ti.Groups.GroupSizes) { if (i != 0) - s.Add_Char(' '); + s.Add_Space(); s.Add_UInt32(ti.Groups.GroupSizes[i]); } } @@ -3773,10 +3773,11 @@ HRESULT Bench( #ifndef Z7_ST - if (threadsInfo.Get() && threadsInfo.GetNumProcessThreads() != 0) - numCPUs = threadsInfo.GetNumProcessThreads(); - else + if (!threadsInfo.Get() + || (numCPUs = threadsInfo.GetNumProcessThreads()) == 0) numCPUs = NSystem::GetNumberOfProcessors(); + // numCPUs : is number of threads assigned to process with affinity, + // or it's total number of threads in all groups, if IsGroupMode == true, and there is default affinity. #endif diff --git a/CPP/7zip/UI/Console/List.cpp b/CPP/7zip/UI/Console/List.cpp index 874caef..2d9f5a3 100644 --- a/CPP/7zip/UI/Console/List.cpp +++ b/CPP/7zip/UI/Console/List.cpp @@ -201,8 +201,8 @@ static const CFieldInfoInit kStandardFieldTable[] = { kpidPath, "Name", kLeft, kLeft, 2, 24 } }; -const unsigned kNumSpacesMax = 32; // it must be larger than max CFieldInfoInit.Width -static const char *g_Spaces = +static const unsigned kNumSpacesMax = 32; // it must be larger than max CFieldInfoInit.Width +static const char * const g_Spaces = " " ; static void PrintSpaces(unsigned numSpaces) diff --git a/CPP/7zip/UI/FileManager/BrowseDialog2.cpp b/CPP/7zip/UI/FileManager/BrowseDialog2.cpp index ee98ab4..9f083c5 100644 --- a/CPP/7zip/UI/FileManager/BrowseDialog2.cpp +++ b/CPP/7zip/UI/FileManager/BrowseDialog2.cpp @@ -9,6 +9,7 @@ #include #include "../../../Common/IntToString.h" +#include "../../../Common/MyCom.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" @@ -19,6 +20,7 @@ #include "../../../Windows/Menu.h" #include "../../../Windows/ProcessUtils.h" #include "../../../Windows/PropVariantConv.h" +#include "../../../Windows/Shell.h" #include "../../../Windows/Control/ComboBox.h" #include "../../../Windows/Control/Dialog.h" #include "../../../Windows/Control/Edit.h" @@ -57,7 +59,7 @@ static const int kParentIndex = -1; // static const UINT k_Message_RefreshPathEdit = WM_APP + 1; -static const wchar_t *k_Message_Link_operation_was_Blocked = +static const wchar_t * const k_Message_Link_operation_was_Blocked = L"link openning was blocked by 7-Zip"; extern UString HResultToMessage(HRESULT errorCode); @@ -978,35 +980,61 @@ void CBrowseDialog2::OnHelp() #endif +HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, + HWND hwnd, const UString &path, LPITEMIDLIST *ppidl); + HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process); HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process) { UString path2 = path; - - #ifdef _WIN32 + UINT32 result; + { +#ifdef _WIN32 + NShell::CItemIDList pidl; + // SHELLEXECUTEINFO::pidl is more accurate way than SHELLEXECUTEINFO::lpFile + { + CMyComPtr desktop; + if (SHGetDesktopFolder(&desktop) == S_OK && desktop) + if (ShellFolder_ParseDisplayName(desktop, + NULL, // HWND : do we need (window) or NULL here? + path, + &pidl) != S_OK) + pidl.Detach(); + } { const int dot = path2.ReverseFind_Dot(); const int separ = path2.ReverseFind_PathSepar(); - if (dot < 0 || dot < separ) - path2.Add_Dot(); + if (separ != (int)path2.Len() - 1) + if (dot < 0 || dot < separ) + path2.Add_Dot(); } - #endif +#endif // _WIN32 - UINT32 result; - #ifndef _UNICODE if (g_IsNT) { SHELLEXECUTEINFOW execInfo; + memset(&execInfo, 0, sizeof(execInfo)); + // execInfo.hwnd = NULL; + // execInfo.lpVerb = NULL; + // execInfo.lpFile = NULL; + // execInfo.lpDirectory = NULL; + // execInfo.lpParameters = NULL; + // execInfo.hProcess = NULL; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT; - execInfo.hwnd = NULL; - execInfo.lpVerb = NULL; - execInfo.lpFile = path2; - execInfo.lpParameters = NULL; - execInfo.lpDirectory = dir.IsEmpty() ? NULL : (LPCWSTR)dir; + if (!dir.IsEmpty()) + execInfo.lpDirectory = dir; execInfo.nShow = SW_SHOWNORMAL; - execInfo.hProcess = NULL; + + if ((LPCITEMIDLIST)pidl) + { + execInfo.lpIDList = pidl; + execInfo.fMask |= SEE_MASK_IDLIST; + } + else + execInfo.lpFile = path2; + typedef BOOL (WINAPI * Func_ShellExecuteExW)(LPSHELLEXECUTEINFOW lpExecInfo); Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION const @@ -1024,34 +1052,40 @@ Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION #endif { SHELLEXECUTEINFO execInfo; + memset(&execInfo, 0, sizeof(execInfo)); + // execInfo.hwnd = NULL; + // execInfo.lpVerb = NULL; + // execInfo.lpFile = NULL; + // execInfo.lpDirectory = NULL; + // execInfo.lpParameters = NULL; + // execInfo.hProcess = NULL; execInfo.cbSize = sizeof(execInfo); execInfo.fMask = SEE_MASK_NOCLOSEPROCESS #ifndef UNDER_CE | SEE_MASK_FLAG_DDEWAIT #endif ; - execInfo.hwnd = NULL; - execInfo.lpVerb = NULL; + execInfo.nShow = SW_SHOWNORMAL; const CSysString sysPath (GetSystemString(path2)); const CSysString sysDir (GetSystemString(dir)); - execInfo.lpFile = sysPath; - execInfo.lpParameters = NULL; - execInfo.lpDirectory = - #ifdef UNDER_CE - NULL - #else - sysDir.IsEmpty() ? NULL : (LPCTSTR)sysDir - #endif - ; - execInfo.nShow = SW_SHOWNORMAL; - execInfo.hProcess = NULL; + #ifndef UNDER_CE + if (!sysDir.IsEmpty()) + execInfo.lpDirectory = sysDir; + #endif + + if ((LPCITEMIDLIST)pidl) + { + execInfo.lpIDList = pidl; + execInfo.fMask |= SEE_MASK_IDLIST; + } + else + execInfo.lpFile = sysPath; ::ShellExecuteEx(&execInfo); result = (UINT32)(UINT_PTR)execInfo.hInstApp; process.Attach(execInfo.hProcess); } - // DEBUG_PRINT_NUM("-- ShellExecuteEx -- execInfo.hInstApp = ", result) - + } if (result <= 32) { switch (result) @@ -1063,10 +1097,8 @@ Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION // L"There is no application associated with the given file name extension", ); } - return E_FAIL; // fixed in 15.13. Can we use it for any Windows version? } - return S_OK; } diff --git a/CPP/7zip/UI/FileManager/FSFolder.cpp b/CPP/7zip/UI/FileManager/FSFolder.cpp index 7956d86..51dfaa9 100644 --- a/CPP/7zip/UI/FileManager/FSFolder.cpp +++ b/CPP/7zip/UI/FileManager/FSFolder.cpp @@ -748,8 +748,8 @@ Z7_COM7F_IMF2(Int32, CFSFolder::CompareItems(UInt32 index1, UInt32 index2, PROPI case kpidMTime: return CompareFileTime(&fi1.MTime, &fi2.MTime); case kpidIsDir: { - bool isDir1 = /* ss1 ? false : */ fi1.IsDir(); - bool isDir2 = /* ss2 ? false : */ fi2.IsDir(); + const bool isDir1 = /* ss1 ? false : */ fi1.IsDir(); + const bool isDir2 = /* ss2 ? false : */ fi2.IsDir(); if (isDir1 == isDir2) return 0; return isDir1 ? -1 : 1; @@ -798,7 +798,9 @@ Z7_COM7F_IMF2(Int32, CFSFolder::CompareItems(UInt32 index1, UInt32 index2, PROPI return MyStringCompareNoCase(comment1, comment2); } case kpidPrefix: - if (fi1.Parent < 0) return (fi2.Parent < 0) ? 0 : -1; + if (fi1.Parent == fi2.Parent) + return 0; + if (fi1.Parent < 0) return -1; if (fi2.Parent < 0) return 1; return CompareFileNames_ForFolderList( Folders[fi1.Parent], diff --git a/CPP/7zip/UI/FileManager/LangPage.cpp b/CPP/7zip/UI/FileManager/LangPage.cpp index 3aeaf13..626c91b 100644 --- a/CPP/7zip/UI/FileManager/LangPage.cpp +++ b/CPP/7zip/UI/FileManager/LangPage.cpp @@ -253,8 +253,7 @@ bool CLangPage::OnInit() temp += " "; temp += rec.Mark; } - const int index = (int)_langCombo.AddString(temp); - _langCombo.SetItemData(index, (LPARAM)rec.LangInfoIndex); + const int index = (int)_langCombo.AddString_SetItemData(temp, (LPARAM)rec.LangInfoIndex); if (rec.IsSelected) _langCombo.SetCurSel(index); } diff --git a/CPP/7zip/UI/FileManager/MenuPage.cpp b/CPP/7zip/UI/FileManager/MenuPage.cpp index e8736b8..61dd8cb 100644 --- a/CPP/7zip/UI/FileManager/MenuPage.cpp +++ b/CPP/7zip/UI/FileManager/MenuPage.cpp @@ -222,8 +222,7 @@ bool CMenuPage::OnInit() s.Add_UInt32(val); if (i == 0) s.Insert(0, L"* "); - const int index = (int)_zoneCombo.AddString(s); - _zoneCombo.SetItemData(index, (LPARAM)val); + const int index = (int)_zoneCombo.AddString_SetItemData(s, (LPARAM)val); if (val == wz) _zoneCombo.SetCurSel(index); } diff --git a/CPP/7zip/UI/FileManager/PanelItemOpen.cpp b/CPP/7zip/UI/FileManager/PanelItemOpen.cpp index aa56ef5..9d78368 100644 --- a/CPP/7zip/UI/FileManager/PanelItemOpen.cpp +++ b/CPP/7zip/UI/FileManager/PanelItemOpen.cpp @@ -825,7 +825,10 @@ void CPanel::EditItem(unsigned index, bool useEditor) return; } CProcess process; - StartEditApplication(GetItemFullPath(index), useEditor, (HWND)*this, process); + StartEditApplication(GetItemFullPath(index), useEditor, + // (HWND)*this, + GetParent(), + process); } @@ -854,7 +857,10 @@ void CPanel::OpenFolderExternal(unsigned index) path.Add_PathSepar(); } - StartApplicationDontWait(prefix, path, (HWND)*this); + StartApplicationDontWait(prefix, path, + // (HWND)*this + GetParent() + ); } @@ -981,7 +987,10 @@ void CPanel::OpenItem(unsigned index, bool tryInternal, bool tryExternal, const { // SetCurrentDirectory opens HANDLE to folder!!! // NDirectory::MySetCurrentDirectory(prefix); - StartApplicationDontWait(prefix, fullPath, (HWND)*this); + StartApplicationDontWait(prefix, fullPath, + // (HWND)*this + GetParent() + ); } } @@ -1732,9 +1741,15 @@ void CPanel::OpenItemInArchive(unsigned index, bool tryInternal, bool tryExterna CProcess process; HRESULT res; if (editMode) - res = StartEditApplication(fs2us(tempFilePath), useEditor, (HWND)*this, process); + res = StartEditApplication(fs2us(tempFilePath), useEditor, + // (HWND)*this, + GetParent(), + process); else - res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), (HWND)*this, process); + res = StartApplication(fs2us(tempDirNorm), fs2us(tempFilePath), + // (HWND)*this, + GetParent(), + process); if ((HANDLE)process == NULL) { diff --git a/CPP/7zip/UI/FileManager/PanelMenu.cpp b/CPP/7zip/UI/FileManager/PanelMenu.cpp index 9086996..e655843 100644 --- a/CPP/7zip/UI/FileManager/PanelMenu.cpp +++ b/CPP/7zip/UI/FileManager/PanelMenu.cpp @@ -488,7 +488,9 @@ struct CFolderPidls }; -static HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, +HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, + HWND hwnd, const UString &path, LPITEMIDLIST *ppidl); +HRESULT ShellFolder_ParseDisplayName(IShellFolder *shellFolder, HWND hwnd, const UString &path, LPITEMIDLIST *ppidl) { ULONG eaten = 0; diff --git a/CPP/7zip/UI/FileManager/PanelSort.cpp b/CPP/7zip/UI/FileManager/PanelSort.cpp index f95f8ee..57ac877 100644 --- a/CPP/7zip/UI/FileManager/PanelSort.cpp +++ b/CPP/7zip/UI/FileManager/PanelSort.cpp @@ -82,7 +82,7 @@ static inline const wchar_t *GetExtensionPtr(const UString &name) void CPanel::SetSortRawStatus() { - _isRawSortProp = false; + _isRawSortProp = 0; // false; FOR_VECTOR (i, _columns) { const CPropColumn &prop = _columns[i]; @@ -95,21 +95,15 @@ void CPanel::SetSortRawStatus() } -static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) +static int CALLBACK CompareItems2(const LPARAM lParam1, const LPARAM lParam2, + const CPanel * const panel, const PROPID propID, const Int32 isRawProp) { - if (lpData == 0) - return 0; - CPanel *panel = (CPanel*)lpData; - - - PROPID propID = panel->_sortID; - if (propID == kpidNoProperty) return MyCompare(lParam1, lParam2); - if (panel->_isRawSortProp) + if (isRawProp) { - // Sha1, NtSecurity, NtReparse + // Sha1, Checksum, NtSecurity, NtReparse const void *data1; const void *data2; UInt32 dataSize1; @@ -135,7 +129,7 @@ static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) } if (panel->_folderCompare) - return panel->_folderCompare->CompareItems((UInt32)lParam1, (UInt32)lParam2, propID, panel->_isRawSortProp); + return panel->_folderCompare->CompareItems((UInt32)lParam1, (UInt32)lParam2, propID, isRawProp); switch (propID) { @@ -189,16 +183,41 @@ int CALLBACK CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) if (lParam1 == (int)kParentIndex) return -1; if (lParam2 == (int)kParentIndex) return 1; - CPanel *panel = (CPanel*)lpData; + const CPanel *panel = (CPanel*)lpData; const bool isDir1 = panel->IsItem_Folder((unsigned)lParam1); const bool isDir2 = panel->IsItem_Folder((unsigned)lParam2); - - if (isDir1 && !isDir2) return -1; - if (isDir2 && !isDir1) return 1; + if (isDir1 != isDir2) + return isDir1 ? -1 : 1; - const int result = CompareItems2(lParam1, lParam2, lpData); - return panel->_ascending ? result: (-result); + /* + we have up to 3 iterations: + 1: prop, + 2: kpidName, kpidPrefix + 3: prop, kpidName, kpidPrefix + 3: kpidPrefix, kpidName, kpidPrefix : is some rare case + */ + PROPID propID = panel->_sortID; + int res = 0; + for (unsigned iter = 0; iter < 3; iter++) + { + res = CompareItems2(lParam1, lParam2, panel, propID, + iter ? 0 : panel->_isRawSortProp); + if (res) + break; + if (propID == kpidName) + { + // if (!_flatMode.IsEmpty()) break; // !_flatMode ; + propID = kpidPrefix; + continue; + } + if (iter) + break; + propID = kpidName; + } + if (res == 0) + res = MyCompare(lParam1, lParam2); // order of LoadSubItems() + return panel->_ascending ? res: -res; } diff --git a/CPP/7zip/UI/GUI/BenchmarkDialog.cpp b/CPP/7zip/UI/GUI/BenchmarkDialog.cpp index 1686c69..980161f 100644 --- a/CPP/7zip/UI/GUI/BenchmarkDialog.cpp +++ b/CPP/7zip/UI/GUI/BenchmarkDialog.cpp @@ -440,11 +440,9 @@ static const size_t kMaxDicSize = (size_t)1 << (22 + sizeof(size_t) / 4 * 5); static int ComboBox_Add_UInt32(NWindows::NControl::CComboBox &cb, UInt32 v) { - TCHAR s[16]; + WCHAR s[16]; ConvertUInt32ToString(v, s); - const int index = (int)cb.AddString(s); - cb.SetItemData(index, (LPARAM)v); - return index; + return (int)cb.AddString_SetItemData(s, (LPARAM)v); } @@ -481,21 +479,17 @@ bool CBenchmarkDialog::OnInit() _consoleEdit.SendMsg(WM_SETFONT, (WPARAM)_font._font, TRUE); } - UInt32 numCPUs = 1; + UInt32 numCPUs = 1; // process threads + UInt32 numCPUs_Sys = 1; // system threads { - AString s ("/ "); - NSystem::CProcessAffinity threadsInfo; threadsInfo.InitST(); +#ifndef Z7_ST + threadsInfo.Get_and_return_NumProcessThreads_and_SysThreads(numCPUs, numCPUs_Sys); +#endif - #ifndef Z7_ST - if (threadsInfo.Get() && threadsInfo.processAffinityMask != 0) - numCPUs = threadsInfo.GetNumProcessThreads(); - else - numCPUs = NSystem::GetNumberOfProcessors(); - #endif - + AString s ("/ "); s.Add_UInt32(numCPUs); s += GetProcessThreadsInfo(threadsInfo); SetItemTextA(IDT_BENCH_HARDWARE_THREADS, s); @@ -506,10 +500,8 @@ bool CBenchmarkDialog::OnInit() SetItemTextA(IDT_BENCH_SYS1, s); if (s != s2 && !s2.IsEmpty()) SetItemTextA(IDT_BENCH_SYS2, s2); - } - { - AString registers; - GetCpuName_MultiLine(s, registers); + + GetCpuName_MultiLine(s, s2); // s2==registers SetItemTextA(IDT_BENCH_CPU, s); } { @@ -526,22 +518,18 @@ bool CBenchmarkDialog::OnInit() // ----- Num Threads ---------- - if (numCPUs < 1) - numCPUs = 1; - numCPUs = MyMin(numCPUs, (UInt32)(1 << 6)); // it's WIN32 limit - UInt32 numThreads = Sync.NumThreads; - if (numThreads == (UInt32)(Int32)-1) numThreads = numCPUs; - if (numThreads > 1) - numThreads &= ~(UInt32)1; - const UInt32 kNumThreadsMax = (1 << 12); - if (numThreads > kNumThreadsMax) - numThreads = kNumThreadsMax; + numThreads &= ~(UInt32)1; + if (numThreads == 0) + numThreads = 1; + numThreads = MyMin(numThreads, (UInt32)(1u << 14)); m_NumThreads.Attach(GetItem(IDC_BENCH_NUM_THREADS)); - const UInt32 numTheads_Combo = numCPUs * 2; + if (numCPUs_Sys == 0) + numCPUs_Sys = 1; + const UInt32 numTheads_Combo = numCPUs_Sys * 2; UInt32 v = 1; int cur = 0; for (; v <= numTheads_Combo;) @@ -1069,16 +1057,17 @@ static void AddUsageString(UString &s, const CTotalBenchRes &info) numIter = 1000000; UInt64 usage = GetUsagePercents(info.Usage / numIter); - wchar_t w[64]; - ConvertUInt64ToString(usage, w); - unsigned len = MyStringLen(w); + wchar_t w[32]; + wchar_t *p = ConvertUInt64ToString(usage, w); + p[0] = '%'; + p[1] = 0; + unsigned len = (unsigned)(size_t)(p - w); while (len < 5) { s.Add_Space(); len++; } s += w; - s += "%"; } diff --git a/CPP/7zip/UI/GUI/CompressDialog.cpp b/CPP/7zip/UI/GUI/CompressDialog.cpp index 85d7186..53e56fe 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.cpp +++ b/CPP/7zip/UI/GUI/CompressDialog.cpp @@ -506,8 +506,7 @@ bool CCompressDialog::OnInit() { const unsigned arcIndex = ArcIndices[i]; const CArcInfoEx &ai = (*ArcFormats)[arcIndex]; - const int index = (int)m_Format.AddString(ai.Name); - m_Format.SetItemData(index, (LPARAM)arcIndex); + const int index = (int)m_Format.AddString_SetItemData(ai.Name, (LPARAM)arcIndex); if (!needSetMain) { if (Info.FormatIndex == (int)arcIndex) @@ -540,11 +539,6 @@ bool CCompressDialog::OnInit() AddComboItems(m_PathMode, k_PathMode_IDs, Z7_ARRAY_SIZE(k_PathMode_IDs), k_PathMode_Vals, Info.PathMode); - - TCHAR s[32] = { TEXT('/'), TEXT(' '), 0 }; - ConvertUInt32ToString(NSystem::GetNumberOfProcessors(), s + 2); - SetItemText(IDT_COMPRESS_HARDWARE_THREADS, s); - CheckButton(IDX_COMPRESS_SHARED, Info.OpenShareForWrite); CheckButton(IDX_COMPRESS_DEL, Info.DeleteAfterCompressing); @@ -653,7 +647,19 @@ void CCompressDialog::EnableMultiCombo(unsigned id) EnableItem(id, enable); } -static LRESULT ComboBox_AddStringAscii(NControl::CComboBox &cb, const char *s); +static LRESULT ComboBox_AddStringAscii(NControl::CComboBox &cb, const char *s) +{ + return cb.AddString((CSysString)s); +} + +static LRESULT ComboBox_AddStringAscii_SetItemData(NControl::CComboBox &cb, + const char *s, LPARAM lParam) +{ + const LRESULT index = ComboBox_AddStringAscii(cb, s); + if (index >= 0) // optional check + cb.SetItemData((int)index, lParam); + return index; +} static void Combine_Two_BoolPairs(const CBoolPair &b1, const CBoolPair &b2, CBool1 &res) { @@ -1604,20 +1610,14 @@ void CCompressDialog::SetLevel2() AddLangString(s, langID); } } - const int index = (int)m_Level.AddString(s); - m_Level.SetItemData(index, (LPARAM)i); + m_Level.AddString_SetItemData(s, (LPARAM)i); } } SetNearestSelectComboBox(m_Level, level); } -static LRESULT ComboBox_AddStringAscii(NControl::CComboBox &cb, const char *s) -{ - return cb.AddString((CSysString)s); -} - -static const char *k_Auto_Prefix = "* "; +static const char * const k_Auto_Prefix = "* "; static void Modify_Auto(AString &s) { @@ -1690,8 +1690,8 @@ void CCompressDialog::SetMethod2(int keepMethodId) writtenMethodId = -1; Modify_Auto(s); } - const int itemIndex = (int)ComboBox_AddStringAscii(m_Method, s); - m_Method.SetItemData(itemIndex, writtenMethodId); + const int itemIndex = (int)ComboBox_AddStringAscii_SetItemData(m_Method, + s, writtenMethodId); if (keepMethodId == methodID) { m_Method.SetCurSel(itemIndex); @@ -1731,7 +1731,7 @@ void CCompressDialog::SetEncryptionMethod() } else if (ai.Is_Zip()) { - int index = FindRegistryFormat(ai.Name); + const int index = FindRegistryFormat(ai.Name); UString encryptionMethod; if (index >= 0) { @@ -1836,9 +1836,7 @@ static int Combo_AddDict2(NWindows::NControl::CComboBox &cb, size_t sizeReal, si s.Add_Char('B'); if (sizeReal == k_Auto_Dict) Modify_Auto(s); - const int index = (int)ComboBox_AddStringAscii(cb, s); - cb.SetItemData(index, (LPARAM)sizeReal); - return index; + return (int)ComboBox_AddStringAscii_SetItemData(cb, s, (LPARAM)sizeReal); } int CCompressDialog::AddDict2(size_t sizeReal, size_t sizeShow) @@ -2201,9 +2199,7 @@ int CCompressDialog::AddOrder(UInt32 size) { char s[32]; ConvertUInt32ToString(size, s); - const int index = (int)ComboBox_AddStringAscii(m_Order, s); - m_Order.SetItemData(index, (LPARAM)size); - return index; + return (int)ComboBox_AddStringAscii_SetItemData(m_Order, s, (LPARAM)size); } int CCompressDialog::AddOrder_Auto() @@ -2211,9 +2207,7 @@ int CCompressDialog::AddOrder_Auto() AString s; s.Add_UInt32(_auto_Order); Modify_Auto(s); - int index = (int)ComboBox_AddStringAscii(m_Order, s); - m_Order.SetItemData(index, (LPARAM)(INT_PTR)(-1)); - return index; + return (int)ComboBox_AddStringAscii_SetItemData(m_Order, s, (LPARAM)(INT_PTR)(-1)); } void CCompressDialog::SetOrder2() @@ -2490,9 +2484,7 @@ void CCompressDialog::SetSolidBlockSize2() AString s; Add_Size(s, _auto_Solid); Modify_Auto(s); - const int index = (int)ComboBox_AddStringAscii(m_Solid, s); - m_Solid.SetItemData(index, (LPARAM)(UInt32)(Int32)-1); - curSel = index; + curSel = (int)ComboBox_AddStringAscii_SetItemData(m_Solid, s, (LPARAM)(UInt32)(Int32)-1); } if (is7z) @@ -2501,8 +2493,7 @@ void CCompressDialog::SetSolidBlockSize2() // kSolidLog_NoSolid = 0 for xz means default blockSize if (is7z) LangString(IDS_COMPRESS_NON_SOLID, s); - const int index = (int)m_Solid.AddString(s); - m_Solid.SetItemData(index, (LPARAM)(UInt32)kSolidLog_NoSolid); + const int index = (int)m_Solid.AddString_SetItemData(s, (LPARAM)(UInt32)kSolidLog_NoSolid); if (defaultBlockSize == kSolidLog_NoSolid) curSel = index; } @@ -2511,16 +2502,15 @@ void CCompressDialog::SetSolidBlockSize2() { AString s; Add_Size(s, (UInt64)1 << i); - const int index = (int)ComboBox_AddStringAscii(m_Solid, s); - m_Solid.SetItemData(index, (LPARAM)(UInt32)i); + const int index = (int)ComboBox_AddStringAscii_SetItemData(m_Solid, s, (LPARAM)(UInt32)i); if (defaultBlockSize != (UInt32)(Int32)-1) if (i <= defaultBlockSize || index <= 1) curSel = index; } { - const int index = (int)m_Solid.AddString(LangString(IDS_COMPRESS_SOLID)); - m_Solid.SetItemData(index, (LPARAM)kSolidLog_FullSolid); + const int index = (int)m_Solid.AddString_SetItemData( + LangString(IDS_COMPRESS_SOLID), (LPARAM)kSolidLog_FullSolid); if (defaultBlockSize == kSolidLog_FullSolid) curSel = index; } @@ -2564,7 +2554,7 @@ static bool Is_Zstd_Mt_Supported() } */ -static const char *k_ST_Threads = " (ST)"; +static const char * const k_ST_Threads = " (ST)"; void CCompressDialog::SetNumThreads2() { @@ -2575,15 +2565,31 @@ void CCompressDialog::SetNumThreads2() if (!fi.MultiThread_()) return; - const UInt32 numHardwareThreads = NSystem::GetNumberOfProcessors(); - // 64; // for debug: + UInt32 numCPUs = 1; // process threads + UInt32 numHardwareThreads = 1; // system threads + NSystem::CProcessAffinity threadsInfo; + threadsInfo.InitST(); +#ifndef Z7_ST + threadsInfo.Get_and_return_NumProcessThreads_and_SysThreads(numCPUs, numHardwareThreads); +#endif - UInt32 defaultValue = numHardwareThreads; + AString s ("/ "); + { + s.Add_UInt32(numCPUs); + if (numCPUs != numHardwareThreads) + { + s += " / "; + s.Add_UInt32(numHardwareThreads); + } + SetItemTextA(IDT_COMPRESS_HARDWARE_THREADS, s.Ptr()); + } + + UInt32 defaultValue = numCPUs; bool useAutoThreads = true; { const CArcInfoEx &ai = Get_ArcInfoEx(); - int index = FindRegistryFormat(ai.Name); + const int index = FindRegistryFormat(ai.Name); if (index >= 0) { const NCompression::CFormatOptions &fo = m_RegistryInfo.Formats[index]; @@ -2597,19 +2603,19 @@ void CCompressDialog::SetNumThreads2() // const UInt32 num_ZSTD_threads_MAX = Is_Zstd_Mt_Supported() ? MY_ZSTDMT_NBWORKERS_MAX : 0; - UInt32 numAlgoThreadsMax = numHardwareThreads * 2; const int methodID = GetMethodID(); - const bool isZip = IsZipFormat(); + + UInt32 numAlgoThreadsMax = numHardwareThreads * 2; // for unknow methods if (isZip) numAlgoThreadsMax = 8 << (sizeof(size_t) / 2); // 32 threads for 32-bit : 128 threads for 64-bit else if (IsXzFormat()) - numAlgoThreadsMax = 256 * 2; + numAlgoThreadsMax = 256 * 2; // MTCODER_THREADS_MAX * 2 else switch (methodID) { case kLZMA: numAlgoThreadsMax = 2; break; - case kLZMA2: numAlgoThreadsMax = 256; break; + case kLZMA2: numAlgoThreadsMax = 256 * 2; break; // MTCODER_THREADS_MAX * 2 case kBZip2: numAlgoThreadsMax = 64; break; // case kZSTD: numAlgoThreadsMax = num_ZSTD_threads_MAX; break; case kCopy: @@ -2619,9 +2625,9 @@ void CCompressDialog::SetNumThreads2() case kPPMdZip: numAlgoThreadsMax = 1; } - UInt32 autoThreads = numHardwareThreads; + UInt32 autoThreads = numCPUs; if (autoThreads > numAlgoThreadsMax) - autoThreads = numAlgoThreadsMax; + autoThreads = numAlgoThreadsMax; const UInt64 memUse_Limit = Get_MemUse_Bytes(); @@ -2676,13 +2682,12 @@ void CCompressDialog::SetNumThreads2() int curSel = -1; { - AString s; + s.Empty(); s.Add_UInt32(autoThreads); if (autoThreads == 0) s += k_ST_Threads; Modify_Auto(s); - const int index = (int)ComboBox_AddStringAscii(m_NumThreads, s); - m_NumThreads.SetItemData(index, (LPARAM)(INT_PTR)(-1)); - // m_NumThreads.SetItemData(index, autoThreads); + const int index = (int)ComboBox_AddStringAscii_SetItemData(m_NumThreads, + s, (LPARAM)(INT_PTR)(-1)); if (useAutoThreads) curSel = index; } @@ -2693,11 +2698,11 @@ void CCompressDialog::SetNumThreads2() 1; i <= numHardwareThreads * 2 && i <= numAlgoThreadsMax; i++) { - AString s; + s.Empty(); s.Add_UInt32(i); if (i == 0) s += k_ST_Threads; - const int index = (int)ComboBox_AddStringAscii(m_NumThreads, s); - m_NumThreads.SetItemData(index, (LPARAM)(UInt32)i); + const int index = (int)ComboBox_AddStringAscii_SetItemData(m_NumThreads, + s, (LPARAM)(UInt32)i); if (!useAutoThreads && i == defaultValue) curSel = index; } @@ -2754,9 +2759,7 @@ int CCompressDialog::AddMemComboItem(UInt64 val, bool isPercent, bool isDefault) sRegistry.DeleteBack(); } const unsigned dataIndex = _memUse_Strings.Add(sRegistry); - const int index = (int)m_MemUse.AddString(sUser); - m_MemUse.SetItemData(index, (LPARAM)dataIndex); - return index; + return (int)m_MemUse.AddString_SetItemData(sUser, (LPARAM)dataIndex); } @@ -3439,11 +3442,7 @@ static const unsigned kTimePrec_1ns = 3; static void AddTimeOption(UString &s, UInt32 val, const UString &unit, const char *sys = NULL) { // s += " : "; - { - AString s2; - s2.Add_UInt32(val); - s += s2; - } + s.Add_UInt32(val); s.Add_Space(); s += unit; if (sys) @@ -3476,9 +3475,7 @@ int COptionsDialog::AddPrec(unsigned prec, bool isDefault) } else s.Add_UInt32(prec); - const int index = (int)m_Prec.AddString(s); - m_Prec.SetItemData(index, (LPARAM)writePrec); - return index; + return (int)m_Prec.AddString_SetItemData(s, (LPARAM)writePrec); } diff --git a/CPP/7zip/UI/GUI/CompressDialog.rc b/CPP/7zip/UI/GUI/CompressDialog.rc index 9c3ed88..df1516c 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.rc +++ b/CPP/7zip/UI/GUI/CompressDialog.rc @@ -87,8 +87,8 @@ BEGIN COMBOBOX IDC_COMPRESS_SOLID, g1x, 144, g1xs, 140, MY_COMBO LTEXT "Number of CPU &threads:", IDT_COMPRESS_THREADS, m, 167, g0xs, 8 - COMBOBOX IDC_COMPRESS_THREADS, g1x, 165, g1xs - 35, 140, MY_COMBO - RTEXT "", IDT_COMPRESS_HARDWARE_THREADS, g1x + g1xs - 35 + 10, 167, 25, MY_TEXT_NOPREFIX + COMBOBOX IDC_COMPRESS_THREADS, g1x, 165, g1xs - 40, 140, MY_COMBO + RTEXT "", IDT_COMPRESS_HARDWARE_THREADS, g1x + g1xs - 40, 167, 40, 16, SS_NOPREFIX LTEXT "Memory usage for Compressing:", IDT_COMPRESS_MEMORY, m, 184, g2xs, 8 diff --git a/CPP/7zip/UI/GUI/ExtractDialog.cpp b/CPP/7zip/UI/GUI/ExtractDialog.cpp index 4628482..467cf18 100644 --- a/CPP/7zip/UI/GUI/ExtractDialog.cpp +++ b/CPP/7zip/UI/GUI/ExtractDialog.cpp @@ -102,8 +102,7 @@ void AddComboItems(NControl::CComboBox &combo, const UInt32 *langIDs, unsigned n { UString s = LangString(langIDs[i]); s.RemoveChar(L'&'); - const int index = (int)combo.AddString(s); - combo.SetItemData(index, (LPARAM)i); + combo.AddString_SetItemData(s, (LPARAM)i); if (values[i] == curVal) curSel = i; } diff --git a/CPP/Common/Common0.h b/CPP/Common/Common0.h index 55606cd..5781a95 100644 --- a/CPP/Common/Common0.h +++ b/CPP/Common/Common0.h @@ -126,8 +126,9 @@ if compiled with new GCC libstdc++, GCC libstdc++ can use: #pragma GCC diagnostic ignored "-Wglobal-constructors" #pragma GCC diagnostic ignored "-Wexit-time-destructors" -#if defined(Z7_LLVM_CLANG_VERSION) && __clang_major__ >= 18 // 18.1.0RC -#pragma GCC diagnostic ignored "-Wswitch-default" +#if defined(Z7_LLVM_CLANG_VERSION) && __clang_major__ >= 18 /* 18.1.0RC */ \ + || defined(Z7_APPLE_CLANG_VERSION) && __clang_major__ >= 16 // for APPLE=17 (LLVM=19) + #pragma GCC diagnostic ignored "-Wswitch-default" #endif // #pragma GCC diagnostic ignored "-Wunused-private-field" // #pragma GCC diagnostic ignored "-Wnonportable-system-include-path" diff --git a/CPP/Common/MyBuffer.h b/CPP/Common/MyBuffer.h index 80f0205..08c10a3 100644 --- a/CPP/Common/MyBuffer.h +++ b/CPP/Common/MyBuffer.h @@ -202,7 +202,53 @@ public: } }; -typedef CObjArray CByteArr; + +/* CSmallObjArray can be used for Byte arrays + or for arrays whose total size in bytes does not exceed size_t ranges. + So there is no need to use Z7_ARRAY_NEW macro in CSmallObjArray code. */ +template class CSmallObjArray +{ +protected: + T *_items; +private: + // we disable copy + CSmallObjArray(const CSmallObjArray &buffer); + void operator=(const CSmallObjArray &buffer); +public: + void Free() + { + delete []_items; + _items = NULL; + } + CSmallObjArray(size_t size): _items(NULL) + { + if (size != 0) + { + // Z7_ARRAY_NEW(_items, T, size) + _items = new T[size]; + } + } + CSmallObjArray(): _items(NULL) {} + ~CSmallObjArray() { delete []_items; } + + operator T *() { return _items; } + operator const T *() const { return _items; } + const T* ConstData() const { return _items; } + T* NonConstData() const { return _items; } + T* NonConstData() { return _items; } + // const T* Data() const { return _items; } + // T* Data() { return _items; } + + void Alloc(size_t newSize) + { + delete []_items; + _items = NULL; + // Z7_ARRAY_NEW(_items, T, newSize) + _items = new T[newSize]; + } +}; + +typedef CSmallObjArray CByteArr; typedef CObjArray CBoolArr; typedef CObjArray CIntArr; typedef CObjArray CUIntArr; diff --git a/CPP/Windows/Control/ComboBox.cpp b/CPP/Windows/Control/ComboBox.cpp index 8da487d..2e9c8cb 100644 --- a/CPP/Windows/Control/ComboBox.cpp +++ b/CPP/Windows/Control/ComboBox.cpp @@ -63,4 +63,13 @@ LRESULT CComboBox::GetLBText(int index, UString &s) } #endif +LRESULT CComboBox::AddString_SetItemData(LPCWSTR s, LPARAM lParam) +{ + const LRESULT index = AddString(s); + // NOTE: SetItemData((int)-1, lParam) works as unexpected. + if (index >= 0) // optional check, because (index < 0) is not expected for normal inputs + SetItemData((int)index, lParam); + return index; +} + }} diff --git a/CPP/Windows/Control/ComboBox.h b/CPP/Windows/Control/ComboBox.h index 2a60b8a..224efca 100644 --- a/CPP/Windows/Control/ComboBox.h +++ b/CPP/Windows/Control/ComboBox.h @@ -21,6 +21,8 @@ public: LRESULT AddString(LPCWSTR s); #endif + LRESULT AddString_SetItemData(LPCWSTR s, LPARAM lParam); + /* If this parameter is -1, any current selection in the list is removed and the edit control is cleared.*/ LRESULT SetCurSel(int index) { return SendMsg(CB_SETCURSEL, MY_int_TO_WPARAM(index), 0); } LRESULT SetCurSel(unsigned index) { return SendMsg(CB_SETCURSEL, index, 0); } diff --git a/CPP/Windows/FileFind.cpp b/CPP/Windows/FileFind.cpp index 64075ab..669541e 100644 --- a/CPP/Windows/FileFind.cpp +++ b/CPP/Windows/FileFind.cpp @@ -1162,6 +1162,15 @@ void CFileInfoBase::SetFrom_stat(const struct stat &st) MTime = st.st_mtimespec; ATime = st.st_atimespec; + #elif defined(__QNXNTO__) && defined(__ARM__) && !defined(__aarch64__) + + // CTime = ST_CTIME(st); + // MTime = ST_MTIME(st); + // ATime = ST_ATIME(st); + CTime.tv_sec = st.st_ctime; CTime.tv_nsec = 0; + MTime.tv_sec = st.st_mtime; MTime.tv_nsec = 0; + ATime.tv_sec = st.st_atime; ATime.tv_nsec = 0; + #else // timespec_To_FILETIME(st.st_ctim, CTime, &CTime_ns100); // timespec_To_FILETIME(st.st_mtim, MTime, &MTime_ns100); @@ -1312,7 +1321,7 @@ bool CDirEntry::IsDots() const throw() /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN) we can call fstatat() for that case, but we use only (Name) check here */ -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) if (Type != DT_DIR && Type != DT_UNKNOWN) return false; #endif @@ -1352,7 +1361,7 @@ bool CEnumerator::NextAny(CDirEntry &fi, bool &found) fi.iNode = de->d_ino; -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) fi.Type = de->d_type; /* some systems (like CentOS 7.x on XFS) have (Type == DT_UNKNOWN) we can set (Type) from fstatat() in that case. diff --git a/CPP/Windows/FileFind.h b/CPP/Windows/FileFind.h index f68673a..944bca2 100644 --- a/CPP/Windows/FileFind.h +++ b/CPP/Windows/FileFind.h @@ -277,13 +277,13 @@ typedef CFileInfo CDirEntry; struct CDirEntry { ino_t iNode; -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) Byte Type; #endif FString Name; /* -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) bool IsDir() const { // (Type == DT_UNKNOWN) on some systems @@ -310,7 +310,7 @@ public: bool Fill_FileInfo(const CDirEntry &de, CFileInfo &fileInfo, bool followLink) const; bool DirEntry_IsDir(const CDirEntry &de, bool followLink) const { -#if !defined(_AIX) && !defined(__sun) +#if !defined(_AIX) && !defined(__sun) && !defined(__QNXNTO__) if (de.Type == DT_DIR) return true; if (de.Type != DT_UNKNOWN) diff --git a/CPP/Windows/SecurityUtils.h b/CPP/Windows/SecurityUtils.h index 7219f06..022a8f3 100644 --- a/CPP/Windows/SecurityUtils.h +++ b/CPP/Windows/SecurityUtils.h @@ -3,7 +3,11 @@ #ifndef ZIP7_INC_WINDOWS_SECURITY_UTILS_H #define ZIP7_INC_WINDOWS_SECURITY_UTILS_H +#if defined(__MINGW32__) || defined(__MINGW64__) +#include +#else #include +#endif #include "Defs.h" diff --git a/CPP/Windows/System.cpp b/CPP/Windows/System.cpp index 4745785..6999ef9 100644 --- a/CPP/Windows/System.cpp +++ b/CPP/Windows/System.cpp @@ -5,8 +5,9 @@ #ifndef _WIN32 #include #include -#if defined(__APPLE__) || defined(__DragonFly__) || \ - defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__APPLE__) || defined(__DragonFly__) \ + || defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__QNXNTO__) #include #else #include @@ -299,8 +300,9 @@ bool GetRamSize(size_t &size) size = (size_t)sizeof(size_t) << 29; size64 = size; -#if defined(__APPLE__) || defined(__DragonFly__) || \ - defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__APPLE__) || defined(__DragonFly__) \ + || defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__QNXNTO__) uint64_t val = 0; int mib[2]; diff --git a/CPP/Windows/System.h b/CPP/Windows/System.h index 0c80373..041a44d 100644 --- a/CPP/Windows/System.h +++ b/CPP/Windows/System.h @@ -15,6 +15,8 @@ namespace NWindows { namespace NSystem { +UInt32 GetNumberOfProcessors(); + #ifdef _WIN32 struct CCpuGroups @@ -103,6 +105,25 @@ struct CProcessAffinity return CountAffinity(systemAffinityMask); } + // it returns normilized number of threads + void Get_and_return_NumProcessThreads_and_SysThreads(UInt32 &numProcessThreads, UInt32 &numSysThreads) + { + UInt32 num1 = 0, num2 = 0; + if (Get()) + { + num1 = GetNumProcessThreads(); + num2 = GetNumSystemThreads(); + } + if (num1 == 0) + num1 = NSystem::GetNumberOfProcessors(); + if (num1 == 0) + num1 = 1; + if (num2 < num1) + num2 = num1; + numProcessThreads = num1; + numSysThreads = num2; + } + BOOL Get(); BOOL SetProcAffinity() const @@ -177,8 +198,6 @@ struct CProcessAffinity #endif // _WIN32 -UInt32 GetNumberOfProcessors(); - bool GetRamSize(size_t &size); // returns false, if unknown ram size unsigned long Get_File_OPEN_MAX(); diff --git a/CPP/Windows/SystemInfo.cpp b/CPP/Windows/SystemInfo.cpp index 35846e0..2eced2a 100644 --- a/CPP/Windows/SystemInfo.cpp +++ b/CPP/Windows/SystemInfo.cpp @@ -22,7 +22,7 @@ #if defined(__GLIBC__) && (__GLIBC__ * 100 + __GLIBC_MINOR__ >= 216) #define Z7_GETAUXV_AVAILABLE -#else +#elif !defined(__QNXNTO__) // #pragma message("=== is not NEW GLIBC === ") #if defined __has_include #if __has_include () @@ -58,7 +58,7 @@ #ifdef USE_HWCAP -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) // #if (__FreeBSD__ >= 13) // (FreeBSD 12.01 is required for elf_aux_info() ???) static unsigned long MY_getauxval(int aux) diff --git a/CPP/Windows/TimeUtils.h b/CPP/Windows/TimeUtils.h index 4a9d0f2..8e1e478 100644 --- a/CPP/Windows/TimeUtils.h +++ b/CPP/Windows/TimeUtils.h @@ -65,6 +65,14 @@ inline bool FILETIME_IsZero(const FILETIME &ft) #define ST_MTIME(st) st.st_mtimespec #define ST_ATIME(st) st.st_atimespec #define ST_CTIME(st) st.st_ctimespec + #elif defined(__QNXNTO__) && defined(__ARM__) && !defined(__aarch64__) + // QNX armv7le (32-bit) for "struct stat" timestamps uses time_t instead of timespec + inline CFiTime ST_MTIME(const struct stat &st) + { timespec ts; ts.tv_sec = st.st_mtime; ts.tv_nsec = 0; return ts; } + inline CFiTime ST_ATIME(const struct stat &st) + { timespec ts; ts.tv_sec = st.st_atime; ts.tv_nsec = 0; return ts; } + inline CFiTime ST_CTIME(const struct stat &st) + { timespec ts; ts.tv_sec = st.st_ctime; ts.tv_nsec = 0; return ts; } #else #define ST_MTIME(st) st.st_mtim #define ST_ATIME(st) st.st_atim diff --git a/DOC/7zip.wxs b/DOC/7zip.wxs index 703e22e..8c6ef4c 100644 --- a/DOC/7zip.wxs +++ b/DOC/7zip.wxs @@ -1,7 +1,7 @@ - - + + diff --git a/DOC/License.txt b/DOC/License.txt index bbb56a3..b1a421a 100644 --- a/DOC/License.txt +++ b/DOC/License.txt @@ -3,7 +3,7 @@ License for use and distribution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 7-Zip Copyright (C) 1999-2025 Igor Pavlov. + 7-Zip Copyright (C) 1999-2026 Igor Pavlov. The licenses for files are: @@ -58,7 +58,7 @@ BSD 3-clause License in 7-Zip code Copyright (c) 2015-2016, Apple Inc. All rights reserved. Copyright (c) Facebook, Inc. All rights reserved. - Copyright (c) 2023-2025 Igor Pavlov. + Copyright (c) 2023-2026 Igor Pavlov. Text of the "BSD 3-clause License" ---------------------------------- @@ -102,7 +102,7 @@ BSD 2-clause License in 7-Zip code XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet. Copyright (c) 2012-2021 Yann Collet. - Copyright (c) 2023-2025 Igor Pavlov. + Copyright (c) 2023-2026 Igor Pavlov. Text of the "BSD 2-clause License" ---------------------------------- diff --git a/DOC/readme.txt b/DOC/readme.txt index cc89a39..26d0f5a 100644 --- a/DOC/readme.txt +++ b/DOC/readme.txt @@ -1,15 +1,15 @@ -7-Zip 25.01 Sources +7-Zip 26.00 Sources ------------------- -7-Zip is a file archiver for Windows. +7-Zip is a file archiver for Windows. -7-Zip Copyright (C) 1999-2025 Igor Pavlov. +7-Zip Copyright (C) 1999-2026 Igor Pavlov. License Info ------------ -7-Zip is free software distributed under the GNU LGPL +7-Zip is free software distributed under the GNU LGPL (except for unRar code). Also some code is licensed under the "BSD 3-clause License". Read "License.txt" for more infomation about license. @@ -27,7 +27,7 @@ Please check main restriction from unRar license: not be used to develop a RAR (WinRAR) compatible archiver. In brief it means: -1) You can compile and use compiled files under GNU LGPL rules, since +1) You can compile and use compiled files under GNU LGPL rules, since unRAR license almost has no restrictions for compiled files. You can link these compiled files to LGPL programs. 2) You can fix bugs in source code and use compiled fixed version. @@ -60,7 +60,7 @@ Tools / Options / Directories - Library files Also you need Microsoft Macro Assembler: - - ml.exe for x86 + - ml.exe for x86 - ml64.exe for x64 You can use ml.exe from Windows SDK for Windows Vista or some later versions. @@ -85,7 +85,7 @@ OLD_COMPILER for old VC compiler, like MSCV 6.0. MY_DYNAMIC_LINK - for dynamic linking to the run-time library (msvcrt.dll). + for dynamic linking to the run-time library (msvcrt.dll). The default makefile option is static linking to the run-time library. To compile all 7-Zip files for x64 with Visual Studio 2022, @@ -116,23 +116,23 @@ So if you compile the version with Assembeler code, you will get faster 7-Zip bi 7-Zip's assembler code uses the following syntax for different platforms: -1) x86 and x86-64 (AMD64): MASM syntax. +1) x86 and x86-64 (AMD64): MASM syntax. Now there are 3 programs that supports MASM syntax in Linux. -' 'Asmc Macro Assembler, JWasm, and UASM. Note that JWasm now doesn't support some +' 'Asmc Macro Assembler, JWasm, and UASM. Note that JWasm now doesn't support some cpu instructions used in 7-Zip. - So you must install Asmc Macro Assembler in Linux or UASM, if you want to compile + So you must install Asmc Macro Assembler in Linux or UASM, if you want to compile fastest version of 7-Zip x86 and x86-64: https://github.com/nidud/asmc https://github.com/Terraspace/UASM -2) arm64: GNU assembler for ARM64 with preprocessor. +2) arm64: GNU assembler for ARM64 with preprocessor. That systax is supported by GCC and CLANG for ARM64. There are different binaries that can be compiled from 7-Zip source. There are 2 main files in folder for compiling: makefile - that can be used for compiling Windows version of 7-Zip with nmake command - makefile.gcc - that can be used for compiling Linux/macOS versions of 7-Zip or Windows version + makefile.gcc - that can be used for compiling Linux/macOS versions of 7-Zip or Windows version with MINGW (GCC) with make command. At first you must change the current folder to folder that contains `makefile.gcc`: @@ -143,7 +143,7 @@ Then you can compile `makefile.gcc` with the command: make -j -f makefile.gcc -Also there are additional "*.mak" files in folder "CPP/7zip/" that can be used to compile +Also there are additional "*.mak" files in folder "CPP/7zip/" that can be used to compile 7-Zip binaries with optimized code and optimzing options. To compile with GCC without assembler: @@ -171,10 +171,10 @@ makefile.gcc supports some variables that can change compile options USE_JWASM=1 use JWasm assembler instead of Asmc. - Note that JWasm doesn't support AES instructions. So AES code from C version AesOpt.c + Note that JWasm doesn't support AES instructions. So AES code from C version AesOpt.c will be used instead of assembler code from AesOpt.asm. -If you want to use UASM for x86-64 compiling, you can change 7zip_gcc.mak, +If you want to use UASM for x86-64 compiling, you can change 7zip_gcc.mak, or send IS_X64=1 USE_ASM=1 MY_ASM="$UASM" to make command calling: UASM="$PWD/GccUnixR/uasm" cd "7zip-src/CPP/7zip/Bundles/Alone2" @@ -187,11 +187,11 @@ DISABLE_RAR=1 DISABLE_RAR_COMPRESS=1 removes "not fully free" code of RAR decompression codecs from compilation. -RAR decompression codecs in 7-Zip code has some additional license restrictions, +RAR decompression codecs in 7-Zip code has some additional license restrictions, that can be treated as not fully compatible with free-software licenses. DISABLE_RAR_COMPRESS=1 allows to exclude such "not-fully-free" RAR code from compilation. -if DISABLE_RAR_COMPRESS=1 is specified, 7-zip will not be able to decompress files -from rar archives, but 7-zip still will be able to open rar archives to get list of +if DISABLE_RAR_COMPRESS=1 is specified, 7-zip will not be able to decompress files +from rar archives, but 7-zip still will be able to open rar archives to get list of files or to extract files that are stored without compression. if DISABLE_RAR=1 is specified, 7-zip will not be able to work with RAR archives. @@ -203,11 +203,11 @@ Now there are two different ports of 7-Zip for Linux/macOS: 1) p7zip - another port of 7-Zip for Linux, made by an independent developer. The latest version of p7zip now is 16.02, and that p7zip 16.02 is outdated now. - http://sourceforge.net/projects/p7zip/ + http://sourceforge.net/projects/p7zip/ 2) 7-Zip for Linux/macOS - this package - it's new code with all changes from latest 7-Zip for Windows. -These two ports are not identical. +These two ports are not identical. Note also that some Linux specific things can be implemented better in p7zip than in new 7-Zip for Linux. @@ -218,13 +218,13 @@ Notes: 7-Zip consists of COM modules (DLL files). But 7-Zip doesn't use standard COM interfaces for creating objects. Look at -7zip\UI\Client7z folder for example of using DLL files of 7-Zip. +7zip\UI\Client7z folder for example of using DLL files of 7-Zip. Some DLL files can use other DLL files from 7-Zip. If you don't like it, you must use standalone version of DLL. To compile standalone version of DLL you must include all used parts -to project and define some defs. -For example, 7zip\Bundles\Format7z is a standalone version of 7z.dll -that works with 7z format. So you can use such DLL in your project +to project and define some defs. +For example, 7zip\Bundles\Format7z is a standalone version of 7z.dll +that works with 7z format. So you can use such DLL in your project without additional DLL files. @@ -284,7 +284,7 @@ Windows common files for Windows related code UI Agent Intermediary modules for FAR plugin and Explorer plugin - Client7z Test application for 7za.dll + Client7z Test application for 7za.dll Common Common UI files Console 7z.exe : Console version Explorer 7-zip.dll: 7-Zip Shell extension diff --git a/DOC/src-history.txt b/DOC/src-history.txt index 48c9647..657f04f 100644 --- a/DOC/src-history.txt +++ b/DOC/src-history.txt @@ -1,11 +1,20 @@ HISTORY of the 7-Zip source code -------------------------------- +26.00 2026-02-12 +------------------------- +- improved code for ZIP, CPIO, RAR, UFD, QCOW, Compound. +- 7-Zip File Manager: improved sorting order of the file list. It uses file name as secondary sorting key. +- 7-Zip File Manager: improved Benchmark to support systems with more than 64 CPU threads. +- the bug was fixed: 7-Zip could not correctly extract TAR archives containing sparse files. +- some bugs were fixed. + + 25.01 2025-08-03 ------------------------- -- The code for handling symbolic links has been changed +- CVE-2025-55188 : The code for handling symbolic links has been changed to provide greater security when extracting files from archives. - Command line switch -snld20 can be used to bypass default security + Command line switch -snld20 can be used to bypass default security checks when creating symbolic links. @@ -18,17 +27,19 @@ HISTORY of the 7-Zip source code - bzip2 compression speed was increased by 15-40%. - deflate (zip/gz) compression speed was increased by 1-3%. - improved support for zip, cpio and fat archives. -- fixed some bugs and vulnerabilities. -- the bug was fixed : CVE-2025-53816 : 7-Zip could work incorrectly for some incorrect RAR archives. -- the bug was fixed : CVE-2025-53817 : 7-Zip could crash for some incorrect COM (Compound File) archives. +- fixed some bugs. +- CVE-2025-11001 and CVE-2025-11002 : A vulnerability was fixed for symbolic links processing, + when extracting files from archives. +- the bug was fixed : CVE-2025-53816 : 7-Zip could work incorrectly for some incorrect RAR archives. +- the bug was fixed : CVE-2025-53817 : 7-Zip could crash for some incorrect COM (Compound File) archives. 24.09 2024-11-29 ------------------------- - The default dictionary size values for LZMA/LZMA2 compression methods were increased: dictionary size compression level - v24.08 v24.09 v24.09 - 32-bit 64-bit + v24.08 v24.09 v24.09 + 32-bit 64-bit 8 MB 16 MB 16 MB -mx4 16 MB 32 MB 32 MB -mx5 : Normal 32 MB 64 MB 64 MB -mx6 @@ -38,11 +49,11 @@ HISTORY of the 7-Zip source code The default dictionary size values for 32-bit versions of LZMA/LZMA2 don't exceed 64 MB. - 7-Zip now can calculate the following hash checksums: SHA-512, SHA-384, SHA3-256 and MD5. - APM and HFS support was improved. -- If an archive update operation uses a temporary archive folder and - the archive is moved to the destination folder, 7-Zip shows the progress of moving +- If an archive update operation uses a temporary archive folder and + the archive is moved to the destination folder, 7-Zip shows the progress of moving the archive file, as this operation can take a long time if the archive is large. - The bug was fixed: 7-Zip File Manager didn't propagate Zone.Identifier stream - for extacted files from nested archives (if there is open archive inside another open archive). + for extracted files from nested archives (if there is open archive inside another open archive). - Some bugs were fixed. @@ -76,7 +87,7 @@ HISTORY of the 7-Zip source code ------------------------- - New switch -myv={MMNN} to set decoder compatibility version for 7z archive creating. {MMNN} is 4-digit number that represents the version of 7-Zip without a dot. - If -myv={MMNN} switch is specified, 7-Zip will only use compression methods that can + If -myv={MMNN} switch is specified, 7-Zip will only use compression methods that can be decoded by the specified version {MMNN} of 7-Zip and newer versions. If -myv={MMNN} switch is not specified, -myv=2300 is used, and 7-Zip will only use compression methods that can be decoded by 7-Zip 23.00 and newer versions. @@ -127,15 +138,15 @@ HISTORY of the 7-Zip source code And some warning types are disabled in 2 files: - C/Compiler.h for C/C++ code warnings. - CPP/Common/Common.h for C++ code warnings. -- Linux/macOS versions of 7-Zip: IUnknown interface in new code doesn't use +- Linux/macOS versions of 7-Zip: IUnknown interface in new code doesn't use virtual destructor that was used in previous 7-Zip and p7zip: // virtual ~IUnknown() {} - So 7-Zip's dynamically linked shared libraries (codecs) are not compatible + So 7-Zip's dynamically linked shared libraries (codecs) are not compatible between new 7-Zip for Linux/macOS and old 7-Zip (and p7zip). - Some optimizations in filters code: BCJ, BCJ2, Swap* and opthers. -- If 7-Zip uses BCJ2 filter for big datasets compressing, it can use additional temp - files in system's TEMP folder. 7-Zip uses temp file for additional compressed - data stream, if size of such compressed stream is larger than predefined limit: +- If 7-Zip uses BCJ2 filter for big datasets compressing, it can use additional temp + files in system's TEMP folder. 7-Zip uses temp file for additional compressed + data stream, if size of such compressed stream is larger than predefined limit: 16 MiB in 32-bit version, 4 GiB in 64-bit version. - Some bugs were fixed. @@ -157,7 +168,7 @@ HISTORY of the 7-Zip source code 21.06 2021-11-24 ------------------------- - Bug in LZMA encoder in file LzmaEnc.c was fixed: - LzmaEnc_MemEncode(), LzmaEncode() and LzmaCompress() could work incorrectly, + LzmaEnc_MemEncode(), LzmaEncode() and LzmaCompress() could work incorrectly, if size value for output buffer is smaller than size required for all compressed data. LzmaEnc_Encode() could work incorrectly, if callback ISeqOutStream::Write() doesn't write all compressed data. @@ -171,8 +182,8 @@ HISTORY of the 7-Zip source code ------------------------- - 7-Zip now reduces the number of working CPU threads for compression, if RAM size is not enough for compression with big LZMA2 dictionary. -- 7-Zip now can create and check "file.sha256" and "file.sha1" text files - that contain the list of file names and SHA-1 / SHA-256 checksums in format +- 7-Zip now can create and check "file.sha256" and "file.sha1" text files + that contain the list of file names and SHA-1 / SHA-256 checksums in format compatible with sha1sum/sha256sum programs. @@ -187,7 +198,7 @@ HISTORY of the 7-Zip source code - 7-Zip now writes additional field for filename in UTF-8 encoding to zip archives. It allows to extract correct file name from zip archives on different systems. - The command line version of 7-Zip for macOS was released. -- The speed for LZMA and LZMA2 decompression in arm64 versions for macOS and Linux +- The speed for LZMA and LZMA2 decompression in arm64 versions for macOS and Linux was increased by 20%-60%. - Some changes and improvements in ZIP, TAR and NSIS code. @@ -195,7 +206,7 @@ HISTORY of the 7-Zip source code 21.01 alpha 2021-03-09 ------------------------- - The command line version of 7-Zip for Linux was released. -- The improvements for speed of ARM64 version using hardware CPU instructions +- The improvements for speed of ARM64 version using hardware CPU instructions for AES, CRC-32, SHA-1 and SHA-256. - The bug in versions 18.02 - 21.00 was fixed: 7-Zip could not correctly extract some ZIP archives created with xz compression method. @@ -205,30 +216,30 @@ HISTORY of the 7-Zip source code 20.02 alpha 2020-08-08 ------------------------- - The default number of LZMA2 chunks per solid block in 7z archive was increased to 64. - It allows to increase the compression speed for big 7z archives, if there is a big number + It allows to increase the compression speed for big 7z archives, if there is a big number of CPU cores and threads. - The speed of PPMd compressing/decompressing was increased for 7z/ZIP/RAR archives. -- The new -ssp switch. If the switch -ssp is specified, 7-Zip doesn't allow the system - to modify "Last Access Time" property of source files for archiving and hashing operations. +- The new -ssp switch. If the switch -ssp is specified, 7-Zip doesn't allow the system + to modify "Last Access Time" property of source files for archiving and hashing operations. - Some bugs were fixed. 20.00 alpha 2020-02-06 ------------------------- -- 7-Zip now supports new optional match finders for LZMA/LZMA2 compression: bt5 and hc5, +- 7-Zip now supports new optional match finders for LZMA/LZMA2 compression: bt5 and hc5, that can work faster than bt4 and hc4 match finders for the data with big redundancy. -- The compression ratio was improved for Fast and Fastest compression levels with the +- The compression ratio was improved for Fast and Fastest compression levels with the following default settings: - Fastest level (-mx1) : hc5 match finder with 256 KB dictionary. - Fast level (-mx3) : hc5 match finder with 4 MB dictionary. -- Minor speed optimizations in multithreaded LZMA/LZMA2 compression for Normal/Maximum/Ultra +- Minor speed optimizations in multithreaded LZMA/LZMA2 compression for Normal/Maximum/Ultra compression levels. - bzip2 decoding code was updated to support bzip2 archives, created by lbzip2 program. 19.02 2019-09-05 ------------------------- -- Support for SHA-1/SHA-256 optimized code in +- Support for SHA-1/SHA-256 optimized code in Sha1Opt.c, Sha256Opt.c, Sha256Opt.asm, Sha1Opt.asm. @@ -249,7 +260,7 @@ HISTORY of the 7-Zip source code There was memory leak in multithreading xz decoder - XzDecMt_Decode(), if xz stream contains only one block. - 7-Zip 18.02-18.05 used only one CPU thread for bz2 archive creation. -- The changes for MSVS compiler makefiles: +- The changes for MSVS compiler makefiles: - the makefiles now use "PLATFORM" macroname with values (x64, x86, arm64) instead of "CPU" macroname with values (AMD64, ARM64). - the makefiles by default now use static version of the run-time library. @@ -257,17 +268,17 @@ HISTORY of the 7-Zip source code 18.05 2018-04-30 ------------------------- -- The speed for LZMA/LZMA2 compressing was increased - by 8% for fastest/fast compression levels and +- The speed for LZMA/LZMA2 compressing was increased + by 8% for fastest/fast compression levels and by 3% for normal/maximum compression levels. - Previous versions of 7-Zip could work incorrectly in "Large memory pages" mode in - Windows 10 because of some BUG with "Large Pages" in Windows 10. + Windows 10 because of some BUG with "Large Pages" in Windows 10. Now 7-Zip doesn't use "Large Pages" on Windows 10 up to revision 1709 (16299). 18.03 beta 2018-03-04 ------------------------- -- Asm\x86\LzmaDecOpt.asm: new optimized LZMA decoder written in asm +- Asm\x86\LzmaDecOpt.asm: new optimized LZMA decoder written in asm for x64 with about 30% higher speed than main version of LZMA decoder written in C. - The speed for single-thread LZMA/LZMA2 decoder written in C was increased by 3%. - 7-Zip now can use multi-threading for 7z/LZMA2 decoding, @@ -278,7 +289,7 @@ HISTORY of the 7-Zip source code 17.00 beta 2017-04-29 ------------------------- -- NewHandler.h / NewHandler.cpp: +- NewHandler.h / NewHandler.cpp: now it redefines operator new() only for old MSVC compilers (_MSC_VER < 1900). - C/7zTypes.h : the names of variables in interface structures were changed (vt). - Some bugs were fixed. 7-Zip could crash in some cases. @@ -288,53 +299,53 @@ HISTORY of the 7-Zip source code 16.02 2016-05-21 ------------------------- - The BUG in 16.00 - 16.01 was fixed: - Split Handler (SplitHandler.cpp) returned incorrect + Split Handler (SplitHandler.cpp) returned incorrect total size value (kpidSize) for split archives. 16.01 2016-05-19 -------------------------- +------------------------- - Some bugs were fixed, - Some internal changes to reduce the number of compiler warnings. 16.00 2016-05-10 -------------------------- +------------------------- - 7-Zip now can extract multivolume ZIP archives (z01, z02, ... , zip). - Some bugs were fixed, 15.12 2015-11-19 -------------------------- +------------------------- - The BUG in C version of 7z decoder was fixed: 7zDec.c : SzDecodeLzma2() 7z decoder could mistakenly report about decoding error for some 7z archives that use LZMA2 compression method. - The probability to get that mistaken decoding error report was about - one error per 16384 solid blocks for solid blocks larger than 16 KB (compressed size). + The probability to get that mistaken decoding error report was about + one error per 16384 solid blocks for solid blocks larger than 16 KB (compressed size). - The BUG (in 9.26-15.11) in C version of 7z decoder was fixed: 7zArcIn.c : SzReadHeader2() - 7z decoder worked incorrectly for 7z archives that contain - empty solid blocks, that can be placed to 7z archive, if some file is + 7z decoder worked incorrectly for 7z archives that contain + empty solid blocks, that can be placed to 7z archive, if some file is unavailable for reading during archive creation. 15.09 beta 2015-10-16 -------------------------- +------------------------- - The BUG in LZMA / LZMA2 encoding code was fixed. The BUG in LzFind.c::MatchFinder_ReadBlock() function. If input data size is larger than (4 GiB - dictionary_size), the following code worked incorrectly: - - LZMA : LzmaEnc_MemEncode(), LzmaEncode() : LZMA encoding functions - for compressing from memory to memory. + - LZMA : LzmaEnc_MemEncode(), LzmaEncode() : LZMA encoding functions + for compressing from memory to memory. That BUG is not related to LZMA encoder version that works via streams. - - LZMA2 : multi-threaded version of LZMA2 encoder worked incorrectly, if - default value of chunk size (CLzma2EncProps::blockSize) is changed + - LZMA2 : multi-threaded version of LZMA2 encoder worked incorrectly, if + default value of chunk size (CLzma2EncProps::blockSize) is changed to value larger than (4 GiB - dictionary_size). 9.38 beta 2015-01-03 -------------------------- +------------------------- - The BUG in 9.31-9.37 was fixed: IArchiveGetRawProps interface was disabled for 7z archives. - The BUG in 9.26-9.36 was fixed: @@ -342,10 +353,10 @@ HISTORY of the 7-Zip source code 9.36 beta 2014-12-26 -------------------------- +------------------------- - The BUG in command line version was fixed: 7-Zip created temporary archive in current folder during update archive - operation, if -w{Path} switch was not specified. + operation, if -w{Path} switch was not specified. The fixed 7-Zip creates temporary archive in folder that contains updated archive. - The BUG in 9.33-9.35 was fixed: 7-Zip silently ignored file reading errors during 7z or gz archive creation, @@ -355,8 +366,8 @@ HISTORY of the 7-Zip source code 9.31 2012-10-31 ------------------------- -- InBuffer.h : CInBuffer uses ISequentialInStream *_stream; instead of CMyComPtr - OutBuffer.h: COutBuffer uses ISequentialOutStream *_stream; instead of CMyComPtr +- InBuffer.h : CInBuffer uses ISequentialInStream *_stream; instead of CMyComPtr + OutBuffer.h: COutBuffer uses ISequentialOutStream *_stream; instead of CMyComPtr 9.26 2011-04-11 @@ -366,13 +377,13 @@ HISTORY of the 7-Zip source code 9.21 2011-04-11 -------------------------- +------------------------- - New class FString for file names at file systems. - Speed optimization in CRC code for big-endian CPUs. 9.18 2010-11-02 -------------------------- +------------------------- - New small SFX module for installers (C/Util/SfxSetup). @@ -409,7 +420,7 @@ HISTORY of the 7-Zip source code 4.61 2008-11-23 ------------------------- - Bug in ver. 4.58+ was fixed: - 7-Zip didn't use any -m* switch after -mtc, -mcl or -mcu for .zip archives. + 7-Zip didn't use any -m* switch after -mtc, -mcl or -mcu for .zip archives. - Bug in .CAB code was fixed. 7-Zip didn't show some empty files, if .CAB archive contains more than one empty file. @@ -417,13 +428,13 @@ HISTORY of the 7-Zip source code 4.59 2008-07-27 ------------------------- - Bug was fixed: - LZMA Encoder in fast compression mode could access memory outside of + LZMA Encoder in fast compression mode could access memory outside of allocated range in some rare cases. 4.59 alpha 2008-05-30 ------------------------- -- BUGS was fixed: +- BUGS was fixed: 7zOut.cpp: 7-Zip incorrectly wrote size of property records in some cases. 7zIn.cpp: 7-Zip incorrectly work with archive, containg archive properties. @@ -440,13 +451,13 @@ HISTORY of the 7-Zip source code 1) Default mode: 7-Zip uses UTF-8, if the local code page doesn't contain required symbols. 2) -mcu switch: 7-Zip uses UTF-8, if there are non-ASCII symbols. 3) -mcl switch: 7-Zip uses local code page. -- Now it's possible to use -mSW- and -mSW+ switches instead of -mSW=off and -mSW=on +- Now it's possible to use -mSW- and -mSW+ switches instead of -mSW=off and -mSW=on 4.58 alpha 7 2008-04-08 ------------------------- -- BUG was fixed: BZip2Encoder and BZip2Decoder used CEvent objects without - creating, when BZip2 code was called with one thread (with -mmt1 switch or with +- BUG was fixed: BZip2Encoder and BZip2Decoder used CEvent objects without + creating, when BZip2 code was called with one thread (with -mmt1 switch or with default switches on single thread CPU). - .lzma support. - RPM and NSIS support was improved. @@ -472,7 +483,7 @@ HISTORY of the 7-Zip source code - 7-Zip now has 128 MB dictionary limit for 32-bit version: It's for speed optimization: kNumLogBits = 9 + sizeof(size_t) / 2; - TAR: 'D' link flag support. -- 7-Zip now can unpack multivolume RAR archives created with +- 7-Zip now can unpack multivolume RAR archives created with "old style volume names" scheme (-vn switch) and names *.001, *.002, ... - Fixed bugs: - 7-Zip FM could not copy / move files to root network folders like \\COMPNAME\FOLDERNAME\ @@ -484,7 +495,7 @@ HISTORY of the 7-Zip source code 7-zip tries to delete all extra fileds (except for WzAES). And that code could hang. - 7-Zip GUI didn't suggest BZip2 dictionary size used in previous run. - - If creation time stamp was included in .RAR archive, 7-zip used creation time stamp + - If creation time stamp was included in .RAR archive, 7-zip used creation time stamp as modification time stamp. 4.58 alpha 2 2007-12-31 @@ -531,7 +542,7 @@ HISTORY of the 7-Zip source code 4.45 beta 2007-04-16 ------------------------- -- 7-Zip now uses C version of CRC, so you must call CrcGenerateTable at +- 7-Zip now uses C version of CRC, so you must call CrcGenerateTable at stratup code, or you must add CPP/Common/CRC.cpp to your project. - Method ID in .7z now is 63-bit integer (UInt64). - Open error messages @@ -606,7 +617,7 @@ HISTORY of the 7-Zip source code 4.07 beta 2004-10-03 ------------------------- -- some interfaces were changed slightly to support +- some interfaces were changed slightly to support -stdin -stdout mode. - FilterCoder for simple filters - Wildcard censor class was changed. @@ -682,7 +693,7 @@ HISTORY of the 7-Zip source code 2.30 Beta 24 2002-11-01 ------------------------- - SDK/Windows/Synchronization.h + SDK/Windows/Synchronization.h SDK/Windows/Synchronization.cpp - some changes. @@ -711,9 +722,9 @@ HISTORY of the 7-Zip source code 2.30 Beta 20 2002-07-01 ------------------------- -- SDK/Stream/WindowOut.h +- SDK/Stream/WindowOut.h now it uses only required memory (dictionary size). -- Project/Archiver/Resource +- Project/Archiver/Resource contains common resurces @@ -727,8 +738,8 @@ HISTORY of the 7-Zip source code - SDK/Archive/Cab/MSZipDecoder.cpp SDK/Archive/Cab/LZXDecoder.cpp: bug with corrupted archives was fixed -- Project/Compress/LZ/MatchFinder/BinTree/BinTree.h -- Project/Compress/LZ/MatchFinder/BinTree/BinTreeMain.h +- Project/Compress/LZ/MatchFinder/BinTree/BinTree.h +- Project/Compress/LZ/MatchFinder/BinTree/BinTreeMain.h some speed optimization (using prefetching) @@ -743,7 +754,7 @@ HISTORY of the 7-Zip source code Bug was fixed: LZMA could not extract more than 4 GB. - RPM and CPIO formats. - Project/Compress/LZ/LZMA/Encoder.* - Project/Archiver/Format/7z/OutHandler.cpp + Project/Archiver/Format/7z/OutHandler.cpp New fast compression mode for LZMA: -m0a=0. - New match finders for LZMA: bt4b, hc3, hc4. @@ -752,23 +763,23 @@ HISTORY of the 7-Zip source code ------------------------- - Compression ratio in LZMA was slightly improved: Project/Compress/LZ/LZMA/Encoder.* - Project/Archiver/Format/7z/OutHandler.cpp + Project/Archiver/Format/7z/OutHandler.cpp 2.30 Beta 14 2002-02-10 ------------------------- - Supporting multithreading for LZMA: - Project/Compress/LZ/MatchFinder/MT + Project/Compress/LZ/MatchFinder/MT - Common/String.h: CStringBase::Replace function was fixed. 2.30 Beta 13 2002-01-27 ------------------------- -- Compress/LZ/MatchFinder/BinTree3.h: +- Compress/LZ/MatchFinder/BinTree3.h: method -- Compress/LZ/MatchFinder/BinTreemain.h: - - one VirtualAlloc array was splitted to +- Compress/LZ/MatchFinder/BinTreemain.h: + - one VirtualAlloc array was splitted to the for 3 arrays. - Hash-functions were changed. @@ -776,23 +787,23 @@ HISTORY of the 7-Zip source code 2.30 Beta 12 2002-01-16 ------------------------- -- Compress/LZ/MatchFinder/BinTreemain.h: - Compress/LZ/MatchFinder/Patricia.h: - Compress/PPM/PPMd/SubAlloc.h: +- Compress/LZ/MatchFinder/BinTreemain.h: + Compress/LZ/MatchFinder/Patricia.h: + Compress/PPM/PPMd/SubAlloc.h: Beta 11 bugs were fixed: - VirtualFree was used incorrectly - checking WIN32 instead _WINDOWS. - Compress/LZ/MatchFinder/Patricia.h: + Compress/LZ/MatchFinder/Patricia.h: Beta 11 bug with deleting m_Hash2Descendants was fixed. 2.30 Beta 11 2002-01-15 ------------------------- -- Compress/LZ/MatchFinder/BinTreemain.h: - Compress/LZ/MatchFinder/Patricia.h: - Compress/PPM/PPMd/SubAlloc.h: +- Compress/LZ/MatchFinder/BinTreemain.h: + Compress/LZ/MatchFinder/Patricia.h: + Compress/PPM/PPMd/SubAlloc.h: using VirtualAlloc for memory allocating -- Exlorer/ContextMenu.cpp: +- Exlorer/ContextMenu.cpp: Testing supporting. CreateProcess instead WinExec - Format/Common/IArchiveHandler.h: @@ -808,9 +819,9 @@ HISTORY of the 7-Zip source code 2.30 Beta 10 2002-01-11 ------------------------- -- Exlorer/ContextMenu.cpp: bug with context menu on +- Exlorer/ContextMenu.cpp: bug with context menu on Windows NT4 in Unicode version was fixed. -- Format/7z/UpdateArchiveEngine.cpp: bug was fixed - +- Format/7z/UpdateArchiveEngine.cpp: bug was fixed - Updating in Beta 8 and 9 didn't work. - Exlorer/CCompressDialog.cpp: history growing bug was fixed. @@ -823,4 +834,3 @@ HISTORY of the 7-Zip source code - SDK/Archive/Zip/InEngine.cpp: bug was fixed. - SDK/Windows/FileDir.cpp: function CreateComplexDirectory was changed. - -- cgit v1.2.3-55-g6feb