[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1109782: marked as done (unblock: 7zip/25.00+dfsg-1)



Your message dated Mon, 11 Aug 2025 00:26:06 +0200
with message-id <aJkcfpkrRBkBLunY@duagon-BXN3S64.localdomain>
and subject line unblock: 7zip/25.00+dfsg-1
has caused the Debian Bug report #1109782,
regarding unblock: 7zip/25.00+dfsg-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1109782: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1109782
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: 7zip@packages.debian.org
Control: affects -1 + src:7zip
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package 7zip

[ Reason ]
7zip-rar 25.00 has migrated to trixie.
The 7zip-rar interface might not be compatible with 7zip in trixie.

[ Impact ]
7zip-rar might fail to be called for specific rar file operations.

[ Tests ]
I have tried to provoke a crash by extracting a rar file but have not
succeeded.

[ Risks ]
None. The risks are in not migrating.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

unblock 7zip/25.00+dfsg-1
diff -Nru 7zip-24.09+dfsg/Asm/x86/Sort.asm 7zip-25.00+dfsg/Asm/x86/Sort.asm
--- 7zip-24.09+dfsg/Asm/x86/Sort.asm	1970-01-01 01:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/Asm/x86/Sort.asm	2025-01-08 09:00:00.000000000 +0100
@@ -0,0 +1,860 @@
+; SortTest.asm -- ASM version of HeapSort() function
+; Igor Pavlov : Public domain
+
+include ../../../../Asm/x86/7zAsm.asm
+
+MY_ASM_START
+
+ifndef Z7_SORT_ASM_USE_SEGMENT
+if (IS_LINUX gt 0)
+  ; Z7_SORT_ASM_USE_SEGMENT equ 1
+else
+  ; Z7_SORT_ASM_USE_SEGMENT equ 1
+endif
+endif
+
+ifdef Z7_SORT_ASM_USE_SEGMENT
+_TEXT$Z7_SORT SEGMENT ALIGN(64) 'CODE'
+MY_ALIGN macro num:req
+        align  num
+endm
+else
+MY_ALIGN macro num:req
+        ; We expect that ".text" is aligned for 16-bytes.
+        ; So we don't need large alignment inside our function.
+        align  16
+endm
+endif
+
+
+MY_ALIGN_16 macro
+        MY_ALIGN 16
+endm
+
+MY_ALIGN_32 macro
+        MY_ALIGN 32
+endm
+
+MY_ALIGN_64 macro
+        MY_ALIGN 64
+endm
+
+ifdef x64
+
+NUM_PREFETCH_LEVELS     equ 3 ; to prefetch 1x 64-bytes line (is good for most cases)
+; NUM_PREFETCH_LEVELS     equ 4 ; to prefetch 2x 64-bytes lines (better for big arrays)
+
+acc           equ x0
+k             equ r0
+k_x           equ x0
+
+p             equ r1
+
+s             equ r2
+s_x           equ x2
+
+a0            equ x3
+t0            equ a0
+
+a3            equ x5
+qq            equ a3
+
+a1            equ x6
+t1            equ a1
+t1_r          equ r6
+
+a2            equ x7
+t2            equ a2
+
+i             equ r8
+e0            equ x8
+
+e1            equ x9
+
+num_last      equ r10
+num_last_x    equ x10
+
+next4_lim     equ r11
+pref_lim      equ r12
+
+
+
+SORT_2_WITH_TEMP_REG macro b0, b1, temp_reg
+        mov     temp_reg, b0
+        cmp     b0, b1
+        cmovae  b0, b1 ; min
+        cmovae  b1, temp_reg ; max
+endm
+
+SORT macro b0, b1
+        SORT_2_WITH_TEMP_REG b0, b1, acc
+endm
+
+LOAD macro dest:req, index:req
+        mov     dest, [p + 4 * index]
+endm
+
+STORE macro reg:req, index:req
+        mov     [p + 4 * index], reg
+endm
+
+
+if (NUM_PREFETCH_LEVELS gt 3)
+   num_prefetches equ (1 SHL (NUM_PREFETCH_LEVELS - 3))
+else
+   num_prefetches equ 1
+endif
+
+PREFETCH_OP macro offs
+  cur_offset = 7 * 4    ; it's average offset in 64-bytes cache line.
+  ; cur_offset = 0      ; we can use zero offset, if we are sure that array is aligned for 64-bytes.
+  rept num_prefetches
+    if 1
+        prefetcht0 byte ptr [p + offs + cur_offset]
+    else
+        mov     pref_x, dword ptr [p + offs + cur_offset]
+    endif
+        cur_offset = cur_offset + 64
+  endm
+endm
+
+PREFETCH_MY macro
+if 1
+    if 1
+        shl     k, NUM_PREFETCH_LEVELS + 3
+    else
+        ; we delay prefetch instruction to improve main loads
+        shl     k, NUM_PREFETCH_LEVELS
+        shl     k, 3
+        ; shl     k, 0
+    endif
+        PREFETCH_OP k
+elseif 1
+        shl     k, 3
+        PREFETCH_OP k * (1 SHL NUM_PREFETCH_LEVELS) ; change it
+endif
+endm
+
+
+STEP_1 macro exit_label, prefetch_macro
+use_cmov_1 equ 1  ; set 1 for cmov, but it's slower in some cases
+                  ; set 0 for LOAD after adc s, 0
+        cmp     t0, t1
+    if use_cmov_1
+        cmovb   t0, t1
+        ; STORE   t0, k
+    endif
+        adc     s, 0
+    if use_cmov_1 eq 0
+        LOAD    t0, s
+    endif
+        cmp     qq, t0
+        jae     exit_label
+    if 1 ; use_cmov_1 eq 0
+        STORE   t0, k
+    endif
+        prefetch_macro
+        mov     t0, [p + s * 8]
+        mov     t1, [p + s * 8 + 4]
+        mov     k, s
+        add     s, s                 ; slower  for some cpus
+        ; lea     s, dword ptr [s + s] ; slower for some cpus
+        ; shl     s, 1               ; faster for some cpus
+        ; lea     s, dword ptr [s * 2] ; faster for some cpus
+    rept 0 ; 1000 for debug : 0 for normal
+        ; number of calls in generate_stage : ~0.6 of number of items
+        shl     k, 0
+    endm
+endm
+
+
+STEP_2 macro exit_label, prefetch_macro
+use_cmov_2 equ 0  ; set 1 for cmov, but it's slower in some cases
+                  ; set 0 for LOAD after adc s, 0
+        cmp     t0, t1
+    if use_cmov_2
+        mov     t2, t0
+        cmovb   t2, t1
+        ; STORE   t2, k
+    endif
+        mov     t0, [p + s * 8]
+        mov     t1, [p + s * 8 + 4]
+        cmovb   t0, [p + s * 8 + 8]
+        cmovb   t1, [p + s * 8 + 12]
+        adc     s, 0
+    if use_cmov_2 eq 0
+        LOAD    t2, s
+    endif
+        cmp     qq, t2
+        jae     exit_label
+    if 1 ; use_cmov_2 eq 0
+        STORE   t2, k
+    endif
+        prefetch_macro
+        mov     k, s
+        ; add     s, s
+        ; lea     s, [s + s]
+        shl     s, 1
+        ; lea     s, [s * 2]
+endm
+
+
+MOVE_SMALLEST_UP macro STEP, use_prefetch, num_unrolls
+        LOCAL exit_1, exit_2, leaves, opt_loop, last_nodes
+
+        ; s == k * 2
+        ; t0 == (p)[s]
+        ; t1 == (p)[s + 1]
+        cmp     k, next4_lim
+        jae     leaves
+
+    rept num_unrolls
+        STEP    exit_2
+        cmp     k, next4_lim
+        jae     leaves
+    endm
+  
+    if use_prefetch
+        prefetch_macro  equ PREFETCH_MY
+        pref_lim_2      equ pref_lim
+        ; lea     pref_lim, dword ptr [num_last + 1]
+        ; shr     pref_lim, NUM_PREFETCH_LEVELS + 1
+        cmp     k, pref_lim_2
+        jae     last_nodes
+    else
+        prefetch_macro  equ
+        pref_lim_2      equ next4_lim
+    endif
+  
+MY_ALIGN_16
+opt_loop:
+        STEP    exit_2, prefetch_macro
+        cmp     k, pref_lim_2
+        jb      opt_loop
+
+last_nodes:
+        ; k >= pref_lim_2
+        ; 2 cases are possible:
+        ;   case-1: num_after_prefetch_levels == 0 && next4_lim = pref_lim_2
+        ;   case-2: num_after_prefetch_levels == NUM_PREFETCH_LEVELS - 1 &&
+        ;        next4_lim = pref_lim_2 / (NUM_PREFETCH_LEVELS - 1)
+  if use_prefetch
+    yyy = NUM_PREFETCH_LEVELS - 1
+    while yyy
+        yyy = yyy - 1
+        STEP    exit_2
+      if yyy
+        cmp     k, next4_lim
+        jae     leaves
+      endif
+    endm
+  endif
+
+leaves:
+        ; k >= next4_lim == (num_last + 1) / 4 must be provided by previous code.
+        ;   we     have    2 nodes in (s)     level :  always
+        ;   we can have some nodes in (s * 2) level :  low probability case
+        ;   we     have   no nodes in (s * 4) level
+        ; s == k * 2
+        ; t0 == (p)[s]
+        ; t1 == (p)[s + 1]
+        cmp     t0, t1
+        cmovb   t0, t1
+        adc     s, 0
+        STORE   t0, k
+
+        ; t0 == (p)[s]
+        ; s / 2 == k  : (s) is index of max item from (p)[k * 2], (p)[k * 2 + 1]
+        ; we have 3 possible cases here:
+        ;   s * 2 >  num_last : (s) node has no childs
+        ;   s * 2 == num_last : (s) node has 1 leaf child that is last item of array
+        ;   s * 2 <  num_last : (s) node has 2 leaf childs. We provide (s * 4 > num_last)
+        ; we check for (s * 2 > num_last) before "cmp qq, t0" check, because
+        ; we will replace conditional jump with cmov instruction later.
+        lea     t1_r, dword ptr [s + s]
+        cmp     t1_r, num_last
+        ja      exit_1 ; if (s * 2 > num_last), we have no childs : it's high probability branch
+        
+        ; it's low probability branch
+        ; s * 2 <= num_last
+        cmp     qq, t0
+        jae     exit_2
+
+        ; qq < t0, so we go to next level
+        ; we check 1 or 2 childs in next level
+        mov     t0, [p + s * 8]
+        mov     k, s
+        mov     s, t1_r
+        cmp     t1_r, num_last
+        je      @F ; (s == num_last) means that we have single child in tree
+
+        ; (s < num_last) : so we must read both childs and select max of them.
+        mov     t1, [p + k * 8 + 4]
+        cmp     t0, t1
+        cmovb   t0, t1
+        adc     s, 0
+@@:
+        STORE   t0, k
+exit_1:
+        ; t0 == (p)[s],  s / 2 == k  : (s) is index of max item from (p)[k * 2], (p)[k * 2 + 1]
+        cmp     qq, t0
+        cmovb   k, s
+exit_2:
+        STORE   qq, k
+endm
+
+
+
+
+ifdef Z7_SORT_ASM_USE_SEGMENT
+; MY_ALIGN_64
+else
+  MY_ALIGN_16
+endif
+
+MY_PROC HeapSort, 2
+
+if (IS_LINUX gt 0)
+        mov     p, REG_ABI_PARAM_0    ; r1 <- r7 : linux
+endif
+        mov     num_last, REG_ABI_PARAM_1  ; r10 <- r6 : linux
+                                      ; r10 <- r2 : win64
+        cmp     num_last, 2
+        jb      end_1
+        
+        ; MY_PUSH_PRESERVED_ABI_REGS
+        MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11
+        push    r12
+        
+        cmp     num_last, 4
+        ja      sort_5
+        
+        LOAD    a0, 0
+        LOAD    a1, 1
+        SORT    a0, a1
+        cmp     num_last, 3
+        jb      end_2
+        
+        LOAD    a2, 2
+        je      sort_3
+        
+        LOAD    a3, 3
+        SORT    a2, a3
+        SORT    a1, a3
+        STORE   a3, 3
+sort_3:
+        SORT    a0, a2
+        SORT    a1, a2
+        STORE   a2, 2
+        jmp     end_2
+        
+sort_5:
+        ; (num_last > 4) is required here
+        ; if (num_last >= 6) : we will use optimized loop for leaf nodes loop_down_1
+        mov     next4_lim, num_last
+        shr     next4_lim, 2
+        
+        dec     num_last
+        mov     k, num_last
+        shr     k, 1
+        mov     i, num_last
+        shr     i, 2
+        test    num_last, 1
+        jnz     size_even
+
+        ; ODD number of items. So we compare parent with single child
+        LOAD    t1, num_last
+        LOAD    t0, k
+        SORT_2_WITH_TEMP_REG  t1, t0, t2
+        STORE   t1, num_last
+        STORE   t0, k
+        dec     k
+
+size_even:
+        cmp     k, i
+        jbe     loop_down ; jump for num_last == 4 case
+
+if 0 ; 1 for debug
+        mov     r15, k
+        mov     r14d, 1 ; 100
+loop_benchmark:
+endif
+        ; optimized loop for leaf nodes:
+        mov     t0, [p + k * 8]
+        mov     t1, [p + k * 8 + 4]
+
+MY_ALIGN_16
+loop_down_1:
+        ; we compare parent with max of childs:
+        ; lea     s, dword ptr [2 * k]
+        mov     s, k
+        cmp     t0, t1
+        cmovb   t0, t1
+        adc     s, s
+        LOAD    t2, k
+        STORE   t0, k
+        cmp     t2, t0
+        cmovae  s, k
+        dec     k
+        ; we preload next items before STORE operation for calculated address
+        mov     t0, [p + k * 8]
+        mov     t1, [p + k * 8 + 4]
+        STORE   t2, s
+        cmp     k, i
+        jne     loop_down_1
+
+if 0 ; 1 for debug
+        mov     k, r15
+        dec     r14d
+        jnz     loop_benchmark
+        ; jmp end_debug
+endif
+       
+MY_ALIGN_16
+loop_down:
+        mov     t0, [p + i * 8]
+        mov     t1, [p + i * 8 + 4]
+        LOAD    qq, i
+        mov     k, i
+        lea     s, dword ptr [i + i]
+        ; jmp end_debug
+    DOWN_use_prefetch  equ 0
+    DOWN_num_unrolls   equ 0
+        MOVE_SMALLEST_UP  STEP_1, DOWN_use_prefetch, DOWN_num_unrolls
+        sub     i, 1
+        jnb     loop_down
+
+        ; jmp end_debug
+        LOAD    e0, 0
+        LOAD    e1, 1
+
+   LEVEL_3_LIMIT equ 8   ; 8 is default, but 7 also can work
+
+        cmp     num_last, LEVEL_3_LIMIT + 1
+        jb      main_loop_sort_5
+
+MY_ALIGN_16
+main_loop_sort:
+        ; num_last > LEVEL_3_LIMIT
+        ; p[size--] = p[0];
+        LOAD    qq, num_last
+        STORE   e0, num_last
+        mov     e0, e1
+        
+        mov     next4_lim, num_last
+        shr     next4_lim, 2
+        mov     pref_lim, num_last
+        shr     pref_lim, NUM_PREFETCH_LEVELS + 1
+        
+        dec     num_last
+if 0    ; 1 for debug
+        ; that optional optimization can improve the performance, if there are identical items in array
+        ;    3 times improvement : if all items in array are identical
+        ;   20%  improvement : if items are different for 1 bit only
+        ; 1-10%  improvement : if items are different for (2+) bits
+        ; no gain : if items are different
+        cmp     qq, e1
+        jae     next_iter_main
+endif
+        LOAD    e1, 2
+        LOAD    t0, 3
+        mov     k_x, 2
+        cmp     e1, t0
+        cmovb   e1, t0
+        mov     t0, [p + 4 * (4 + 0)]
+        mov     t1, [p + 4 * (4 + 1)]
+        cmovb   t0, [p + 4 * (4 + 2)]
+        cmovb   t1, [p + 4 * (4 + 3)]
+        adc     k_x, 0
+        ; (qq <= e1), because the tree is correctly sorted
+        ; also here we could check (qq >= e1) or (qq == e1) for faster exit
+        lea     s, dword ptr [k + k]
+    MAIN_use_prefetch  equ 1
+    MAIN_num_unrolls   equ 0
+        MOVE_SMALLEST_UP  STEP_2, MAIN_use_prefetch, MAIN_num_unrolls
+
+next_iter_main:
+        cmp     num_last, LEVEL_3_LIMIT
+        jne     main_loop_sort
+
+        ; num_last == LEVEL_3_LIMIT
+main_loop_sort_5:
+        ; 4 <= num_last <= LEVEL_3_LIMIT
+        ; p[size--] = p[0];
+        LOAD    qq, num_last
+        STORE   e0, num_last
+        mov     e0, e1
+        dec     num_last_x
+        
+        LOAD    e1, 2
+        LOAD    t0, 3
+        mov     k_x, 2
+        cmp     e1, t0
+        cmovb   e1, t0
+        adc     k_x, 0
+
+        lea     s_x, dword ptr [k * 2]
+        cmp     s_x, num_last_x
+        ja      exit_2
+
+        mov     t0, [p + k * 8]
+        je      exit_1
+
+        ; s < num_last
+        mov     t1, [p + k * 8 + 4]
+        cmp     t0, t1
+        cmovb   t0, t1
+        adc     s_x, 0
+exit_1:
+        STORE   t0, k
+        cmp     qq, t0
+        cmovb   k_x, s_x
+exit_2:
+        STORE   qq, k
+        cmp     num_last_x, 3
+        jne     main_loop_sort_5
+
+        ; num_last == 3 (real_size == 4)
+        LOAD    a0, 2
+        LOAD    a1, 3
+        STORE   e1, 2
+        STORE   e0, 3
+        SORT    a0, a1
+end_2:
+        STORE   a0, 0
+        STORE   a1, 1
+; end_debug:
+        ; MY_POP_PRESERVED_ABI_REGS
+        pop     r12
+        MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11
+end_1:
+MY_ENDP
+
+
+
+else
+; ------------ x86 32-bit ------------
+
+ifdef x64
+IS_CDECL = 0
+endif
+
+acc           equ x0
+k             equ r0
+k_x           equ acc
+
+p             equ r1
+
+num_last      equ r2
+num_last_x    equ x2
+
+a0            equ x3
+t0            equ a0
+
+a3            equ x5
+i             equ r5
+e0            equ a3
+
+a1            equ x6
+qq            equ a1
+ 
+a2            equ x7
+s             equ r7
+s_x           equ a2
+
+
+SORT macro b0, b1
+        cmp     b1, b0
+        jae     @F
+    if 1
+        xchg    b0, b1
+    else
+        mov     acc, b0
+        mov     b0, b1 ; min
+        mov     b1, acc ; max
+    endif
+@@:
+endm
+
+LOAD macro dest:req, index:req
+        mov     dest, [p + 4 * index]
+endm
+
+STORE macro reg:req, index:req
+        mov     [p + 4 * index], reg
+endm
+
+
+STEP_1 macro exit_label
+        mov     t0, [p + k * 8]
+        cmp     t0, [p + k * 8 + 4]
+        adc     s, 0
+        LOAD    t0, s
+        STORE   t0, k ; we lookahed stooring for most expected branch
+        cmp     qq, t0
+        jae     exit_label
+        ; STORE   t0, k  ; use if
+        mov     k, s
+        add     s, s
+        ; lea     s, dword ptr [s + s]
+        ; shl     s, 1
+        ; lea     s, dword ptr [s * 2]
+endm
+
+STEP_BRANCH macro exit_label
+        mov     t0, [p + k * 8]
+        cmp     t0, [p + k * 8 + 4]
+        jae     @F
+        inc     s
+        mov     t0, [p + k * 8 + 4]
+@@:
+        cmp     qq, t0
+        jae     exit_label
+        STORE   t0, k
+        mov     k, s
+        add     s, s
+endm
+
+
+
+MOVE_SMALLEST_UP macro STEP, num_unrolls, exit_2
+        LOCAL leaves, opt_loop, single
+
+        ; s == k * 2
+    rept num_unrolls
+        cmp     s, num_last
+        jae     leaves
+        STEP_1  exit_2
+    endm
+        cmp     s, num_last
+        jb      opt_loop
+
+leaves:
+        ; (s >= num_last)
+        jne     exit_2
+single:
+        ; (s == num_last)
+        mov     t0, [p + k * 8]
+        cmp     qq, t0
+        jae     exit_2
+        STORE   t0, k
+        mov     k, s
+        jmp     exit_2
+ 
+MY_ALIGN_16
+opt_loop:
+        STEP    exit_2
+        cmp     s, num_last
+        jb      opt_loop
+        je      single
+exit_2:
+        STORE   qq, k
+endm
+
+
+
+
+ifdef Z7_SORT_ASM_USE_SEGMENT
+; MY_ALIGN_64
+else
+  MY_ALIGN_16
+endif
+
+MY_PROC HeapSort, 2
+  ifdef x64
+    if (IS_LINUX gt 0)
+        mov     num_last, REG_ABI_PARAM_1  ; r2 <- r6 : linux
+        mov     p,        REG_ABI_PARAM_0  ; r1 <- r7 : linux
+    endif
+  elseif (IS_CDECL gt 0)
+        mov     num_last, [r4 + REG_SIZE * 2]
+        mov     p,        [r4 + REG_SIZE * 1]
+  endif
+        cmp     num_last, 2
+        jb      end_1
+        MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11
+        
+        cmp     num_last, 4
+        ja      sort_5
+        
+        LOAD    a0, 0
+        LOAD    a1, 1
+        SORT    a0, a1
+        cmp     num_last, 3
+        jb      end_2
+        
+        LOAD    a2, 2
+        je      sort_3
+        
+        LOAD    a3, 3
+        SORT    a2, a3
+        SORT    a1, a3
+        STORE   a3, 3
+sort_3:
+        SORT    a0, a2
+        SORT    a1, a2
+        STORE   a2, 2
+        jmp     end_2
+        
+sort_5:
+        ; num_last > 4
+        lea     i, dword ptr [num_last - 2]
+        dec     num_last
+        test    i, 1
+        jz      loop_down
+
+        ; single child
+        mov     t0, [p + num_last * 4]
+        mov     qq, [p + num_last * 2]
+        dec     i
+        cmp     qq, t0
+        jae     loop_down
+
+        mov     [p + num_last * 2], t0
+        mov     [p + num_last * 4], qq
+       
+MY_ALIGN_16
+loop_down:
+        mov     t0, [p + i * 4]
+        cmp     t0, [p + i * 4 + 4]
+        mov     k, i
+        mov     qq, [p + i * 2]
+        adc     k, 0
+        LOAD    t0, k
+        cmp     qq, t0
+        jae     down_next
+        mov     [p + i * 2], t0
+        lea     s, dword ptr [k + k]
+        
+        DOWN_num_unrolls   equ 0
+        MOVE_SMALLEST_UP  STEP_1, DOWN_num_unrolls, down_exit_label
+down_next:
+        sub     i, 2
+        jnb     loop_down
+        ; jmp end_debug
+
+        LOAD    e0, 0
+
+MY_ALIGN_16
+main_loop_sort:
+        ; num_last > 3
+        mov     t0, [p + 2 * 4]
+        cmp     t0, [p + 3 * 4]
+        LOAD    qq, num_last
+        STORE   e0, num_last
+        LOAD    e0, 1
+        mov     s_x, 2
+        mov     k_x, 1
+        adc     s, 0
+        LOAD    t0, s
+        dec     num_last
+        cmp     qq, t0
+        jae     main_exit_label
+        STORE   t0, 1
+        mov     k, s
+        add     s, s
+    if 1
+        ; for branch data prefetch mode :
+        ; it's faster for large arrays : larger than (1 << 13) items.
+        MAIN_num_unrolls   equ 10
+        STEP_LOOP          equ STEP_BRANCH
+    else
+        MAIN_num_unrolls   equ 0
+        STEP_LOOP          equ STEP_1
+    endif
+        
+        MOVE_SMALLEST_UP  STEP_LOOP, MAIN_num_unrolls, main_exit_label
+        
+        ; jmp end_debug
+        cmp     num_last, 3
+        jne     main_loop_sort
+
+        ; num_last == 3 (real_size == 4)
+        LOAD    a0, 2
+        LOAD    a1, 3
+        LOAD    a2, 1
+        STORE   e0, 3  ; e0 is alias for a3
+        STORE   a2, 2
+        SORT    a0, a1
+end_2:
+        STORE   a0, 0
+        STORE   a1, 1
+; end_debug:
+        MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11
+end_1:
+MY_ENDP
+
+endif
+
+ifdef Z7_SORT_ASM_USE_SEGMENT
+_TEXT$Z7_SORT ENDS
+endif
+
+if 0
+LEA_IS_D8 (R64) [R2 * 4 + 16]
+ Lat : TP
+   2 :  1 :      adl-e
+   2 :  3   p056 adl-p
+   1 :  2 : p15  hsw-rocket
+   1 :  2 : p01  snb-ivb
+   1 :  1 : p1   conroe-wsm
+   1 :  4 : zen3,zen4
+   2 :  4 : zen1,zen2
+
+LEA_B_IS (R64) [R2 + R3 * 4]
+ Lat : TP
+   1 :  1 :      adl-e
+   2 :  3   p056 adl-p
+   1 :  2 : p15  hsw-rocket
+   1 :  2 : p01  snb-ivb
+   1 :  1 : p1   nhm-wsm
+   1 :  1 : p0   conroe-wsm
+   1 :  4 : zen3,zen4
+   2 :2,4 : zen1,zen2
+   
+LEA_B_IS_D8 (R64) [R2 + R3 * 4 + 16]
+ Lat : TP
+   2 :  1 :      adl-e
+   2 :  3   p056 adl-p
+   1 :  2 : p15  ice-rocket
+   3 :  1 : p1/p15 hsw-rocket
+   3 :  1 : p01  snb-ivb
+   1 :  1 : p1   nhm-wsm
+   1 :  1 : p0   conroe-wsm
+ 2,1 :  2 : zen3,zen4
+   2 :  2 : zen1,zen2
+   
+CMOVB (R64, R64)
+ Lat : TP
+ 1,2 :  2 :      adl-e
+   1 :  2   p06  adl-p
+   1 :  2 : p06  bwd-rocket
+ 1,2 :  2 : p0156+p06 hsw
+ 1,2 :1.5 : p015+p05  snb-ivb
+ 1,2 :  1 : p015+p05  nhm
+   1 :  1 : 2*p015  conroe
+   1 :  2 : zen3,zen4
+   1 :  4 : zen1,zen2
+
+ADC (R64, 0)
+ Lat : TP
+ 1,2 :  2 :      adl-e
+   1 :  2   p06  adl-p
+   1 :  2 : p06  bwd-rocket
+   1 :1.5 : p0156+p06 hsw
+   1 :1.5 : p015+p05  snb-ivb
+   2 :  1 : 2*p015    conroe-wstm
+   1 :  2 : zen1,zen2,zen3,zen4
+   
+PREFETCHNTA : fetch data into non-temporal cache close to the processor, minimizing cache pollution.
+  L1 : Pentium3
+  L2 : NetBurst
+  L1, not L2: Core duo, Core 2, Atom processors
+  L1, not L2, may fetch into L3 with fast replacement: Nehalem, Westmere, Sandy Bridge, ...
+      NEHALEM: Fills L1/L3, L1 LRU is not updated
+  L3 with fast replacement: Xeon Processors based on Nehalem, Westmere, Sandy Bridge, ...
+PREFETCHT0 : fetch data into all cache levels.
+PREFETCHT1 : fetch data into L2 and L3
+endif
+
+end
diff -Nru 7zip-24.09+dfsg/C/7zVersion.h 7zip-25.00+dfsg/C/7zVersion.h
--- 7zip-24.09+dfsg/C/7zVersion.h	2024-11-29 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/7zVersion.h	2025-07-05 13:00:00.000000000 +0200
@@ -1,7 +1,7 @@
-#define MY_VER_MAJOR 24
-#define MY_VER_MINOR 9
+#define MY_VER_MAJOR 25
+#define MY_VER_MINOR 0
 #define MY_VER_BUILD 0
-#define MY_VERSION_NUMBERS "24.09"
+#define MY_VERSION_NUMBERS "25.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 "2024-11-29"
+#define MY_DATE "2025-07-05"
 #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-2024 Igor Pavlov"
+#define MY_COPYRIGHT_CR "Copyright (c) 1999-2025 Igor Pavlov"
 
 #ifdef USE_COPYRIGHT_CR
   #define MY_COPYRIGHT MY_COPYRIGHT_CR
diff -Nru 7zip-24.09+dfsg/C/BwtSort.c 7zip-25.00+dfsg/C/BwtSort.c
--- 7zip-24.09+dfsg/C/BwtSort.c	2023-04-02 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/BwtSort.c	2025-01-13 14:00:00.000000000 +0100
@@ -1,5 +1,5 @@
 /* BwtSort.c -- BWT block sorting
-2023-04-02 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -7,6 +7,44 @@
 #include "Sort.h"
 
 /* #define BLOCK_SORT_USE_HEAP_SORT */
+// #define BLOCK_SORT_USE_HEAP_SORT
+
+#ifdef BLOCK_SORT_USE_HEAP_SORT
+
+#define HeapSortRefDown(p, vals, n, size, temp) \
+  { size_t k = n; UInt32 val = vals[temp]; for (;;) { \
+    size_t s = k << 1; \
+    if (s > size) break; \
+    if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \
+    if (val >= vals[p[s]]) break; \
+    p[k] = p[s]; k = s; \
+  } p[k] = temp; }
+
+void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size)
+{
+  if (size <= 1)
+    return;
+  p--;
+  {
+    size_t i = size / 2;
+    do
+    {
+      UInt32 temp = p[i];
+      HeapSortRefDown(p, vals, i, size, temp);
+    }
+    while (--i != 0);
+  }
+  do
+  {
+    UInt32 temp = p[size];
+    p[size--] = p[1];
+    HeapSortRefDown(p, vals, 1, size, temp);
+  }
+  while (size > 1);
+}
+
+#endif // BLOCK_SORT_USE_HEAP_SORT
+
 
 /* Don't change it !!! */
 #define kNumHashBytes 2
@@ -27,26 +65,27 @@
 
 #else
 
-#define kNumBitsMax 20
-#define kIndexMask ((1 << kNumBitsMax) - 1)
-#define kNumExtraBits (32 - kNumBitsMax)
-#define kNumExtra0Bits (kNumExtraBits - 2)
-#define kNumExtra0Mask ((1 << kNumExtra0Bits) - 1)
+#define kNumBitsMax     20
+#define kIndexMask      (((UInt32)1 << kNumBitsMax) - 1)
+#define kNumExtraBits   (32 - kNumBitsMax)
+#define kNumExtra0Bits  (kNumExtraBits - 2)
+#define kNumExtra0Mask  ((1 << kNumExtra0Bits) - 1)
 
 #define SetFinishedGroupSize(p, size) \
-  {  *(p) |= ((((size) - 1) & kNumExtra0Mask) << kNumBitsMax); \
+  {  *(p) |= ((((UInt32)(size) - 1) & kNumExtra0Mask) << kNumBitsMax); \
     if ((size) > (1 << kNumExtra0Bits)) { \
-    *(p) |= 0x40000000;  *((p) + 1) |= ((((size) - 1)>> kNumExtra0Bits) << kNumBitsMax); } } \
+      *(p) |= 0x40000000; \
+      *((p) + 1) |= (((UInt32)(size) - 1) >> kNumExtra0Bits) << kNumBitsMax; } } \
 
-static void SetGroupSize(UInt32 *p, UInt32 size)
+static void SetGroupSize(UInt32 *p, size_t size)
 {
   if (--size == 0)
     return;
-  *p |= 0x80000000 | ((size & kNumExtra0Mask) << kNumBitsMax);
+  *p |= 0x80000000 | (((UInt32)size & kNumExtra0Mask) << kNumBitsMax);
   if (size >= (1 << kNumExtra0Bits))
   {
     *p |= 0x40000000;
-    p[1] |= ((size >> kNumExtra0Bits) << kNumBitsMax);
+    p[1] |= (((UInt32)size >> kNumExtra0Bits) << kNumBitsMax);
   }
 }
 
@@ -59,12 +98,14 @@
 */
 
 static
-UInt32
+unsigned
 Z7_FASTCALL
-SortGroup(UInt32 BlockSize, UInt32 NumSortedBytes, UInt32 groupOffset, UInt32 groupSize, int NumRefBits, UInt32 *Indices
-  #ifndef BLOCK_SORT_USE_HEAP_SORT
-  , UInt32 left, UInt32 range
-  #endif
+SortGroup(size_t BlockSize, size_t NumSortedBytes,
+    size_t groupOffset, size_t groupSize,
+    unsigned NumRefBits, UInt32 *Indices
+#ifndef BLOCK_SORT_USE_HEAP_SORT
+    , size_t left, size_t range
+#endif
   )
 {
   UInt32 *ind2 = Indices + groupOffset;
@@ -79,90 +120,93 @@
     return 0;
   }
   Groups = Indices + BlockSize + BS_TEMP_SIZE;
-  if (groupSize <= ((UInt32)1 << NumRefBits)
-      #ifndef BLOCK_SORT_USE_HEAP_SORT
+  if (groupSize <= ((size_t)1 << NumRefBits)
+#ifndef BLOCK_SORT_USE_HEAP_SORT
       && groupSize <= range
-      #endif
+#endif
       )
   {
     UInt32 *temp = Indices + BlockSize;
-    UInt32 j;
-    UInt32 mask, thereAreGroups, group, cg;
+    size_t j, group;
+    UInt32 mask, cg;
+    unsigned thereAreGroups;
     {
       UInt32 gPrev;
       UInt32 gRes = 0;
       {
-        UInt32 sp = ind2[0] + NumSortedBytes;
-        if (sp >= BlockSize) sp -= BlockSize;
+        size_t sp = ind2[0] + NumSortedBytes;
+        if (sp >= BlockSize)
+            sp -= BlockSize;
         gPrev = Groups[sp];
-        temp[0] = (gPrev << NumRefBits);
+        temp[0] = gPrev << NumRefBits;
       }
       
       for (j = 1; j < groupSize; j++)
       {
-        UInt32 sp = ind2[j] + NumSortedBytes;
+        size_t sp = ind2[j] + NumSortedBytes;
         UInt32 g;
-        if (sp >= BlockSize) sp -= BlockSize;
+        if (sp >= BlockSize)
+            sp -= BlockSize;
         g = Groups[sp];
-        temp[j] = (g << NumRefBits) | j;
+        temp[j] = (g << NumRefBits) | (UInt32)j;
         gRes |= (gPrev ^ g);
       }
       if (gRes == 0)
       {
-        #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
         SetGroupSize(ind2, groupSize);
-        #endif
+#endif
         return 1;
       }
     }
     
     HeapSort(temp, groupSize);
-    mask = (((UInt32)1 << NumRefBits) - 1);
+    mask = ((UInt32)1 << NumRefBits) - 1;
     thereAreGroups = 0;
     
     group = groupOffset;
-    cg = (temp[0] >> NumRefBits);
+    cg = temp[0] >> NumRefBits;
     temp[0] = ind2[temp[0] & mask];
 
     {
-    #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
     UInt32 *Flags = Groups + BlockSize;
-    #else
-    UInt32 prevGroupStart = 0;
-    #endif
+#else
+    size_t prevGroupStart = 0;
+#endif
     
     for (j = 1; j < groupSize; j++)
     {
-      UInt32 val = temp[j];
-      UInt32 cgCur = (val >> NumRefBits);
+      const UInt32 val = temp[j];
+      const UInt32 cgCur = val >> NumRefBits;
       
       if (cgCur != cg)
       {
         cg = cgCur;
         group = groupOffset + j;
 
-        #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
         {
-        UInt32 t = group - 1;
-        Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask));
+          const size_t t = group - 1;
+          Flags[t >> kNumFlagsBits] &= ~((UInt32)1 << (t & kFlagsMask));
         }
-        #else
+#else
         SetGroupSize(temp + prevGroupStart, j - prevGroupStart);
         prevGroupStart = j;
-        #endif
+#endif
       }
       else
         thereAreGroups = 1;
       {
-      UInt32 ind = ind2[val & mask];
-      temp[j] = ind;
-      Groups[ind] = group;
+        const UInt32 ind = ind2[val & mask];
+        temp[j] = ind;
+        Groups[ind] = (UInt32)group;
       }
     }
 
-    #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
     SetGroupSize(temp + prevGroupStart, j - prevGroupStart);
-    #endif
+#endif
     }
 
     for (j = 0; j < groupSize; j++)
@@ -172,37 +216,42 @@
 
   /* Check that all strings are in one group (cannot sort) */
   {
-    UInt32 group, j;
-    UInt32 sp = ind2[0] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize;
+    UInt32 group;
+    size_t j;
+    size_t sp = ind2[0] + NumSortedBytes;
+    if (sp >= BlockSize)
+        sp -= BlockSize;
     group = Groups[sp];
     for (j = 1; j < groupSize; j++)
     {
-      sp = ind2[j] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize;
+      sp = ind2[j] + NumSortedBytes;
+      if (sp >= BlockSize)
+          sp -= BlockSize;
       if (Groups[sp] != group)
         break;
     }
     if (j == groupSize)
     {
-      #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
       SetGroupSize(ind2, groupSize);
-      #endif
+#endif
       return 1;
     }
   }
 
-  #ifndef BLOCK_SORT_USE_HEAP_SORT
+#ifndef BLOCK_SORT_USE_HEAP_SORT
   {
   /* ---------- Range Sort ---------- */
-  UInt32 i;
-  UInt32 mid;
+  size_t i;
+  size_t mid;
   for (;;)
   {
-    UInt32 j;
+    size_t j;
     if (range <= 1)
     {
-      #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
       SetGroupSize(ind2, groupSize);
-      #endif
+#endif
       return 1;
     }
     mid = left + ((range + 1) >> 1);
@@ -210,7 +259,7 @@
     i = 0;
     do
     {
-      UInt32 sp = ind2[i] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize;
+      size_t sp = ind2[i] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize;
       if (Groups[sp] >= mid)
       {
         for (j--; j > i; j--)
@@ -238,51 +287,53 @@
       break;
   }
 
-  #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
   {
-    UInt32 t = (groupOffset + i - 1);
+    const size_t t = groupOffset + i - 1;
     UInt32 *Flags = Groups + BlockSize;
-    Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask));
+    Flags[t >> kNumFlagsBits] &= ~((UInt32)1 << (t & kFlagsMask));
   }
-  #endif
+#endif
 
   {
-    UInt32 j;
+    size_t j;
     for (j = i; j < groupSize; j++)
-      Groups[ind2[j]] = groupOffset + i;
+      Groups[ind2[j]] = (UInt32)(groupOffset + i);
   }
 
   {
-  UInt32 res = SortGroup(BlockSize, NumSortedBytes, groupOffset, i, NumRefBits, Indices, left, mid - left);
-  return res | SortGroup(BlockSize, NumSortedBytes, groupOffset + i, groupSize - i, NumRefBits, Indices, mid, range - (mid - left));
+    unsigned res = SortGroup(BlockSize, NumSortedBytes, groupOffset, i, NumRefBits, Indices, left, mid - left);
+    return   res | SortGroup(BlockSize, NumSortedBytes, groupOffset + i, groupSize - i, NumRefBits, Indices, mid, range - (mid - left));
   }
 
   }
 
-  #else
+#else // BLOCK_SORT_USE_HEAP_SORT
 
   /* ---------- Heap Sort ---------- */
 
   {
-    UInt32 j;
+    size_t j;
     for (j = 0; j < groupSize; j++)
     {
-      UInt32 sp = ind2[j] + NumSortedBytes; if (sp >= BlockSize) sp -= BlockSize;
-      ind2[j] = sp;
+      size_t sp = ind2[j] + NumSortedBytes;
+      if (sp >= BlockSize)
+          sp -= BlockSize;
+      ind2[j] = (UInt32)sp;
     }
 
     HeapSortRef(ind2, Groups, groupSize);
 
     /* Write Flags */
     {
-    UInt32 sp = ind2[0];
+    size_t sp = ind2[0];
     UInt32 group = Groups[sp];
 
-    #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
     UInt32 *Flags = Groups + BlockSize;
-    #else
-    UInt32 prevGroupStart = 0;
-    #endif
+#else
+    size_t prevGroupStart = 0;
+#endif
 
     for (j = 1; j < groupSize; j++)
     {
@@ -290,149 +341,210 @@
       if (Groups[sp] != group)
       {
         group = Groups[sp];
-        #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
         {
-        UInt32 t = groupOffset + j - 1;
+        const size_t t = groupOffset + j - 1;
         Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask));
         }
-        #else
+#else
         SetGroupSize(ind2 + prevGroupStart, j - prevGroupStart);
         prevGroupStart = j;
-        #endif
+#endif
       }
     }
 
-    #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
     SetGroupSize(ind2 + prevGroupStart, j - prevGroupStart);
-    #endif
+#endif
     }
     {
     /* Write new Groups values and Check that there are groups */
-    UInt32 thereAreGroups = 0;
+    unsigned thereAreGroups = 0;
     for (j = 0; j < groupSize; j++)
     {
-      UInt32 group = groupOffset + j;
-      #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+      size_t group = groupOffset + j;
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
       UInt32 subGroupSize = ((ind2[j] & ~0xC0000000) >> kNumBitsMax);
-      if ((ind2[j] & 0x40000000) != 0)
+      if (ind2[j] & 0x40000000)
         subGroupSize += ((ind2[(size_t)j + 1] >> kNumBitsMax) << kNumExtra0Bits);
       subGroupSize++;
       for (;;)
       {
-        UInt32 original = ind2[j];
-        UInt32 sp = original & kIndexMask;
-        if (sp < NumSortedBytes) sp += BlockSize; sp -= NumSortedBytes;
-        ind2[j] = sp | (original & ~kIndexMask);
-        Groups[sp] = group;
+        const UInt32 original = ind2[j];
+        size_t sp = original & kIndexMask;
+        if (sp < NumSortedBytes)
+          sp += BlockSize;
+        sp -= NumSortedBytes;
+        ind2[j] = (UInt32)sp | (original & ~kIndexMask);
+        Groups[sp] = (UInt32)group;
         if (--subGroupSize == 0)
           break;
         j++;
         thereAreGroups = 1;
       }
-      #else
+#else
       UInt32 *Flags = Groups + BlockSize;
       for (;;)
       {
-        UInt32 sp = ind2[j]; if (sp < NumSortedBytes) sp += BlockSize; sp -= NumSortedBytes;
-        ind2[j] = sp;
-        Groups[sp] = group;
+        size_t sp = ind2[j];
+        if (sp < NumSortedBytes)
+          sp += BlockSize;
+        sp -= NumSortedBytes;
+        ind2[j] = (UInt32)sp;
+        Groups[sp] = (UInt32)group;
         if ((Flags[(groupOffset + j) >> kNumFlagsBits] & (1 << ((groupOffset + j) & kFlagsMask))) == 0)
           break;
         j++;
         thereAreGroups = 1;
       }
-      #endif
+#endif
     }
     return thereAreGroups;
     }
   }
-  #endif
+#endif // BLOCK_SORT_USE_HEAP_SORT
 }
 
+
 /* conditions: blockSize > 0 */
-UInt32 BlockSort(UInt32 *Indices, const Byte *data, UInt32 blockSize)
+UInt32 BlockSort(UInt32 *Indices, const Byte *data, size_t blockSize)
 {
   UInt32 *counters = Indices + blockSize;
-  UInt32 i;
+  size_t i;
   UInt32 *Groups;
-  #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
   UInt32 *Flags;
-  #endif
+#endif
 
-  /* Radix-Sort for 2 bytes */
+/* Radix-Sort for 2 bytes */
+// { UInt32 yyy; for (yyy = 0; yyy < 100; yyy++) {
   for (i = 0; i < kNumHashValues; i++)
     counters[i] = 0;
-  for (i = 0; i < blockSize - 1; i++)
-    counters[((UInt32)data[i] << 8) | data[(size_t)i + 1]]++;
-  counters[((UInt32)data[i] << 8) | data[0]]++;
+  {
+    const Byte *data2 = data;
+    size_t a = data[(size_t)blockSize - 1];
+    const Byte *data_lim = data + blockSize;
+    if (blockSize >= 4)
+    {
+      data_lim -= 3;
+      do
+      {
+        size_t b;
+        b = data2[0]; counters[(a << 8) | b]++;
+        a = data2[1]; counters[(b << 8) | a]++;
+        b = data2[2]; counters[(a << 8) | b]++;
+        a = data2[3]; counters[(b << 8) | a]++;
+        data2 += 4;
+      }
+      while (data2 < data_lim);
+      data_lim += 3;
+    }
+    while (data2 != data_lim)
+    {
+      size_t b = *data2++;
+      counters[(a << 8) | b]++;
+      a = b;
+    }
+  }
+// }}
 
   Groups = counters + BS_TEMP_SIZE;
-  #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
   Flags = Groups + blockSize;
-    {
-      UInt32 numWords = (blockSize + kFlagsMask) >> kNumFlagsBits;
-      for (i = 0; i < numWords; i++)
-        Flags[i] = kAllFlags;
-    }
-  #endif
+  {
+    const size_t numWords = (blockSize + kFlagsMask) >> kNumFlagsBits;
+    for (i = 0; i < numWords; i++)
+      Flags[i] = kAllFlags;
+  }
+#endif
 
   {
     UInt32 sum = 0;
     for (i = 0; i < kNumHashValues; i++)
     {
-      UInt32 groupSize = counters[i];
-      if (groupSize > 0)
+      const UInt32 groupSize = counters[i];
+      counters[i] = sum;
+      sum += groupSize;
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
+      if (groupSize)
       {
-        #ifdef BLOCK_SORT_EXTERNAL_FLAGS
-        UInt32 t = sum + groupSize - 1;
-        Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask));
-        #endif
-        sum += groupSize;
+        const UInt32 t = sum - 1;
+        Flags[t >> kNumFlagsBits] &= ~((UInt32)1 << (t & kFlagsMask));
       }
-      counters[i] = sum - groupSize;
+#endif
     }
+  }
 
-    for (i = 0; i < blockSize - 1; i++)
-      Groups[i] = counters[((UInt32)data[i] << 8) | data[(size_t)i + 1]];
-    Groups[i] = counters[((UInt32)data[i] << 8) | data[0]];
+  for (i = 0; i < blockSize - 1; i++)
+    Groups[i] = counters[((unsigned)data[i] << 8) | data[(size_t)i + 1]];
+  Groups[i] = counters[((unsigned)data[i] << 8) | data[0]];
+  
+  {
+#define SET_Indices(a, b, i)  \
+    { UInt32 c; \
+      a = (a << 8) | (b); \
+      c = counters[a]; \
+      Indices[c] = (UInt32)i++; \
+      counters[a] = c + 1; \
+    }
 
-    for (i = 0; i < blockSize - 1; i++)
-      Indices[counters[((UInt32)data[i] << 8) | data[(size_t)i + 1]]++] = i;
-    Indices[counters[((UInt32)data[i] << 8) | data[0]]++] = i;
-    
-    #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+    size_t a = data[0];
+    const Byte *data_ptr = data + 1;
+    i = 0;
+    if (blockSize >= 3)
+    {
+      blockSize -= 2;
+      do
+      {
+        size_t b;
+        b = data_ptr[0];  SET_Indices(a, b, i)
+        a = data_ptr[1];  SET_Indices(b, a, i)
+        data_ptr += 2;
+      }
+      while (i < blockSize);
+      blockSize += 2;
+    }
+    if (i < blockSize - 1)
     {
+      SET_Indices(a, data[(size_t)i + 1], i)
+      a = (Byte)a;
+    }
+    SET_Indices(a, data[0], i)
+  }
+  
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
+  {
     UInt32 prev = 0;
     for (i = 0; i < kNumHashValues; i++)
     {
-      UInt32 prevGroupSize = counters[i] - prev;
+      const UInt32 prevGroupSize = counters[i] - prev;
       if (prevGroupSize == 0)
         continue;
       SetGroupSize(Indices + prev, prevGroupSize);
       prev = counters[i];
     }
-    }
-    #endif
   }
+#endif
 
   {
-  int NumRefBits;
-  UInt32 NumSortedBytes;
-  for (NumRefBits = 0; ((blockSize - 1) >> NumRefBits) != 0; NumRefBits++);
+  unsigned NumRefBits;
+  size_t NumSortedBytes;
+  for (NumRefBits = 0; ((blockSize - 1) >> NumRefBits) != 0; NumRefBits++)
+  {}
   NumRefBits = 32 - NumRefBits;
   if (NumRefBits > kNumRefBitsMax)
-    NumRefBits = kNumRefBitsMax;
+      NumRefBits = kNumRefBitsMax;
 
   for (NumSortedBytes = kNumHashBytes; ; NumSortedBytes <<= 1)
   {
-    #ifndef BLOCK_SORT_EXTERNAL_FLAGS
-    UInt32 finishedGroupSize = 0;
-    #endif
-    UInt32 newLimit = 0;
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
+    size_t finishedGroupSize = 0;
+#endif
+    size_t newLimit = 0;
     for (i = 0; i < blockSize;)
     {
-      UInt32 groupSize;
-      #ifdef BLOCK_SORT_EXTERNAL_FLAGS
+      size_t groupSize;
+#ifdef BLOCK_SORT_EXTERNAL_FLAGS
 
       if ((Flags[i >> kNumFlagsBits] & (1 << (i & kFlagsMask))) == 0)
       {
@@ -441,56 +553,56 @@
       }
       for (groupSize = 1;
         (Flags[(i + groupSize) >> kNumFlagsBits] & (1 << ((i + groupSize) & kFlagsMask))) != 0;
-        groupSize++);
-      
+        groupSize++)
+        {}
       groupSize++;
 
-      #else
+#else
 
-      groupSize = ((Indices[i] & ~0xC0000000) >> kNumBitsMax);
+      groupSize = (Indices[i] & ~0xC0000000) >> kNumBitsMax;
       {
-      BoolInt finishedGroup = ((Indices[i] & 0x80000000) == 0);
-      if ((Indices[i] & 0x40000000) != 0)
-      {
-        groupSize += ((Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits);
-        Indices[(size_t)i + 1] &= kIndexMask;
-      }
-      Indices[i] &= kIndexMask;
-      groupSize++;
-      if (finishedGroup || groupSize == 1)
-      {
-        Indices[i - finishedGroupSize] &= kIndexMask;
-        if (finishedGroupSize > 1)
-          Indices[(size_t)(i - finishedGroupSize) + 1] &= kIndexMask;
+        const BoolInt finishedGroup = ((Indices[i] & 0x80000000) == 0);
+        if (Indices[i] & 0x40000000)
         {
-        UInt32 newGroupSize = groupSize + finishedGroupSize;
-        SetFinishedGroupSize(Indices + i - finishedGroupSize, newGroupSize)
-        finishedGroupSize = newGroupSize;
+          groupSize += ((Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits);
+          Indices[(size_t)i + 1] &= kIndexMask;
         }
-        i += groupSize;
-        continue;
-      }
-      finishedGroupSize = 0;
+        Indices[i] &= kIndexMask;
+        groupSize++;
+        if (finishedGroup || groupSize == 1)
+        {
+          Indices[i - finishedGroupSize] &= kIndexMask;
+          if (finishedGroupSize > 1)
+            Indices[(size_t)(i - finishedGroupSize) + 1] &= kIndexMask;
+          {
+            const size_t newGroupSize = groupSize + finishedGroupSize;
+            SetFinishedGroupSize(Indices + i - finishedGroupSize, newGroupSize)
+            finishedGroupSize = newGroupSize;
+          }
+          i += groupSize;
+          continue;
+        }
+        finishedGroupSize = 0;
       }
 
-      #endif
+#endif
       
       if (NumSortedBytes >= blockSize)
       {
-        UInt32 j;
+        size_t j;
         for (j = 0; j < groupSize; j++)
         {
-          UInt32 t = (i + j);
+          size_t t = i + j;
           /* Flags[t >> kNumFlagsBits] &= ~(1 << (t & kFlagsMask)); */
-          Groups[Indices[t]] = t;
+          Groups[Indices[t]] = (UInt32)t;
         }
       }
       else
         if (SortGroup(blockSize, NumSortedBytes, i, groupSize, NumRefBits, Indices
-          #ifndef BLOCK_SORT_USE_HEAP_SORT
-          , 0, blockSize
-          #endif
-          ) != 0)
+            #ifndef BLOCK_SORT_USE_HEAP_SORT
+              , 0, blockSize
+            #endif
+            ))
           newLimit = i + groupSize;
       i += groupSize;
     }
@@ -498,19 +610,19 @@
       break;
   }
   }
-  #ifndef BLOCK_SORT_EXTERNAL_FLAGS
+#ifndef BLOCK_SORT_EXTERNAL_FLAGS
   for (i = 0; i < blockSize;)
   {
-    UInt32 groupSize = ((Indices[i] & ~0xC0000000) >> kNumBitsMax);
-    if ((Indices[i] & 0x40000000) != 0)
+    size_t groupSize = (Indices[i] & ~0xC0000000) >> kNumBitsMax;
+    if (Indices[i] & 0x40000000)
     {
-      groupSize += ((Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits);
+      groupSize += (Indices[(size_t)i + 1] >> kNumBitsMax) << kNumExtra0Bits;
       Indices[(size_t)i + 1] &= kIndexMask;
     }
     Indices[i] &= kIndexMask;
     groupSize++;
     i += groupSize;
   }
-  #endif
+#endif
   return Groups[0];
 }
diff -Nru 7zip-24.09+dfsg/C/BwtSort.h 7zip-25.00+dfsg/C/BwtSort.h
--- 7zip-24.09+dfsg/C/BwtSort.h	2023-03-03 11:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/BwtSort.h	2024-12-18 19:00:00.000000000 +0100
@@ -1,5 +1,5 @@
 /* BwtSort.h -- BWT block sorting
-2023-03-03 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_BWT_SORT_H
 #define ZIP7_INC_BWT_SORT_H
@@ -10,16 +10,17 @@
 
 /* use BLOCK_SORT_EXTERNAL_FLAGS if blockSize can be > 1M */
 /* #define BLOCK_SORT_EXTERNAL_FLAGS */
+// #define BLOCK_SORT_EXTERNAL_FLAGS
 
 #ifdef BLOCK_SORT_EXTERNAL_FLAGS
-#define BLOCK_SORT_EXTERNAL_SIZE(blockSize) ((((blockSize) + 31) >> 5))
+#define BLOCK_SORT_EXTERNAL_SIZE(blockSize) (((blockSize) + 31) >> 5)
 #else
 #define BLOCK_SORT_EXTERNAL_SIZE(blockSize) 0
 #endif
 
 #define BLOCK_SORT_BUF_SIZE(blockSize) ((blockSize) * 2 + BLOCK_SORT_EXTERNAL_SIZE(blockSize) + (1 << 16))
 
-UInt32 BlockSort(UInt32 *indices, const Byte *data, UInt32 blockSize);
+UInt32 BlockSort(UInt32 *indices, const Byte *data, size_t blockSize);
 
 EXTERN_C_END
 
diff -Nru 7zip-24.09+dfsg/C/Compiler.h 7zip-25.00+dfsg/C/Compiler.h
--- 7zip-24.09+dfsg/C/Compiler.h	2024-03-01 16:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/Compiler.h	2025-06-30 19:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* Compiler.h : Compiler specific defines and pragmas
-2024-01-22 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_COMPILER_H
 #define ZIP7_INC_COMPILER_H
@@ -183,6 +183,16 @@
   #define Z7_ATTRIB_NO_VECTORIZE
 #endif
 
+#if defined(Z7_MSC_VER_ORIGINAL) && (Z7_MSC_VER_ORIGINAL >= 1920)
+  #define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE _Pragma("optimize ( \"s\", on )")
+  #define Z7_PRAGMA_OPTIMIZE_DEFAULT       _Pragma("optimize ( \"\", on )")
+#else
+  #define Z7_PRAGMA_OPTIMIZE_FOR_CODE_SIZE
+  #define Z7_PRAGMA_OPTIMIZE_DEFAULT
+#endif
+
+
+
 #if defined(MY_CPU_X86_OR_AMD64) && ( \
        defined(__clang__) && (__clang_major__ >= 4) \
     || defined(__GNUC__) && (__GNUC__ >= 5))
diff -Nru 7zip-24.09+dfsg/C/CpuArch.h 7zip-25.00+dfsg/C/CpuArch.h
--- 7zip-24.09+dfsg/C/CpuArch.h	2024-11-13 09:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/CpuArch.h	2025-04-05 10:00:00.000000000 +0200
@@ -47,6 +47,12 @@
   #define MY_CPU_SIZEOF_POINTER 4
 #endif
 
+#if defined(__SSE2__) \
+    || defined(MY_CPU_AMD64) \
+    || defined(_M_IX86_FP) && (_M_IX86_FP >= 2)
+#define MY_CPU_SSE2
+#endif
+
 
 #if  defined(_M_ARM64) \
   || defined(_M_ARM64EC) \
@@ -571,10 +577,12 @@
 #define Z7_CONV_BE_TO_NATIVE_CONST32(v)  (v)
 #define Z7_CONV_LE_TO_NATIVE_CONST32(v)  Z7_BSWAP32_CONST(v)
 #define Z7_CONV_NATIVE_TO_BE_32(v)       (v)
+// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1)  ((b1) | ((b0) << 8))
 #elif defined(MY_CPU_LE)
 #define Z7_CONV_BE_TO_NATIVE_CONST32(v)  Z7_BSWAP32_CONST(v)
 #define Z7_CONV_LE_TO_NATIVE_CONST32(v)  (v)
 #define Z7_CONV_NATIVE_TO_BE_32(v)       Z7_BSWAP32(v)
+// #define Z7_GET_NATIVE16_FROM_2_BYTES(b0, b1)  ((b0) | ((b1) << 8))
 #else
 #error Stop_Compiling_Unknown_Endian_CONV
 #endif
diff -Nru 7zip-24.09+dfsg/C/HuffEnc.c 7zip-25.00+dfsg/C/HuffEnc.c
--- 7zip-24.09+dfsg/C/HuffEnc.c	2023-09-07 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/HuffEnc.c	2025-03-20 15:00:00.000000000 +0100
@@ -1,60 +1,125 @@
 /* HuffEnc.c -- functions for Huffman encoding
-2023-09-07 : Igor Pavlov : Public domain */
+Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
+#include <string.h>
+
 #include "HuffEnc.h"
 #include "Sort.h"
+#include "CpuArch.h"
 
-#define kMaxLen 16
-#define NUM_BITS 10
-#define MASK ((1u << NUM_BITS) - 1)
-
-#define NUM_COUNTERS 64
-
-#define HUFFMAN_SPEED_OPT
+#define kMaxLen       Z7_HUFFMAN_LEN_MAX
+#define NUM_BITS      10
+#define MASK          ((1u << NUM_BITS) - 1)
+#define FREQ_MASK     (~(UInt32)MASK)
+#define NUM_COUNTERS  (48 * 2)
+
+#if 1 && (defined(MY_CPU_LE) || defined(MY_CPU_BE))
+#if defined(MY_CPU_LE)
+  #define HI_HALF_OFFSET 1
+#else
+  #define HI_HALF_OFFSET 0
+#endif
+#define LOAD_PARENT(p)                  ((unsigned)*((const UInt16 *)(p) + HI_HALF_OFFSET))
+#define STORE_PARENT(p, fb, val)         *((UInt16 *)(p) + HI_HALF_OFFSET) = (UInt16)(val);
+#define STORE_PARENT_DIRECT(p, fb, hi)  STORE_PARENT(p, fb, hi)
+#define UPDATE_E(eHi)                   eHi++;
+#else
+#define LOAD_PARENT(p)                  ((unsigned)(*(p) >> NUM_BITS))
+#define STORE_PARENT_DIRECT(p, fb, hi)  *(p) = ((fb) & MASK) | (hi); // set parent field
+#define STORE_PARENT(p, fb, val)        STORE_PARENT_DIRECT(p, fb, ((UInt32)(val) << NUM_BITS))
+#define UPDATE_E(eHi)                   eHi += 1 << NUM_BITS;
+#endif
 
-void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, UInt32 numSymbols, UInt32 maxLen)
+void Huffman_Generate(const UInt32 *freqs, UInt32 *p, Byte *lens, unsigned numSymbols, unsigned maxLen)
 {
-  UInt32 num = 0;
-  /* if (maxLen > 10) maxLen = 10; */
+#if NUM_COUNTERS > 2
+  unsigned counters[NUM_COUNTERS];
+#endif
+#if 1 && NUM_COUNTERS > (kMaxLen + 4) * 2
+  #define lenCounters   (counters)
+  #define codes         (counters + kMaxLen + 4)
+#else
+  unsigned lenCounters[kMaxLen + 1];
+  UInt32 codes[kMaxLen + 1];
+#endif
+
+  unsigned num;
   {
-    UInt32 i;
-    
-    #ifdef HUFFMAN_SPEED_OPT
+    unsigned i;
+    // UInt32 sum = 0;
+
+#if NUM_COUNTERS > 2
     
-    UInt32 counters[NUM_COUNTERS];
+#define CTR_ITEM_FOR_FREQ(freq) \
+    counters[(freq) >= NUM_COUNTERS - 1 ? NUM_COUNTERS - 1 : (unsigned)(freq)]
+
     for (i = 0; i < NUM_COUNTERS; i++)
       counters[i] = 0;
-    for (i = 0; i < numSymbols; i++)
+    memset(lens, 0, numSymbols);
     {
-      UInt32 freq = freqs[i];
-      counters[(freq < NUM_COUNTERS - 1) ? freq : NUM_COUNTERS - 1]++;
+      const UInt32 *fp = freqs + numSymbols;
+#define NUM_UNROLLS 1
+#if NUM_UNROLLS > 1 // use 1 if odd (numSymbols) is possisble
+      if (numSymbols & 1)
+      {
+        UInt32 f;
+        f = *--fp;  CTR_ITEM_FOR_FREQ(f)++;
+        // sum += f;
+      }
+#endif
+      do
+      {
+        UInt32 f;
+        fp -= NUM_UNROLLS;
+        f = fp[0];  CTR_ITEM_FOR_FREQ(f)++;
+        // sum += f;
+#if NUM_UNROLLS > 1
+        f = fp[1];  CTR_ITEM_FOR_FREQ(f)++;
+        // sum += f;
+#endif
+      }
+      while (fp != freqs);
     }
- 
-    for (i = 1; i < NUM_COUNTERS; i++)
+#if 0
+    printf("\nsum=%8u numSymbols =%3u ctrs:", sum, numSymbols);
     {
-      UInt32 temp = counters[i];
-      counters[i] = num;
-      num += temp;
+      unsigned k = 0;
+      for (k = 0; k < NUM_COUNTERS; k++)
+        printf(" %u", counters[k]);
     }
-
-    for (i = 0; i < numSymbols; i++)
+#endif
+        
+    num = counters[1];
+    counters[1] = 0;
+    for (i = 2; i != NUM_COUNTERS; i += 2)
     {
-      UInt32 freq = freqs[i];
-      if (freq == 0)
-        lens[i] = 0;
-      else
-        p[counters[((freq < NUM_COUNTERS - 1) ? freq : NUM_COUNTERS - 1)]++] = i | (freq << NUM_BITS);
+      unsigned c;
+      c = (counters    )[i];  (counters    )[i] = num;  num += c;
+      c = (counters + 1)[i];  (counters + 1)[i] = num;  num += c;
+    }
+    counters[0] = num; // we want to write (freq==0) symbols to the end of (p) array
+    {
+      i = 0;
+      do
+      {
+        const UInt32 f = freqs[i];
+#if 0
+        if (f == 0) lens[i] = 0; else
+#endif
+          p[CTR_ITEM_FOR_FREQ(f)++] = i | (f << NUM_BITS);
+      }
+      while (++i != numSymbols);
     }
-    counters[0] = 0;
     HeapSort(p + counters[NUM_COUNTERS - 2], counters[NUM_COUNTERS - 1] - counters[NUM_COUNTERS - 2]);
     
-    #else
-
+#else // NUM_COUNTERS <= 2
+    
+    num = 0;
     for (i = 0; i < numSymbols; i++)
     {
-      UInt32 freq = freqs[i];
+      const UInt32 freq = freqs[i];
       if (freq == 0)
         lens[i] = 0;
       else
@@ -62,17 +127,27 @@
     }
     HeapSort(p, num);
 
-    #endif
+#endif
   }
 
-  if (num < 2)
+  if (num <= 2)
   {
     unsigned minCode = 0;
     unsigned maxCode = 1;
-    if (num == 1)
+    if (num)
     {
-      maxCode = (unsigned)p[0] & MASK;
-      if (maxCode == 0)
+      maxCode = (unsigned)p[(size_t)num - 1] & MASK;
+      if (num == 2)
+      {
+        minCode = (unsigned)p[0] & MASK;
+        if (minCode > maxCode)
+        {
+          const unsigned temp = minCode;
+          minCode = maxCode;
+          maxCode = temp;
+        }
+      }
+      else if (maxCode == 0)
         maxCode++;
     }
     p[minCode] = 0;
@@ -80,69 +155,191 @@
     lens[minCode] = lens[maxCode] = 1;
     return;
   }
-  
   {
-    UInt32 b, e, i;
-  
-    i = b = e = 0;
-    do
-    {
-      UInt32 n, m, freq;
-      n = (i != num && (b == e || (p[i] >> NUM_BITS) <= (p[b] >> NUM_BITS))) ? i++ : b++;
-      freq = (p[n] & ~MASK);
-      p[n] = (p[n] & MASK) | (e << NUM_BITS);
-      m = (i != num && (b == e || (p[i] >> NUM_BITS) <= (p[b] >> NUM_BITS))) ? i++ : b++;
-      freq += (p[m] & ~MASK);
-      p[m] = (p[m] & MASK) | (e << NUM_BITS);
-      p[e] = (p[e] & MASK) | freq;
-      e++;
-    }
-    while (num - e > 1);
-    
+    unsigned i;
+    for (i = 0; i <= kMaxLen; i++)
+      lenCounters[i] = 0;
+    lenCounters[1] = 2;  // by default root node has 2 child leaves at level 1.
+  }
+  // if (num != 2)
+  {
+    // num > 2
+    // the binary tree will contain (num - 1) internal nodes.
+    // p[num - 2] will be root node of binary tree.
+    UInt32 *b;
+    UInt32 *n;
+    // first node will have two leaf childs: p[0] and p[1]:
+    // p[0] += p[1] & FREQ_MASK; // set frequency sum of child leafs
+    // if (pi == n) exit(0);
+    // if (pi != n)
     {
-      UInt32 lenCounters[kMaxLen + 1];
-      for (i = 0; i <= kMaxLen; i++)
-        lenCounters[i] = 0;
-      
-      p[--e] &= MASK;
-      lenCounters[1] = 2;
-      while (e != 0)
-      {
-        UInt32 len = (p[p[--e] >> NUM_BITS] >> NUM_BITS) + 1;
-        p[e] = (p[e] & MASK) | (len << NUM_BITS);
-        if (len >= maxLen)
-          for (len = maxLen - 1; lenCounters[len] == 0; len--);
-        lenCounters[len]--;
-        lenCounters[(size_t)len + 1] += 2;
-      }
-      
+      UInt32 fb = (p[1] & FREQ_MASK) + p[0];
+      UInt32 f = p[2] & FREQ_MASK;
+      const UInt32 *pi = p + 2;
+      UInt32 *e = p;
+      UInt32 eHi = 0;
+      n = p + num;
+      b = p;
+      // p[0] = fb;
+      for (;;)
       {
-        UInt32 len;
-        i = 0;
-        for (len = maxLen; len != 0; len--)
+        // (b <= e)
+        UInt32 sum;
+        e++;
+        UPDATE_E(eHi)
+
+        // (b < e)
+
+        // p range : high bits
+        // [0, b)  : parent :     processed nodes that have parent and childs
+        // [b, e)  : FREQ   : non-processed nodes that have no parent but have childs
+        // [e, pi) : FREQ   :     processed leaves for which parent node was     created
+        // [pi, n) : FREQ   : non-processed leaves for which parent node was not created
+
+        // first child
+        // note : (*b < f) is same result as ((*b & FREQ_MASK) < f)
+        if (fb < f)
+        {
+          // node freq is smaller
+          sum = fb & FREQ_MASK;
+          STORE_PARENT_DIRECT (b, fb, eHi)
+          b++;
+          fb = *b;
+          if (b == e)
+          {
+            if (++pi == n)
+              break;
+            sum += f;
+            fb &= MASK;
+            fb |= sum;
+            *e = fb;
+            f = *pi & FREQ_MASK;
+            continue;
+          }
+        }
+        else if (++pi == n)
+        {
+          STORE_PARENT_DIRECT (b, fb, eHi)
+          b++;
+          break;
+        }
+        else
+        {
+          sum = f;
+          f = *pi & FREQ_MASK;
+        }
+
+        // (b < e)
+
+        // second child
+        if (fb < f)
         {
-          UInt32 k;
-          for (k = lenCounters[len]; k != 0; k--)
-            lens[p[i++] & MASK] = (Byte)len;
+          sum += fb;
+          sum &= FREQ_MASK;
+          STORE_PARENT_DIRECT (b, fb, eHi)
+          b++;
+          *e = (*e & MASK) | sum; // set frequency sum
+          // (b <= e) is possible here
+          fb = *b;
+        }
+        else if (++pi == n)
+          break;
+        else
+        {
+          sum += f;
+          f = *pi & FREQ_MASK;
+          *e = (*e & MASK) | sum; // set frequency sum
         }
       }
-      
+    }
+    
+    // printf("\nnum-e=%3u, numSymbols=%3u, num=%3u, b=%3u", n - e, numSymbols, n - p, b - p);
+    {
+      n -= 2;
+      *n &= MASK; // root node : we clear high bits (zero bits mean level == 0)
+      if (n != b)
       {
-        UInt32 nextCodes[kMaxLen + 1];
+        // We go here, if we have some number of non-created nodes up to root.
+        // We process them in simplified code:
+        // position of parent for each pair of nodes is known.
+        // n[-2], n[-1] : current pair of child nodes
+        // (p1) : parent node for current pair.
+        UInt32 *p1 = n;
+        do
         {
-          UInt32 code = 0;
-          UInt32 len;
-          for (len = 1; len <= kMaxLen; len++)
-            nextCodes[len] = code = (code + lenCounters[(size_t)len - 1]) << 1;
+          const unsigned len = LOAD_PARENT(p1) + 1;
+          p1--;
+          (lenCounters    )[len] -= 2;                  // we remove 2 leaves from level (len)
+          (lenCounters + 1)[len] += 2 * 2;  // we add 4 leaves at level (len + 1)
+          n -= 2;
+          STORE_PARENT (n    , n[0], len)
+          STORE_PARENT (n + 1, n[1], len)
         }
-        /* if (code + lenCounters[kMaxLen] - 1 != (1 << kMaxLen) - 1) throw 1; */
-
+        while (n != b);
+      }
+    }
+    
+    if (b != p)
+    {
+      // we detect level of each node (realtive to root),
+      // and update lenCounters[].
+      // We process only intermediate nodes and we don't process leaves.
+      do
+      {
+        // if (ii <  b) : parent_bits_of (p[ii]) == index of parent node : ii < (p[ii])
+        // if (ii >= b) : parent_bits_of (p[ii]) == level of this (ii) node in tree
+        unsigned len;
+        b--;
+        len = (unsigned)LOAD_PARENT(p + LOAD_PARENT(b)) + 1;
+        STORE_PARENT (b, *b, len)
+        if (len >= maxLen)
         {
-          UInt32 k;
-          for (k = 0; k < numSymbols; k++)
-            p[k] = nextCodes[lens[k]]++;
+          // We are not allowed to create node at level (maxLen) and higher,
+          // because all leaves must be placed to level (maxLen) or lower.
+          // We find nearest allowed leaf and place current node to level of that leaf:
+          for (len = maxLen - 1; lenCounters[len] == 0; len--) {}
         }
+        lenCounters[len]--;                 // we remove 1 leaf from level (len)
+        (lenCounters + 1)[len] += 2;  // we add 2 leaves at level (len + 1)
+      }
+      while (b != p);
+    }
+  }
+  {
+    {
+      unsigned len = maxLen;
+      const UInt32 *p2 = p;
+      do
+      {
+        unsigned k = lenCounters[len];
+        if (k)
+          do
+            lens[(unsigned)*p2++ & MASK] = (Byte)len;
+          while (--k);
+      }
+      while (--len);
+    }
+    codes[0] = 0; // we don't want garbage values to be written to p[] array.
+    // codes[1] = 0;
+    {
+      UInt32 code = 0;
+      unsigned len;
+      for (len = 0; len < kMaxLen; len++)
+        (codes + 1)[len] = code = (code + lenCounters[len]) << 1;
+    }
+    /* if (code + lenCounters[kMaxLen] - 1 != (1 << kMaxLen) - 1) throw 1; */
+    {
+      const Byte * const limit = lens + numSymbols;
+      do
+      {
+        unsigned len;
+        UInt32 c;
+        len = lens[0];  c = codes[len];  p[0] = c;  codes[len] = c + 1;
+        // len = lens[1];  c = codes[len];  p[1] = c;  codes[len] = c + 1;
+        p += 1;
+        lens += 1;
       }
+      while (lens != limit);
     }
   }
 }
@@ -150,5 +347,14 @@
 #undef kMaxLen
 #undef NUM_BITS
 #undef MASK
+#undef FREQ_MASK
 #undef NUM_COUNTERS
-#undef HUFFMAN_SPEED_OPT
+#undef CTR_ITEM_FOR_FREQ
+#undef LOAD_PARENT
+#undef STORE_PARENT
+#undef STORE_PARENT_DIRECT
+#undef UPDATE_E
+#undef HI_HALF_OFFSET
+#undef NUM_UNROLLS
+#undef lenCounters
+#undef codes
diff -Nru 7zip-24.09+dfsg/C/HuffEnc.h 7zip-25.00+dfsg/C/HuffEnc.h
--- 7zip-24.09+dfsg/C/HuffEnc.h	2023-03-05 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/HuffEnc.h	2025-03-20 15:00:00.000000000 +0100
@@ -1,5 +1,5 @@
 /* HuffEnc.h -- Huffman encoding
-2023-03-05 : Igor Pavlov : Public domain */
+Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_HUFF_ENC_H
 #define ZIP7_INC_HUFF_ENC_H
@@ -8,14 +8,14 @@
 
 EXTERN_C_BEGIN
 
+#define Z7_HUFFMAN_LEN_MAX 16
 /*
 Conditions:
-  num <= 1024 = 2 ^ NUM_BITS
+  2 <= num <= 1024 = 2 ^ NUM_BITS
   Sum(freqs) < 4M = 2 ^ (32 - NUM_BITS)
-  maxLen <= 16 = kMaxLen
+  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);
 
 EXTERN_C_END
diff -Nru 7zip-24.09+dfsg/C/LzFind.c 7zip-25.00+dfsg/C/LzFind.c
--- 7zip-24.09+dfsg/C/LzFind.c	2024-03-01 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/LzFind.c	2025-05-19 08:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* LzFind.c -- Match finder for LZ algorithms
-2024-03-01 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -404,7 +404,7 @@
         const unsigned nbMax =
             (p->numHashBytes == 2 ? 16 :
             (p->numHashBytes == 3 ? 24 : 32));
-        if (numBits > nbMax)
+        if (numBits >= nbMax)
           numBits = nbMax;
         if (numBits >= 32)
           hs = (UInt32)0 - 1;
@@ -416,14 +416,14 @@
           hs |= (256 << kLzHash_CrcShift_2) - 1;
         {
           const UInt32 hs2 = MatchFinder_GetHashMask2(p, historySize);
-          if (hs > hs2)
+          if (hs >= hs2)
             hs = hs2;
         }
         hsCur = hs;
         if (p->expectedDataSize < historySize)
         {
           const UInt32 hs2 = MatchFinder_GetHashMask2(p, (UInt32)p->expectedDataSize);
-          if (hsCur > hs2)
+          if (hsCur >= hs2)
             hsCur = hs2;
         }
       }
@@ -434,7 +434,7 @@
         if (p->expectedDataSize < historySize)
         {
           hsCur = MatchFinder_GetHashMask(p, (UInt32)p->expectedDataSize);
-          if (hsCur > hs) // is it possible?
+          if (hsCur >= hs) // is it possible?
             hsCur = hs;
         }
       }
@@ -890,7 +890,7 @@
       return d;
     {
       const Byte *pb = cur - delta;
-      curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+      curMatch = son[_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)];
       if (pb[maxLen] == cur[maxLen] && *pb == *cur)
       {
         UInt32 len = 0;
@@ -925,7 +925,7 @@
       break;
     {
       ptrdiff_t diff;
-      curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+      curMatch = son[_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)];
       diff = (ptrdiff_t)0 - (ptrdiff_t)delta;
       if (cur[maxLen] == cur[(ptrdiff_t)maxLen + diff])
       {
@@ -972,7 +972,7 @@
   // if (curMatch >= pos) { *ptr0 = *ptr1 = kEmptyHashValue; return NULL; }
 
   cmCheck = (UInt32)(pos - _cyclicBufferSize);
-  if ((UInt32)pos <= _cyclicBufferSize)
+  if ((UInt32)pos < _cyclicBufferSize)
     cmCheck = 0;
 
   if (cmCheck < curMatch)
@@ -980,7 +980,7 @@
   {
     const UInt32 delta = pos - curMatch;
     {
-      CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+      CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)) << 1);
       const Byte *pb = cur - delta;
       unsigned len = (len0 < len1 ? len0 : len1);
       const UInt32 pair0 = pair[0];
@@ -1039,7 +1039,7 @@
   UInt32 cmCheck;
 
   cmCheck = (UInt32)(pos - _cyclicBufferSize);
-  if ((UInt32)pos <= _cyclicBufferSize)
+  if ((UInt32)pos < _cyclicBufferSize)
     cmCheck = 0;
 
   if (// curMatch >= pos ||  // failure
@@ -1048,7 +1048,7 @@
   {
     const UInt32 delta = pos - curMatch;
     {
-      CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+      CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + (_cyclicBufferPos < delta ? _cyclicBufferSize : 0)) << 1);
       const Byte *pb = cur - delta;
       unsigned len = (len0 < len1 ? len0 : len1);
       if (pb[len] == cur[len])
@@ -1595,7 +1595,7 @@
     UInt32 pos = p->pos; \
     UInt32 num2 = num; \
     /* (p->pos == p->posLimit) is not allowed here !!! */ \
-    { const UInt32 rem = p->posLimit - pos; if (num2 > rem) num2 = rem; } \
+    { const UInt32 rem = p->posLimit - pos; if (num2 >= rem) num2 = rem; } \
     num -= num2; \
     { const UInt32 cycPos = p->cyclicBufferPos; \
       son = p->son + cycPos; \
diff -Nru 7zip-24.09+dfsg/C/LzFindMt.c 7zip-25.00+dfsg/C/LzFindMt.c
--- 7zip-24.09+dfsg/C/LzFindMt.c	2024-01-22 15:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/LzFindMt.c	2025-06-30 18:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* LzFindMt.c -- multithreaded Match finder for LZ algorithms
-2024-01-22 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -82,6 +82,8 @@
 Z7_NO_INLINE
 static void MtSync_Construct(CMtSync *p)
 {
+  p->affinityGroup = -1;
+  p->affinityInGroup = 0;
   p->affinity = 0;
   p->wasCreated = False;
   p->csWasInitialized = False;
@@ -259,6 +261,12 @@
   // return ERROR_TOO_MANY_POSTS; // for debug
   // return EINVAL; // for debug
 
+#ifdef _WIN32
+  if (p->affinityGroup >= 0)
+    wres = Thread_Create_With_Group(&p->thread, startAddress, obj,
+        (unsigned)(UInt32)p->affinityGroup, (CAffinityMask)p->affinityInGroup);
+  else
+#endif
   if (p->affinity != 0)
     wres = Thread_Create_With_Affinity(&p->thread, startAddress, obj, (CAffinityMask)p->affinity);
   else
diff -Nru 7zip-24.09+dfsg/C/LzFindMt.h 7zip-25.00+dfsg/C/LzFindMt.h
--- 7zip-24.09+dfsg/C/LzFindMt.h	2024-01-22 15:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/LzFindMt.h	2025-05-10 09:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* LzFindMt.h -- multithreaded Match finder for LZ algorithms
-2024-01-22 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_LZ_FIND_MT_H
 #define ZIP7_INC_LZ_FIND_MT_H
@@ -12,8 +12,10 @@
 typedef struct
 {
   UInt32 numProcessedBlocks;
-  CThread thread;
+  Int32 affinityGroup;
+  UInt64 affinityInGroup;
   UInt64 affinity;
+  CThread thread;
 
   BoolInt wasCreated;
   BoolInt needStart;
diff -Nru 7zip-24.09+dfsg/C/Lzma2Enc.c 7zip-25.00+dfsg/C/Lzma2Enc.c
--- 7zip-24.09+dfsg/C/Lzma2Enc.c	2023-04-13 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/Lzma2Enc.c	2025-06-30 19:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* Lzma2Enc.c -- LZMA2 Encoder
-2023-04-13 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -235,6 +235,7 @@
   p->numBlockThreads_Reduced = -1;
   p->numBlockThreads_Max = -1;
   p->numTotalThreads = -1;
+  p->numThreadGroups = 0;
 }
 
 void Lzma2EncProps_Normalize(CLzma2EncProps *p)
@@ -781,6 +782,7 @@
     }
 
     p->mtCoder.numThreadsMax = (unsigned)p->props.numBlockThreads_Max;
+    p->mtCoder.numThreadGroups = p->props.numThreadGroups;
     p->mtCoder.expectedDataSize = p->expectedDataSize;
     
     {
diff -Nru 7zip-24.09+dfsg/C/Lzma2Enc.h 7zip-25.00+dfsg/C/Lzma2Enc.h
--- 7zip-24.09+dfsg/C/Lzma2Enc.h	2023-04-13 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/Lzma2Enc.h	2025-06-28 14:00:00.000000000 +0200
@@ -18,6 +18,7 @@
   int numBlockThreads_Reduced;
   int numBlockThreads_Max;
   int numTotalThreads;
+  unsigned numThreadGroups; // 0 : no groups
 } CLzma2EncProps;
 
 void Lzma2EncProps_Init(CLzma2EncProps *p);
diff -Nru 7zip-24.09+dfsg/C/LzmaEnc.c 7zip-25.00+dfsg/C/LzmaEnc.c
--- 7zip-24.09+dfsg/C/LzmaEnc.c	2024-11-12 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/LzmaEnc.c	2025-05-11 16:00:00.000000000 +0200
@@ -62,7 +62,9 @@
   p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1;
   p->numHashOutBits = 0;
   p->writeEndMark = 0;
+  p->affinityGroup = -1;
   p->affinity = 0;
+  p->affinityInGroup = 0;
 }
 
 void LzmaEncProps_Normalize(CLzmaEncProps *p)
@@ -598,6 +600,10 @@
   p->multiThread = (props.numThreads > 1);
   p->matchFinderMt.btSync.affinity =
   p->matchFinderMt.hashSync.affinity = props.affinity;
+  p->matchFinderMt.btSync.affinityGroup =
+  p->matchFinderMt.hashSync.affinityGroup = props.affinityGroup;
+  p->matchFinderMt.btSync.affinityInGroup =
+  p->matchFinderMt.hashSync.affinityInGroup = props.affinityInGroup;
   #endif
 
   return SZ_OK;
diff -Nru 7zip-24.09+dfsg/C/LzmaEnc.h 7zip-25.00+dfsg/C/LzmaEnc.h
--- 7zip-24.09+dfsg/C/LzmaEnc.h	2023-04-13 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/LzmaEnc.h	2025-05-10 09:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /*  LzmaEnc.h -- LZMA Encoder
-2023-04-13 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_LZMA_ENC_H
 #define ZIP7_INC_LZMA_ENC_H
@@ -29,11 +29,13 @@
   int numThreads;  /* 1 or 2, default = 2 */
 
   // int _pad;
+  Int32 affinityGroup;
 
   UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1.
                         Encoder uses this value to reduce dictionary size */
 
   UInt64 affinity;
+  UInt64 affinityInGroup;
 } CLzmaEncProps;
 
 void LzmaEncProps_Init(CLzmaEncProps *p);
diff -Nru 7zip-24.09+dfsg/C/MtCoder.c 7zip-25.00+dfsg/C/MtCoder.c
--- 7zip-24.09+dfsg/C/MtCoder.c	2023-09-07 15:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/MtCoder.c	2025-07-04 08:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* MtCoder.c -- Multi-thread Coder
-2023-09-07 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -39,14 +39,28 @@
 static THREAD_FUNC_DECL ThreadFunc(void *pp);
 
 
-static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t)
+static SRes MtCoderThread_CreateAndStart(CMtCoderThread *t
+#ifdef _WIN32
+    , CMtCoder * const mtc
+#endif
+    )
 {
   WRes wres = AutoResetEvent_OptCreate_And_Reset(&t->startEvent);
+  // printf("\n====== MtCoderThread_CreateAndStart : \n");
   if (wres == 0)
   {
     t->stop = False;
     if (!Thread_WasCreated(&t->thread))
-      wres = Thread_Create(&t->thread, ThreadFunc, t);
+    {
+#ifdef _WIN32
+      if (mtc->numThreadGroups)
+        wres = Thread_Create_With_Group(&t->thread, ThreadFunc, t,
+            ThreadNextGroup_GetNext(&mtc->nextGroup), // group
+            0); // affinityMask
+      else
+#endif
+        wres = Thread_Create(&t->thread, ThreadFunc, t);
+    }
     if (wres == 0)
       wres = Event_Set(&t->startEvent);
   }
@@ -56,6 +70,7 @@
 }
 
 
+Z7_FORCE_INLINE
 static void MtCoderThread_Destruct(CMtCoderThread *t)
 {
   if (Thread_WasCreated(&t->thread))
@@ -85,7 +100,7 @@
 
 static SRes ThreadFunc2(CMtCoderThread *t)
 {
-  CMtCoder *mtc = t->mtCoder;
+  CMtCoder * const mtc = t->mtCoder;
 
   for (;;)
   {
@@ -185,7 +200,11 @@
       if (mtc->numStartedThreads < mtc->numStartedThreadsLimit
           && mtc->expectedDataSize != readProcessed)
       {
-        res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads]);
+        res = MtCoderThread_CreateAndStart(&mtc->threads[mtc->numStartedThreads]
+#ifdef _WIN32
+            , mtc
+#endif
+            );
         if (res == SZ_OK)
           mtc->numStartedThreads++;
         else
@@ -221,7 +240,7 @@
     }
 
     {
-      CMtCoderBlock *block = &mtc->blocks[bi];
+      CMtCoderBlock * const block = &mtc->blocks[bi];
       block->res = res;
       block->bufIndex = bufIndex;
       block->finished = finished;
@@ -311,7 +330,7 @@
 
 static THREAD_FUNC_DECL ThreadFunc(void *pp)
 {
-  CMtCoderThread *t = (CMtCoderThread *)pp;
+  CMtCoderThread * const t = (CMtCoderThread *)pp;
   for (;;)
   {
     if (Event_Wait(&t->startEvent) != 0)
@@ -319,7 +338,7 @@
     if (t->stop)
       return 0;
     {
-      SRes res = ThreadFunc2(t);
+      const SRes res = ThreadFunc2(t);
       CMtCoder *mtc = t->mtCoder;
       if (res != SZ_OK)
       {
@@ -328,7 +347,7 @@
       
       #ifndef MTCODER_USE_WRITE_THREAD
       {
-        unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads);
+        const unsigned numFinished = (unsigned)InterlockedIncrement(&mtc->numFinishedThreads);
         if (numFinished == mtc->numStartedThreads)
           if (Event_Set(&mtc->finishedEvent) != 0)
             return (THREAD_FUNC_RET_TYPE)SZ_ERROR_THREAD;
@@ -346,6 +365,7 @@
   
   p->blockSize = 0;
   p->numThreadsMax = 0;
+  p->numThreadGroups = 0;
   p->expectedDataSize = (UInt64)(Int64)-1;
 
   p->inStream = NULL;
@@ -429,6 +449,8 @@
   unsigned i;
   SRes res = SZ_OK;
 
+  // printf("\n====== MtCoder_Code : \n");
+
   if (numThreads > MTCODER_THREADS_MAX)
       numThreads = MTCODER_THREADS_MAX;
   numBlocksMax = MTCODER_GET_NUM_BLOCKS_FROM_THREADS(numThreads);
@@ -492,11 +514,22 @@
 
   p->numStartedThreadsLimit = numThreads;
   p->numStartedThreads = 0;
+  ThreadNextGroup_Init(&p->nextGroup, p->numThreadGroups, 0); // startGroup
 
   // for (i = 0; i < numThreads; i++)
   {
+    // here we create new thread for first block.
+    // And each new thread will create another new thread after block reading
+    // until numStartedThreadsLimit is reached.
     CMtCoderThread *nextThread = &p->threads[p->numStartedThreads++];
-    RINOK(MtCoderThread_CreateAndStart(nextThread))
+    {
+      const SRes res2 = MtCoderThread_CreateAndStart(nextThread
+#ifdef _WIN32
+            , p
+#endif
+            );
+      RINOK(res2)
+    }
   }
 
   RINOK_THREAD(Event_Set(&p->readEvent))
@@ -513,9 +546,9 @@
       RINOK_THREAD(Event_Wait(&p->writeEvents[bi]))
 
       {
-        const CMtCoderBlock *block = &p->blocks[bi];
-        unsigned bufIndex = block->bufIndex;
-        BoolInt finished = block->finished;
+        const CMtCoderBlock * const block = &p->blocks[bi];
+        const unsigned bufIndex = block->bufIndex;
+        const BoolInt finished = block->finished;
         if (res == SZ_OK && block->res != SZ_OK)
           res = block->res;
 
@@ -545,7 +578,7 @@
   }
   #else
   {
-    WRes wres = Event_Wait(&p->finishedEvent);
+    const WRes wres = Event_Wait(&p->finishedEvent);
     res = MY_SRes_HRESULT_FROM_WRes(wres);
   }
   #endif
diff -Nru 7zip-24.09+dfsg/C/MtCoder.h 7zip-25.00+dfsg/C/MtCoder.h
--- 7zip-24.09+dfsg/C/MtCoder.h	2023-04-13 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/MtCoder.h	2025-05-15 08:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* MtCoder.h -- Multi-thread Coder
-2023-04-13 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_MT_CODER_H
 #define ZIP7_INC_MT_CODER_H
@@ -16,7 +16,7 @@
 
 #ifndef Z7_ST
   #define MTCODER_GET_NUM_BLOCKS_FROM_THREADS(numThreads) ((numThreads) + (numThreads) / 8 + 1)
-  #define MTCODER_THREADS_MAX 64
+  #define MTCODER_THREADS_MAX 256
   #define MTCODER_BLOCKS_MAX (MTCODER_GET_NUM_BLOCKS_FROM_THREADS(MTCODER_THREADS_MAX) + 3)
 #else
   #define MTCODER_THREADS_MAX 1
@@ -77,6 +77,7 @@
   
   size_t blockSize;        /* size of input block */
   unsigned numThreadsMax;
+  unsigned numThreadGroups;
   UInt64 expectedDataSize;
 
   ISeqInStreamPtr inStream;
@@ -125,6 +126,8 @@
   CMtProgress mtProgress;
   CMtCoderBlock blocks[MTCODER_BLOCKS_MAX];
   CMtCoderThread threads[MTCODER_THREADS_MAX];
+
+  CThreadNextGroup nextGroup;
 } CMtCoder;
 
 
diff -Nru 7zip-24.09+dfsg/C/Sha512.c 7zip-25.00+dfsg/C/Sha512.c
--- 7zip-24.09+dfsg/C/Sha512.c	2024-11-09 11:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/Sha512.c	2024-12-03 12:00:00.000000000 +0100
@@ -439,26 +439,78 @@
 
 
 
+// #define Z7_SHA512_PROBE_DEBUG // for debug
 
-#if defined(_WIN32) && defined(Z7_COMPILER_SHA512_SUPPORTED) \
-    && defined(MY_CPU_ARM64)  // we can disable this check to debug in x64
+#if defined(Z7_SHA512_PROBE_DEBUG) || defined(Z7_COMPILER_SHA512_SUPPORTED)
 
-#if 1  // 0 for debug
+#if defined(Z7_SHA512_PROBE_DEBUG) \
+    || defined(_WIN32) && defined(MY_CPU_ARM64)
+#ifndef Z7_SHA512_USE_PROBE
+#define Z7_SHA512_USE_PROBE
+#endif
+#endif
 
-#include "7zWindows.h"
-// #include <stdio.h>
-#if 0 && defined(MY_CPU_X86_OR_AMD64)
-#include <intrin.h> // for debug : for __ud2()
+#ifdef Z7_SHA512_USE_PROBE
+
+#ifdef Z7_SHA512_PROBE_DEBUG
+#include <stdio.h>
+#define PRF(x) x
+#else
+#define PRF(x)
 #endif
 
-BoolInt CPU_IsSupported_SHA512(void)
+#if 0 || !defined(_MSC_VER) // 1 || : for debug LONGJMP mode
+// MINGW doesn't support __try. So we use signal() / longjmp().
+// Note: signal() / longjmp() probably is not thread-safe.
+// So we must call Sha512Prepare() from main thread at program start.
+#ifndef Z7_SHA512_USE_LONGJMP
+#define Z7_SHA512_USE_LONGJMP
+#endif
+#endif
+
+#ifdef Z7_SHA512_USE_LONGJMP
+#include <signal.h>
+#include <setjmp.h>
+static jmp_buf g_Sha512_jmp_buf;
+// static int g_Sha512_Unsupported;
+
+#if defined(__GNUC__) && (__GNUC__ >= 8) \
+    || defined(__clang__) && (__clang_major__ >= 3)
+  __attribute__((noreturn))
+#endif
+static void Z7_CDECL Sha512_signal_Handler(int v)
 {
+  PRF(printf("======== Sha512_signal_Handler = %x\n", (unsigned)v);)
+  // g_Sha512_Unsupported = 1;
+  longjmp(g_Sha512_jmp_buf, 1);
+}
+#endif // Z7_SHA512_USE_LONGJMP
+
+
+#if defined(_WIN32)
+#include "7zWindows.h"
+#endif
+
 #if defined(MY_CPU_ARM64)
+// #define Z7_SHA512_USE_SIMPLIFIED_PROBE // for debug
+#endif
+
+#ifdef Z7_SHA512_USE_SIMPLIFIED_PROBE
+#include <arm_neon.h>
+#if defined(__clang__)
+  __attribute__((__target__("sha3")))
+#elif !defined(_MSC_VER)
+  __attribute__((__target__("arch=armv8.2-a+sha3")))
+#endif
+#endif
+static BoolInt CPU_IsSupported_SHA512_Probe(void)
+{
+  PRF(printf("\n== CPU_IsSupported_SHA512_Probe\n");)
+#if defined(_WIN32) && defined(MY_CPU_ARM64)
   // we have no SHA512 flag for IsProcessorFeaturePresent() still.
   if (!CPU_IsSupported_CRYPTO())
     return False;
-#endif
-  // printf("\nCPU_IsSupported_SHA512\n");
+  PRF(printf("==== Registry check\n");)
   {
     // we can't read ID_AA64ISAR0_EL1 register from application.
     // but ID_AA64ISAR0_EL1 register is mapped to "CP 4030" registry value.
@@ -486,6 +538,7 @@
       //   2 : SHA256 and SHA512 implemented
     }
   }
+#endif // defined(_WIN32) && defined(MY_CPU_ARM64)
 
 
 #if 1  // 0 for debug to disable SHA512 PROBE code
@@ -509,59 +562,97 @@
 Are there any ways to fix the problems with arm64-wine and x64-SDE cases?
 */
 
-  // printf("\n========== CPU_IsSupported_SHA512 PROBE ========\n");
+  PRF(printf("==== CPU_IsSupported_SHA512 PROBE\n");)
   {
+    BoolInt isSupported = False;
+#ifdef Z7_SHA512_USE_LONGJMP
+    void (Z7_CDECL *signal_prev)(int);
+    /*
+    if (g_Sha512_Unsupported)
+    {
+      PRF(printf("==== g_Sha512_Unsupported\n");)
+      return False;
+    }
+    */
+    printf("====== signal(SIGILL)\n");
+    signal_prev = signal(SIGILL, Sha512_signal_Handler);
+    if (signal_prev == SIG_ERR)
+    {
+      PRF(printf("====== signal fail\n");)
+      return False;
+    }
+    // PRF(printf("==== signal_prev = %p\n", (void *)signal_prev);)
+    // docs: Before the specified function is executed,
+    // the value of func is set to SIG_DFL.
+    // So we can exit if (setjmp(g_Sha512_jmp_buf) != 0).
+    PRF(printf("====== setjmp\n");)
+    if (!setjmp(g_Sha512_jmp_buf))
+#else //  Z7_SHA512_USE_LONGJMP
+
+#ifdef _MSC_VER
 #ifdef __clang_major__
   #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
 #endif
     __try
+#endif
+#endif //  Z7_SHA512_USE_LONGJMP
+
     {
-#if 0 // 1 : for debug (reduced version to detect sha512)
+#if defined(Z7_COMPILER_SHA512_SUPPORTED)
+#ifdef Z7_SHA512_USE_SIMPLIFIED_PROBE
+      // simplified sha512 check for arm64:
       const uint64x2_t a = vdupq_n_u64(1);
       const uint64x2_t b = vsha512hq_u64(a, a, a);
+      PRF(printf("======== vsha512hq_u64 probe\n");)
       if ((UInt32)vgetq_lane_u64(b, 0) == 0x11800002)
-        return True;
 #else
       MY_ALIGN(16)
       UInt64 temp[SHA512_NUM_DIGEST_WORDS + SHA512_NUM_BLOCK_WORDS];
       memset(temp, 0x5a, sizeof(temp));
-#if 0 && defined(MY_CPU_X86_OR_AMD64)
-      __ud2(); // for debug : that exception is not problem for SDE
-#endif
-#if 1
+      PRF(printf("======== Sha512_UpdateBlocks_HW\n");)
       Sha512_UpdateBlocks_HW(temp,
           (const Byte *)(const void *)(temp + SHA512_NUM_DIGEST_WORDS), 1);
-      // printf("\n==== t = %x\n", (UInt32)temp[0]);
+      // PRF(printf("======== t = %x\n", (UInt32)temp[0]);)
       if ((UInt32)temp[0] == 0xa33cfdf7)
+#endif
       {
-        // printf("\n=== PROBE SHA512: SHA512 supported\n");
-        return True;
+        PRF(printf("======== PROBE SHA512: SHA512 is supported\n");)
+        isSupported = True;
       }
+#else // Z7_COMPILER_SHA512_SUPPORTED
+      // for debug : we generate bad instrction or raise exception.
+      // __except() doesn't catch raise() calls.
+#ifdef Z7_SHA512_USE_LONGJMP
+      PRF(printf("====== raise(SIGILL)\n");)
+      raise(SIGILL);
+#else
+#if defined(_MSC_VER) && defined(MY_CPU_X86)
+      __asm  ud2
 #endif
-#endif
+#endif // Z7_SHA512_USE_LONGJMP
+#endif // Z7_COMPILER_SHA512_SUPPORTED
     }
+
+#ifdef Z7_SHA512_USE_LONGJMP
+    PRF(printf("====== restore signal SIGILL\n");)
+    signal(SIGILL, signal_prev);
+#elif _MSC_VER
     __except (EXCEPTION_EXECUTE_HANDLER)
     {
-      // printf("\n==== CPU_IsSupported_SHA512 EXCEPTION_EXECUTE_HANDLER\n");
+      PRF(printf("==== CPU_IsSupported_SHA512 __except(EXCEPTION_EXECUTE_HANDLER)\n");)
     }
+#endif
+    PRF(printf("== return (sha512 supported) = %d\n", isSupported);)
+    return isSupported;
   }
-  return False;
 #else
   // without SHA512 PROBE code
   return True;
 #endif
-
 }
 
-#else
-
-BoolInt CPU_IsSupported_SHA512(void)
-{
-  return False;
-}
-
-#endif
-#endif // WIN32 arm64
+#endif // Z7_SHA512_USE_PROBE
+#endif // defined(Z7_SHA512_PROBE_DEBUG) || defined(Z7_COMPILER_SHA512_SUPPORTED)
 
 
 void Sha512Prepare(void)
@@ -570,10 +661,10 @@
   SHA512_FUNC_UPDATE_BLOCKS f, f_hw;
   f = Sha512_UpdateBlocks;
   f_hw = NULL;
-#ifdef MY_CPU_X86_OR_AMD64
-  if (CPU_IsSupported_SHA512()
-      && CPU_IsSupported_AVX2()
-      )
+#ifdef Z7_SHA512_USE_PROBE
+  if (CPU_IsSupported_SHA512_Probe())
+#elif defined(MY_CPU_X86_OR_AMD64)
+  if (CPU_IsSupported_SHA512() && CPU_IsSupported_AVX2())
 #else
   if (CPU_IsSupported_SHA512())
 #endif
@@ -583,6 +674,8 @@
   }
   g_SHA512_FUNC_UPDATE_BLOCKS    = f;
   g_SHA512_FUNC_UPDATE_BLOCKS_HW = f_hw;
+#elif defined(Z7_SHA512_PROBE_DEBUG)
+  CPU_IsSupported_SHA512_Probe(); // for debug
 #endif
 }
 
diff -Nru 7zip-24.09+dfsg/C/Sort.c 7zip-25.00+dfsg/C/Sort.c
--- 7zip-24.09+dfsg/C/Sort.c	2014-04-05 14:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/Sort.c	2025-01-08 10:00:00.000000000 +0100
@@ -1,141 +1,268 @@
 /* Sort.c -- Sort functions
-2014-04-05 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
 #include "Sort.h"
+#include "CpuArch.h"
 
-#define HeapSortDown(p, k, size, temp) \
-  { for (;;) { \
-    size_t s = (k << 1); \
-    if (s > size) break; \
-    if (s < size && p[s + 1] > p[s]) s++; \
-    if (temp >= p[s]) break; \
-    p[k] = p[s]; k = s; \
-  } p[k] = temp; }
+#if (  (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \
+    || (defined(__clang__) && Z7_has_builtin(__builtin_prefetch)) \
+    )
+// the code with prefetch is slow for small arrays on x86.
+// So we disable prefetch for x86.
+#ifndef MY_CPU_X86
+  // #pragma message("Z7_PREFETCH : __builtin_prefetch")
+  #define Z7_PREFETCH(a)  __builtin_prefetch((a))
+#endif
 
-void HeapSort(UInt32 *p, size_t size)
-{
-  if (size <= 1)
-    return;
-  p--;
-  {
-    size_t i = size / 2;
-    do
-    {
-      UInt32 temp = p[i];
-      size_t k = i;
-      HeapSortDown(p, k, size, temp)
-    }
-    while (--i != 0);
-  }
-  /*
-  do
-  {
-    size_t k = 1;
-    UInt32 temp = p[size];
-    p[size--] = p[1];
-    HeapSortDown(p, k, size, temp)
-  }
-  while (size > 1);
-  */
-  while (size > 3)
-  {
-    UInt32 temp = p[size];
-    size_t k = (p[3] > p[2]) ? 3 : 2;
-    p[size--] = p[1];
-    p[1] = p[k];
-    HeapSortDown(p, k, size, temp)
-  }
-  {
-    UInt32 temp = p[size];
-    p[size] = p[1];
-    if (size > 2 && p[2] < temp)
-    {
-      p[1] = p[2];
-      p[2] = temp;
-    }
-    else
-      p[1] = temp;
-  }
+#elif defined(_WIN32) // || defined(_MSC_VER) && (_MSC_VER >= 1200)
+
+#include "7zWindows.h"
+
+// NOTE: CLANG/GCC/MSVC can define different values for _MM_HINT_T0 / PF_TEMPORAL_LEVEL_1.
+// For example, clang-cl can generate "prefetcht2" instruction for
+// PreFetchCacheLine(PF_TEMPORAL_LEVEL_1) call.
+// But we want to generate "prefetcht0" instruction.
+// So for CLANG/GCC we must use __builtin_prefetch() in code branch above
+// instead of PreFetchCacheLine() / _mm_prefetch().
+
+// New msvc-x86 compiler generates "prefetcht0" instruction for PreFetchCacheLine() call.
+// But old x86 cpus don't support "prefetcht0".
+// So we will use PreFetchCacheLine(), only if we are sure that
+// generated instruction is supported by all cpus of that isa.
+#if defined(MY_CPU_AMD64) \
+    || defined(MY_CPU_ARM64) \
+    || defined(MY_CPU_IA64)
+// we need to use additional braces for (a) in PreFetchCacheLine call, because
+// PreFetchCacheLine macro doesn't use braces:
+//   #define PreFetchCacheLine(l, a)  _mm_prefetch((CHAR CONST *) a, l)
+  // #pragma message("Z7_PREFETCH : PreFetchCacheLine")
+  #define Z7_PREFETCH(a)  PreFetchCacheLine(PF_TEMPORAL_LEVEL_1, (a))
+#endif
+
+#endif // _WIN32
+
+
+#define PREFETCH_NO(p,k,s,size)
+
+#ifndef Z7_PREFETCH
+  #define SORT_PREFETCH(p,k,s,size)
+#else
+
+// #define PREFETCH_LEVEL 2  // use it if cache line is 32-bytes
+#define PREFETCH_LEVEL 3  // it is fast for most cases (64-bytes cache line prefetch)
+// #define PREFETCH_LEVEL 4  // it can be faster for big array (128-bytes prefetch)
+
+#if PREFETCH_LEVEL == 0
+
+  #define SORT_PREFETCH(p,k,s,size)
+
+#else // PREFETCH_LEVEL != 0
+
+/*
+if  defined(USE_PREFETCH_FOR_ALIGNED_ARRAY)
+    we prefetch one value per cache line.
+    Use it if array is aligned for cache line size (64 bytes)
+    or if array is small (less than L1 cache size).
+
+if !defined(USE_PREFETCH_FOR_ALIGNED_ARRAY)
+    we perfetch all cache lines that can be required.
+    it can be faster for big unaligned arrays.
+*/
+  #define USE_PREFETCH_FOR_ALIGNED_ARRAY
+
+// s == k * 2
+#if 0 && PREFETCH_LEVEL <= 3 && defined(MY_CPU_X86_OR_AMD64)
+  // x86 supports (lea r1*8+offset)
+  #define PREFETCH_OFFSET(k,s)  ((s) << PREFETCH_LEVEL)
+#else
+  #define PREFETCH_OFFSET(k,s)  ((k) << (PREFETCH_LEVEL + 1))
+#endif
+
+#if 1 && PREFETCH_LEVEL <= 3 && defined(USE_PREFETCH_FOR_ALIGNED_ARRAY)
+  #define PREFETCH_ADD_OFFSET   0
+#else
+  // last offset that can be reqiured in PREFETCH_LEVEL step:
+  #define PREFETCH_RANGE        ((2 << PREFETCH_LEVEL) - 1)
+  #define PREFETCH_ADD_OFFSET   PREFETCH_RANGE / 2
+#endif
+
+#if PREFETCH_LEVEL <= 3
+
+#ifdef USE_PREFETCH_FOR_ALIGNED_ARRAY
+  #define SORT_PREFETCH(p,k,s,size) \
+  { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_ADD_OFFSET; \
+    if (s2 <= size) { \
+      Z7_PREFETCH((p + s2)); \
+  }}
+#else /* for unaligned array */
+  #define SORT_PREFETCH(p,k,s,size) \
+  { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE; \
+    if (s2 <= size) { \
+      Z7_PREFETCH((p + s2 - PREFETCH_RANGE)); \
+      Z7_PREFETCH((p + s2)); \
+  }}
+#endif
+
+#else // PREFETCH_LEVEL > 3
+
+#ifdef USE_PREFETCH_FOR_ALIGNED_ARRAY
+  #define SORT_PREFETCH(p,k,s,size) \
+  { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE - 16 / 2; \
+    if (s2 <= size) { \
+      Z7_PREFETCH((p + s2 - 16)); \
+      Z7_PREFETCH((p + s2)); \
+  }}
+#else /* for unaligned array */
+  #define SORT_PREFETCH(p,k,s,size) \
+  { const size_t s2 = PREFETCH_OFFSET(k,s) + PREFETCH_RANGE; \
+    if (s2 <= size) { \
+      Z7_PREFETCH((p + s2 - PREFETCH_RANGE)); \
+      Z7_PREFETCH((p + s2 - PREFETCH_RANGE / 2)); \
+      Z7_PREFETCH((p + s2)); \
+  }}
+#endif
+
+#endif // PREFETCH_LEVEL > 3
+#endif // PREFETCH_LEVEL != 0
+#endif // Z7_PREFETCH
+
+
+#if defined(MY_CPU_ARM64) \
+    /* || defined(MY_CPU_AMD64) */ \
+    /* || defined(MY_CPU_ARM) && !defined(_MSC_VER) */
+  // we want to use cmov, if cmov is very fast:
+  // - this cmov version is slower for clang-x64.
+  // - this cmov version is faster for gcc-arm64 for some fast arm64 cpus.
+  #define Z7_FAST_CMOV_SUPPORTED
+#endif
+ 
+#ifdef Z7_FAST_CMOV_SUPPORTED
+  // we want to use cmov here, if cmov is fast: new arm64 cpus.
+  // we want the compiler to use conditional move for this branch
+  #define GET_MAX_VAL(n0, n1, max_val_slow)  if (n0 < n1) n0 = n1;
+#else
+  // use this branch, if cpu doesn't support fast conditional move.
+  // it uses slow array access reading:
+  #define GET_MAX_VAL(n0, n1, max_val_slow)  n0 = max_val_slow;
+#endif
+
+#define HeapSortDown(p, k, size, temp, macro_prefetch) \
+{ \
+  for (;;) { \
+    UInt32 n0, n1; \
+    size_t s = k * 2; \
+    if (s >= size) { \
+      if (s == size) { \
+        n0 = p[s]; \
+        p[k] = n0; \
+        if (temp < n0) k = s; \
+      } \
+      break; \
+    } \
+    n0 = p[k * 2]; \
+    n1 = p[k * 2 + 1]; \
+    s += n0 < n1; \
+    GET_MAX_VAL(n0, n1, p[s]) \
+    if (temp >= n0) break; \
+    macro_prefetch(p, k, s, size) \
+    p[k] = n0; \
+    k = s; \
+  } \
+  p[k] = temp; \
 }
 
-void HeapSort64(UInt64 *p, size_t size)
+
+/*
+stage-1 : O(n) :
+  we generate intermediate partially sorted binary tree:
+  p[0]  : it's additional item for better alignment of tree structure in memory.
+  p[1]
+  p[2]       p[3]
+  p[4] p[5]  p[6] p[7]
+  ...
+  p[x] >= p[x * 2]
+  p[x] >= p[x * 2 + 1]
+  
+stage-2 : O(n)*log2(N):
+  we move largest item p[0] from head of tree to the end of array
+  and insert last item to sorted binary tree.
+*/
+
+// (p) must be aligned for cache line size (64-bytes) for best performance
+
+void Z7_FASTCALL HeapSort(UInt32 *p, size_t size)
 {
-  if (size <= 1)
+  if (size < 2)
     return;
-  p--;
+  if (size == 2)
   {
-    size_t i = size / 2;
-    do
-    {
-      UInt64 temp = p[i];
-      size_t k = i;
-      HeapSortDown(p, k, size, temp)
-    }
-    while (--i != 0);
-  }
-  /*
-  do
-  {
-    size_t k = 1;
-    UInt64 temp = p[size];
-    p[size--] = p[1];
-    HeapSortDown(p, k, size, temp)
-  }
-  while (size > 1);
-  */
-  while (size > 3)
-  {
-    UInt64 temp = p[size];
-    size_t k = (p[3] > p[2]) ? 3 : 2;
-    p[size--] = p[1];
-    p[1] = p[k];
-    HeapSortDown(p, k, size, temp)
+    const UInt32 a0 = p[0];
+    const UInt32 a1 = p[1];
+    const unsigned k = a1 < a0;
+    p[k] = a0;
+    p[k ^ 1] = a1;
+    return;
   }
   {
-    UInt64 temp = p[size];
-    p[size] = p[1];
-    if (size > 2 && p[2] < temp)
+    // stage-1 : O(n)
+    // we transform array to partially sorted binary tree.
+    size_t i = --size / 2;
+    // (size) now is the index of the last item in tree,
+    // if (i)
+    {
+      do
+      {
+        const UInt32 temp = p[i];
+        size_t k = i;
+        HeapSortDown(p, k, size, temp, PREFETCH_NO)
+      }
+      while (--i);
+    }
     {
-      p[1] = p[2];
-      p[2] = temp;
+      const UInt32 temp = p[0];
+      const UInt32 a1 = p[1];
+      if (temp < a1)
+      {
+        size_t k = 1;
+        p[0] = a1;
+        HeapSortDown(p, k, size, temp, PREFETCH_NO)
+      }
     }
-    else
-      p[1] = temp;
   }
-}
-
-/*
-#define HeapSortRefDown(p, vals, n, size, temp) \
-  { size_t k = n; UInt32 val = vals[temp]; for (;;) { \
-    size_t s = (k << 1); \
-    if (s > size) break; \
-    if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \
-    if (val >= vals[p[s]]) break; \
-    p[k] = p[s]; k = s; \
-  } p[k] = temp; }
 
-void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size)
-{
-  if (size <= 1)
+  if (size < 3)
+  {
+    // size == 2
+    const UInt32 a0 = p[0];
+    p[0] = p[2];
+    p[2] = a0;
     return;
-  p--;
+  }
+  if (size != 3)
   {
-    size_t i = size / 2;
+    // stage-2 : O(size) * log2(size):
+    // we move largest item p[0] from head to the end of array,
+    // and insert last item to sorted binary tree.
     do
     {
-      UInt32 temp = p[i];
-      HeapSortRefDown(p, vals, i, size, temp);
+      const UInt32 temp = p[size];
+      size_t k = p[2] < p[3] ? 3 : 2;
+      p[size--] = p[0];
+      p[0] = p[1];
+      p[1] = p[k];
+      HeapSortDown(p, k, size, temp, SORT_PREFETCH) // PREFETCH_NO
     }
-    while (--i != 0);
+    while (size != 3);
   }
-  do
   {
-    UInt32 temp = p[size];
-    p[size--] = p[1];
-    HeapSortRefDown(p, vals, 1, size, temp);
+    const UInt32 a2 = p[2];
+    const UInt32 a3 = p[3];
+    const size_t k = a2 < a3;
+    p[2] = p[1];
+    p[3] = p[0];
+    p[k] = a3;
+    p[k ^ 1] = a2;
   }
-  while (size > 1);
 }
-*/
diff -Nru 7zip-24.09+dfsg/C/Sort.h 7zip-25.00+dfsg/C/Sort.h
--- 7zip-24.09+dfsg/C/Sort.h	2023-03-05 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/Sort.h	2025-01-06 10:00:00.000000000 +0100
@@ -1,5 +1,5 @@
 /* Sort.h -- Sort functions
-2023-03-05 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_SORT_H
 #define ZIP7_INC_SORT_H
@@ -8,10 +8,7 @@
 
 EXTERN_C_BEGIN
 
-void HeapSort(UInt32 *p, size_t size);
-void HeapSort64(UInt64 *p, size_t size);
-
-/* void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size); */
+void Z7_FASTCALL HeapSort(UInt32 *p, size_t size);
 
 EXTERN_C_END
 
diff -Nru 7zip-24.09+dfsg/C/Threads.c 7zip-25.00+dfsg/C/Threads.c
--- 7zip-24.09+dfsg/C/Threads.c	2024-03-28 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/Threads.c	2025-07-05 11:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* Threads.c -- multithreading library
-2024-03-28 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -59,6 +59,100 @@
   return (res != 0 ? res : res2);
 }
 
+typedef struct MY_PROCESSOR_NUMBER {
+    WORD  Group;
+    BYTE  Number;
+    BYTE  Reserved;
+} MY_PROCESSOR_NUMBER, *MY_PPROCESSOR_NUMBER;
+
+typedef struct MY_GROUP_AFFINITY {
+#if defined(Z7_GCC_VERSION) && (Z7_GCC_VERSION < 100000)
+    // KAFFINITY is not defined in old mingw
+    ULONG_PTR
+#else
+    KAFFINITY
+#endif
+      Mask;
+    WORD   Group;
+    WORD   Reserved[3];
+} MY_GROUP_AFFINITY, *MY_PGROUP_AFFINITY;
+
+typedef BOOL (WINAPI *Func_SetThreadGroupAffinity)(
+    HANDLE hThread,
+    CONST MY_GROUP_AFFINITY *GroupAffinity,
+    MY_PGROUP_AFFINITY PreviousGroupAffinity);
+
+typedef BOOL (WINAPI *Func_GetThreadGroupAffinity)(
+    HANDLE hThread,
+    MY_PGROUP_AFFINITY GroupAffinity);
+
+typedef BOOL (WINAPI *Func_GetProcessGroupAffinity)(
+    HANDLE hProcess,
+    PUSHORT GroupCount,
+    PUSHORT GroupArray);
+
+Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
+
+#if 0
+#include <stdio.h>
+#define PRF(x) x
+/*
+--
+  before call of SetThreadGroupAffinity()
+    GetProcessGroupAffinity return one group.
+  after call of SetThreadGroupAffinity():
+    GetProcessGroupAffinity return more than group,
+    if SetThreadGroupAffinity() was to another group.
+--
+  GetProcessAffinityMask MS DOCs:
+  {
+    If the calling process contains threads in multiple groups,
+    the function returns zero for both affinity masks.
+  }
+  but tests in win10 with 2 groups (less than 64 cores total):
+    GetProcessAffinityMask() still returns non-zero affinity masks
+    even after SetThreadGroupAffinity() calls.
+*/
+static void PrintProcess_Info()
+{
+  {
+    const
+      Func_GetProcessGroupAffinity fn_GetProcessGroupAffinity =
+     (Func_GetProcessGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+          "GetProcessGroupAffinity");
+    if (fn_GetProcessGroupAffinity)
+    {
+      unsigned i;
+      USHORT GroupCounts[64];
+      USHORT GroupCount = Z7_ARRAY_SIZE(GroupCounts);
+      BOOL boolRes = fn_GetProcessGroupAffinity(GetCurrentProcess(),
+          &GroupCount, GroupCounts);
+      printf("\n====== GetProcessGroupAffinity : "
+          "boolRes=%u GroupCounts = %u :",
+          boolRes, (unsigned)GroupCount);
+      for (i = 0; i < GroupCount; i++)
+        printf(" %u", GroupCounts[i]);
+      printf("\n");
+    }
+  }
+  {
+    DWORD_PTR processAffinityMask, systemAffinityMask;
+    if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask))
+    {
+      PRF(printf("\n====== GetProcessAffinityMask : "
+        ": processAffinityMask=%x, systemAffinityMask=%x\n",
+        (UInt32)processAffinityMask, (UInt32)systemAffinityMask);)
+    }
+    else
+      printf("\n==GetProcessAffinityMask FAIL");
+  }
+}
+#else
+#ifndef USE_THREADS_CreateThread
+// #define PRF(x)
+#endif
+#endif
+
 WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
 {
   /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
@@ -72,7 +166,43 @@
   
   unsigned threadId;
   *p = (HANDLE)(_beginthreadex(NULL, 0, func, param, 0, &threadId));
-  
+
+#if 0 // 1 : for debug
+  {
+      DWORD_PTR prevMask;
+      DWORD_PTR affinity = 1 << 0;
+      prevMask = SetThreadAffinityMask(*p, (DWORD_PTR)affinity);
+      prevMask = prevMask;
+  }
+#endif
+#if 0 // 1 : for debug
+  {
+      /* win10: new thread will be created in same group that is assigned to parent thread
+                but affinity mask will contain all allowed threads of that group,
+                even if affinity mask of parent group is not full
+         win11: what group it will be created, if we have set
+                affinity of parent thread with ThreadGroupAffinity?
+      */
+      const
+         Func_GetThreadGroupAffinity fn =
+        (Func_GetThreadGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+             "GetThreadGroupAffinity");
+      if (fn)
+      {
+        // BOOL wres2;
+        MY_GROUP_AFFINITY groupAffinity;
+        memset(&groupAffinity, 0, sizeof(groupAffinity));
+        /* wres2 = */ fn(*p, &groupAffinity);
+        PRF(printf("\n==Thread_Create cur = %6u GetThreadGroupAffinity(): "
+            "wres2_BOOL = %u, group=%u mask=%x\n",
+            GetCurrentThreadId(),
+            wres2,
+            groupAffinity.Group,
+            (UInt32)groupAffinity.Mask);)
+      }
+  }
+#endif
+
   #endif
 
   /* maybe we must use errno here, but probably GetLastError() is also OK. */
@@ -110,7 +240,84 @@
       */
     }
     {
-      DWORD prevSuspendCount = ResumeThread(h);
+      const DWORD prevSuspendCount = ResumeThread(h);
+      /* ResumeThread() returns:
+         0 : was_not_suspended
+         1 : was_resumed
+        -1 : error
+      */
+      if (prevSuspendCount == (DWORD)-1)
+        wres = GetError();
+    }
+  }
+
+  /* maybe we must use errno here, but probably GetLastError() is also OK. */
+  return wres;
+
+  #endif
+}
+
+
+WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinityMask)
+{
+#ifdef USE_THREADS_CreateThread
+
+  UNUSED_VAR(group)
+  UNUSED_VAR(affinityMask)
+  return Thread_Create(p, func, param);
+  
+#else
+  
+  /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
+  HANDLE h;
+  WRes wres;
+  unsigned threadId;
+  h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId));
+  *p = h;
+  wres = HandleToWRes(h);
+  if (h)
+  {
+    // PrintProcess_Info();
+    {
+      const
+         Func_SetThreadGroupAffinity fn =
+        (Func_SetThreadGroupAffinity) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+             "SetThreadGroupAffinity");
+      if (fn)
+      {
+        // WRes wres2;
+        MY_GROUP_AFFINITY groupAffinity, prev_groupAffinity;
+        memset(&groupAffinity, 0, sizeof(groupAffinity));
+        // groupAffinity.Mask must use only bits that supported by current group
+        // (groupAffinity.Mask = 0) means all allowed bits
+        groupAffinity.Mask = affinityMask;
+        groupAffinity.Group = (WORD)group;
+        // wres2 =
+        fn(h, &groupAffinity, &prev_groupAffinity);
+        /*
+        if (groupAffinity.Group == prev_groupAffinity.Group)
+          wres2 = wres2;
+        else
+          wres2 = wres2;
+        if (wres2 == 0)
+        {
+          wres2 = GetError();
+          PRF(printf("\n==SetThreadGroupAffinity error: %u\n", wres2);)
+        }
+        else
+        {
+          PRF(printf("\n==Thread_Create_With_Group::SetThreadGroupAffinity()"
+            " threadId = %6u"
+            " group=%u mask=%x\n",
+            threadId,
+            prev_groupAffinity.Group,
+            (UInt32)prev_groupAffinity.Mask);)
+        }
+        */
+      }
+    }
+    {
+      const DWORD prevSuspendCount = ResumeThread(h);
       /* ResumeThread() returns:
          0 : was_not_suspended
          1 : was_resumed
@@ -297,6 +504,13 @@
   return Thread_Create_With_CpuSet(p, func, param, NULL);
 }
 
+/*
+WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinity)
+{
+  UNUSED_VAR(group)
+  return Thread_Create_With_Affinity(p, func, param, affinity);
+}
+*/
 
 WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity)
 {
@@ -577,5 +791,22 @@
   return AutoResetEvent_CreateNotSignaled(p);
 }
 
+void ThreadNextGroup_Init(CThreadNextGroup *p, UInt32 numGroups, UInt32 startGroup)
+{
+  // printf("\n====== ThreadNextGroup_Init numGroups = %x: startGroup=%x\n", numGroups, startGroup);
+  if (numGroups == 0)
+      numGroups = 1;
+  p->NumGroups = numGroups;
+  p->NextGroup = startGroup % numGroups;
+}
+
+
+UInt32 ThreadNextGroup_GetNext(CThreadNextGroup *p)
+{
+  const UInt32 next = p->NextGroup;
+  p->NextGroup = (next + 1) % p->NumGroups;
+  return next;
+}
+
 #undef PRF
 #undef Print
diff -Nru 7zip-24.09+dfsg/C/Threads.h 7zip-25.00+dfsg/C/Threads.h
--- 7zip-24.09+dfsg/C/Threads.h	2024-03-28 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/Threads.h	2025-06-30 16:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* Threads.h -- multithreading library
-2024-03-28 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_THREADS_H
 #define ZIP7_INC_THREADS_H
@@ -140,12 +140,22 @@
 WRes Thread_Wait_Close(CThread *p);
 
 #ifdef _WIN32
+WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, unsigned group, CAffinityMask affinityMask);
 #define Thread_Create_With_CpuSet(p, func, param, cs) \
   Thread_Create_With_Affinity(p, func, param, *cs)
 #else
 WRes Thread_Create_With_CpuSet(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, const CCpuSet *cpuSet);
 #endif
 
+typedef struct
+{
+  unsigned NumGroups;
+  unsigned NextGroup;
+} CThreadNextGroup;
+
+void ThreadNextGroup_Init(CThreadNextGroup *p, unsigned numGroups, unsigned startGroup);
+unsigned ThreadNextGroup_GetNext(CThreadNextGroup *p);
+
 
 #ifdef _WIN32
 
diff -Nru 7zip-24.09+dfsg/C/Util/Lzma/LzmaUtil.dsp 7zip-25.00+dfsg/C/Util/Lzma/LzmaUtil.dsp
--- 7zip-24.09+dfsg/C/Util/Lzma/LzmaUtil.dsp	2023-04-04 22:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/Util/Lzma/LzmaUtil.dsp	2025-02-05 07:00:00.000000000 +0100
@@ -122,6 +122,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\..\CpuArch.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\..\CpuArch.h
 # End Source File
 # Begin Source File
diff -Nru 7zip-24.09+dfsg/C/Util/LzmaLib/LzmaLib.dsp 7zip-25.00+dfsg/C/Util/LzmaLib/LzmaLib.dsp
--- 7zip-24.09+dfsg/C/Util/LzmaLib/LzmaLib.dsp	2023-04-04 15:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/Util/LzmaLib/LzmaLib.dsp	2025-02-05 07:00:00.000000000 +0100
@@ -43,7 +43,7 @@
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /YX /FD /c
-# ADD CPP /nologo /Gr /MT /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /FD /c
+# ADD CPP /nologo /Gr /MT /W4 /WX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /FD /c
 # SUBTRACT CPP /YX
 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
@@ -71,7 +71,7 @@
 # PROP Ignore_Export_Lib 0
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /D "COMPRESS_MF_MT" /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /WX /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /D "COMPRESS_MF_MT" /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
@@ -128,6 +128,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\..\CpuArch.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\..\CpuArch.h
 # End Source File
 # Begin Source File
diff -Nru 7zip-24.09+dfsg/C/XzCrc64Opt.c 7zip-25.00+dfsg/C/XzCrc64Opt.c
--- 7zip-24.09+dfsg/C/XzCrc64Opt.c	2023-12-08 09:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/XzCrc64Opt.c	2025-01-03 21:00:00.000000000 +0100
@@ -1,5 +1,5 @@
 /* XzCrc64Opt.c -- CRC64 calculation (optimized functions)
-2023-12-08 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -235,7 +235,7 @@
       v  = Q32BE(1, w1) ^ Q32BE(0, w0);
       v ^= Q32BE(3, d1) ^ Q32BE(2, d0);
   #endif
-#elif
+#else
 #error Stop_Compiling_Bad_CRC64_NUM_TABLES
 #endif
       p += Z7_CRC64_NUM_TABLES_USE;
diff -Nru 7zip-24.09+dfsg/C/XzDec.c 7zip-25.00+dfsg/C/XzDec.c
--- 7zip-24.09+dfsg/C/XzDec.c	2024-03-01 07:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/XzDec.c	2025-04-30 15:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* XzDec.c -- Xz Decode
-2024-03-01 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -59,7 +59,7 @@
 
   for (i = 0; i < limit;)
   {
-    Byte b = p[i];
+    const unsigned b = p[i];
     *value |= (UInt64)(b & 0x7F) << (7 * i++);
     if ((b & 0x80) == 0)
       return (b == 0 && i != 1) ? 0 : i;
@@ -796,11 +796,10 @@
 
 static BoolInt Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf)
 {
-  return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2)
-      && GetUi32(buf) == CrcCalc(buf + 4, 6)
-      && flags == GetBe16(buf + 8)
-      && buf[10] == XZ_FOOTER_SIG_0
-      && buf[11] == XZ_FOOTER_SIG_1;
+  return indexSize == (((UInt64)GetUi32a(buf + 4) + 1) << 2)
+      && GetUi32a(buf) == CrcCalc(buf + 4, 6)
+      && flags == GetBe16a(buf + 8)
+      && GetUi16a(buf + 10) == (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8));
 }
 
 #define READ_VARINT_AND_CHECK(buf, pos, size, res) \
@@ -1166,7 +1165,7 @@
             p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks);
             p->indexPos = p->indexPreSize;
             p->indexSize += p->indexPreSize;
-            Sha256_Final(&p->sha, p->shaDigest);
+            Sha256_Final(&p->sha, (Byte *)(void *)p->shaDigest32);
             Sha256_Init(&p->sha);
             p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize);
             p->state = XZ_STATE_STREAM_INDEX;
@@ -1241,10 +1240,10 @@
               break;
           }
           {
-            Byte digest[XZ_CHECK_SIZE_MAX];
+            UInt32 digest32[XZ_CHECK_SIZE_MAX / 4];
             p->state = XZ_STATE_BLOCK_HEADER;
             p->pos = 0;
-            if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0)
+            if (XzCheck_Final(&p->check, (void *)digest32) && memcmp(digest32, p->buf, checkSize) != 0)
               return SZ_ERROR_CRC;
             if (p->decodeOnlyOneBlock)
             {
@@ -1289,12 +1288,12 @@
           }
           else
           {
-            Byte digest[SHA256_DIGEST_SIZE];
+            UInt32 digest32[SHA256_DIGEST_SIZE / 4];
             p->state = XZ_STATE_STREAM_INDEX_CRC;
             p->indexSize += 4;
             p->pos = 0;
-            Sha256_Final(&p->sha, digest);
-            if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0)
+            Sha256_Final(&p->sha, (void *)digest32);
+            if (memcmp(digest32, p->shaDigest32, SHA256_DIGEST_SIZE) != 0)
               return SZ_ERROR_CRC;
           }
         }
@@ -1313,7 +1312,7 @@
           const Byte *ptr = p->buf;
           p->state = XZ_STATE_STREAM_FOOTER;
           p->pos = 0;
-          if (CRC_GET_DIGEST(p->crc) != GetUi32(ptr))
+          if (CRC_GET_DIGEST(p->crc) != GetUi32a(ptr))
             return SZ_ERROR_CRC;
         }
         break;
@@ -1343,7 +1342,7 @@
       {
         if (*src != 0)
         {
-          if (((UInt32)p->padSize & 3) != 0)
+          if ((unsigned)p->padSize & 3)
             return SZ_ERROR_NO_ARCHIVE;
           p->pos = 0;
           p->state = XZ_STATE_STREAM_HEADER;
diff -Nru 7zip-24.09+dfsg/C/XzEnc.c 7zip-25.00+dfsg/C/XzEnc.c
--- 7zip-24.09+dfsg/C/XzEnc.c	2024-03-01 07:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/XzEnc.c	2025-07-04 08:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* XzEnc.c -- Xz Encode
-2024-03-01 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
@@ -411,6 +411,7 @@
   }
 }
 
+Z7_FORCE_INLINE
 static void SeqInFilter_Construct(CSeqInFilter *p)
 {
   p->buf = NULL;
@@ -418,6 +419,7 @@
   p->vt.Read = SeqInFilter_Read;
 }
 
+Z7_FORCE_INLINE
 static void SeqInFilter_Free(CSeqInFilter *p, ISzAllocPtr alloc)
 {
   if (p->StateCoder.p)
@@ -507,6 +509,7 @@
 void XzProps_Init(CXzProps *p)
 {
   p->checkId = XZ_CHECK_CRC32;
+  p->numThreadGroups = 0;
   p->blockSize = XZ_PROPS_BLOCK_SIZE_AUTO;
   p->numBlockThreads_Reduced = -1;
   p->numBlockThreads_Max = -1;
@@ -689,6 +692,7 @@
 } CLzma2WithFilters;
 
 
+Z7_FORCE_INLINE
 static void Lzma2WithFilters_Construct(CLzma2WithFilters *p)
 {
   p->lzma2 = NULL;
@@ -712,6 +716,7 @@
 }
 
 
+Z7_FORCE_INLINE
 static void Lzma2WithFilters_Free(CLzma2WithFilters *p, ISzAllocPtr alloc)
 {
   #ifdef USE_SUBBLOCK
@@ -1236,6 +1241,7 @@
     }
 
     p->mtCoder.numThreadsMax = (unsigned)props->numBlockThreads_Max;
+    p->mtCoder.numThreadGroups = props->numThreadGroups;
     p->mtCoder.expectedDataSize = p->expectedDataSize;
     
     RINOK(MtCoder_Code(&p->mtCoder))
diff -Nru 7zip-24.09+dfsg/C/XzEnc.h 7zip-25.00+dfsg/C/XzEnc.h
--- 7zip-24.09+dfsg/C/XzEnc.h	2023-04-13 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/XzEnc.h	2025-07-02 14:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* XzEnc.h -- Xz Encode
-2023-04-13 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_XZ_ENC_H
 #define ZIP7_INC_XZ_ENC_H
@@ -31,6 +31,7 @@
   CLzma2EncProps lzma2Props;
   CXzFilterProps filterProps;
   unsigned checkId;
+  unsigned numThreadGroups; // 0 : no groups
   UInt64 blockSize;
   int numBlockThreads_Reduced;
   int numBlockThreads_Max;
diff -Nru 7zip-24.09+dfsg/C/Xz.h 7zip-25.00+dfsg/C/Xz.h
--- 7zip-24.09+dfsg/C/Xz.h	2024-01-26 15:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/C/Xz.h	2025-05-03 13:00:00.000000000 +0200
@@ -1,5 +1,5 @@
 /* Xz.h - Xz interface
-2024-01-26 : Igor Pavlov : Public domain */
+Igor Pavlov : Public domain */
 
 #ifndef ZIP7_INC_XZ_H
 #define ZIP7_INC_XZ_H
@@ -121,6 +121,7 @@
   UInt64 startOffset;
 } CXzStream;
 
+#define Xz_CONSTRUCT(p) { (p)->numBlocks = 0;  (p)->blocks = NULL;  (p)->flags = 0; }
 void Xz_Construct(CXzStream *p);
 void Xz_Free(CXzStream *p, ISzAllocPtr alloc);
 
@@ -136,8 +137,13 @@
   CXzStream *streams;
 } CXzs;
 
+#define Xzs_CONSTRUCT(p) { (p)->num = 0;  (p)->numAllocated = 0;  (p)->streams = NULL; }
 void Xzs_Construct(CXzs *p);
 void Xzs_Free(CXzs *p, ISzAllocPtr alloc);
+/*
+Xzs_ReadBackward() must be called for empty CXzs object.
+Xzs_ReadBackward() can return non empty object with (p->num != 0) even in case of error.
+*/
 SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr inStream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc);
 
 UInt64 Xzs_GetNumBlocks(const CXzs *p);
@@ -268,8 +274,8 @@
   size_t outBufSize;
   size_t outDataWritten; // the size of data in (outBuf) that were fully unpacked
 
-  Byte shaDigest[SHA256_DIGEST_SIZE];
-  Byte buf[XZ_BLOCK_HEADER_SIZE_MAX];
+  UInt32 shaDigest32[SHA256_DIGEST_SIZE / 4];
+  Byte buf[XZ_BLOCK_HEADER_SIZE_MAX]; // it must be aligned for 4-bytes
 } CXzUnpacker;
 
 /* alloc : aligned for cache line allocation is better */
diff -Nru 7zip-24.09+dfsg/C/XzIn.c 7zip-25.00+dfsg/C/XzIn.c
--- 7zip-24.09+dfsg/C/XzIn.c	2023-09-07 15:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/C/XzIn.c	2025-05-04 12:00:00.000000000 +0200
@@ -1,38 +1,39 @@
 /* XzIn.c - Xz input
-2023-09-07 : Igor Pavlov : Public domain */
+: Igor Pavlov : Public domain */
 
 #include "Precomp.h"
 
 #include <string.h>
 
 #include "7zCrc.h"
-#include "CpuArch.h"
 #include "Xz.h"
+#include "CpuArch.h"
 
-/*
-#define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0)
-*/
-#define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1)
-
+#define XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(p) \
+    (GetUi16a((const Byte *)(const void *)(p) + 10) == \
+      (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8)))
 
 SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream)
 {
-  Byte sig[XZ_STREAM_HEADER_SIZE];
+  UInt32 data32[XZ_STREAM_HEADER_SIZE / 4];
   size_t processedSize = XZ_STREAM_HEADER_SIZE;
-  RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize))
+  RINOK(SeqInStream_ReadMax(inStream, data32, &processedSize))
   if (processedSize != XZ_STREAM_HEADER_SIZE
-      || memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0)
+      || memcmp(data32, XZ_SIG, XZ_SIG_SIZE) != 0)
     return SZ_ERROR_NO_ARCHIVE;
-  return Xz_ParseHeader(p, sig);
+  return Xz_ParseHeader(p, (const Byte *)(const void *)data32);
 }
 
-#define READ_VARINT_AND_CHECK(buf, pos, size, res) \
-  { const unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \
+#define READ_VARINT_AND_CHECK(buf, size, res) \
+{ const unsigned s = Xz_ReadVarInt(buf, size, res); \
   if (s == 0) return SZ_ERROR_ARCHIVE; \
-  pos += s; }
+  size -= s; \
+  buf += s; \
+}
 
 SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes)
 {
+  MY_ALIGN(4)
   Byte header[XZ_BLOCK_HEADER_SIZE_MAX];
   unsigned headerSize;
   *headerSizeRes = 0;
@@ -57,8 +58,12 @@
   return XzBlock_Parse(p, header);
 }
 
+
 #define ADD_SIZE_CHECK(size, val) \
-  { const UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; }
+{ const UInt64 newSize = size + (val); \
+  if (newSize < size) return XZ_SIZE_OVERFLOW; \
+  size = newSize; \
+}
 
 UInt64 Xz_GetUnpackSize(const CXzStream *p)
 {
@@ -82,76 +87,85 @@
   return size;
 }
 
-/*
-SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream)
-{
-  return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f));
-}
-*/
 
-static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc)
+// input;
+//   CXzStream (p) is empty object.
+//   size != 0
+//   (size & 3) == 0
+//   (buf) is aligned for at least 4 bytes.
+// output:
+//   p->numBlocks is number of allocated items in p->blocks
+//   p->blocks[*] values must be ignored, if function returns error.
+static SRes Xz_ParseIndex(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc)
 {
-  size_t numBlocks, pos = 1;
-  UInt32 crc;
-
+  size_t numBlocks;
   if (size < 5 || buf[0] != 0)
     return SZ_ERROR_ARCHIVE;
-
   size -= 4;
-  crc = CrcCalc(buf, size);
-  if (crc != GetUi32(buf + size))
-    return SZ_ERROR_ARCHIVE;
-
+  {
+    const UInt32 crc = CrcCalc(buf, size);
+    if (crc != GetUi32a(buf + size))
+      return SZ_ERROR_ARCHIVE;
+  }
+  buf++;
+  size--;
   {
     UInt64 numBlocks64;
-    READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64)
-    numBlocks = (size_t)numBlocks64;
-    if (numBlocks != numBlocks64 || numBlocks * 2 > size)
+    READ_VARINT_AND_CHECK(buf, size, &numBlocks64)
+    // (numBlocks64) is 63-bit value, so we can calculate (numBlocks64 * 2):
+    if (numBlocks64 * 2 > size)
       return SZ_ERROR_ARCHIVE;
+    if (numBlocks64 >= ((size_t)1 << (sizeof(size_t) * 8 - 1)) / sizeof(CXzBlockSizes))
+      return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE
+    numBlocks = (size_t)numBlocks64;
   }
-  
-  Xz_Free(p, alloc);
-  if (numBlocks != 0)
+  // Xz_Free(p, alloc); // it's optional, because (p) is empty already
+  if (numBlocks)
   {
-    size_t i;
-    p->numBlocks = numBlocks;
-    p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);
-    if (!p->blocks)
+    CXzBlockSizes *blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);
+    if (!blocks)
       return SZ_ERROR_MEM;
-    for (i = 0; i < numBlocks; i++)
+    p->blocks = blocks;
+    p->numBlocks = numBlocks;
+    // the caller will call Xz_Free() in case of error
+    do
     {
-      CXzBlockSizes *block = &p->blocks[i];
-      READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize)
-      READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize)
-      if (block->totalSize == 0)
+      READ_VARINT_AND_CHECK(buf, size, &blocks->totalSize)
+      READ_VARINT_AND_CHECK(buf, size, &blocks->unpackSize)
+      if (blocks->totalSize == 0)
         return SZ_ERROR_ARCHIVE;
+      blocks++;
     }
+    while (--numBlocks);
   }
-  while ((pos & 3) != 0)
-    if (buf[pos++] != 0)
+  if (size >= 4)
+    return SZ_ERROR_ARCHIVE;
+  while (size)
+    if (buf[--size])
       return SZ_ERROR_ARCHIVE;
-  return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;
+  return SZ_OK;
 }
 
+
+/*
 static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc)
 {
   SRes res;
   size_t size;
   Byte *buf;
-  if (indexSize > ((UInt32)1 << 31))
-    return SZ_ERROR_UNSUPPORTED;
+  if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1)))
+    return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE
   size = (size_t)indexSize;
-  if (size != indexSize)
-    return SZ_ERROR_UNSUPPORTED;
   buf = (Byte *)ISzAlloc_Alloc(alloc, size);
   if (!buf)
     return SZ_ERROR_MEM;
   res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);
   if (res == SZ_OK)
-    res = Xz_ReadIndex2(p, buf, size, alloc);
+    res = Xz_ParseIndex(p, buf, size, alloc);
   ISzAlloc_Free(alloc, buf);
   return res;
 }
+*/
 
 static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size)
 {
@@ -160,84 +174,102 @@
   /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */
 }
 
+
+/*
+in:
+  (*startOffset) is position in (stream) where xz_stream must be finished.
+out:
+  if returns SZ_OK, then (*startOffset) is position in stream that shows start of xz_stream.
+*/
 static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc)
 {
-  UInt64 indexSize;
-  Byte buf[XZ_STREAM_FOOTER_SIZE];
+  #define TEMP_BUF_SIZE  (1 << 10)
+  UInt32 buf32[TEMP_BUF_SIZE / 4];
   UInt64 pos = (UInt64)*startOffset;
 
-  if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE)
+  if ((pos & 3) || pos < XZ_STREAM_FOOTER_SIZE)
     return SZ_ERROR_NO_ARCHIVE;
-
   pos -= XZ_STREAM_FOOTER_SIZE;
-  RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE))
+  RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE))
   
-  if (!XZ_FOOTER_SIG_CHECK(buf + 10))
+  if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32))
   {
-    UInt32 total = 0;
     pos += XZ_STREAM_FOOTER_SIZE;
-    
     for (;;)
     {
-      size_t i;
-      #define TEMP_BUF_SIZE (1 << 10)
-      Byte temp[TEMP_BUF_SIZE];
-      
-      i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos;
+      // pos != 0
+      // (pos & 3) == 0
+      size_t i = pos >= TEMP_BUF_SIZE ? TEMP_BUF_SIZE : (size_t)pos;
       pos -= i;
-      RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i))
-      total += (UInt32)i;
-      for (; i != 0; i--)
-        if (temp[i - 1] != 0)
+      RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, i))
+      i /= 4;
+      do
+        if (buf32[i - 1] != 0)
           break;
-      if (i != 0)
-      {
-        if ((i & 3) != 0)
-          return SZ_ERROR_NO_ARCHIVE;
-        pos += i;
-        break;
-      }
-      if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16))
+      while (--i);
+
+      pos += i * 4;
+      #define XZ_STREAM_BACKWARD_READING_PAD_MAX (1 << 16)
+      // here we don't support rare case with big padding for xz stream.
+      // so we have padding limit for backward reading.
+      if ((UInt64)*startOffset - pos > XZ_STREAM_BACKWARD_READING_PAD_MAX)
         return SZ_ERROR_NO_ARCHIVE;
+      if (i)
+        break;
     }
-    
+    // we try to open xz stream after skipping zero padding.
+    // ((UInt64)*startOffset == pos) is possible here!
     if (pos < XZ_STREAM_FOOTER_SIZE)
       return SZ_ERROR_NO_ARCHIVE;
     pos -= XZ_STREAM_FOOTER_SIZE;
-    RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE))
-    if (!XZ_FOOTER_SIG_CHECK(buf + 10))
+    RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE))
+    if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32))
       return SZ_ERROR_NO_ARCHIVE;
   }
   
-  p->flags = (CXzStreamFlags)GetBe16(buf + 8);
-
+  p->flags = (CXzStreamFlags)GetBe16a(buf32 + 2);
   if (!XzFlags_IsSupported(p->flags))
     return SZ_ERROR_UNSUPPORTED;
-
   {
     /* to eliminate GCC 6.3 warning:
        dereferencing type-punned pointer will break strict-aliasing rules */
-    const Byte *buf_ptr = buf;
-    if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6))
+    const UInt32 *buf_ptr = buf32;
+    if (GetUi32a(buf_ptr) != CrcCalc(buf32 + 1, 6))
       return SZ_ERROR_ARCHIVE;
   }
-
-  indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2;
-
-  if (pos < indexSize)
-    return SZ_ERROR_ARCHIVE;
-
-  pos -= indexSize;
-  RINOK(LookInStream_SeekTo(stream, pos))
-  RINOK(Xz_ReadIndex(p, stream, indexSize, alloc))
-
   {
-    UInt64 totalSize = Xz_GetPackSize(p);
-    if (totalSize == XZ_SIZE_OVERFLOW
-        || totalSize >= ((UInt64)1 << 63)
-        || pos < totalSize + XZ_STREAM_HEADER_SIZE)
+    const UInt64 indexSize = ((UInt64)GetUi32a(buf32 + 1) + 1) << 2;
+    if (pos < indexSize)
       return SZ_ERROR_ARCHIVE;
-    pos -= (totalSize + XZ_STREAM_HEADER_SIZE);
+    pos -= indexSize;
+    // v25.00: relaxed indexSize check. We allow big index table.
+    // if (indexSize > ((UInt32)1 << 31))
+    if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1)))
+      return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE
+    RINOK(LookInStream_SeekTo(stream, pos))
+    // RINOK(Xz_ReadIndex(p, stream, indexSize, alloc))
+    {
+      SRes res;
+      const size_t size = (size_t)indexSize;
+      // if (size != indexSize) return SZ_ERROR_UNSUPPORTED;
+      Byte *buf = (Byte *)ISzAlloc_Alloc(alloc, size);
+      if (!buf)
+        return SZ_ERROR_MEM;
+      res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);
+      if (res == SZ_OK)
+        res = Xz_ParseIndex(p, buf, size, alloc);
+      ISzAlloc_Free(alloc, buf);
+      RINOK(res)
+    }
+  }
+  {
+    UInt64 total = Xz_GetPackSize(p);
+    if (total == XZ_SIZE_OVERFLOW || total >= ((UInt64)1 << 63))
+      return SZ_ERROR_ARCHIVE;
+    total += XZ_STREAM_HEADER_SIZE;
+    if (pos < total)
+      return SZ_ERROR_ARCHIVE;
+    pos -= total;
     RINOK(LookInStream_SeekTo(stream, pos))
     *startOffset = (Int64)pos;
   }
@@ -246,7 +278,6 @@
     CSecToRead secToRead;
     SecToRead_CreateVTable(&secToRead);
     secToRead.realStream = stream;
-
     RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt))
     return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE;
   }
@@ -257,8 +288,7 @@
 
 void Xzs_Construct(CXzs *p)
 {
-  p->num = p->numAllocated = 0;
-  p->streams = 0;
+  Xzs_CONSTRUCT(p)
 }
 
 void Xzs_Free(CXzs *p, ISzAllocPtr alloc)
@@ -268,7 +298,7 @@
     Xz_Free(&p->streams[i], alloc);
   ISzAlloc_Free(alloc, p->streams);
   p->num = p->numAllocated = 0;
-  p->streams = 0;
+  p->streams = NULL;
 }
 
 UInt64 Xzs_GetNumBlocks(const CXzs *p)
@@ -307,34 +337,49 @@
 SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc)
 {
   Int64 endOffset = 0;
+  // it's supposed that CXzs object is empty here.
+  // if CXzs object is not empty, it will add new streams to that non-empty object.
+  // Xzs_Free(p, alloc); // it's optional call to empty CXzs object.
   RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END))
   *startOffset = endOffset;
   for (;;)
   {
     CXzStream st;
     SRes res;
-    Xz_Construct(&st);
+    Xz_CONSTRUCT(&st)
     res = Xz_ReadBackward(&st, stream, startOffset, alloc);
+    // if (res == SZ_OK), then (*startOffset) is start offset of new stream if
+    // if (res != SZ_OK), then (*startOffset) is unchend or it's expected start offset of stream with error
     st.startOffset = (UInt64)*startOffset;
-    RINOK(res)
+    // we must store (st) object to array, or we must free (st) local object.
+    if (res != SZ_OK)
+    {
+      Xz_Free(&st, alloc);
+      return res;
+    }
     if (p->num == p->numAllocated)
     {
       const size_t newNum = p->num + p->num / 4 + 1;
       void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream));
       if (!data)
+      {
+        Xz_Free(&st, alloc);
         return SZ_ERROR_MEM;
+      }
       p->numAllocated = newNum;
       if (p->num != 0)
         memcpy(data, p->streams, p->num * sizeof(CXzStream));
       ISzAlloc_Free(alloc, p->streams);
       p->streams = (CXzStream *)data;
     }
+    // we use direct copying of raw data from local variable (st) to object in array.
+    // so we don't need to call Xz_Free(&st, alloc) after copying and after p->num++
     p->streams[p->num++] = st;
     if (*startOffset == 0)
-      break;
-    RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset))
+      return SZ_OK;
+    // seek operation is optional:
+    // RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset))
     if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK)
       return SZ_ERROR_PROGRESS;
   }
-  return SZ_OK;
 }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/7zip_gcc.mak 7zip-25.00+dfsg/CPP/7zip/7zip_gcc.mak
--- 7zip-24.09+dfsg/CPP/7zip/7zip_gcc.mak	2024-11-25 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/7zip_gcc.mak	2025-07-02 07:00:00.000000000 +0200
@@ -1245,8 +1245,6 @@
 	$(CC) $(CFLAGS) $<
 $O/Sha512Opt.o: ../../../../C/Sha512Opt.c
 	$(CC) $(CFLAGS) $<
-$O/Sort.o: ../../../../C/Sort.c
-	$(CC) $(CFLAGS) $<
 $O/SwapBytes.o: ../../../../C/SwapBytes.c
 	$(CC) $(CFLAGS) $<
 $O/Xxh64.o: ../../../../C/Xxh64.c
@@ -1285,6 +1283,8 @@
 	$(MY_ASM) $(AFLAGS) $<
 $O/Sha256Opt.o: ../../../../Asm/x86/Sha256Opt.asm
 	$(MY_ASM) $(AFLAGS) $<
+$O/Sort.o: ../../../../Asm/x86/Sort.asm
+	$(MY_ASM) $(AFLAGS) $<
 
 ifndef USE_JWASM
 USE_X86_ASM_AES=1
@@ -1299,6 +1299,8 @@
 	$(CC) $(CFLAGS) $<
 $O/Sha256Opt.o: ../../../../C/Sha256Opt.c
 	$(CC) $(CFLAGS) $<
+$O/Sort.o: ../../../../C/Sort.c
+	$(CC) $(CFLAGS) $<
 endif
 
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h 7zip-25.00+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h
--- 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h	2023-11-24 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/7z/7zCompressionMode.h	2024-12-14 10:00:00.000000000 +0100
@@ -59,6 +59,7 @@
   bool NumThreads_WasForced;
   bool MultiThreadMixer;
   UInt32 NumThreads;
+  UInt32 NumThreadGroups;
   #endif
 
   UString Password; // _Wipe
@@ -74,6 +75,7 @@
       , NumThreads_WasForced(false)
       , MultiThreadMixer(true)
       , NumThreads(1)
+      , NumThreadGroups(0)
       #endif
       , MemoryUsageLimit((UInt64)1 << 30)
   {}
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp	2024-05-12 14:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/7z/7zHandlerOut.cpp	2025-07-03 09:00:00.000000000 +0200
@@ -111,8 +111,8 @@
     }
   }
 
-  const UInt64 kSolidBytes_Min = (1 << 24);
-  const UInt64 kSolidBytes_Max = ((UInt64)1 << 32);
+  const UInt64 kSolidBytes_Min = 1 << 24;
+  const UInt64 kSolidBytes_Max = (UInt64)1 << 32;  // for non-LZMA2 methods
 
   bool needSolid = false;
   
@@ -122,22 +122,24 @@
 
     SetGlobalLevelTo(oneMethodInfo);
 
-    #ifndef Z7_ST
+#ifndef Z7_ST
     const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0);
     if (!numThreads_WasSpecifiedInMethod)
     {
       // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already
       CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, methodMode.NumThreads);
     }
-    #endif
+    if (methodMode.NumThreadGroups > 1)
+      CMultiMethodProps::Set_Method_NumThreadGroups_IfNotFinded(oneMethodInfo, methodMode.NumThreadGroups);
+#endif
 
     CMethodFull &methodFull = methodMode.Methods.AddNew();
     RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo))
 
-    #ifndef Z7_ST
+#ifndef Z7_ST
     methodFull.Set_NumThreads = true;
     methodFull.NumThreads = methodMode.NumThreads;
-    #endif
+#endif
 
     if (methodFull.Id != k_Copy)
       needSolid = true;
@@ -217,19 +219,18 @@
       // here we get real chunkSize
       cs = oneMethodInfo.Get_Xz_BlockSize();
       if (dicSize > cs)
-        dicSize = cs;
+          dicSize = cs;
 
-      const UInt64 kSolidBytes_Lzma2_Max = ((UInt64)1 << 34);
+      const UInt64 kSolidBytes_Lzma2_Max = (UInt64)1 << 34;
       if (numSolidBytes > kSolidBytes_Lzma2_Max)
-        numSolidBytes = kSolidBytes_Lzma2_Max;
+          numSolidBytes = kSolidBytes_Lzma2_Max;
 
       methodFull.Set_NumThreads = false; // we don't use ICompressSetCoderMt::SetNumberOfThreads() for LZMA2 encoder
 
       #ifndef Z7_ST
       if (!numThreads_WasSpecifiedInMethod
           && !methodMode.NumThreads_WasForced
-          && methodMode.MemoryUsageLimit_WasSet
-          )
+          && methodMode.MemoryUsageLimit_WasSet)
       {
         const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads();
         const UInt32 numBlockThreads_Original = methodMode.NumThreads / lzmaThreads;
@@ -273,14 +274,14 @@
     {
       numSolidBytes = (UInt64)dicSize << 7;
       if (numSolidBytes > kSolidBytes_Max)
-        numSolidBytes = kSolidBytes_Max;
+          numSolidBytes = kSolidBytes_Max;
     }
 
     if (_numSolidBytesDefined)
       continue;
 
     if (numSolidBytes < kSolidBytes_Min)
-      numSolidBytes = kSolidBytes_Min;
+        numSolidBytes = kSolidBytes_Min;
     _numSolidBytes = numSolidBytes;
     _numSolidBytesDefined = true;
   }
@@ -704,6 +705,9 @@
     methodMode.NumThreads = numThreads;
     methodMode.NumThreads_WasForced = _numThreads_WasForced;
     methodMode.MultiThreadMixer = _useMultiThreadMixer;
+#ifdef _WIN32
+    methodMode.NumThreadGroups = _numThreadGroups; // _change it
+#endif
     // headerMethod.NumThreads = 1;
     headerMethod.MultiThreadMixer = _useMultiThreadMixer;
   }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/ArHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/ArHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/ArHandler.cpp	2024-02-25 20:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/ArHandler.cpp	2025-06-16 12:00:00.000000000 +0200
@@ -325,7 +325,7 @@
 {
   unsigned i;
   for (i = 0; i < _items.Size(); i++)
-    if (_items[i].Name == "//")
+    if (_items[i].Name.IsEqualTo("//"))
       break;
   if (i == _items.Size())
     return S_OK;
@@ -378,7 +378,7 @@
     if (item.Name[0] == '/')
       continue;
     CItem &prev = _items[i - 1];
-    if (item.Name == prev.Name)
+    if (item.Name.IsEqualTo(prev.Name))
     {
       if (prev.SameNameIndex < 0)
         prev.SameNameIndex = 0;
@@ -448,9 +448,9 @@
 HRESULT CHandler::ParseLibSymbols(IInStream *stream, unsigned fileIndex)
 {
   CItem &item = _items[fileIndex];
-  if (item.Name != "/" &&
-      item.Name != "__.SYMDEF"  &&
-      item.Name != "__.SYMDEF SORTED")
+  if (!item.Name.IsEqualTo("/") &&
+      !item.Name.IsEqualTo("__.SYMDEF")  &&
+      !item.Name.IsEqualTo("__.SYMDEF SORTED"))
     return S_OK;
   if (item.Size > ((UInt32)1 << 30) ||
       item.Size < 4)
@@ -462,7 +462,7 @@
  
   size_t pos = 0;
 
-  if (item.Name != "/")
+  if (!item.Name.IsEqualTo("/"))
   {
     // "__.SYMDEF" parsing (BSD)
     unsigned be;
@@ -603,7 +603,7 @@
     if (_longNames_FileIndex >= 0)
       _items.Delete((unsigned)_longNames_FileIndex);
 
-    if (!_items.IsEmpty() && _items[0].Name == "debian-binary")
+    if (!_items.IsEmpty() && _items[0].Name.IsEqualTo("debian-binary"))
     {
       _type = kType_Deb;
       _items.DeleteFrontal(1);
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Bz2Handler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Bz2Handler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Bz2Handler.cpp	2024-02-26 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Bz2Handler.cpp	2025-07-02 14:00:00.000000000 +0200
@@ -427,9 +427,13 @@
     }
 
     CMethodProps props2 = _props;
-    #ifndef Z7_ST
+#ifndef Z7_ST
     props2.AddProp_NumThreads(_props._numThreads);
-    #endif
+#ifdef _WIN32
+    if (_props._numThreadGroups > 1)
+      props2.AddProp32(NCoderPropID::kNumThreadGroups, _props._numThreadGroups);
+#endif
+#endif
 
     return UpdateArchive(size, outStream, props2, updateCallback);
   }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/ComHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/ComHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/ComHandler.cpp	2024-02-15 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/ComHandler.cpp	2025-04-26 13:18:00.000000000 +0200
@@ -68,7 +68,7 @@
   static const Byte kRootStorage = 5;
 }
 
-static const UInt32 kNameSizeMax = 64;
+static const unsigned kNameSizeMax = 64;
 
 struct CItem
 {
@@ -98,30 +98,30 @@
 
 class CDatabase
 {
-  UInt32 NumSectorsInMiniStream;
   CObjArray<UInt32> MiniSids;
 
   HRESULT AddNode(int parent, UInt32 did);
-public:
 
+public:
   CObjArray<UInt32> Fat;
-  UInt32 FatSize;
-  
   CObjArray<UInt32> Mat;
-  UInt32 MatSize;
-
   CObjectVector<CItem> Items;
   CRecordVector<CRef> Refs;
+private:
+  UInt32 NumSectorsInMiniStream;
+public:
+  UInt32 MatSize;
+  UInt32 FatSize;
 
   UInt32 LongStreamMinSize;
   unsigned SectorSizeBits;
   unsigned MiniSectorSizeBits;
 
   Int32 MainSubfile;
+  EType Type;
 
   UInt64 PhySize;
   UInt64 PhySize_Aligned;
-  EType Type;
 
   bool IsNotArcType() const
   {
@@ -148,14 +148,14 @@
 
   UInt64 GetItemPackSize(UInt64 size) const
   {
-    UInt64 mask = ((UInt64)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1;
+    const UInt64 mask = ((UInt32)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1;
     return (size + mask) & ~mask;
   }
 
   bool GetMiniCluster(UInt32 sid, UInt64 &res) const
   {
-    unsigned subBits = SectorSizeBits - MiniSectorSizeBits;
-    UInt32 fid = sid >> subBits;
+    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));
@@ -177,7 +177,7 @@
 HRESULT CDatabase::ReadIDs(IInStream *inStream, Byte *buf, unsigned sectorSizeBits, UInt32 sid, UInt32 *dest)
 {
   RINOK(ReadSector(inStream, buf, sectorSizeBits, sid))
-  UInt32 sectorSize = (UInt32)1 << sectorSizeBits;
+  const UInt32 sectorSize = (UInt32)1 << sectorSizeBits;
   for (UInt32 t = 0; t < sectorSize; t += 4)
     *dest++ = Get32(buf + t);
   return S_OK;
@@ -373,7 +373,7 @@
 HRESULT CDatabase::Update_PhySize_WithItem(unsigned index)
 {
   const CItem &item = Items[index];
-  bool isLargeStream = (index == 0 || IsLargeStream(item.Size));
+  const bool isLargeStream = (index == 0 || IsLargeStream(item.Size));
   if (!isLargeStream)
     return S_OK;
   const unsigned bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits;
@@ -527,6 +527,10 @@
       {
         CItem item;
         item.Parse(sect + i, mode64bit);
+        // 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))
+          return S_FALSE;
         Items.Add(item);
       }
       sid = Fat[sid];
@@ -767,11 +771,8 @@
   UInt64 totalPackSize;
   totalSize = totalPackSize = 0;
   
-  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
-  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
-
-  CLocalProgress *lps = new CLocalProgress;
-  CMyComPtr<ICompressProgressInfo> progress = lps;
+  CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
+  CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
   lps->Init(extractCallback, false);
 
   for (i = 0; i < numItems; i++)
@@ -781,7 +782,8 @@
     RINOK(lps->SetCur())
     const UInt32 index = allFilesMode ? i : indices[i];
     const CItem &item = _db.Items[_db.Refs[index].Did];
-
+    Int32 res;
+   {
     CMyComPtr<ISequentialOutStream> outStream;
     const Int32 askMode = testMode ?
         NExtract::NAskMode::kTest :
@@ -801,7 +803,7 @@
     if (!testMode && !outStream)
       continue;
     RINOK(extractCallback->PrepareOperation(askMode))
-    Int32 res = NExtract::NOperationResult::kDataError;
+    res = NExtract::NOperationResult::kDataError;
     CMyComPtr<ISequentialInStream> inStream;
     HRESULT hres = GetStream(index, &inStream);
     if (hres == S_FALSE)
@@ -813,12 +815,12 @@
       RINOK(hres)
       if (inStream)
       {
-        RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
-        if (copyCoderSpec->TotalSize == item.Size)
+        RINOK(copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps))
+        if (copyCoder->TotalSize == item.Size)
           res = NExtract::NOperationResult::kOK;
       }
     }
-    outStream.Release();
+   }
     RINOK(extractCallback->SetOperationResult(res))
   }
   return S_OK;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp	2023-05-07 17:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Common/HandlerOut.cpp	2025-07-02 08:00:00.000000000 +0200
@@ -4,8 +4,6 @@
 
 #include "../../../Common/StringToInt.h"
 
-#include "../Common/ParseProperties.h"
-
 #include "HandlerOut.h"
 
 namespace NArchive {
@@ -82,6 +80,7 @@
   return true;
 }
 
+
 bool CCommonMethodProps::SetCommonProperty(const UString &name, const PROPVARIANT &value, HRESULT &hres)
 {
   hres = S_OK;
@@ -151,6 +150,11 @@
   SetMethodProp32_Replace(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
 }
 
+void CMultiMethodProps::Set_Method_NumThreadGroups_IfNotFinded(CMethodProps &oneMethodInfo, UInt32 numThreadGroups)
+{
+  SetMethodProp32(oneMethodInfo, NCoderPropID::kNumThreadGroups, numThreadGroups);
+}
+
 #endif // Z7_ST
 
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.h 7zip-25.00+dfsg/CPP/7zip/Archive/Common/HandlerOut.h
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/HandlerOut.h	2024-10-22 11:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Common/HandlerOut.h	2025-07-02 09:00:00.000000000 +0200
@@ -17,11 +17,21 @@
   void InitCommon()
   {
     // _Write_MTime = true;
-    #ifndef Z7_ST
-    _numProcessors = _numThreads = NWindows::NSystem::GetNumberOfProcessors();
-    _numThreads_WasForced = false;
-    #endif
-
+    {
+#ifndef Z7_ST
+      _numThreads_WasForced = false;
+      UInt32 numThreads;
+#ifdef _WIN32
+      NWindows::NSystem::CProcessAffinity aff;
+      numThreads = aff.Load_and_GetNumberOfThreads();
+      _numThreadGroups = aff.IsGroupMode ? aff.Groups.GroupSizes.Size() : 0;
+#else
+      numThreads = NWindows::NSystem::GetNumberOfProcessors();
+#endif // _WIN32
+      _numProcessors = _numThreads = numThreads;
+#endif // Z7_ST
+    }
+    
     size_t memAvail = (size_t)sizeof(size_t) << 28;
     _memAvail = memAvail;
     _memUsage_Compress = memAvail;
@@ -46,11 +56,14 @@
   }
 
 public:
-  #ifndef Z7_ST
+#ifndef Z7_ST
   UInt32 _numThreads;
   UInt32 _numProcessors;
+#ifdef _WIN32
+  UInt32 _numThreadGroups;
+#endif
   bool _numThreads_WasForced;
-  #endif
+#endif
 
   bool _memUsage_WasSet;
   UInt64 _memUsage_Compress;
@@ -80,10 +93,12 @@
   
   void SetGlobalLevelTo(COneMethodInfo &oneMethodInfo) const;
 
-  #ifndef Z7_ST
+#ifndef Z7_ST
   static void SetMethodThreadsTo_IfNotFinded(CMethodProps &props, UInt32 numThreads);
   static void SetMethodThreadsTo_Replace(CMethodProps &props, UInt32 numThreads);
-  #endif
+  
+  static void Set_Method_NumThreadGroups_IfNotFinded(CMethodProps &props, UInt32 numThreadGroups);
+#endif
 
 
   unsigned GetNumEmptyMethods() const
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp	2022-01-09 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.cpp	2025-06-18 13:00:00.000000000 +0200
@@ -47,6 +47,25 @@
 }
 
 
+#if WCHAR_PATH_SEPARATOR != L'/'
+void ReplaceToWinSlashes(UString &name, bool useBackslashReplacement)
+{
+  // name.Replace(kUnixPathSepar, kOsPathSepar);
+  const unsigned len = name.Len();
+  for (unsigned i = 0; i < len; i++)
+  {
+    wchar_t c = name[i];
+    if (c == L'/')
+      c = WCHAR_PATH_SEPARATOR;
+    else if (useBackslashReplacement && c == L'\\')
+      c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // WSL scheme
+    else
+      continue;
+    name.ReplaceOneCharAtPos(i, c);
+  }
+}
+#endif
+
 void ReplaceToOsSlashes_Remove_TailSlash(UString &name, bool
     #if WCHAR_PATH_SEPARATOR != L'/'
       useBackslashReplacement
@@ -57,21 +76,7 @@
     return;
 
   #if WCHAR_PATH_SEPARATOR != L'/'
-  {
-    // name.Replace(kUnixPathSepar, kOsPathSepar);
-    const unsigned len = name.Len();
-    for (unsigned i = 0; i < len; i++)
-    {
-      wchar_t c = name[i];
-      if (c == L'/')
-        c = WCHAR_PATH_SEPARATOR;
-      else if (useBackslashReplacement && c == L'\\')
-        c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // WSL scheme
-      else
-        continue;
-      name.ReplaceOneCharAtPos(i, c);
-    }
-  }
+  ReplaceToWinSlashes(name, useBackslashReplacement);
   #endif
     
   if (name.Back() == kOsPathSepar)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h 7zip-25.00+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h	2023-01-10 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Common/ItemNameUtils.h	2025-06-16 14:00:00.000000000 +0200
@@ -13,6 +13,9 @@
 UString GetOsPath(const UString &name);
 UString GetOsPath_Remove_TailSlash(const UString &name);
   
+#if WCHAR_PATH_SEPARATOR != L'/'
+void ReplaceToWinSlashes(UString &name, bool useBackslashReplacement);
+#endif
 void ReplaceToOsSlashes_Remove_TailSlash(UString &name, bool useBackslashReplacement = false);
 void NormalizeSlashes_in_FileName_for_OsPath(wchar_t *s, unsigned len);
 void NormalizeSlashes_in_FileName_for_OsPath(UString &name);
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/CpioHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/CpioHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/CpioHandler.cpp	2023-10-02 12:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/CpioHandler.cpp	2025-06-21 09:00:00.000000000 +0200
@@ -437,7 +437,14 @@
     return S_OK;
 
   /* v23.02: we have disabled rDevMinor check because real file
-     from Apple contains rDevMinor==255 by some unknown reason */
+     from Apple contains rDevMinor==255 by some unknown reason
+     cpio 2.13 and older versions: it copies stat::st_rdev to archive.
+        and stat::st_rdev can be non-zero for some old linux/filesystems cases for regular files.
+     cpio 2.14 (2023) copies st_rdev to archive only if (S_ISBLK (st->st_mode) || S_ISCHR (st->st_mode))
+     v25.00: we have disabled RDevMajor check here to support some rare case created by cpio 2.13- with old linux.
+     But we still keep full check in IsArc_Cpio() to reduce false cpio detection cases.
+  */
+#if 0 // 0 : to disable check to support some old linux cpio archives.
   if (item.RDevMajor != 0
       // || item.RDevMinor != 0
       )
@@ -446,6 +453,7 @@
         !MY_LIN_S_ISBLK(item.Mode))
       return S_OK;
   }
+#endif
 
   // Size must be 0 for FIFOs and directories
   if (item.IsDir() || MY_LIN_S_ISFIFO(item.Mode))
@@ -873,17 +881,13 @@
   {
     case kpidPath:
     {
-      UString res;
-      bool needConvert = true;
-      #ifdef _WIN32
-      // if (
-      ConvertUTF8ToUnicode(item.Name, res);
-      // )
-        needConvert = false;
-      #endif
-      if (needConvert)
-        res = MultiByteToUnicodeString(item.Name, CP_OEMCP);
-      prop = NItemName::GetOsPath(res);
+#ifdef _WIN32
+      UString u;
+      ConvertUTF8ToUnicode(item.Name, u);
+#else
+      const UString u = MultiByteToUnicodeString(item.Name, CP_OEMCP);
+#endif
+      prop = NItemName::GetOsPath(u);
       break;
     }
     case kpidIsDir: prop = item.IsDir(); break;
@@ -921,16 +925,12 @@
         s.SetFrom_CalcLen((const char *)(const void *)(const Byte *)item.Data, (unsigned)item.Data.Size());
         if (s.Len() == item.Data.Size())
         {
+#ifdef _WIN32
           UString u;
-          bool needConvert = true;
-          #ifdef _WIN32
-            // if (
-            ConvertUTF8ToUnicode(item.Name, u);
-            // )
-            needConvert = false;
-          #endif
-          if (needConvert)
-            u = MultiByteToUnicodeString(s, CP_OEMCP);
+          ConvertUTF8ToUnicode(item.Name, u);
+#else
+          const UString u = MultiByteToUnicodeString(s, CP_OEMCP);
+#endif
           prop = u;
         }
       }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/DmgHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/DmgHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/DmgHandler.cpp	2024-03-28 13:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/DmgHandler.cpp	2025-06-16 11:00:00.000000000 +0200
@@ -444,7 +444,7 @@
   {
     const CAppleName &a = k_Names[i];
     if (a.Ext)
-      if (name == a.AppleName)
+      if (name.IsEqualTo(a.AppleName))
         return a.Ext;
   }
   return NULL;
@@ -784,7 +784,7 @@
   for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++)
   {
     const CXmlItem &si = item.SubItems[i];
-    if (si.IsTagged("key") && si.GetSubString() == key)
+    if (si.IsTagged("key") && si.GetSubString().IsEqualTo(key))
     {
       const CXmlItem *si_1 = &item.SubItems[i + 1];
       if (si_1->IsTagged(nextTag))
@@ -1251,7 +1251,7 @@
 #endif
     }
     
-    if (xml.Root.Name != "plist")
+    if (!xml.Root.Name.IsEqualTo("plist"))
       return S_FALSE;
     
     const CXmlItem *dictItem = xml.Root.FindSubTag_GetPtr("dict");
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/FatHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/FatHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/FatHandler.cpp	2023-06-26 19:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/FatHandler.cpp	2025-02-09 10:00:00.000000000 +0100
@@ -2,13 +2,12 @@
 
 #include "StdAfx.h"
 
-// #include <stdio.h>
-
 #include "../../../C/CpuArch.h"
 
 #include "../../Common/ComTry.h"
 #include "../../Common/IntToString.h"
 #include "../../Common/MyBuffer.h"
+#include "../../Common/MyBuffer2.h"
 #include "../../Common/MyCom.h"
 #include "../../Common/StringConvert.h"
 
@@ -22,14 +21,19 @@
 
 #include "../Compress/CopyCoder.h"
 
-#include "Common/DummyOutStream.h"
+#include "Common/ItemNameUtils.h"
 
 #define Get16(p) GetUi16(p)
 #define Get32(p) GetUi32(p)
 #define Get16a(p) GetUi16a(p)
 #define Get32a(p) GetUi32a(p)
 
-#define PRF(x) /* x */
+#if 0
+#include <stdio.h>
+#define PRF(x) x
+#else
+#define PRF(x)
+#endif
 
 namespace NArchive {
 namespace NFat {
@@ -38,35 +42,34 @@
 
 struct CHeader
 {
-  UInt32 NumSectors;
-  UInt16 NumReservedSectors;
+  Byte NumFatBits;
+  Byte SectorSizeLog;
+  Byte SectorsPerClusterLog;
+  Byte ClusterSizeLog;
   Byte NumFats;
+  Byte MediaType;
+
+  bool VolFieldsDefined;
+  bool HeadersWarning;
+
+  UInt32 FatSize;
+  UInt32 BadCluster;
+
+  UInt16 NumReservedSectors;
+  UInt32 NumSectors;
   UInt32 NumFatSectors;
   UInt32 RootDirSector;
   UInt32 NumRootDirSectors;
   UInt32 DataSector;
 
-  UInt32 FatSize;
-  UInt32 BadCluster;
-
-  Byte NumFatBits;
-  Byte SectorSizeLog;
-  Byte SectorsPerClusterLog;
-  Byte ClusterSizeLog;
-  
   UInt16 SectorsPerTrack;
   UInt16 NumHeads;
   UInt32 NumHiddenSectors;
-
-  bool VolFieldsDefined;
-  bool HeadersWarning;
   
   UInt32 VolId;
   // Byte VolName[11];
   // Byte FileSys[8];
-
   // Byte OemName[5];
-  Byte MediaType;
 
   // 32-bit FAT
   UInt16 Flags;
@@ -104,15 +107,8 @@
   bool Parse(const Byte *p);
 };
 
-static int GetLog(UInt32 num)
-{
-  for (int i = 0; i < 31; i++)
-    if (((UInt32)1 << i) == num)
-      return i;
-  return -1;
-}
 
-static const UInt32 kHeaderSize = 512;
+static const unsigned kHeaderSize = 512;
 
 API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size);
 API_FUNC_IsArc IsArc_Fat(const Byte *p, size_t size)
@@ -125,7 +121,7 @@
 
 bool CHeader::Parse(const Byte *p)
 {
-  if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA)
+  if (Get16(p + 0x1FE) != 0xAA55)
     return false;
 
   HeadersWarning = false;
@@ -139,22 +135,40 @@
   }
   {
     {
-      const UInt32 val32 = Get16(p + 11);
-      const int s = GetLog(val32);
-      if (s < 9 || s > 12)
-        return false;
-      SectorSizeLog = (Byte)s;
+      const unsigned num = Get16(p + 11);
+      unsigned i = 9;
+      unsigned m = 1 << i;
+      for (;;)
+      {
+        if (m == num)
+          break;
+        m <<= 1;
+        if (++i > 12)
+          return false;
+      }
+      SectorSizeLog = (Byte)i;
     }
     {
-      const UInt32 val32 = p[13];
-      const int s = GetLog(val32);
-      if (s < 0)
+      const unsigned num = p[13];
+      unsigned i = 0;
+      unsigned m = 1 << i;
+      for (;;)
+      {
+        if (m == num)
+          break;
+        m <<= 1;
+        if (++i > 7)
+          return false;
+      }
+      SectorsPerClusterLog = (Byte)i;
+      i += SectorSizeLog;
+      ClusterSizeLog = (Byte)i;
+      // (2^15 = 32 KB is safe cluster size that is suported by all system.
+      // (2^16 = 64 KB is supported by some systems
+      // (128 KB / 256 KB) can be created by some tools, but it is not supported by many tools.
+      if (i > 18) // 256 KB
         return false;
-      SectorsPerClusterLog = (Byte)s;
     }
-    ClusterSizeLog = (Byte)(SectorSizeLog + SectorsPerClusterLog);
-    if (ClusterSizeLog > 24)
-      return false;
   }
 
   NumReservedSectors = Get16(p + 14);
@@ -169,7 +183,7 @@
   const bool isOkOffset = (codeOffset == 0)
       || (codeOffset == (p[0] == 0xEB ? 2 : 3));
 
-  const UInt16 numRootDirEntries = Get16(p + 17);
+  const unsigned numRootDirEntries = Get16(p + 17);
   if (numRootDirEntries == 0)
   {
     if (codeOffset < 90 && !isOkOffset)
@@ -183,10 +197,10 @@
     if (codeOffset < 62 - 24 && !isOkOffset)
       return false;
     NumFatBits = 0;
-    const UInt32 mask = (1 << (SectorSizeLog - 5)) - 1;
-    if ((numRootDirEntries & mask) != 0)
+    const unsigned mask = (1u << (SectorSizeLog - 5)) - 1;
+    if (numRootDirEntries & mask)
       return false;
-    NumRootDirSectors = (numRootDirEntries + mask) >> (SectorSizeLog - 5);
+    NumRootDirSectors = (numRootDirEntries /* + mask */) >> (SectorSizeLog - 5);
   }
 
   NumSectors = Get16(p + 19);
@@ -198,7 +212,6 @@
   else if (IsFat32())
     return false;
   */
-
   MediaType = p[21];
   NumFatSectors = Get16(p + 22);
   SectorsPerTrack = Get16(p + 24);
@@ -222,7 +235,7 @@
       return false;
     RootCluster = Get32(p + 8);
     FsInfoSector = Get16(p + 12);
-    for (int i = 16; i < 28; i++)
+    for (unsigned i = 16; i < 28; i++)
       if (p[i] != 0)
         return false;
     p += 28;
@@ -260,7 +273,7 @@
     if (numClusters >= 0xFFF5)
       return false;
     NumFatBits = (Byte)(numClusters < 0xFF5 ? 12 : 16);
-    BadCluster &= ((1 << NumFatBits) - 1);
+    BadCluster &= (((UInt32)1 << NumFatBits) - 1);
   }
 
   FatSize = numClusters + 2;
@@ -283,103 +296,157 @@
   return true;
 }
 
-struct CItem
+
+
+class CItem
 {
-  UString UName;
-  char DosName[11];
+  Z7_CLASS_NO_COPY(CItem)
+public:
+  UInt32 Size;
+  Byte Attrib;
   Byte CTime2;
-  UInt32 CTime;
-  UInt32 MTime;
   UInt16 ADate;
-  Byte Attrib;
+  CByteBuffer LongName; // if LongName.Size() == 0 : no long name
+                        // if LongName.Size() != 0 : it's NULL terminated UTF16-LE string.
+  char DosName[11];
   Byte Flags;
-  UInt32 Size;
+  UInt32 MTime;
+  UInt32 CTime;
   UInt32 Cluster;
   Int32 Parent;
 
+  CItem() {}
+
   // NT uses Flags to store Low Case status
   bool NameIsLow() const { return (Flags & 0x8) != 0; }
   bool ExtIsLow() const { return (Flags & 0x10) != 0; }
   bool IsDir() const { return (Attrib & 0x10) != 0; }
-  UString GetShortName() const;
-  UString GetName() const;
-  UString GetVolName() const;
+  void GetShortName(UString &dest) const;
+  void GetName(UString &name) const;
 };
 
-static unsigned CopyAndTrim(char *dest, const char *src, unsigned size, bool toLower)
+
+static char *CopyAndTrim(char *dest, const char *src,
+    unsigned size, unsigned toLower)
 {
-  memcpy(dest, src, size);
-  if (toLower)
+  do
   {
-    for (unsigned i = 0; i < size; i++)
+    if (src[(size_t)size - 1] != ' ')
     {
-      char c = dest[i];
-      if (c >= 'A' && c <= 'Z')
-        dest[i] = (char)(c + 0x20);
+      const unsigned range = toLower ? 'Z' - 'A' + 1 : 0;
+      do
+      {
+        unsigned c = (Byte)*src++;
+        if ((unsigned)(c - 'A') < range)
+          c += 0x20;
+        *dest++ = (char)c;
+      }
+      while (--size);
+      break;
     }
   }
-  
-  for (unsigned i = size;;)
-  {
-    if (i == 0)
-      return 0;
-    if (dest[i - 1] != ' ')
-      return i;
-    i--;
-  }
+  while (--size);
+  *dest = 0;
+  return dest;
 }
 
-static UString FatStringToUnicode(const char *s)
+
+static void FatStringToUnicode(UString &dest, const char *s)
 {
-  return MultiByteToUnicodeString(s, CP_OEMCP);
+  MultiByteToUnicodeString2(dest, AString(s), CP_OEMCP);
 }
 
-UString CItem::GetShortName() const
+void CItem::GetShortName(UString &shortName) const
 {
   char s[16];
-  unsigned i = CopyAndTrim(s, DosName, 8, NameIsLow());
-  s[i++] = '.';
-  unsigned j = CopyAndTrim(s + i, DosName + 8, 3, ExtIsLow());
-  if (j == 0)
-    i--;
-  s[i + j] = 0;
-  return FatStringToUnicode(s);
+  char *dest = CopyAndTrim(s, DosName, 8, NameIsLow());
+  *dest++ = '.';
+  char *dest2 = CopyAndTrim(dest, DosName + 8, 3, ExtIsLow());
+  if (dest == dest2)
+    dest[-1] = 0;
+  FatStringToUnicode(shortName, s);
 }
 
-UString CItem::GetName() const
+
+
+// numWords != 0
+static unsigned ParseLongName(UInt16 *buf, unsigned numWords)
 {
-  if (!UName.IsEmpty())
-    return UName;
-  return GetShortName();
+  unsigned i;
+  for (i = 0; i < numWords; i++)
+  {
+    const unsigned c = buf[i];
+    if (c == 0)
+      break;
+    if (c == 0xFFFF)
+      return 0;
+  }
+  if (i == 0)
+    return 0;
+  buf[i] = 0;
+  numWords -= i;
+  i++;
+  if (numWords > 1)
+  {
+    numWords--;
+    buf += i;
+    do
+      if (*buf++ != 0xFFFF)
+        return 0;
+    while (--numWords);
+  }
+  return i; // it includes NULL terminator
+}
+
+
+void CItem::GetName(UString &name) const
+{
+  if (LongName.Size() >= 2)
+  {
+    const Byte * const p = LongName;
+    const unsigned numWords = ((unsigned)LongName.Size() - 2) / 2;
+    wchar_t *dest = name.GetBuf(numWords);
+    for (unsigned i = 0; i < numWords; i++)
+      dest[i] = (wchar_t)Get16(p + (size_t)i * 2);
+    name.ReleaseBuf_SetEnd(numWords);
+  }
+  else
+    GetShortName(name);
+  if (name.IsEmpty()) // it's unexpected
+    name = '_';
+  NItemName::NormalizeSlashes_in_FileName_for_OsPath(name);
 }
 
-UString CItem::GetVolName() const
+
+static void GetVolName(const char dosName[11], NWindows::NCOM::CPropVariant &prop)
 {
-  if (!UName.IsEmpty())
-    return UName;
   char s[12];
-  unsigned i = CopyAndTrim(s, DosName, 11, false);
-  s[i] = 0;
-  return FatStringToUnicode(s);
+  CopyAndTrim(s, dosName, 11, false);
+  UString u;
+  FatStringToUnicode(u, AString(s));
+  prop = u;
 }
 
+
 struct CDatabase
 {
-  CHeader Header;
   CObjectVector<CItem> Items;
   UInt32 *Fat;
+  CHeader Header;
   CMyComPtr<IInStream> InStream;
   IArchiveOpenCallback *OpenCallback;
+  CAlignedBuffer ByteBuf;
+  CByteBuffer LfnBuf;
 
   UInt32 NumFreeClusters;
-  bool VolItemDefined;
-  CItem VolItem;
   UInt32 NumDirClusters;
-  CByteBuffer ByteBuf;
   UInt64 NumCurUsedBytes;
-
   UInt64 PhySize;
 
+  UInt32 Vol_MTime;
+  char VolLabel[11];
+  bool VolItem_Defined;
+
   CDatabase(): Fat(NULL) {}
   ~CDatabase() { ClearAndClose(); }
 
@@ -388,7 +455,7 @@
   HRESULT OpenProgressFat(bool changeTotal = true);
   HRESULT OpenProgress();
 
-  UString GetItemPath(UInt32 index) const;
+  void GetItemPath(UInt32 index, UString &s) const;
   HRESULT Open();
   HRESULT ReadDir(Int32 parent, UInt32 cluster, unsigned level);
 
@@ -400,6 +467,7 @@
   HRESULT SeekToCluster(UInt32 cluster) { return SeekToSector(Header.ClusterToSector(cluster)); }
 };
 
+
 HRESULT CDatabase::SeekToSector(UInt32 sector)
 {
   return InStream_SeekSet(InStream, (UInt64)sector << Header.SectorSizeLog);
@@ -408,7 +476,7 @@
 void CDatabase::Clear()
 {
   PhySize = 0;
-  VolItemDefined = false;
+  VolItem_Defined = false;
   NumDirClusters = 0;
   NumCurUsedBytes = 0;
 
@@ -440,49 +508,35 @@
 {
   if (!OpenCallback)
     return S_OK;
-  UInt64 numItems = Items.Size();
+  const UInt64 numItems = Items.Size();
   return OpenCallback->SetCompleted(&numItems, &NumCurUsedBytes);
 }
 
-UString CDatabase::GetItemPath(UInt32 index) const
+void CDatabase::GetItemPath(UInt32 index, UString &s) const
 {
-  const CItem *item = &Items[index];
-  UString name = item->GetName();
+  UString name;
   for (;;)
   {
-    index = (UInt32)item->Parent;
-    if (item->Parent < 0)
-      return name;
-    item = &Items[index];
-    name.InsertAtFront(WCHAR_PATH_SEPARATOR);
-    if (item->UName.IsEmpty())
-      name.Insert(0, item->GetShortName());
-    else
-      name.Insert(0, item->UName);
+    const CItem &item = Items[index];
+    item.GetName(name);
+    if (item.Parent >= 0)
+      name.InsertAtFront(WCHAR_PATH_SEPARATOR);
+    s.Insert(0, name);
+    index = (UInt32)item.Parent;
+    if (item.Parent < 0)
+      break;
   }
 }
 
-static wchar_t *AddSubStringToName(wchar_t *dest, const Byte *p, unsigned numChars)
-{
-  for (unsigned i = 0; i < numChars; i++)
-  {
-    wchar_t c = Get16(p + i * 2);
-    if (c != 0 && c != 0xFFFF)
-      *dest++ = c;
-  }
-  *dest = 0;
-  return dest;
-}
 
 HRESULT CDatabase::ReadDir(Int32 parent, UInt32 cluster, unsigned level)
 {
-  unsigned startIndex = Items.Size();
+  const unsigned startIndex = Items.Size();
   if (startIndex >= (1 << 30) || level > 256)
     return S_FALSE;
 
-  UInt32 sectorIndex = 0;
   UInt32 blockSize = Header.ClusterSize();
-  bool clusterMode = (Header.IsFat32() || parent >= 0);
+  const bool clusterMode = (Header.IsFat32() || parent >= 0);
   if (!clusterMode)
   {
     blockSize = Header.SectorSize();
@@ -490,21 +544,26 @@
   }
 
   ByteBuf.Alloc(blockSize);
-  UString curName;
-  int checkSum = -1;
-  int numLongRecords = -1;
+
+  const unsigned k_NumLfnRecords_MAX = 20; // 260 symbols limit (strict limit)
+  // const unsigned k_NumLfnRecords_MAX = 0x40 - 1; // 1260 symbols limit (relaxed limit)
+  const unsigned k_NumLfnBytes_in_Record = 13 * 2;
+  // we reserve 2 additional bytes for NULL terminator
+  LfnBuf.Alloc(k_NumLfnRecords_MAX * k_NumLfnBytes_in_Record + 2 * 1);
   
+  UInt32 curDirBytes_read = 0;
+  UInt32 sectorIndex = 0;
+  unsigned num_lfn_records = 0;
+  unsigned lfn_RecordIndex = 0;
+  int checkSum = -1;
+  bool is_record_error = false;
+
   for (UInt32 pos = blockSize;; pos += 32)
   {
     if (pos == blockSize)
     {
       pos = 0;
 
-      if ((NumDirClusters & 0xFF) == 0)
-      {
-        RINOK(OpenProgress())
-      }
-
       if (clusterMode)
       {
         if (Header.IsEoc(cluster))
@@ -514,21 +573,37 @@
         PRF(printf("\nCluster = %4X", cluster));
         RINOK(SeekToCluster(cluster))
         const UInt32 newCluster = Fat[cluster];
-        if ((newCluster & kFatItemUsedByDirMask) != 0)
+        if (newCluster & kFatItemUsedByDirMask)
           return S_FALSE;
         Fat[cluster] |= kFatItemUsedByDirMask;
         cluster = newCluster;
         NumDirClusters++;
+        if ((NumDirClusters & 0xFF) == 0)
+        {
+          RINOK(OpenProgress())
+        }
         NumCurUsedBytes += Header.ClusterSize();
       }
       else if (sectorIndex++ >= Header.NumRootDirSectors)
         break;
       
+      // if (curDirBytes_read > (1u << 28)) // do we need some relaxed limit for non-MS FATs?
+      if (curDirBytes_read >= (1u << 21)) // 2MB limit from FAT specification.
+        return S_FALSE;
       RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize))
+      curDirBytes_read += blockSize;
     }
   
-    const Byte *p = ByteBuf + pos;
-    
+    if (is_record_error)
+    {
+      Header.HeadersWarning = true;
+      num_lfn_records = 0;
+      lfn_RecordIndex = 0;
+      checkSum = -1;
+    }
+
+    const Byte * const p = ByteBuf + pos;
+   
     if (p[0] == 0)
     {
       /*
@@ -538,125 +613,191 @@
       */
       break;
     }
-    
+
+    is_record_error = true;
+
     if (p[0] == 0xE5)
     {
-      if (numLongRecords > 0)
-        return S_FALSE;
+      // deleted entry
+      if (num_lfn_records == 0)
+        is_record_error = false;
       continue;
     }
-    
-    Byte attrib = p[11];
-    if ((attrib & 0x3F) == 0xF)
+    // else
     {
-      if (p[0] > 0x7F || Get16(p + 26) != 0)
-        return S_FALSE;
-      int longIndex = p[0] & 0x3F;
-      if (longIndex == 0)
-        return S_FALSE;
-      bool isLast = (p[0] & 0x40) != 0;
-      if (numLongRecords < 0)
+      const Byte attrib = p[11];
+      // maybe we can use more strick check : (attrib == 0xF) ?
+      if ((attrib & 0x3F) == 0xF)
       {
-        if (!isLast)
+        // long file name (LFN) entry
+        const unsigned longIndex = p[0] & 0x3F;
+        if (longIndex == 0
+            || longIndex > k_NumLfnRecords_MAX
+            || p[0] > 0x7F
+            || Get16a(p + 26) != 0 // LDIR_FstClusLO
+            )
+        {
+          return S_FALSE;
+          // break;
+        }
+        const bool isLast = (p[0] & 0x40) != 0;
+        if (num_lfn_records == 0)
+        {
+          if (!isLast)
+            continue; // orphan
+          num_lfn_records = longIndex;
+        }
+        else if (isLast || longIndex != lfn_RecordIndex)
+        {
           return S_FALSE;
-        numLongRecords = longIndex;
+          // break;
+        }
+        
+        lfn_RecordIndex = longIndex - 1;
+        
+        if (p[12] == 0)
+        {
+          Byte * const dest = LfnBuf + k_NumLfnBytes_in_Record * lfn_RecordIndex;
+          memcpy(dest,          p +  1, 5 * 2);
+          memcpy(dest +  5 * 2, p + 14, 6 * 2);
+          memcpy(dest + 11 * 2, p + 28, 2 * 2);
+          if (isLast)
+            checkSum = p[13];
+          if (checkSum == p[13])
+            is_record_error = false;
+          // else return S_FALSE;
+          continue;
+        }
+        // else
+        checkSum = -1; // we will ignore LfnBuf in this case
+        continue;
       }
-      else if (isLast || numLongRecords != longIndex)
-        return S_FALSE;
-
-      numLongRecords--;
       
-      if (p[12] == 0)
+      if (lfn_RecordIndex)
       {
-        wchar_t nameBuf[14];
-        wchar_t *dest;
-        
-        dest = AddSubStringToName(nameBuf, p + 1, 5);
-        dest = AddSubStringToName(dest, p + 14, 6);
-        AddSubStringToName(dest, p + 28, 2);
-        curName = nameBuf + curName;
-        if (isLast)
-          checkSum = p[13];
-        if (checkSum != p[13])
-          return S_FALSE;
+        Header.HeadersWarning = true;
+        // return S_FALSE;
       }
-    }
-    else
-    {
-      if (numLongRecords > 0)
-        return S_FALSE;
-      CItem item;
-      memcpy(item.DosName, p, 11);
+      // lfn_RecordIndex = 0;
 
-      if (checkSum >= 0)
+      const unsigned type_in_attrib = attrib & 0x18;
+      if (type_in_attrib == 0x18)
       {
-        Byte sum = 0;
-        for (unsigned i = 0; i < 11; i++)
-          sum = (Byte)(((sum & 1) ? 0x80 : 0) + (sum >> 1) + (Byte)item.DosName[i]);
-        if (sum == checkSum)
-          item.UName = curName;
+        // invalid directory record (both flags are set: dir_flag and volume_flag)
+        return S_FALSE;
+        // break;
+        // continue;
       }
-
-      if (item.DosName[0] == 5)
-        item.DosName[0] = (char)(Byte)0xE5;
-      item.Attrib = attrib;
-      item.Flags = p[12];
-      item.Size = Get32(p + 28);
-      item.Cluster = Get16(p + 26);
-      if (Header.NumFatBits > 16)
-        item.Cluster |= ((UInt32)Get16(p + 20) << 16);
-      else
+      if (type_in_attrib == 8) // volume_flag
       {
-        // OS/2 and WinNT probably can store EA (extended atributes) in that field.
+        if (!VolItem_Defined && level == 0)
+        {
+          VolItem_Defined = true;
+          memcpy(VolLabel, p, 11);
+          Vol_MTime = Get32(p + 22);
+          is_record_error = false;
+        }
       }
-
-      item.CTime = Get32(p + 14);
-      item.CTime2 = p[13];
-      item.ADate = Get16(p + 18);
-      item.MTime = Get32(p + 22);
-      item.Parent = parent;
-
-      if (attrib == 8)
+      else if (memcmp(p, ".          ", 11) == 0
+            || memcmp(p, "..         ", 11) == 0)
       {
-        VolItem = item;
-        VolItemDefined = true;
+        if (num_lfn_records == 0 && type_in_attrib == 0x10) // dir_flag
+          is_record_error = false;
       }
       else
-        if (memcmp(item.DosName, ".          ", 11) != 0 &&
-            memcmp(item.DosName, "..         ", 11) != 0)
       {
-        if (!item.IsDir())
-          NumCurUsedBytes += Header.GetFilePackSize(item.Size);
-        Items.Add(item);
-        PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1)));
+        CItem &item = Items.AddNew();
+        memcpy(item.DosName, p, 11);
+        if (item.DosName[0] == 5)
+          item.DosName[0] = (char)(Byte)0xE5; // 0xE5 is valid KANJI lead byte value.
+        item.Attrib = attrib;
+        item.Flags = p[12];
+        item.Size = Get32a(p + 28);
+        item.Cluster = Get16a(p + 26);
+        if (Header.NumFatBits > 16)
+          item.Cluster |= ((UInt32)Get16a(p + 20) << 16);
+        else
+        {
+          // OS/2 and WinNT probably can store EA (extended atributes) in that field.
+        }
+        item.CTime = Get32(p + 14);
+        item.CTime2 = p[13];
+        item.ADate = Get16a(p + 18);
+        item.MTime = Get32(p + 22);
+        item.Parent = parent;
+        {
+          if (!item.IsDir())
+            NumCurUsedBytes += Header.GetFilePackSize(item.Size);
+          // PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1)));
+          PRF(printf("\n%7d" /* ": %S" */, Items.Size() /* , item.GetShortName() */ );)
+        }
+        if (num_lfn_records == 0)
+          is_record_error = false;
+        else if (checkSum >= 0 && lfn_RecordIndex == 0)
+        {
+          Byte sum = 0;
+          for (unsigned i = 0; i < 11; i++)
+            sum = (Byte)((sum << 7) + (sum >> 1) + (Byte)item.DosName[i]);
+          if (sum == checkSum)
+          {
+            const unsigned numWords = ParseLongName((UInt16 *)(void *)(Byte *)LfnBuf,
+                num_lfn_records * k_NumLfnBytes_in_Record / 2);
+            if (numWords > 1)
+            {
+              // numWords includes NULL terminator
+              item.LongName.CopyFrom(LfnBuf, numWords * 2);
+              is_record_error = false;
+            }
+          }
+        }
+        
+        if (
+            // item.LongName.Size() < 20 ||  // for debug
+            item.LongName.Size() <= 2 * 1
+            && memcmp(p, "           ", 11) == 0)
+        {
+          char s[16 + 16];
+          const size_t numChars = (size_t)(ConvertUInt32ToString(
+              Items.Size() - 1 - startIndex,
+              MyStpCpy(s, "[NONAME]-")) - s) + 1;
+          item.LongName.Alloc(numChars * 2);
+          for (size_t i = 0; i < numChars; i++)
+          {
+            SetUi16a(item.LongName + i * 2, (Byte)s[i])
+          }
+          Header.HeadersWarning = true;
+        }
       }
-      numLongRecords = -1;
-      curName.Empty();
-      checkSum = -1;
+      num_lfn_records = 0;
     }
   }
 
-  unsigned finishIndex = Items.Size();
+  if (is_record_error)
+    Header.HeadersWarning = true;
+
+  const unsigned finishIndex = Items.Size();
   for (unsigned i = startIndex; i < finishIndex; i++)
   {
     const CItem &item = Items[i];
     if (item.IsDir())
     {
-      PRF(printf("\n%S", GetItemPath(i)));
-      RINOK(CDatabase::ReadDir((int)i, item.Cluster, level + 1))
+      PRF(printf("\n---- %c%c%c%c%c", item.DosName[0], item.DosName[1], item.DosName[2], item.DosName[3], item.DosName[4]));
+      RINOK(ReadDir((int)i, item.Cluster, level + 1))
     }
   }
   return S_OK;
 }
 
+
+
 HRESULT CDatabase::Open()
 {
   Clear();
-  bool numFreeClustersDefined = false;
+  bool numFreeClusters_Defined = false;
   {
-    Byte buf[kHeaderSize];
-    RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize))
-    if (!Header.Parse(buf))
+    UInt32 buf32[kHeaderSize / 4];
+    RINOK(ReadStream_FALSE(InStream, buf32, kHeaderSize))
+    if (!Header.Parse((Byte *)(void *)buf32))
       return S_FALSE;
     UInt64 fileSize;
     RINOK(InStream_GetSize_SeekToEnd(InStream, fileSize))
@@ -671,21 +812,21 @@
     {
       if (((UInt32)Header.FsInfoSector << Header.SectorSizeLog) + kHeaderSize <= fileSize
           && SeekToSector(Header.FsInfoSector) == S_OK
-          && ReadStream_FALSE(InStream, buf, kHeaderSize) == S_OK
-          && 0xaa550000 == Get32(buf + 508)
-          && 0x41615252 == Get32(buf)
-          && 0x61417272 == Get32(buf + 484))
+          && ReadStream_FALSE(InStream, buf32, kHeaderSize) == S_OK
+          && 0xaa550000 == Get32a(buf32 + 508 / 4)
+          && 0x41615252 == Get32a(buf32)
+          && 0x61417272 == Get32a(buf32 + 484 / 4))
       {
-        NumFreeClusters = Get32(buf + 488);
-        numFreeClustersDefined = (NumFreeClusters <= Header.FatSize);
+        NumFreeClusters = Get32a(buf32 + 488 / 4);
+        numFreeClusters_Defined = (NumFreeClusters <= Header.FatSize);
       }
       else
         Header.HeadersWarning = true;
     }
   }
 
-  // numFreeClustersDefined = false; // to recalculate NumFreeClusters
-  if (!numFreeClustersDefined)
+  // numFreeClusters_Defined = false; // to recalculate NumFreeClusters
+  if (!numFreeClusters_Defined)
     NumFreeClusters = 0;
 
   CByteBuffer byteBuf;
@@ -695,7 +836,7 @@
   RINOK(SeekToSector(Header.GetFatSector()))
   if (Header.NumFatBits == 32)
   {
-    const UInt32 kBufSize = (1 << 15);
+    const UInt32 kBufSize = 1 << 15;
     byteBuf.Alloc(kBufSize);
     for (UInt32 i = 0;;)
     {
@@ -712,7 +853,7 @@
       const UInt32 *src = (const UInt32 *)(const void *)(const Byte *)byteBuf;
       UInt32 *dest = Fat + i;
       const UInt32 *srcLim = src + size;
-      if (numFreeClustersDefined)
+      if (numFreeClusters_Defined)
         do
           *dest++ = Get32a(src) & 0x0FFFFFFF;
         while (++src != srcLim);
@@ -731,7 +872,7 @@
       i += size;
       if ((i & 0xFFFFF) == 0)
       {
-        RINOK(OpenProgressFat(!numFreeClustersDefined))
+        RINOK(OpenProgressFat(!numFreeClusters_Defined))
       }
     }
   }
@@ -751,7 +892,7 @@
       for (UInt32 j = 0; j < fatSize; j++)
         fat[j] = (Get16(p + j * 3 / 2) >> ((j & 1) << 2)) & 0xFFF;
 
-    if (!numFreeClustersDefined)
+    if (!numFreeClusters_Defined)
     {
       UInt32 numFreeClusters = 0;
       for (UInt32 i = 0; i < fatSize; i++)
@@ -781,11 +922,12 @@
 
 Z7_class_CHandler_final:
   public IInArchive,
+  public IArchiveGetRawProps,
   public IInArchiveGetStream,
   public CMyUnknownImp,
   CDatabase
 {
-  Z7_IFACES_IMP_UNK_2(IInArchive, IInArchiveGetStream)
+  Z7_IFACES_IMP_UNK_3(IInArchive, IArchiveGetRawProps, IInArchiveGetStream)
 };
 
 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
@@ -831,6 +973,8 @@
   COM_TRY_END
 }
 
+
+
 static const Byte kProps[] =
 {
   kpidPath,
@@ -842,6 +986,7 @@
   kpidATime,
   kpidAttrib,
   kpidShortName
+  // , kpidCharacts
 };
 
 enum
@@ -922,15 +1067,16 @@
     case kpidPhySize: prop = PhySize; break;
     case kpidFreeSpace: prop = (UInt64)NumFreeClusters << Header.ClusterSizeLog; break;
     case kpidHeadersSize: prop = GetHeadersSize(); break;
-    case kpidMTime: if (VolItemDefined) PropVariant_SetFrom_DosTime(prop, VolItem.MTime); break;
+    case kpidMTime: if (VolItem_Defined) PropVariant_SetFrom_DosTime(prop, Vol_MTime); break;
     case kpidShortComment:
-    case kpidVolumeName: if (VolItemDefined) prop = VolItem.GetVolName(); break;
+    case kpidVolumeName: if (VolItem_Defined) GetVolName(VolLabel, prop); break;
     case kpidNumFats: if (Header.NumFats != 2) prop = Header.NumFats; break;
     case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break;
     // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break;
     // case kpidNumHeads: prop = Header.NumHeads; break;
     // case kpidOemName: STRING_TO_PROP(Header.OemName, prop); break;
     case kpidId: if (Header.VolFieldsDefined) prop = Header.VolId; break;
+    case kpidIsTree: prop = true; break;
     // case kpidVolName: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.VolName, prop); break;
     // case kpidFileSysType: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.FileSys, prop); break;
     // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break;
@@ -948,6 +1094,52 @@
   COM_TRY_END
 }
 
+
+Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps))
+{
+  *numProps = 0;
+  return S_OK;
+}
+
+Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 /* index */ , BSTR *name, PROPID *propID))
+{
+  *name = NULL;
+  *propID = 0;
+  return S_OK;
+}
+
+Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType))
+{
+  *parentType = NParentType::kDir;
+  int par = -1;
+  if (index < Items.Size())
+    par = Items[index].Parent;
+  *parent = (UInt32)(Int32)par;
+  return S_OK;
+}
+
+Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
+{
+  *data = NULL;
+  *dataSize = 0;
+  *propType = 0;
+
+  if (index < Items.Size()
+      && propID == kpidName)
+  {
+    CByteBuffer &buf = Items[index].LongName;
+    const UInt32 size = (UInt32)buf.Size();
+    if (size != 0)
+    {
+      *dataSize = size;
+      *propType = NPropDataType::kUtf16z;
+      *data = (const void *)(const Byte *)buf;
+    }
+  }
+  return S_OK;
+}
+
+
 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
 {
   COM_TRY_BEGIN
@@ -955,8 +1147,28 @@
   const CItem &item = Items[index];
   switch (propID)
   {
-    case kpidPath: prop = GetItemPath(index); break;
-    case kpidShortName: prop = item.GetShortName(); break;
+    case kpidPath:
+    case kpidName:
+    case kpidShortName:
+    {
+      UString s;
+      if (propID == kpidPath)
+        GetItemPath(index, s);
+      else if (propID == kpidName)
+        item.GetName(s);
+      else
+        item.GetShortName(s);
+      prop = s;
+      break;
+    }
+/*
+    case kpidCharacts:
+    {
+      if (item.LongName.Size())
+        prop = "LFN";
+      break;
+    }
+*/
     case kpidIsDir: prop = item.IsDir(); break;
     case kpidMTime: PropVariant_SetFrom_DosTime(prop, item.MTime); break;
     case kpidCTime: FatTimeToProp(item.CTime, item.CTime2, prop); break;
@@ -1004,34 +1216,44 @@
     Int32 testMode, IArchiveExtractCallback *extractCallback))
 {
   COM_TRY_BEGIN
-  const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
-  if (allFilesMode)
+  if (numItems == (UInt32)(Int32)-1)
+  {
+    indices = NULL;
     numItems = Items.Size();
-  if (numItems == 0)
-    return S_OK;
-  UInt32 i;
+    if (numItems == 0)
+      return S_OK;
+  }
+  else
+  {
+    if (numItems == 0)
+      return S_OK;
+    if (!indices)
+      return E_INVALIDARG;
+  }
   UInt64 totalSize = 0;
-  for (i = 0; i < numItems; i++)
   {
-    const CItem &item = Items[allFilesMode ? i : indices[i]];
-    if (!item.IsDir())
-      totalSize += item.Size;
+    UInt32 i = 0;
+    do
+    {
+      UInt32 index = i;
+      if (indices)
+        index = indices[i];
+      const CItem &item = Items[index];
+      if (!item.IsDir())
+        totalSize += item.Size;
+    }
+    while (++i != numItems);
   }
   RINOK(extractCallback->SetTotal(totalSize))
 
-  UInt64 totalPackSize;
-  totalSize = totalPackSize = 0;
-  
-  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
-  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
-
-  CLocalProgress *lps = new CLocalProgress;
-  CMyComPtr<ICompressProgressInfo> progress = lps;
+  CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
   lps->Init(extractCallback, false);
+  CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
 
-  CDummyOutStream *outStreamSpec = new CDummyOutStream;
-  CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
+  UInt64 totalPackSize;
+  totalSize = totalPackSize = 0;
 
+  UInt32 i;
   for (i = 0;; i++)
   {
     lps->InSize = totalPackSize;
@@ -1039,46 +1261,45 @@
     RINOK(lps->SetCur())
     if (i == numItems)
       break;
-    CMyComPtr<ISequentialOutStream> realOutStream;
-    const Int32 askMode = testMode ?
-        NExtract::NAskMode::kTest :
-        NExtract::NAskMode::kExtract;
-    const UInt32 index = allFilesMode ? i : indices[i];
-    const CItem &item = Items[index];
-    RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
-
-    if (item.IsDir())
+    int res;
     {
+      CMyComPtr<ISequentialOutStream> realOutStream;
+      const Int32 askMode = testMode ?
+          NExtract::NAskMode::kTest :
+          NExtract::NAskMode::kExtract;
+      UInt32 index = i;
+      if (indices)
+        index = indices[i];
+      const CItem &item = Items[index];
+      RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
+        
+      if (item.IsDir())
+      {
+        RINOK(extractCallback->PrepareOperation(askMode))
+        RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
+        continue;
+      }
+      
+      totalPackSize += Header.GetFilePackSize(item.Size);
+      totalSize += item.Size;
+      
+      if (!testMode && !realOutStream)
+        continue;
       RINOK(extractCallback->PrepareOperation(askMode))
-      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
-      continue;
-    }
-
-    totalPackSize += Header.GetFilePackSize(item.Size);
-    totalSize += item.Size;
-
-    if (!testMode && !realOutStream)
-      continue;
-    RINOK(extractCallback->PrepareOperation(askMode))
-
-    outStreamSpec->SetStream(realOutStream);
-    realOutStream.Release();
-    outStreamSpec->Init();
-
-    int res = NExtract::NOperationResult::kDataError;
-    CMyComPtr<ISequentialInStream> inStream;
-    HRESULT hres = GetStream(index, &inStream);
-    if (hres != S_FALSE)
-    {
-      RINOK(hres)
-      if (inStream)
+      res = NExtract::NOperationResult::kDataError;
+      CMyComPtr<ISequentialInStream> inStream;
+      const HRESULT hres = GetStream(index, &inStream);
+      if (hres != S_FALSE)
       {
-        RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
-        if (copyCoderSpec->TotalSize == item.Size)
-          res = NExtract::NOperationResult::kOK;
+        RINOK(hres)
+        if (inStream)
+        {
+          RINOK(copyCoder.Interface()->Code(inStream, realOutStream, NULL, NULL, lps))
+          if (copyCoder->TotalSize == item.Size)
+            res = NExtract::NOperationResult::kOK;
+        }
       }
     }
-    outStreamSpec->ReleaseStream();
     RINOK(extractCallback->SetOperationResult(res))
   }
   return S_OK;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp	2023-12-11 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Nsis/NsisIn.cpp	2025-06-16 10:00:00.000000000 +0200
@@ -4005,7 +4005,7 @@
         AddParam_Var(params[0]);
         AString temp;
         ReadString2(temp, params[1]);
-        if (temp != "$TEMP")
+        if (!temp.IsEqualTo("$TEMP"))
           SpaceQuStr(temp);
         break;
       }
@@ -4410,7 +4410,7 @@
         }
         else
         {
-          if (func == "DllUnregisterServer")
+          if (func.IsEqualTo("DllUnregisterServer"))
           {
             s += "UnRegDLL";
             printFunc = false;
@@ -4418,7 +4418,7 @@
           else
           {
             s += "RegDLL";
-            if (func == "DllRegisterServer")
+            if (func.IsEqualTo("DllRegisterServer"))
               printFunc = false;
           }
           AddParam(params[0]);
@@ -4886,7 +4886,7 @@
             AddParam_Var(params[1]);
             AddParam(params[2]);
             AddParam(params[4]);
-            // if (params[2] == "0") AddCommentAndString("GetWinVer");
+            // if (params[2].IsEqualTo("0")) AddCommentAndString("GetWinVer");
           }
           else
             s += "GetOsInfo";
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/NtfsHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/NtfsHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/NtfsHandler.cpp	2024-03-28 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/NtfsHandler.cpp	2025-06-16 10:00:00.000000000 +0200
@@ -1907,7 +1907,7 @@
   for (i = 0; i < SecurityAttrs.Size(); i++)
   {
     const CAttr &attr = SecurityAttrs[i];
-    if (attr.Name == L"$SII")
+    if (attr.Name.IsEqualTo("$SII"))
     {
       if (attr.Type == ATTR_TYPE_INDEX_ROOT)
       {
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/PeHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/PeHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/PeHandler.cpp	2024-07-12 11:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/PeHandler.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -2638,7 +2638,7 @@
   {
     const CSection &sect = _sections[i];
     if (IsOpt())
-    if (_parseResources && sect.Name == ".rsrc")
+    if (_parseResources && sect.Name.IsEqualTo(".rsrc"))
     {
       // 20.01: we try to parse only first copy of .rsrc section.
       _parseResources = false;
@@ -2727,7 +2727,7 @@
   for (i = 0; i < _mixItems.Size(); i++)
   {
     const CMixItem &mixItem = _mixItems[i];
-    if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_")
+    if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name.IsEqualTo("_winzip_"))
     {
       _mainSubfile = (Int32)(int)i;
       break;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp	2024-11-26 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.cpp	2025-06-18 09:00:00.000000000 +0200
@@ -393,6 +393,7 @@
   if (!FindExtra_Link(link))
     return;
 
+  bool isWindows = (HostOS == kHost_Windows);
   if (link.Type != linkType)
   {
     if (linkType != NLinkType::kUnixSymLink)
@@ -400,8 +401,11 @@
     switch ((unsigned)link.Type)
     {
       case NLinkType::kUnixSymLink:
+        isWindows = false;
+        break;
       case NLinkType::kWinSymLink:
       case NLinkType::kWinJunction:
+        isWindows = true;
         break;
       default: return;
     }
@@ -409,10 +413,15 @@
 
   AString s;
   s.SetFrom_CalcLen((const char *)(Extra + link.NameOffset), link.NameLen);
-
   UString unicode;
   ConvertUTF8ToUnicode(s, unicode);
-  prop = NItemName::GetOsPath(unicode);
+  // rar5.0  used '\\' separator for windows symlinks and \??\ prefix for abs paths.
+  // rar5.1+ uses '/'  separator for windows symlinks and /??/ prefix for abs paths.
+  // v25.00: we convert Windows slashes to Linux slashes:
+  if (isWindows)
+    unicode.Replace(L'\\', L'/');
+  prop = unicode;
+  // prop = NItemName::GetOsPath(unicode);
 }
 
 bool CItem::GetAltStreamName(AString &name) const
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h 7zip-25.00+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h	2024-03-18 15:42:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Rar/Rar5Handler.h	2025-06-16 12:00:00.000000000 +0200
@@ -286,10 +286,10 @@
 
   bool IsService() const { return RecordType == NHeaderType::kService; }
   
-  bool Is_STM() const { return IsService() && Name == "STM"; }
-  bool Is_CMT() const { return IsService() && Name == "CMT"; }
-  bool Is_ACL() const { return IsService() && Name == "ACL"; }
-  // bool Is_QO()  const { return IsService() && Name == "QO"; }
+  bool Is_STM() const { return IsService() && Name.IsEqualTo("STM"); }
+  bool Is_CMT() const { return IsService() && Name.IsEqualTo("CMT"); }
+  bool Is_ACL() const { return IsService() && Name.IsEqualTo("ACL"); }
+  // bool Is_QO()  const { return IsService() && Name.IsEqualTo("QO"); }
 
   int FindExtra(unsigned extraID, unsigned &recordDataSize) const;
   void PrintInfo(AString &s) const;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp	2023-03-06 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Rar/RarHandler.cpp	2025-06-16 10:00:00.000000000 +0200
@@ -435,13 +435,13 @@
       size -= sizeof(item.Salt);
       p += sizeof(item.Salt);
     }
-    if (item.Name == "ACL" && size == 0)
+    if (item.Name.IsEqualTo("ACL") && size == 0)
     {
       item.IsAltStream = true;
       item.Name.Empty();
       item.UnicodeName.SetFromAscii(".ACL");
     }
-    else if (item.Name == "STM" && size != 0 && (size & 1) == 0)
+    else if (item.Name.IsEqualTo("STM") && size != 0 && (size & 1) == 0)
     {
       item.IsAltStream = true;
       item.Name.Empty();
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/RpmHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/RpmHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/RpmHandler.cpp	2023-12-18 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/RpmHandler.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -330,11 +330,11 @@
   if (!_compressor.IsEmpty())
   {
     s = _compressor;
-    if (_compressor == "bzip2")
+    if (_compressor.IsEqualTo("bzip2"))
       s = "bz2";
-    else if (_compressor == "gzip")
+    else if (_compressor.IsEqualTo("gzip"))
       s = "gz";
-    else if (_compressor == "zstd")
+    else if (_compressor.IsEqualTo("zstd"))
       s = "zst";
   }
   else
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/VmdkHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/VmdkHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/VmdkHandler.cpp	2023-09-07 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/VmdkHandler.cpp	2025-06-16 11:00:00.000000000 +0200
@@ -202,9 +202,12 @@
   // PartitionUUID
   // DeviceIdentifier
 
-  bool IsType_ZERO() const { return Type == "ZERO"; }
-  // bool IsType_FLAT() const { return Type == "FLAT"; }
-  bool IsType_Flat() const { return Type == "FLAT" || Type == "VMFS" || Type == "VMFSRAW"; }
+  bool IsType_ZERO() const { return Type.IsEqualTo("ZERO"); }
+  // bool IsType_FLAT() const { return Type.IsEqualTo("FLAT"); }
+  bool IsType_Flat() const
+    { return Type.IsEqualTo("FLAT")
+          || Type.IsEqualTo("VMFS")
+          || Type.IsEqualTo("VMFSRAW"); }
   
   bool Parse(const char *s);
 };
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp	2024-01-01 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Wim/WimIn.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -1814,7 +1814,7 @@
 
   if (!Xml.Parse(utf))
     return false;
-  if (Xml.Root.Name != "WIM")
+  if (!Xml.Root.Name.IsEqualTo("WIM"))
     return false;
 
   FOR_VECTOR (i, Xml.Root.SubItems)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/XarHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/XarHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/XarHandler.cpp	2024-11-12 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/XarHandler.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -266,7 +266,7 @@
 
   bool IsCopyMethod() const
   {
-    return Method.IsEmpty() || Method == "octet-stream";
+    return Method.IsEmpty() || Method.IsEqualTo("octet-stream");
   }
 
   void UpdateTotalPackSize(UInt64 &totalSize) const
@@ -416,7 +416,7 @@
     return true;
   if (level >= 1024)
     return false;
-  if (item.Name == "file")
+  if (item.Name.IsEqualTo("file"))
   {
     CFile file(parent);
     parent = (int)files.Size();
@@ -435,19 +435,19 @@
       {
         file.Type = typeItem->GetSubString();
         // file.LinkFrom = typeItem->GetPropVal("link");
-        if (file.Type == "directory")
+        if (file.Type.IsEqualTo("directory"))
           file.IsDir = true;
         else
         {
           // file.IsDir = false;
           /*
-          else if (file.Type == "file")
+          else if (file.Type.IsEqualTo("file"))
           {}
-          else if (file.Type == "hardlink")
+          else if (file.Type.IsEqualTo("hardlink"))
           {}
           else
           */
-          if (file.Type == "symlink")
+          if (file.Type.IsEqualTo("symlink"))
             file.Is_SymLink = true;
           // file.IsDir = false;
         }
@@ -489,7 +489,7 @@
             if (s.IsPrefixedBy(xx))
             {
               s.DeleteFrontal(xx.Len());
-              if (s == "gzip")
+              if (s.IsEqualTo("gzip"))
                 s = METHOD_NAME_ZLIB;
             }
           }
@@ -692,12 +692,13 @@
     file.UpdateTotalPackSize(totalPackSize);
     if (file.Parent == -1)
     {
-      if (file.Name == "Payload" || file.Name == "Content")
+      if (file.Name.IsEqualTo("Payload") ||
+          file.Name.IsEqualTo("Content"))
       {
         _mainSubfile = (Int32)(int)i;
         numMainFiles++;
       }
-      else if (file.Name == "PackageInfo")
+      else if (file.Name.IsEqualTo("PackageInfo"))
         _is_pkg = true;
     }
   }
@@ -1210,9 +1211,9 @@
           else
             opRes = NExtract::NOperationResult::kUnsupportedMethod;
         }
-        else if (item.Method == METHOD_NAME_ZLIB)
+        else if (item.Method.IsEqualTo(METHOD_NAME_ZLIB))
           coder = zlibCoder;
-        else if (item.Method == "bzip2")
+        else if (item.Method.IsEqualTo("bzip2"))
           coder = bzip2Coder;
         else
           opRes = NExtract::NOperationResult::kUnsupportedMethod;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/XzHandler.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/XzHandler.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/XzHandler.cpp	2024-10-02 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/XzHandler.cpp	2025-07-03 13:00:00.000000000 +0200
@@ -446,7 +446,7 @@
 struct CXzsCPP
 {
   CXzs p;
-  CXzsCPP() { Xzs_Construct(&p); }
+  CXzsCPP() { Xzs_CONSTRUCT(&p) }
   ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
 };
 
@@ -536,6 +536,9 @@
 
         if (res2 == SZ_ERROR_ARCHIVE)
           return S_FALSE;
+        // what codes are possible here ?
+        // ?? res2 == SZ_ERROR_MEM           : is possible here
+        // ?? res2 == SZ_ERROR_UNSUPPORTED   : is possible here
       }
       else if (!isIndex)
       {
@@ -1159,6 +1162,13 @@
     */
 
     #ifndef Z7_ST
+
+#ifdef _WIN32
+    // we don't use chunk multithreading inside lzma2 stream.
+    // so we don't set xzProps.lzma2Props.numThreadGroups.
+    if (_numThreadGroups > 1)
+      xzProps.numThreadGroups = _numThreadGroups;
+#endif
     
     UInt32 numThreads = _numThreads;
 
@@ -1183,6 +1193,8 @@
         CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, numThreads);
       }
 
+      // printf("\n====== GetProcessGroupAffinity : \n");
+
       UInt64 cs = _numSolidBytes;
       if (cs != XZ_PROPS_BLOCK_SIZE_AUTO)
         oneMethodInfo.AddProp_BlockSize2(cs);
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp 7zip-25.00+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp	2024-08-09 12:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Archive/Zip/ZipUpdate.cpp	2025-07-03 11:00:00.000000000 +0200
@@ -250,13 +250,26 @@
   
   HRESULT CreateEvents()
   {
-    WRes wres = CompressEvent.CreateIfNotCreated_Reset();
+    const WRes wres = CompressEvent.CreateIfNotCreated_Reset();
     return HRESULT_FROM_WIN32(wres);
   }
   
-  HRESULT CreateThread()
+  // (group < 0) means no_group.
+  HRESULT CreateThread_with_group(
+#ifdef _WIN32
+      int group
+#endif
+      )
   {
-    WRes wres = Thread.Create(CoderThread, this);
+    // tested in win10: If thread is created by another thread,
+    // child thread probably uses same group as parent thread.
+    // So we don't need to send (group) to encoder in created thread.
+    const WRes wres =
+#ifdef _WIN32
+      group >= 0 ?
+        Thread.Create_With_Group(CoderThread, this, (unsigned)group) :
+#endif
+        Thread.Create(CoderThread, this);
     return HRESULT_FROM_WIN32(wres);
   }
 
@@ -450,8 +463,12 @@
   if (ui.NewProps)
   {
     if (item.HasDescriptor())
-      return E_NOTIMPL;
-    
+    {
+      // we know compressed / uncompressed sizes and crc.
+      // so we remove descriptor here
+      item.Flags = (UInt16)(item.Flags & ~NFileHeader::NFlags::kDescriptorUsedMask);
+      // return E_NOTIMPL;
+    }
     // we keep ExternalAttrib and some another properties from old archive
     // item.ExternalAttrib = ui.Attrib;
     // if we don't change Comment, we keep Comment from OldProperties
@@ -1000,6 +1017,9 @@
   #ifndef Z7_ST
 
   UInt32 numThreads = options._numThreads;
+#ifdef _WIN32
+  const UInt32 numThreadGroups = options._numThreadGroups;
+#endif
 
   UInt32 numZipThreads_limit = numThreads;
   if (numZipThreads_limit > numFilesToCompress)
@@ -1014,12 +1034,10 @@
   }
 
   {
+    // we reduce number of threads for 32-bit to reduce memory usege to 256 MB
     const UInt32 kNumMaxThreads =
-      #ifdef _WIN32
-        64; // _WIN32 supports only 64 threads in one group. So no need for more threads here
-      #else
-        128;
-      #endif
+        // _WIN32 (64-bit) supports only 64 threads in one group.
+        8 << (sizeof(size_t) / 2); // 32 threads for 32-bit : 128 threads for 64-bit
     if (numThreads > kNumMaxThreads)
       numThreads = kNumMaxThreads;
   }
@@ -1264,7 +1282,14 @@
       threadInfo.Progress = threadInfo.ProgressSpec;
       threadInfo.ProgressSpec->Init(&mtCompressProgressMixer, i);
       threadInfo.MtSem = &mtSem;
-      RINOK(threadInfo.CreateThread())
+      const HRESULT hres =
+        threadInfo.CreateThread_with_group(
+#ifdef _WIN32
+          (numThreadGroups > 1 && numThreads > 1) ?
+            (int)(i % numThreadGroups) : -1
+#endif
+        );
+      RINOK(hres)
     }
   }
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone/makefile 7zip-25.00+dfsg/CPP/7zip/Bundles/Alone/makefile
--- 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone/makefile	2024-01-29 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Bundles/Alone/makefile	2025-07-02 09:00:00.000000000 +0200
@@ -5,6 +5,7 @@
 # CONSOLE_VARIANT_FLAGS=-DZ7_PROG_VARIANT_A
 # ZIP_FLAGS=-DZ7_ZIP_LZFSE_DISABLE
 
+# USE_C_SORT=1
 # USE_C_AES = 1
 # USE_C_SHA = 1
 # USE_C_LZFINDOPT = 1
@@ -221,7 +222,6 @@
   $O\Ppmd8.obj \
   $O\Ppmd8Dec.obj \
   $O\Ppmd8Enc.obj \
-  $O\Sort.obj \
   $O\SwapBytes.obj \
   $O\Threads.obj \
   $O\Xxh64.obj \
@@ -240,5 +240,6 @@
 !include "../../LzmaDec.mak"
 !include "../../Sha1.mak"
 !include "../../Sha256.mak"
+!include "../../Sort.mak"
 
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone7z/makefile 7zip-25.00+dfsg/CPP/7zip/Bundles/Alone7z/makefile
--- 7zip-24.09+dfsg/CPP/7zip/Bundles/Alone7z/makefile	2024-02-29 09:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Bundles/Alone7z/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -148,7 +148,6 @@
   $O\LzmaEnc.obj \
   $O\MtCoder.obj \
   $O\MtDec.obj \
-  $O\Sort.obj \
   $O\SwapBytes.obj \
   $O\Threads.obj \
   $O\Xz.obj \
@@ -164,5 +163,6 @@
 !include "../../LzFindOpt.mak"
 !include "../../LzmaDec.mak"
 !include "../../Sha256.mak"
+!include "../../Sort.mak"
 
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7z/makefile 7zip-25.00+dfsg/CPP/7zip/Bundles/Format7z/makefile
--- 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7z/makefile	2024-03-20 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Bundles/Format7z/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -135,7 +135,6 @@
   $O\Ppmd7.obj \
   $O\Ppmd7Dec.obj \
   $O\Ppmd7Enc.obj \
-  $O\Sort.obj \
   $O\SwapBytes.obj \
   $O\Threads.obj \
 
@@ -144,5 +143,6 @@
 !include "../../LzFindOpt.mak"
 !include "../../LzmaDec.mak"
 !include "../../Sha256.mak"
+!include "../../Sort.mak"
 
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak 7zip-25.00+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak
--- 7zip-24.09+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak	2024-11-25 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Bundles/Format7zF/Arc.mak	2025-07-01 17:00:00.000000000 +0200
@@ -291,7 +291,6 @@
   $O\Sha3.obj \
   $O\Sha512.obj \
   $O\Sha512Opt.obj \
-  $O\Sort.obj \
   $O\SwapBytes.obj \
   $O\Threads.obj \
   $O\Xxh64.obj \
@@ -308,3 +307,4 @@
 !include "../../LzmaDec.mak"
 !include "../../Sha1.mak"
 !include "../../Sha256.mak"
+!include "../../Sort.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp 7zip-25.00+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp	2024-03-20 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Bundles/SFXSetup/SfxSetup.cpp	2025-06-16 10:00:00.000000000 +0200
@@ -229,7 +229,7 @@
   }
 
   const FString tempDirPath = tempDir.GetPath();
-  // tempDirPath = L"M:\\1\\"; // to test low disk space
+  // tempDirPath = "M:\\1\\"; // to test low disk space
   {
     bool isCorrupt = false;
     UString errorMessage;
@@ -308,7 +308,7 @@
   {
     if (appLaunched.IsEmpty())
     {
-      appLaunched = L"setup.exe";
+      appLaunched = "setup.exe";
       if (!NFind::DoesFileExist_FollowLink(us2fs(appLaunched)))
       {
         if (!assumeYes)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/InBuffer.h 7zip-25.00+dfsg/CPP/7zip/Common/InBuffer.h
--- 7zip-24.09+dfsg/CPP/7zip/Common/InBuffer.h	2023-12-24 14:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Common/InBuffer.h	2025-02-20 09:00:00.000000000 +0100
@@ -97,6 +97,16 @@
   
   size_t ReadBytesPart(Byte *buf, size_t size);
   size_t ReadBytes(Byte *buf, size_t size);
+  const Byte *Lookahead(size_t &rem)
+  {
+    rem = (size_t)(_bufLim - _buf);
+    if (!rem)
+    {
+      ReadBlock();
+      rem = (size_t)(_bufLim - _buf);
+    }
+    return _buf;
+  }
   size_t Skip(size_t size);
 };
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.cpp 7zip-25.00+dfsg/CPP/7zip/Common/MethodProps.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.cpp	2024-02-26 09:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Common/MethodProps.cpp	2025-06-28 11:00:00.000000000 +0200
@@ -324,15 +324,22 @@
 
 HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const
 {
-  return SetCoderProps_DSReduce_Aff(scp, dataSizeReduce, NULL);
+  return SetCoderProps_DSReduce_Aff(scp, dataSizeReduce, NULL, NULL, NULL);
 }
 
 HRESULT CProps::SetCoderProps_DSReduce_Aff(
     ICompressSetCoderProperties *scp,
     const UInt64 *dataSizeReduce,
-    const UInt64 *affinity) const
-{
-  CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0) + (affinity ? 1 : 0) );
+    const UInt64 *affinity,
+    const UInt32 *affinityGroup,
+    const UInt64 *affinityInGroup) const
+{
+  CCoderProps coderProps(Props.Size()
+      + (dataSizeReduce ? 1 : 0)
+      + (affinity ? 1 : 0)
+      + (affinityGroup ? 1 : 0)
+      + (affinityInGroup ? 1 : 0)
+      );
   FOR_VECTOR (i, Props)
     coderProps.AddProp(Props[i]);
   if (dataSizeReduce)
@@ -349,6 +356,20 @@
     prop.Value = *affinity;
     coderProps.AddProp(prop);
   }
+  if (affinityGroup)
+  {
+    CProp prop;
+    prop.Id = NCoderPropID::kThreadGroup;
+    prop.Value = *affinityGroup;
+    coderProps.AddProp(prop);
+  }
+  if (affinityInGroup)
+  {
+    CProp prop;
+    prop.Id = NCoderPropID::kAffinityInGroup;
+    prop.Value = *affinityInGroup;
+    coderProps.AddProp(prop);
+  }
   return coderProps.SetProps(scp);
 }
 
@@ -409,6 +430,11 @@
   { VT_UI4, "offset" },
   { VT_UI4, "zhb" }
   /*
+  , { VT_UI4, "tgn" }, // kNumThreadGroups
+  , { VT_UI4, "tgi" }, // kThreadGroup
+  , { VT_UI8, "tga" }, // kAffinityInGroup
+  */
+  /*
   ,
   // { VT_UI4, "zhc" },
   // { VT_UI4, "zhd" },
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.h 7zip-25.00+dfsg/CPP/7zip/Common/MethodProps.h
--- 7zip-24.09+dfsg/CPP/7zip/Common/MethodProps.h	2024-11-11 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Common/MethodProps.h	2025-06-28 11:00:00.000000000 +0200
@@ -80,7 +80,11 @@
   }
 
   HRESULT SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce = NULL) const;
-  HRESULT SetCoderProps_DSReduce_Aff(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce, const UInt64 *affinity) const;
+  HRESULT SetCoderProps_DSReduce_Aff(ICompressSetCoderProperties *scp,
+      const UInt64 *dataSizeReduce,
+      const UInt64 *affinity,
+      const UInt32 *affinityGroup,
+      const UInt64 *affinityInGroup) const;
 };
 
 class CMethodProps: public CProps
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Common/OutBuffer.h 7zip-25.00+dfsg/CPP/7zip/Common/OutBuffer.h
--- 7zip-24.09+dfsg/CPP/7zip/Common/OutBuffer.h	2023-12-24 14:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Common/OutBuffer.h	2025-02-23 15:00:00.000000000 +0100
@@ -45,6 +45,7 @@
   HRESULT Flush() throw();
   void FlushWithCheck();
 
+  Z7_FORCE_INLINE
   void WriteByte(Byte b)
   {
     UInt32 pos = _pos;
@@ -54,10 +55,34 @@
     if (pos == _limitPos)
       FlushWithCheck();
   }
+  
   void WriteBytes(const void *data, size_t size)
   {
-    for (size_t i = 0; i < size; i++)
-      WriteByte(((const Byte *)data)[i]);
+    while (size)
+    {
+      UInt32 pos = _pos;
+      size_t cur = (size_t)(_limitPos - pos);
+      if (cur >= size)
+        cur = size;
+      size -= cur;
+      Byte *dest = _buf + pos;
+      pos += (UInt32)cur;
+      _pos = pos;
+#if 0
+      memcpy(dest, data, cur);
+      data = (const void *)((const Byte *)data + cur);
+#else
+      const Byte * const lim = (const Byte *)data + cur;
+      do
+      {
+        *dest++ = *(const Byte *)data;
+        data = (const void *)((const Byte *)data + 1);
+      }
+      while (data != lim);
+#endif
+      if (pos == _limitPos)
+        FlushWithCheck();
+    }
   }
 
   Byte *GetOutBuffer(size_t &avail)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BitlEncoder.h 7zip-25.00+dfsg/CPP/7zip/Compress/BitlEncoder.h
--- 7zip-24.09+dfsg/CPP/7zip/Compress/BitlEncoder.h	2023-01-10 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/BitlEncoder.h	2025-03-13 14:00:00.000000000 +0100
@@ -33,6 +33,7 @@
     _bitPos = 8;
     _curByte = 0;
   }
+  Z7_FORCE_INLINE
   void WriteBits(UInt32 value, unsigned numBits)
   {
     while (numBits > 0)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BitmEncoder.h 7zip-25.00+dfsg/CPP/7zip/Compress/BitmEncoder.h
--- 7zip-24.09+dfsg/CPP/7zip/Compress/BitmEncoder.h	2023-01-10 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/BitmEncoder.h	2025-02-28 07:00:00.000000000 +0100
@@ -8,8 +8,9 @@
 template<class TOutByte>
 class CBitmEncoder
 {
-  unsigned _bitPos;
-  Byte _curByte;
+  unsigned _bitPos;  // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte
+  unsigned _curByte; // low (_bitPos) bits are zeros
+                     // high (8 - _bitPos) bits are filled
   TOutByte _stream;
 public:
   bool Create(UInt32 bufferSize) { return _stream.Create(bufferSize); }
@@ -24,25 +25,65 @@
   HRESULT Flush()
   {
     if (_bitPos < 8)
-      WriteBits(0, _bitPos);
+    {
+      _stream.WriteByte((Byte)_curByte);
+      _bitPos = 8;
+      _curByte = 0;
+    }
     return _stream.Flush();
   }
+
+  // required condition: (value >> numBits) == 0
+  // numBits == 0 is allowed
   void WriteBits(UInt32 value, unsigned numBits)
   {
-    while (numBits > 0)
+    do
     {
-      if (numBits < _bitPos)
+      unsigned bp = _bitPos;
+      unsigned curByte = _curByte;
+      if (numBits < bp)
       {
-        _curByte = (Byte)(_curByte | (value << (_bitPos -= numBits)));
+        bp -= numBits;
+        _curByte = curByte | (value << bp);
+        _bitPos = bp;
         return;
       }
-      numBits -= _bitPos;
-      UInt32 newBits = (value >> numBits);
-      value -= (newBits << numBits);
-      _stream.WriteByte((Byte)(_curByte | newBits));
+      numBits -= bp;
+      const UInt32 hi = (value >> numBits);
+      value -= (hi << numBits);
+      _stream.WriteByte((Byte)(curByte | hi));
       _bitPos = 8;
       _curByte = 0;
     }
+    while (numBits);
+  }
+  void WriteByte(unsigned b)
+  {
+    const unsigned bp = _bitPos;
+    const unsigned a = _curByte | (b >> (8 - bp));
+    _curByte = b << bp;
+   _stream.WriteByte((Byte)a);
+  }
+
+  void WriteBytes(const Byte *data, size_t num)
+  {
+    const unsigned bp = _bitPos;
+#if 1 // 1 for optional speed-optimized code branch
+    if (bp == 8)
+    {
+      _stream.WriteBytes(data, num);
+      return;
+    }
+#endif
+    unsigned c = _curByte;
+    const unsigned bp_rev = 8 - bp;
+    for (size_t i = 0; i < num; i++)
+    {
+      const unsigned b = data[i];
+      _stream.WriteByte((Byte)(c | (b >> bp_rev)));
+      c = b << bp;
+    }
+    _curByte = c;
   }
 };
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Const.h 7zip-25.00+dfsg/CPP/7zip/Compress/BZip2Const.h
--- 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Const.h	2023-01-10 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/BZip2Const.h	2024-12-17 12:00:00.000000000 +0100
@@ -46,7 +46,7 @@
 const UInt32 kBlockSizeMax = kBlockSizeMultMax * kBlockSizeStep;
 
 const unsigned kNumSelectorsBits = 15;
-const UInt32 kNumSelectorsMax = (2 + (kBlockSizeMax / kGroupSize));
+const unsigned kNumSelectorsMax = 2 + kBlockSizeMax / kGroupSize;
 
 const unsigned kRleModeRepSize = 4;
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp 7zip-25.00+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp	2023-01-30 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/BZip2Encoder.cpp	2025-06-30 17:00:00.000000000 +0200
@@ -6,18 +6,20 @@
 #include "../../../C/BwtSort.h"
 #include "../../../C/HuffEnc.h"
 
-#include "BZip2Crc.h"
 #include "BZip2Encoder.h"
-#include "Mtf8.h"
 
 namespace NCompress {
 namespace NBZip2 {
 
-const unsigned kMaxHuffmanLenForEncoding = 16; // it must be < kMaxHuffmanLen = 20
-
-static const UInt32 kBufferSize = (1 << 17);
+#define HUFFMAN_LEN 16
+#if HUFFMAN_LEN > Z7_HUFFMAN_LEN_MAX
+  #error Stop_Compiling_Bad_HUFFMAN_LEN_BZip2Encoder
+#endif
+  
+static const size_t kBufferSize = 1 << 17;
 static const unsigned kNumHuffPasses = 4;
 
+
 bool CThreadInfo::Alloc()
 {
   if (!m_BlockSorterIndex)
@@ -27,11 +29,15 @@
       return false;
   }
 
-  if (!m_Block)
+  if (!m_Block_Base)
   {
-    m_Block = (Byte *)::MidAlloc(kBlockSizeMax * 5 + kBlockSizeMax / 10 + (20 << 10));
-    if (!m_Block)
+    const unsigned kPadSize = 1 << 7; // we need at least 1 byte backward padding, becuase we use (m_Block - 1) pointer;
+    m_Block_Base = (Byte *)::MidAlloc(kBlockSizeMax * 5
+        + kBlockSizeMax / 10 + (20 << 10)
+        + kPadSize);
+    if (!m_Block_Base)
       return false;
+    m_Block = m_Block_Base + kPadSize;
     m_MtfArray = m_Block + kBlockSizeMax;
     m_TempArray = m_MtfArray + kBlockSizeMax * 2 + 2;
   }
@@ -42,8 +48,8 @@
 {
   ::BigFree(m_BlockSorterIndex);
   m_BlockSorterIndex = NULL;
-  ::MidFree(m_Block);
-  m_Block = NULL;
+  ::MidFree(m_Block_Base);
+  m_Block_Base = NULL;
 }
 
 #ifndef Z7_ST
@@ -60,6 +66,14 @@
   if (wres == 0) { wres = CanWriteEvent.Create();
   if (wres == 0)
   {
+#ifdef _WIN32
+    if (Encoder->_props.NumThreadGroups != 0)
+    {
+       const UInt32 group = ThreadNextGroup_GetNext(&Encoder->ThreadNextGroup);
+      wres = Thread.Create_With_Group(MFThread, this, group, 0); // affinity
+    }
+    else
+#endif
     if (Encoder->_props.Affinity != 0)
       wres = Thread.Create_With_Affinity(MFThread, this, (CAffinityMask)Encoder->_props.Affinity);
     else
@@ -216,94 +230,251 @@
 }
 #endif
 
+struct CRleEncoder
+{
+  const Byte *_src;
+  const Byte *_srcLim;
+  Byte *_dest;
+  const Byte *_destLim;
+  Byte _prevByte;
+  unsigned _numReps;
+
+  void Encode();
+};
+
+Z7_NO_INLINE
+void CRleEncoder::Encode()
+{
+  const Byte *src = _src;
+  const Byte * const srcLim = _srcLim;
+  Byte *dest = _dest;
+  const Byte * const destLim = _destLim;
+  Byte prev = _prevByte;
+  unsigned numReps = _numReps;
+  // (dest < destLim)
+  // src = srcLim; // for debug
+  while (dest < destLim)
+  {
+    if (src == srcLim)
+      break;
+    const Byte b = *src++;
+    if (b != prev)
+    {
+      if (numReps >= kRleModeRepSize)
+        *dest++ = (Byte)(numReps - kRleModeRepSize);
+      *dest++ = b;
+      numReps = 1;
+      prev = b;
+      /*
+      { // speed optimization code:
+        if (dest >= destLim || src == srcLim)
+          break;
+        const Byte b2 = *src++;
+        *dest++ = b2;
+        numReps += (prev == b2);
+        prev = b2;
+      }
+      */
+      continue;
+    }
+    numReps++;
+    if (numReps <= kRleModeRepSize)
+      *dest++ = b;
+    else if (numReps == kRleModeRepSize + 255)
+    {
+      *dest++ = (Byte)(numReps - kRleModeRepSize);
+      numReps = 0;
+    }
+  }
+  _src = src;
+  _dest = dest;
+  _prevByte = prev;
+  _numReps = numReps;
+  // (dest <= destLim + 1)
+}
+
+
+// out: return value is blockSize: size of data filled in buffer[]:
+// (returned_blockSize <= _props.BlockSizeMult * kBlockSizeStep)
 UInt32 CEncoder::ReadRleBlock(Byte *buffer)
 {
+  CRleEncoder rle;
   UInt32 i = 0;
-  Byte prevByte;
-  if (m_InStream.ReadByte(prevByte))
+  if (m_InStream.ReadByte(rle._prevByte))
   {
     NumBlocks++;
-    const UInt32 blockSize = _props.BlockSizeMult * kBlockSizeStep - 1;
-    unsigned numReps = 1;
-    buffer[i++] = prevByte;
-    while (i < blockSize) // "- 1" to support RLE
-    {
-      Byte b;
-      if (!m_InStream.ReadByte(b))
+    const UInt32 blockSize = _props.BlockSizeMult * kBlockSizeStep - 1; // -1 for RLE
+    rle._destLim = buffer + blockSize;
+    rle._numReps = 1;
+    buffer[i++] = rle._prevByte;
+    while (i < blockSize)
+    {
+      rle._dest = buffer + i;
+      size_t rem;
+      const Byte * const ptr = m_InStream.Lookahead(rem);
+      if (rem == 0)
         break;
-      if (b != prevByte)
-      {
-        if (numReps >= kRleModeRepSize)
-          buffer[i++] = (Byte)(numReps - kRleModeRepSize);
-        buffer[i++] = b;
-        numReps = 1;
-        prevByte = b;
-        continue;
-      }
-      numReps++;
-      if (numReps <= kRleModeRepSize)
-        buffer[i++] = b;
-      else if (numReps == kRleModeRepSize + 255)
-      {
-        buffer[i++] = (Byte)(numReps - kRleModeRepSize);
-        numReps = 0;
-      }
-    }
-    // it's to support original BZip2 decoder
-    if (numReps >= kRleModeRepSize)
-      buffer[i++] = (Byte)(numReps - kRleModeRepSize);
+      rle._src = ptr;
+      rle._srcLim = ptr + rem;
+      rle.Encode();
+      m_InStream.Skip((size_t)(rle._src - ptr));
+      i = (UInt32)(size_t)(rle._dest - buffer);
+      // (i <= blockSize + 1)
+    }
+    const int n = (int)rle._numReps - (int)kRleModeRepSize;
+    if (n >= 0)
+      buffer[i++] = (Byte)n;
   }
   return i;
 }
 
-void CThreadInfo::WriteBits2(UInt32 value, unsigned numBits) { m_OutStreamCurrent->WriteBits(value, numBits); }
-void CThreadInfo::WriteByte2(Byte b) { WriteBits2(b, 8); }
-void CThreadInfo::WriteBit2(Byte v) { WriteBits2(v, 1); }
-void CThreadInfo::WriteCrc2(UInt32 v)
-{
-  for (unsigned i = 0; i < 4; i++)
-    WriteByte2(((Byte)(v >> (24 - i * 8))));
-}
-
-void CEncoder::WriteBits(UInt32 value, unsigned numBits) { m_OutStream.WriteBits(value, numBits); }
-void CEncoder::WriteByte(Byte b) { WriteBits(b, 8); }
-// void CEncoder::WriteBit(Byte v) { WriteBits(v, 1); }
-void CEncoder::WriteCrc(UInt32 v)
-{
-  for (unsigned i = 0; i < 4; i++)
-    WriteByte(((Byte)(v >> (24 - i * 8))));
+
+
+Z7_NO_INLINE
+void CThreadInfo::WriteBits2(UInt32 value, unsigned numBits)
+  { m_OutStreamCurrent.WriteBits(value, numBits); }
+/*
+Z7_NO_INLINE
+void CThreadInfo::WriteByte2(unsigned b)
+  { m_OutStreamCurrent.WriteByte(b); }
+*/
+// void CEncoder::WriteBits(UInt32 value, unsigned numBits) { m_OutStream.WriteBits(value, numBits); }
+Z7_NO_INLINE
+void CEncoder::WriteByte(Byte b) { m_OutStream.WriteByte(b); }
+
+
+#define WRITE_BITS_UPDATE(value, numBits) \
+{ \
+  numBits -= _bitPos; \
+  const UInt32 hi = value >> numBits; \
+  *_buf++ = (Byte)(_curByte | hi); \
+  value -= hi << numBits; \
+  _bitPos = 8; \
+  _curByte = 0; \
+}
+
+#if HUFFMAN_LEN > 16
+
+#define WRITE_BITS_HUFF(value2, numBits2) \
+{ \
+  UInt32 value = value2; \
+  unsigned numBits = numBits2; \
+  while (numBits >= _bitPos) { \
+    WRITE_BITS_UPDATE(value, numBits) \
+  } \
+  _bitPos -= numBits; \
+  _curByte |= (value << _bitPos); \
+}
+
+#else // HUFFMAN_LEN <= 16
+
+// numBits2 <= 16 is supported
+#define WRITE_BITS_HUFF(value2, numBits2) \
+{ \
+  UInt32 value = value2; \
+  unsigned numBits = numBits2; \
+  if (numBits >= _bitPos) \
+  { \
+    WRITE_BITS_UPDATE(value, numBits) \
+    if (numBits >= _bitPos) \
+    { \
+      numBits -= _bitPos; \
+      const UInt32 hi = value >> numBits; \
+      *_buf++ = (Byte)hi; \
+      value -= hi << numBits; \
+    } \
+  } \
+  _bitPos -= numBits; \
+  _curByte |= (value << _bitPos); \
+}
+
+#endif
+
+#define WRITE_BITS_8(value2, numBits2) \
+{ \
+  UInt32 value = value2; \
+  unsigned numBits = numBits2; \
+  if (numBits >= _bitPos) \
+  { \
+    WRITE_BITS_UPDATE(value, numBits) \
+  } \
+  _bitPos -= numBits; \
+  _curByte |= (value << _bitPos); \
+}
+
+#define WRITE_BIT_PRE \
+  { _bitPos--; }
+
+#define WRITE_BIT_POST \
+{ \
+  if (_bitPos == 0) \
+  { \
+    *_buf++ = (Byte)_curByte; \
+    _curByte = 0; \
+    _bitPos = 8; \
+  } \
+}
+
+#define WRITE_BIT_0 \
+{ \
+  WRITE_BIT_PRE \
+  WRITE_BIT_POST \
+}
+
+#define WRITE_BIT_1 \
+{ \
+  WRITE_BIT_PRE \
+  _curByte |= 1u << _bitPos; \
+  WRITE_BIT_POST \
 }
 
 
 // blockSize > 0
 void CThreadInfo::EncodeBlock(const Byte *block, UInt32 blockSize)
 {
-  WriteBit2(0); // Randomised = false
-  
+  // WriteBit2(0); // Randomised = false
   {
-    UInt32 origPtr = BlockSort(m_BlockSorterIndex, block, blockSize);
+    const UInt32 origPtr = BlockSort(m_BlockSorterIndex, block, blockSize);
     // if (m_BlockSorterIndex[origPtr] != 0) throw 1;
     m_BlockSorterIndex[origPtr] = blockSize;
-    WriteBits2(origPtr, kNumOrigBits);
+    WriteBits2(origPtr, kNumOrigBits + 1); // + 1 for additional high bit flag (Randomised = false)
   }
-
-  CMtf8Encoder mtf;
-  unsigned numInUse = 0;
+  Byte mtfBuf[256];
+  // memset(mtfBuf, 0, sizeof(mtfBuf)); // to disable MSVC warning
+  unsigned numInUse;
   {
     Byte inUse[256];
     Byte inUse16[16];
-    UInt32 i;
+    unsigned i;
     for (i = 0; i < 256; i++)
       inUse[i] = 0;
     for (i = 0; i < 16; i++)
       inUse16[i] = 0;
-    for (i = 0; i < blockSize; i++)
-      inUse[block[i]] = 1;
+    {
+      const Byte *       cur = block;
+      block = block + (size_t)blockSize - 1;
+      if (cur != block)
+      {
+        do
+        {
+          const unsigned b0 = cur[0];
+          const unsigned b1 = cur[1];
+          cur += 2;
+          inUse[b0] = 1;
+          inUse[b1] = 1;
+        }
+        while (cur < block);
+      }
+      if (cur == block)
+        inUse[cur[0]] = 1;
+      block -= blockSize; // block pointer is (original_block - 1)
+    }
+    numInUse = 0;
     for (i = 0; i < 256; i++)
       if (inUse[i])
       {
         inUse16[i >> 4] = 1;
-        mtf.Buf[numInUse++] = (Byte)i;
+        mtfBuf[numInUse++] = (Byte)i;
       }
     for (i = 0; i < 16; i++)
       WriteBit2(inUse16[i]);
@@ -311,65 +482,88 @@
       if (inUse16[i >> 4])
         WriteBit2(inUse[i]);
   }
-  unsigned alphaSize = numInUse + 2;
+  const unsigned alphaSize = numInUse + 2;
 
-  Byte *mtfs = m_MtfArray;
-  UInt32 mtfArraySize = 0;
   UInt32 symbolCounts[kMaxAlphaSize];
   {
     for (unsigned i = 0; i < kMaxAlphaSize; i++)
       symbolCounts[i] = 0;
+    symbolCounts[(size_t)alphaSize - 1] = 1;
   }
 
+  Byte *mtfs = m_MtfArray;
   {
-    UInt32 rleSize = 0;
-    UInt32 i = 0;
     const UInt32 *bsIndex = m_BlockSorterIndex;
-    block--;
+    const UInt32 *bsIndex_rle = bsIndex;
+    const UInt32 * const bsIndex_end = bsIndex + blockSize;
+    // block--; // backward fix
+    // block pointer is (original_block - 1)
     do
     {
-      unsigned pos = mtf.FindAndMove(block[bsIndex[i]]);
-      if (pos == 0)
-        rleSize++;
-      else
+      const Byte v = block[*bsIndex++];
+      Byte a = mtfBuf[0];
+      if (v != a)
       {
-        while (rleSize != 0)
+        mtfBuf[0] = v;
         {
-          rleSize--;
-          mtfs[mtfArraySize++] = (Byte)(rleSize & 1);
-          symbolCounts[rleSize & 1]++;
-          rleSize >>= 1;
+          UInt32 rleSize = (UInt32)(size_t)(bsIndex - bsIndex_rle) - 1;
+          bsIndex_rle = bsIndex;
+          while (rleSize)
+          {
+            const unsigned sym = (unsigned)(--rleSize & 1);
+            *mtfs++ = (Byte)sym;
+            symbolCounts[sym]++;
+            rleSize >>= 1;
+          }
         }
-        if (pos >= 0xFE)
+        unsigned pos1 = 2; // = real_pos + 1
+        Byte b;
+               b = mtfBuf[1];  mtfBuf[1] = a;  if (v != b)
+             { a = mtfBuf[2];  mtfBuf[2] = b;  if (v == a) pos1 = 3;
+        else { b = mtfBuf[3];  mtfBuf[3] = a;  if (v == b) pos1 = 4;
+        else
+        {
+          Byte *m = mtfBuf + 7;
+          for (;;)
+          {
+            a = m[-3];  m[-3] = b;           if (v == a) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 2)); break; }
+            b = m[-2];  m[-2] = a;           if (v == b) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 1)); break; }
+            a = m[-1];  m[-1] = b;           if (v == a) { pos1 = (unsigned)(size_t)(m - (mtfBuf    )); break; }
+            b = m[ 0];  m[ 0] = a;  m += 4;  if (v == b) { pos1 = (unsigned)(size_t)(m - (mtfBuf + 3)); break; }
+          }
+        }}}
+        symbolCounts[pos1]++;
+        if (pos1 >= 0xff)
         {
-          mtfs[mtfArraySize++] = 0xFF;
-          mtfs[mtfArraySize++] = (Byte)(pos - 0xFE);
+          *mtfs++ = 0xff;
+          // pos1 -= 0xff;
+          pos1++; // we need only low byte
         }
-        else
-          mtfs[mtfArraySize++] = (Byte)(pos + 1);
-        symbolCounts[(size_t)pos + 1]++;
+        *mtfs++ = (Byte)pos1;
       }
     }
-    while (++i < blockSize);
+    while (bsIndex < bsIndex_end);
 
-    while (rleSize != 0)
+    UInt32 rleSize = (UInt32)(size_t)(bsIndex - bsIndex_rle);
+    while (rleSize)
     {
-      rleSize--;
-      mtfs[mtfArraySize++] = (Byte)(rleSize & 1);
-      symbolCounts[rleSize & 1]++;
+      const unsigned sym = (unsigned)(--rleSize & 1);
+      *mtfs++ = (Byte)sym;
+      symbolCounts[sym]++;
       rleSize >>= 1;
     }
-
-    if (alphaSize < 256)
-      mtfs[mtfArraySize++] = (Byte)(alphaSize - 1);
-    else
+    
+    unsigned d = alphaSize - 1;
+    if (alphaSize >= 256)
     {
-      mtfs[mtfArraySize++] = 0xFF;
-      mtfs[mtfArraySize++] = (Byte)(alphaSize - 256);
+      *mtfs++ = 0xff;
+      d = alphaSize; // (-256)
     }
-    symbolCounts[(size_t)alphaSize - 1]++;
+    *mtfs++ = (Byte)d;
   }
 
+  const Byte * const mtf_lim = mtfs;
+
   UInt32 numSymbols = 0;
   {
     for (unsigned i = 0; i < kMaxAlphaSize; i++)
@@ -378,34 +572,30 @@
 
   unsigned bestNumTables = kNumTablesMin;
   UInt32 bestPrice = 0xFFFFFFFF;
-  UInt32 startPos = m_OutStreamCurrent->GetPos();
-  Byte startCurByte = m_OutStreamCurrent->GetCurByte();
+  const UInt32 startPos = m_OutStreamCurrent.GetPos();
+  const unsigned startCurByte = m_OutStreamCurrent.GetCurByte();
   for (unsigned nt = kNumTablesMin; nt <= kNumTablesMax + 1; nt++)
   {
     unsigned numTables;
 
     if (m_OptimizeNumTables)
     {
-      m_OutStreamCurrent->SetPos(startPos);
-      m_OutStreamCurrent->SetCurState((startPos & 7), startCurByte);
-      if (nt <= kNumTablesMax)
-        numTables = nt;
-      else
-        numTables = bestNumTables;
+      m_OutStreamCurrent.SetPos(startPos);
+      m_OutStreamCurrent.SetCurState(startPos & 7, startCurByte);
+      numTables = (nt <= kNumTablesMax ? nt : bestNumTables);
     }
     else
     {
-      if (numSymbols < 200)  numTables = 2;
-      else if (numSymbols < 600) numTables = 3;
+           if (numSymbols <  200) numTables = 2;
+      else if (numSymbols <  600) numTables = 3;
       else if (numSymbols < 1200) numTables = 4;
       else if (numSymbols < 2400) numTables = 5;
-      else numTables = 6;
+      else                        numTables = 6;
     }
 
     WriteBits2(numTables, kNumTablesBits);
-    
-    UInt32 numSelectors = (numSymbols + kGroupSize - 1) / kGroupSize;
-    WriteBits2(numSelectors, kNumSelectorsBits);
+    const unsigned numSelectors = (numSymbols + kGroupSize - 1) / kGroupSize;
+    WriteBits2((UInt32)numSelectors, kNumSelectorsBits);
     
     {
       UInt32 remFreq = numSymbols;
@@ -436,28 +626,23 @@
     
     for (unsigned pass = 0; pass < kNumHuffPasses; pass++)
     {
+      memset(Freqs, 0, sizeof(Freqs[0]) * numTables);
+      // memset(Freqs, 0, sizeof(Freqs));
       {
-        unsigned t = 0;
-        do
-          memset(Freqs[t], 0, sizeof(Freqs[t]));
-        while (++t < numTables);
-      }
-      
-      {
-        UInt32 mtfPos = 0;
+        mtfs = m_MtfArray;
         UInt32 g = 0;
         do
         {
-          UInt32 symbols[kGroupSize];
+          unsigned symbols[kGroupSize];
           unsigned i = 0;
           do
           {
-            UInt32 symbol = mtfs[mtfPos++];
+            UInt32 symbol = *mtfs++;
             if (symbol >= 0xFF)
-              symbol += mtfs[mtfPos++];
+              symbol += *mtfs++;
             symbols[i] = symbol;
           }
-          while (++i < kGroupSize && mtfPos < mtfArraySize);
+          while (++i < kGroupSize && mtfs < mtf_lim);
           
           UInt32 bestPrice2 = 0xFFFFFFFF;
           unsigned t = 0;
@@ -482,7 +667,7 @@
             freqs[symbols[j]]++;
           while (++j < i);
         }
-        while (mtfPos < mtfArraySize);
+        while (mtfs < mtf_lim);
       }
       
       unsigned t = 0;
@@ -494,11 +679,15 @@
           if (freqs[i] == 0)
             freqs[i] = 1;
         while (++i < alphaSize);
-        Huffman_Generate(freqs, Codes[t], Lens[t], kMaxAlphaSize, kMaxHuffmanLenForEncoding);
+        Huffman_Generate(freqs, Codes[t], Lens[t], kMaxAlphaSize, HUFFMAN_LEN);
       }
       while (++t < numTables);
     }
     
+    unsigned _bitPos;  // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte
+    unsigned _curByte; // low (_bitPos) bits are zeros
+                       // high (8 - _bitPos) bits are filled
+    Byte *_buf;
     {
       Byte mtfSel[kNumTablesMax];
       {
@@ -507,81 +696,97 @@
           mtfSel[t] = (Byte)t;
         while (++t < numTables);
       }
+
+      _bitPos = m_OutStreamCurrent._bitPos;
+      _curByte = m_OutStreamCurrent._curByte;
+      _buf = m_OutStreamCurrent._buf;
+      // stream.Init_from_Global(m_OutStreamCurrent);
       
-      UInt32 i = 0;
+      const Byte *selectors = m_Selectors;
+      const Byte * const selectors_lim = selectors + numSelectors;
+      Byte prev = 0; // mtfSel[0];
       do
       {
-        Byte sel = m_Selectors[i];
-        unsigned pos;
-        for (pos = 0; mtfSel[pos] != sel; pos++)
-          WriteBit2(1);
-        WriteBit2(0);
-        for (; pos > 0; pos--)
-          mtfSel[pos] = mtfSel[(size_t)pos - 1];
-        mtfSel[0] = sel;
+        const Byte sel = *selectors++;
+        if (prev != sel)
+        {
+          Byte *mtfSel_cur = &mtfSel[1];
+          for (;;)
+          {
+            WRITE_BIT_1
+            const Byte next = *mtfSel_cur;
+            *mtfSel_cur++ = prev;
+            prev = next;
+            if (next == sel)
+              break;
+          }
+          // mtfSel[0] = sel;
+        }
+        WRITE_BIT_0
       }
-      while (++i < numSelectors);
+      while (selectors != selectors_lim);
     }
-    
     {
       unsigned t = 0;
       do
       {
         const Byte *lens = Lens[t];
-        UInt32 len = lens[0];
-        WriteBits2(len, kNumLevelsBits);
+        unsigned len = lens[0];
+        WRITE_BITS_8(len, kNumLevelsBits)
         unsigned i = 0;
         do
         {
-          UInt32 level = lens[i];
+          const unsigned level = lens[i];
           while (len != level)
           {
-            WriteBit2(1);
+            WRITE_BIT_1
             if (len < level)
             {
-              WriteBit2(0);
               len++;
+              WRITE_BIT_0
             }
             else
             {
-              WriteBit2(1);
               len--;
+              WRITE_BIT_1
             }
           }
-          WriteBit2(0);
+          WRITE_BIT_0
         }
         while (++i < alphaSize);
       }
       while (++t < numTables);
     }
-    
     {
-      UInt32 groupSize = 0;
-      UInt32 groupIndex = 0;
+      UInt32 groupSize = 1;
+      const Byte *selectors = m_Selectors;
       const Byte *lens = NULL;
       const UInt32 *codes = NULL;
-      UInt32 mtfPos = 0;
+      mtfs = m_MtfArray;
       do
       {
-        UInt32 symbol = mtfs[mtfPos++];
+        unsigned symbol = *mtfs++;
         if (symbol >= 0xFF)
-          symbol += mtfs[mtfPos++];
-        if (groupSize == 0)
+          symbol += *mtfs++;
+        if (--groupSize == 0)
         {
           groupSize = kGroupSize;
-          unsigned t = m_Selectors[groupIndex++];
+          const unsigned t = *selectors++;
           lens = Lens[t];
           codes = Codes[t];
         }
-        groupSize--;
-        m_OutStreamCurrent->WriteBits(codes[symbol], lens[symbol]);
+        WRITE_BITS_HUFF(codes[symbol], lens[symbol])
       }
-      while (mtfPos < mtfArraySize);
+      while (mtfs < mtf_lim);
     }
+    // Restore_from_Local:
+    m_OutStreamCurrent._bitPos = _bitPos;
+    m_OutStreamCurrent._curByte = _curByte;
+    m_OutStreamCurrent._buf = _buf;
 
     if (!m_OptimizeNumTables)
       break;
-    UInt32 price = m_OutStreamCurrent->GetPos() - startPos;
+    const UInt32 price = m_OutStreamCurrent.GetPos() - startPos;
     if (price <= bestPrice)
     {
       if (nt == kNumTablesMax)
@@ -592,6 +797,7 @@
   }
 }
 
+
 // blockSize > 0
 UInt32 CThreadInfo::EncodeBlockWithHeaders(const Byte *block, UInt32 blockSize)
 {
@@ -603,148 +809,134 @@
   WriteByte2(kBlockSig5);
 
   CBZip2Crc crc;
-  unsigned numReps = 0;
-  Byte prevByte = block[0];
-  UInt32 i = 0;
-  do
+  const Byte * const lim = block + blockSize;
+  unsigned b = *block++;
+  crc.UpdateByte(b);
+  for (;;)
   {
-    Byte b = block[i];
-    if (numReps == kRleModeRepSize)
-    {
-      for (; b > 0; b--)
-        crc.UpdateByte(prevByte);
-      numReps = 0;
-      continue;
-    }
-    if (prevByte == b)
-      numReps++;
-    else
-    {
-      numReps = 1;
-      prevByte = b;
-    }
-    crc.UpdateByte(b);
-  }
-  while (++i < blockSize);
-  UInt32 crcRes = crc.GetDigest();
-  WriteCrc2(crcRes);
-  EncodeBlock(block, blockSize);
+    const unsigned prev = b;
+    if (block >= lim) { break; } b = *block++;  crc.UpdateByte(b);  if (prev != b) continue;
+    if (block >= lim) { break; } b = *block++;  crc.UpdateByte(b);  if (prev != b) continue;
+    if (block >= lim) { break; } b = *block++;  crc.UpdateByte(b);  if (prev != b) continue;
+    if (block >= lim) { break; } b = *block++;  if (b) do crc.UpdateByte(prev); while (--b);
+    if (block >= lim) { break; } b = *block++;  crc.UpdateByte(b);
+  }
+  const UInt32 crcRes = crc.GetDigest();
+  for (int i = 24; i >= 0; i -= 8)
+    WriteByte2((Byte)(crcRes >> i));
+  EncodeBlock(lim - blockSize, blockSize);
   return crcRes;
 }
 
+
 void CThreadInfo::EncodeBlock2(const Byte *block, UInt32 blockSize, UInt32 numPasses)
 {
-  UInt32 numCrcs = m_NumCrcs;
-  bool needCompare = false;
+  const UInt32 numCrcs = m_NumCrcs;
 
-  UInt32 startBytePos = m_OutStreamCurrent->GetBytePos();
-  UInt32 startPos = m_OutStreamCurrent->GetPos();
-  Byte startCurByte = m_OutStreamCurrent->GetCurByte();
-  Byte endCurByte = 0;
-  UInt32 endPos = 0;
+  const UInt32 startBytePos = m_OutStreamCurrent.GetBytePos();
+  const UInt32 startPos = m_OutStreamCurrent.GetPos();
+  const unsigned startCurByte = m_OutStreamCurrent.GetCurByte();
+  unsigned endCurByte = 0;
+  UInt32 endPos = 0; // 0 means no no additional passes
   if (numPasses > 1 && blockSize >= (1 << 10))
   {
-    UInt32 blockSize0 = blockSize / 2; // ????
-    
-    for (; (block[blockSize0] == block[(size_t)blockSize0 - 1]
-            || block[(size_t)blockSize0 - 1] == block[(size_t)blockSize0 - 2])
-          && blockSize0 < blockSize;
-        blockSize0++);
+    UInt32 bs0 = blockSize / 2;
+    for (; bs0 < blockSize &&
+           (block[        bs0    ] ==
+            block[(size_t)bs0 - 1] ||
+            block[(size_t)bs0 - 1] ==
+            block[(size_t)bs0 - 2]);
+      bs0++)
+    {}
     
-    if (blockSize0 < blockSize)
+    if (bs0 < blockSize)
     {
-      EncodeBlock2(block, blockSize0, numPasses - 1);
-      EncodeBlock2(block + blockSize0, blockSize - blockSize0, numPasses - 1);
-      endPos = m_OutStreamCurrent->GetPos();
-      endCurByte = m_OutStreamCurrent->GetCurByte();
-      if ((endPos & 7) > 0)
+      EncodeBlock2(block, bs0, numPasses - 1);
+      EncodeBlock2(block + bs0, blockSize - bs0, numPasses - 1);
+      endPos = m_OutStreamCurrent.GetPos();
+      endCurByte = m_OutStreamCurrent.GetCurByte();
+      // we prepare next byte as identical byte to starting byte for main encoding attempt:
+      if (endPos & 7)
         WriteBits2(0, 8 - (endPos & 7));
-      m_OutStreamCurrent->SetCurState((startPos & 7), startCurByte);
-      needCompare = true;
+      m_OutStreamCurrent.SetCurState((startPos & 7), startCurByte);
     }
   }
 
-  UInt32 startBytePos2 = m_OutStreamCurrent->GetBytePos();
-  UInt32 startPos2 = m_OutStreamCurrent->GetPos();
-  UInt32 crcVal = EncodeBlockWithHeaders(block, blockSize);
-  UInt32 endPos2 = m_OutStreamCurrent->GetPos();
+  const UInt32 startBytePos2 = m_OutStreamCurrent.GetBytePos();
+  const UInt32 startPos2 = m_OutStreamCurrent.GetPos();
+  const UInt32 crcVal = EncodeBlockWithHeaders(block, blockSize);
 
-  if (needCompare)
+  if (endPos)
   {
-    UInt32 size2 = endPos2 - startPos2;
-    if (size2 < endPos - startPos)
+    const UInt32 size2 = m_OutStreamCurrent.GetPos() - startPos2;
+    if (size2 >= endPos - startPos)
     {
-      UInt32 numBytes = m_OutStreamCurrent->GetBytePos() - startBytePos2;
-      Byte *buffer = m_OutStreamCurrent->GetStream();
-      for (UInt32 i = 0; i < numBytes; i++)
-        buffer[startBytePos + i] = buffer[startBytePos2 + i];
-      m_OutStreamCurrent->SetPos(startPos + endPos2 - startPos2);
-      m_NumCrcs = numCrcs;
-      m_CRCs[m_NumCrcs++] = crcVal;
-    }
-    else
-    {
-      m_OutStreamCurrent->SetPos(endPos);
-      m_OutStreamCurrent->SetCurState((endPos & 7), endCurByte);
+      m_OutStreamCurrent.SetPos(endPos);
+      m_OutStreamCurrent.SetCurState((endPos & 7), endCurByte);
+      return;
     }
+    const UInt32 numBytes = m_OutStreamCurrent.GetBytePos() - startBytePos2;
+    Byte * const buffer = m_OutStreamCurrent.GetStream();
+    memmove(buffer + startBytePos, buffer + startBytePos2, numBytes);
+    m_OutStreamCurrent.SetPos(startPos + size2);
+    // we don't call m_OutStreamCurrent.SetCurState() here because
+    // m_OutStreamCurrent._curByte is correct already
   }
-  else
-  {
-    m_NumCrcs = numCrcs;
-    m_CRCs[m_NumCrcs++] = crcVal;
-  }
+  m_CRCs[numCrcs] = crcVal;
+  m_NumCrcs = numCrcs + 1;
 }
 
+
 HRESULT CThreadInfo::EncodeBlock3(UInt32 blockSize)
 {
-  CMsbfEncoderTemp outStreamTemp;
+  CMsbfEncoderTemp &outStreamTemp = m_OutStreamCurrent;
   outStreamTemp.SetStream(m_TempArray);
   outStreamTemp.Init();
-  m_OutStreamCurrent = &outStreamTemp;
-
   m_NumCrcs = 0;
 
   EncodeBlock2(m_Block, blockSize, Encoder->_props.NumPasses);
 
-  #ifndef Z7_ST
+#ifndef Z7_ST
   if (Encoder->MtMode)
     Encoder->ThreadsInfo[m_BlockIndex].CanWriteEvent.Lock();
-  #endif
+#endif
+
   for (UInt32 i = 0; i < m_NumCrcs; i++)
     Encoder->CombinedCrc.Update(m_CRCs[i]);
-  Encoder->WriteBytes(m_TempArray, outStreamTemp.GetPos(), outStreamTemp.GetCurByte());
+  Encoder->WriteBytes(m_TempArray, outStreamTemp.GetPos(), outStreamTemp.GetNonFlushedByteBits());
   HRESULT res = S_OK;
-  #ifndef Z7_ST
+
+#ifndef Z7_ST
   if (Encoder->MtMode)
   {
     UInt32 blockIndex = m_BlockIndex + 1;
     if (blockIndex == Encoder->NumThreads)
       blockIndex = 0;
-
     if (Encoder->Progress)
     {
       const UInt64 packSize = Encoder->m_OutStream.GetProcessedSize();
       res = Encoder->Progress->SetRatioInfo(&m_UnpackSize, &packSize);
     }
-
     Encoder->ThreadsInfo[blockIndex].CanWriteEvent.Set();
   }
-  #endif
+#endif
   return res;
 }
 
-void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, Byte lastByte)
+void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByteBits)
 {
-  UInt32 bytesSize = (sizeInBits >> 3);
-  for (UInt32 i = 0; i < bytesSize; i++)
-    m_OutStream.WriteBits(data[i], 8);
-  WriteBits(lastByte, (sizeInBits & 7));
+  m_OutStream.WriteBytes(data, sizeInBits >> 3);
+  sizeInBits &= 7;
+  if (sizeInBits)
+    m_OutStream.WriteBits(lastByteBits, sizeInBits);
 }
 
 
 HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
     const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
 {
+  ThreadNextGroup_Init(&ThreadNextGroup, _props.NumThreadGroups, 0); // startGroup
+
   NumBlocks = 0;
   #ifndef Z7_ST
   Progress = progress;
@@ -823,11 +1015,11 @@
     {
       CThreadInfo &ti =
       #ifndef Z7_ST
-      ThreadsInfo[0];
+          ThreadsInfo[0];
       #else
-      ThreadsInfo;
+          ThreadsInfo;
       #endif
-      UInt32 blockSize = ReadRleBlock(ti.m_Block);
+      const UInt32 blockSize = ReadRleBlock(ti.m_Block);
       if (blockSize == 0)
         break;
       RINOK(ti.EncodeBlock3(blockSize))
@@ -845,8 +1037,11 @@
   WriteByte(kFinSig3);
   WriteByte(kFinSig4);
   WriteByte(kFinSig5);
-
-  WriteCrc(CombinedCrc.GetDigest());
+  {
+    const UInt32 v = CombinedCrc.GetDigest();
+    for (int i = 24; i >= 0; i -= 8)
+      WriteByte((Byte)(v >> i));
+  }
   RINOK(Flush())
   if (!m_InStream.WasFinished())
     return E_FAIL;
@@ -869,14 +1064,21 @@
   for (UInt32 i = 0; i < numProps; i++)
   {
     const PROPVARIANT &prop = coderProps[i];
-    PROPID propID = propIDs[i];
+    const PROPID propID = propIDs[i];
 
     if (propID == NCoderPropID::kAffinity)
     {
-      if (prop.vt == VT_UI8)
-        props.Affinity = prop.uhVal.QuadPart;
-      else
+      if (prop.vt != VT_UI8)
+        return E_INVALIDARG;
+      props.Affinity = prop.uhVal.QuadPart;
+      continue;
+    }
+
+    if (propID == NCoderPropID::kNumThreadGroups)
+    {
+      if (prop.vt != VT_UI4)
         return E_INVALIDARG;
+      props.NumThreadGroups = (UInt32)prop.ulVal;
       continue;
     }
 
@@ -884,7 +1086,7 @@
       continue;
     if (prop.vt != VT_UI4)
       return E_INVALIDARG;
-    UInt32 v = (UInt32)prop.ulVal;
+    const UInt32 v = (UInt32)prop.ulVal;
     switch (propID)
     {
       case NCoderPropID::kNumPasses: props.NumPasses = v; break;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.h 7zip-25.00+dfsg/CPP/7zip/Compress/BZip2Encoder.h
--- 7zip-24.09+dfsg/CPP/7zip/Compress/BZip2Encoder.h	2023-03-28 12:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/BZip2Encoder.h	2025-02-28 07:00:00.000000000 +0100
@@ -3,7 +3,6 @@
 #ifndef ZIP7_INC_COMPRESS_BZIP2_ENCODER_H
 #define ZIP7_INC_COMPRESS_BZIP2_ENCODER_H
 
-#include "../../Common/Defs.h"
 #include "../../Common/MyCom.h"
 
 #ifndef Z7_ST
@@ -23,80 +22,114 @@
 namespace NCompress {
 namespace NBZip2 {
 
-class CMsbfEncoderTemp
+const unsigned kNumPassesMax = 10;
+
+struct CMsbfEncoderTemp
 {
-  UInt32 _pos;
-  unsigned _bitPos;
-  Byte _curByte;
+  unsigned _bitPos;  // 0 < _bitPos <= 8 : number of non-filled low bits in _curByte
+  unsigned _curByte; // low (_bitPos) bits are zeros
+                     // high (8 - _bitPos) bits are filled
   Byte *_buf;
-public:
-  void SetStream(Byte *buf) { _buf = buf;  }
-  Byte *GetStream() const { return _buf; }
+  Byte *_buf_base;
+  void SetStream(Byte *buf) { _buf_base = _buf = buf;  }
+  Byte *GetStream() const { return _buf_base; }
 
   void Init()
   {
-    _pos = 0;
     _bitPos = 8;
     _curByte = 0;
+    _buf = _buf_base;
   }
 
-  void Flush()
-  {
-    if (_bitPos < 8)
-      WriteBits(0, _bitPos);
-  }
-
+  // required condition: (value >> numBits) == 0
+  // numBits == 0 is allowed
   void WriteBits(UInt32 value, unsigned numBits)
   {
-    while (numBits > 0)
+    do
     {
-      unsigned numNewBits = MyMin(numBits, _bitPos);
-      numBits -= numNewBits;
-      
-      _curByte = (Byte)(_curByte << numNewBits);
-      UInt32 newBits = value >> numBits;
-      _curByte |= Byte(newBits);
-      value -= (newBits << numBits);
-      
-      _bitPos -= numNewBits;
-      
-      if (_bitPos == 0)
+      unsigned bp = _bitPos;
+      unsigned curByte = _curByte;
+      if (numBits < bp)
       {
-       _buf[_pos++] = _curByte;
-        _bitPos = 8;
+        bp -= numBits;
+        _curByte = curByte | (value << bp);
+        _bitPos = bp;
+        return;
       }
+      numBits -= bp;
+      const UInt32 hi = value >> numBits;
+      value -= (hi << numBits);
+      Byte *buf = _buf;
+      _bitPos = 8;
+      _curByte = 0;
+      *buf++ = (Byte)(curByte | hi);
+      _buf = buf;
     }
+    while (numBits);
   }
-  
-  UInt32 GetBytePos() const { return _pos ; }
-  UInt32 GetPos() const { return _pos * 8 + (8 - _bitPos); }
-  Byte GetCurByte() const { return _curByte; }
+
+  void WriteBit(unsigned value)
+  {
+    const unsigned bp = _bitPos - 1;
+    const unsigned curByte = _curByte | (value << bp);
+    _curByte = curByte;
+    _bitPos = bp;
+    if (bp == 0)
+    {
+      *_buf++ = (Byte)curByte;
+      _curByte = 0;
+      _bitPos = 8;
+    }
+  }
+
+  void WriteByte(unsigned b)
+  {
+    const unsigned bp = _bitPos;
+    const unsigned a = _curByte | (b >> (8 - bp));
+    _curByte = b << bp;
+    Byte *buf = _buf;
+    *buf++ = (Byte)a;
+    _buf = buf;
+  }
+
+  UInt32 GetBytePos() const { return (UInt32)(size_t)(_buf - _buf_base); }
+  UInt32 GetPos() const { return GetBytePos() * 8 + 8 - _bitPos; }
+  unsigned GetCurByte() const { return _curByte; }
+  unsigned GetNonFlushedByteBits() const { return _curByte >> _bitPos; }
   void SetPos(UInt32 bitPos)
   {
-    _pos = bitPos >> 3;
+    _buf = _buf_base + (bitPos >> 3);
     _bitPos = 8 - ((unsigned)bitPos & 7);
   }
-  void SetCurState(unsigned bitPos, Byte curByte)
+  void SetCurState(unsigned bitPos, unsigned curByte)
   {
     _bitPos = 8 - bitPos;
     _curByte = curByte;
   }
 };
 
-class CEncoder;
 
-const unsigned kNumPassesMax = 10;
+class CEncoder;
 
 class CThreadInfo
 {
+private:
+  CMsbfEncoderTemp m_OutStreamCurrent;
 public:
+  CEncoder *Encoder;
   Byte *m_Block;
 private:
   Byte *m_MtfArray;
   Byte *m_TempArray;
   UInt32 *m_BlockSorterIndex;
 
-  CMsbfEncoderTemp *m_OutStreamCurrent;
+public:
+  bool m_OptimizeNumTables;
+  UInt32 m_NumCrcs;
+  UInt32 m_BlockIndex;
+  UInt64 m_UnpackSize;
+
+  Byte *m_Block_Base;
 
   Byte Lens[kNumTablesMax][kMaxAlphaSize];
   UInt32 Freqs[kNumTablesMax][kMaxAlphaSize];
@@ -105,20 +138,16 @@
   Byte m_Selectors[kNumSelectorsMax];
 
   UInt32 m_CRCs[1 << kNumPassesMax];
-  UInt32 m_NumCrcs;
 
   void WriteBits2(UInt32 value, unsigned numBits);
-  void WriteByte2(Byte b);
-  void WriteBit2(Byte v);
-  void WriteCrc2(UInt32 v);
+  void WriteByte2(unsigned b) { WriteBits2(b, 8); }
+  void WriteBit2(unsigned v)  { m_OutStreamCurrent.WriteBit(v); }
 
   void EncodeBlock(const Byte *block, UInt32 blockSize);
   UInt32 EncodeBlockWithHeaders(const Byte *block, UInt32 blockSize);
   void EncodeBlock2(const Byte *block, UInt32 blockSize, UInt32 numPasses);
 public:
-  bool m_OptimizeNumTables;
-  CEncoder *Encoder;
- #ifndef Z7_ST
+#ifndef Z7_ST
   NWindows::CThread Thread;
 
   NWindows::NSynchronization::CAutoResetEvent StreamWasFinishedEvent;
@@ -127,17 +156,14 @@
   // it's not member of this thread. We just need one event per thread
   NWindows::NSynchronization::CAutoResetEvent CanWriteEvent;
 
-private:
-  UInt32 m_BlockIndex;
-  UInt64 m_UnpackSize;
 public:
   Byte MtPad[1 << 8]; // It's pad for Multi-Threading. Must be >= Cache_Line_Size.
   HRESULT Create();
   void FinishStream(bool needLeave);
   THREAD_FUNC_RET_TYPE ThreadFunc();
- #endif
+#endif
 
-  CThreadInfo(): m_Block(NULL), m_BlockSorterIndex(NULL)  {}
+  CThreadInfo(): m_BlockSorterIndex(NULL), m_Block_Base(NULL) {}
   ~CThreadInfo() { Free(); }
   bool Alloc();
   void Free();
@@ -145,16 +171,19 @@
   HRESULT EncodeBlock3(UInt32 blockSize);
 };
 
+
 struct CEncProps
 {
   UInt32 BlockSizeMult;
   UInt32 NumPasses;
+  UInt32 NumThreadGroups;
   UInt64 Affinity;
   
   CEncProps()
   {
     BlockSizeMult = (UInt32)(Int32)-1;
     NumPasses = (UInt32)(Int32)-1;
+    NumThreadGroups = 0;
     Affinity = 0;
   }
   void Normalize(int level);
@@ -206,6 +235,7 @@
   bool CloseThreads;
   bool StreamWasFinished;
   NWindows::NSynchronization::CManualResetEvent CanStartWaitingEvent;
+  CThreadNextGroup ThreadNextGroup;
 
   HRESULT Result;
   ICompressProgressInfo *Progress;
@@ -218,12 +248,8 @@
   UInt64 GetInProcessedSize() const { return m_InStream.GetProcessedSize(); }
 
   UInt32 ReadRleBlock(Byte *buf);
-  void WriteBytes(const Byte *data, UInt32 sizeInBits, Byte lastByte);
-
-  void WriteBits(UInt32 value, unsigned numBits);
+  void WriteBytes(const Byte *data, UInt32 sizeInBits, unsigned lastByteBits);
   void WriteByte(Byte b);
-  // void WriteBit(Byte v);
-  void WriteCrc(UInt32 v);
 
  #ifndef Z7_ST
   HRESULT Create();
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp 7zip-25.00+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp	2024-01-01 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/DeflateDecoder.cpp	2025-03-04 13:00:00.000000000 +0100
@@ -117,15 +117,13 @@
       if (_numDistLevels > kDistTableSize32)
         return false;
     
-    Byte levelLevels[kLevelTableSize];
-    for (unsigned i = 0; i < kLevelTableSize; i++)
-    {
-      const unsigned position = kCodeLengthAlphabetOrder[i];
-      if (i < numLevelCodes)
-        levelLevels[position] = (Byte)ReadBits(kLevelFieldSize);
-      else
-        levelLevels[position] = 0;
-    }
+    const unsigned kLevelTableSize_aligned4 = kLevelTableSize + 1;
+    Byte levelLevels[kLevelTableSize_aligned4];
+    memset (levelLevels, 0, sizeof(levelLevels));
+    unsigned i = 0;
+    do
+      levelLevels[kCodeLengthAlphabetOrder[i++]] = (Byte)ReadBits(kLevelFieldSize);
+    while (i != numLevelCodes);
     
     if (m_InBitStream.ExtraBitsWereRead())
       return false;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp 7zip-25.00+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp	2024-01-01 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/DeflateEncoder.cpp	2025-01-13 14:00:00.000000000 +0100
@@ -19,12 +19,16 @@
 #define NO_INLINE
 #endif
 
+#define MAX_HUF_LEN_12  12
+
 namespace NCompress {
 namespace NDeflate {
 namespace NEncoder {
 
+static const unsigned k_CodeValue_Len_Is_Literal_Flag = 1u << 15;
+
 static const unsigned kNumDivPassesMax = 10; // [0, 16); ratio/speed/ram tradeoff; use big value for better compression ratio.
-static const UInt32 kNumTables = (1 << kNumDivPassesMax);
+static const unsigned kNumTables = 1u << kNumDivPassesMax;
 
 static const UInt32 kFixedHuffmanCodeBlockSizeMax = (1 << 8); // [0, (1 << 32)); ratio/speed tradeoff; use big value for better compression ratio.
 static const UInt32 kDivideCodeBlockSizeMin = (1 << 7); // [1, (1 << 32)); ratio/speed tradeoff; use small value for better compression ratio.
@@ -77,7 +81,7 @@
 
 static CFastPosInit g_FastPosInit;
 
-inline UInt32 GetPosSlot(UInt32 pos)
+inline unsigned GetPosSlot(UInt32 pos)
 {
   /*
   if (pos < 0x200)
@@ -162,13 +166,13 @@
   // COM_TRY_BEGIN
   if (!m_Values)
   {
-    m_Values = (CCodeValue *)MyAlloc((kMaxUncompressedBlockSize) * sizeof(CCodeValue));
+    m_Values = (CCodeValue *)MyAlloc(kMaxUncompressedBlockSize * sizeof(CCodeValue));
     if (!m_Values)
       return E_OUTOFMEMORY;
   }
   if (!m_Tables)
   {
-    m_Tables = (CTables *)MyAlloc((kNumTables) * sizeof(CTables));
+    m_Tables = (CTables *)MyAlloc(kNumTables * sizeof(CTables));
     if (!m_Tables)
       return E_OUTOFMEMORY;
   }
@@ -268,19 +272,21 @@
 
   UInt32 distanceTmp[kMatchMaxLen * 2 + 3];
   
-  const UInt32 numPairs = (UInt32)((_btMode ?
+  const size_t numPairs = (size_t)((_btMode ?
       Bt3Zip_MatchFinder_GetMatches(&_lzInWindow, distanceTmp):
       Hc3Zip_MatchFinder_GetMatches(&_lzInWindow, distanceTmp)) - distanceTmp);
 
-  *m_MatchDistances = (UInt16)numPairs;
+  UInt16 *matchDistances = m_MatchDistances;
+  *matchDistances++ = (UInt16)numPairs;
    
   if (numPairs != 0)
   {
-    UInt32 i;
+    size_t i;
     for (i = 0; i < numPairs; i += 2)
     {
-      m_MatchDistances[(size_t)i + 1] = (UInt16)distanceTmp[i];
-      m_MatchDistances[(size_t)i + 2] = (UInt16)distanceTmp[(size_t)i + 1];
+      matchDistances[0] = (UInt16)distanceTmp[i];
+      matchDistances[1] = (UInt16)distanceTmp[(size_t)i + 1];
+      matchDistances += 2;
     }
     UInt32 len = distanceTmp[(size_t)numPairs - 2];
     if (len == m_NumFastBytes && m_NumFastBytes != m_MatchMaxLen)
@@ -291,11 +297,11 @@
       if (numAvail > m_MatchMaxLen)
         numAvail = m_MatchMaxLen;
       for (; len < numAvail && pby[len] == pby2[len]; len++);
-      m_MatchDistances[(size_t)i - 1] = (UInt16)len;
+      matchDistances[-2] = (UInt16)len;
     }
   }
   if (m_IsMultiPass)
-    m_Pos += numPairs + 1;
+    m_Pos += (UInt32)numPairs + 1;
   if (!m_SecondPass)
     m_AdditionalOffset++;
 }
@@ -535,6 +541,7 @@
 }
 
 #define WRITE_HF2(codes, lens, i) m_OutStream.WriteBits(codes[i], lens[i])
+#define WRITE_HF2_NO_INLINE(codes, lens, i)   WriteBits(codes[i], lens[i])
 #define WRITE_HF(i) WriteBits(codes[i], lens[i])
 
 NO_INLINE void CCoder::LevelTableCode(const Byte *levels, unsigned numLevels, const Byte *lens, const UInt32 *codes)
@@ -619,17 +626,22 @@
   return price;
 }
 
-static NO_INLINE UInt32 Huffman_GetPrice_Spec(const UInt32 *freqs, const Byte *lens, UInt32 num, const Byte *extraBits, UInt32 extraBase)
+static NO_INLINE UInt32 Huffman_GetPrice_Spec(
+    const UInt32 *freqs, const Byte *lens, UInt32 num,
+    const Byte *extraBits, UInt32 extraBase)
 {
-  return Huffman_GetPrice(freqs, lens, num) +
+  return
+    Huffman_GetPrice(freqs, lens, num) +
     Huffman_GetPrice(freqs + extraBase, extraBits, num - extraBase);
 }
 
 NO_INLINE UInt32 CCoder::GetLzBlockPrice() const
 {
   return
-    Huffman_GetPrice_Spec(mainFreqs, m_NewLevels.litLenLevels, kFixedMainTableSize, m_LenDirectBits, kSymbolMatch) +
-    Huffman_GetPrice_Spec(distFreqs, m_NewLevels.distLevels, kDistTableSize64, kDistDirectBits, 0);
+    Huffman_GetPrice_Spec(mainFreqs, m_NewLevels.litLenLevels,
+        kFixedMainTableSize, m_LenDirectBits, kSymbolMatch) +
+    Huffman_GetPrice_Spec(distFreqs, m_NewLevels.distLevels,
+        kDistTableSize64, kDistDirectBits, 0);
 }
 
 NO_INLINE void CCoder::TryBlock()
@@ -658,7 +670,7 @@
     CCodeValue &codeValue = m_Values[m_ValueIndex++];
     if (len >= kMatchMinLen)
     {
-      UInt32 newLen = len - kMatchMinLen;
+      const UInt32 newLen = len - kMatchMinLen;
       codeValue.Len = (UInt16)newLen;
       mainFreqs[kSymbolMatch + (size_t)g_LenSlots[newLen]]++;
       codeValue.Pos = (UInt16)pos;
@@ -666,10 +678,10 @@
     }
     else
     {
-      Byte b = *(Inline_MatchFinder_GetPointerToCurrentPos(&_lzInWindow) - m_AdditionalOffset);
+      const unsigned b = *(Inline_MatchFinder_GetPointerToCurrentPos(&_lzInWindow) - m_AdditionalOffset);
       mainFreqs[b]++;
-      codeValue.SetAsLiteral();
-      codeValue.Pos = b;
+      codeValue.Len = k_CodeValue_Len_Is_Literal_Flag;
+      codeValue.Pos = (UInt16)b;
     }
     m_AdditionalOffset -= len;
     BlockSizeRes += len;
@@ -704,16 +716,24 @@
   }
 }
 
+#if MAX_HUF_LEN_12 > 12
+// Huffman_ReverseBits() now supports 12-bits values only.
+#error Stop_Compiling_Bad_MAX_HUF_LEN_12
+#endif
 static NO_INLINE void Huffman_ReverseBits(UInt32 *codes, const Byte *lens, UInt32 num)
 {
-  for (UInt32 i = 0; i < num; i++)
+  const Byte * const lens_lim = lens + num;
+  do
   {
-    UInt32 x = codes[i];
-    x = ((x & 0x5555) << 1) | ((x & 0xAAAA) >> 1);
-    x = ((x & 0x3333) << 2) | ((x & 0xCCCC) >> 2);
-    x = ((x & 0x0F0F) << 4) | ((x & 0xF0F0) >> 4);
-    codes[i] = (((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)) >> (16 - lens[i]);
+    // we should change constants, if lens[*] can be larger than 12.
+    UInt32 x = *codes;
+    x = ((x & (0x555     )) << 2) + (x & (0xAAA     ));
+    x = ((x & (0x333 << 1)) << 4) | (x & (0xCCC << 1));
+    x = ((x & (0xF0F << 3)) << 8) | (x & (0x0F0 << 3));
+    // we can use (x) instead of (x & (0xFF << 7)), if we support garabage data after (*lens) bits.
+    *codes++ = (((x & (0xFF << 7)) << 16) | x) >> (*lens ^ 31);
   }
+  while (++lens != lens_lim);
 }
 
 NO_INLINE void CCoder::WriteBlock()
@@ -721,24 +741,28 @@
   Huffman_ReverseBits(mainCodes, m_NewLevels.litLenLevels, kFixedMainTableSize);
   Huffman_ReverseBits(distCodes, m_NewLevels.distLevels, kDistTableSize64);
 
-  for (UInt32 i = 0; i < m_ValueIndex; i++)
+  CCodeValue *values = m_Values;
+  const CCodeValue * const values_lim = values + m_ValueIndex;
+
+  if (values != values_lim)
+  do
   {
-    const CCodeValue &codeValue = m_Values[i];
-    if (codeValue.IsLiteral())
-      WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, codeValue.Pos);
+    const UInt32 len = values->Len;
+    const UInt32 dist = values->Pos;
+    if (len == k_CodeValue_Len_Is_Literal_Flag)
+      WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, dist);
     else
     {
-      UInt32 len = codeValue.Len;
-      UInt32 lenSlot = g_LenSlots[len];
+      const unsigned lenSlot = g_LenSlots[len];
       WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, kSymbolMatch + lenSlot);
       m_OutStream.WriteBits(len - m_LenStart[lenSlot], m_LenDirectBits[lenSlot]);
-      UInt32 dist = codeValue.Pos;
-      UInt32 posSlot = GetPosSlot(dist);
+      const unsigned posSlot = GetPosSlot(dist);
       WRITE_HF2(distCodes, m_NewLevels.distLevels, posSlot);
       m_OutStream.WriteBits(dist - kDistStart[posSlot], kDistDirectBits[posSlot]);
     }
   }
-  WRITE_HF2(mainCodes, m_NewLevels.litLenLevels, kSymbolEndOfBlock);
+  while (++values != values_lim);
+  WRITE_HF2_NO_INLINE(mainCodes, m_NewLevels.litLenLevels, kSymbolEndOfBlock);
 }
 
 static UInt32 GetStorePrice(UInt32 blockSize, unsigned bitPosition)
@@ -787,10 +811,10 @@
   {
     m_Pos = posTemp;
     TryBlock();
-    unsigned numHuffBits =
-        (m_ValueIndex > 18000 ? 12 :
-        (m_ValueIndex >  7000 ? 11 :
-        (m_ValueIndex >  2000 ? 10 : 9)));
+    const unsigned numHuffBits =
+        m_ValueIndex > 18000 ? MAX_HUF_LEN_12 :
+        m_ValueIndex >  7000 ? 11 :
+        m_ValueIndex >  2000 ? 10 : 9;
     MakeTables(numHuffBits);
     SetPrices(m_NewLevels);
   }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp 7zip-25.00+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp	2023-03-28 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/Lzma2Encoder.cpp	2025-06-28 14:00:00.000000000 +0200
@@ -52,7 +52,15 @@
     case NCoderPropID::kNumThreads:
       if (prop.vt != VT_UI4)
         return E_INVALIDARG;
-      lzma2Props.numTotalThreads = (int)(prop.ulVal);
+      lzma2Props.numTotalThreads = (int)prop.ulVal;
+      break;
+    case NCoderPropID::kNumThreadGroups:
+      if (prop.vt != VT_UI4)
+        return E_INVALIDARG;
+      // 16-bit value supported by Windows
+      if (prop.ulVal >= (1u << 16))
+        return E_INVALIDARG;
+      lzma2Props.numThreadGroups = (unsigned)prop.ulVal;
       break;
     default:
       RINOK(NLzma::SetLzmaProp(propID, prop, lzma2Props.lzmaProps))
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp 7zip-25.00+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp	2023-03-28 15:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/LzmaEncoder.cpp	2024-12-19 09:00:00.000000000 +0100
@@ -101,6 +101,24 @@
     return S_OK;
   }
 
+  if (propID == NCoderPropID::kAffinityInGroup)
+  {
+    if (prop.vt == VT_UI8)
+      ep.affinityInGroup = prop.uhVal.QuadPart;
+    else
+      return E_INVALIDARG;
+    return S_OK;
+  }
+
+  if (propID == NCoderPropID::kThreadGroup)
+  {
+    if (prop.vt == VT_UI4)
+      ep.affinityGroup = (Int32)(UInt32)prop.ulVal;
+    else
+      return E_INVALIDARG;
+    return S_OK;
+  }
+
   if (propID == NCoderPropID::kHashBits)
   {
     if (prop.vt == VT_UI4)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Compress/Mtf8.h 7zip-25.00+dfsg/CPP/7zip/Compress/Mtf8.h
--- 7zip-24.09+dfsg/CPP/7zip/Compress/Mtf8.h	2023-03-28 15:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/Compress/Mtf8.h	2024-12-17 19:00:00.000000000 +0100
@@ -13,6 +13,18 @@
 
   unsigned FindAndMove(Byte v) throw()
   {
+#if 1
+    Byte b = Buf[0];
+    if (v == b)
+      return 0;
+    Buf[0] = v;
+    for (unsigned pos = 0;;)
+    {
+      Byte a;
+      a = Buf[++pos];  Buf[pos] = b;  if (v == a) return pos;
+      b = Buf[++pos];  Buf[pos] = a;  if (v == b) return pos;
+    }
+#else
     size_t pos;
     for (pos = 0; Buf[pos] != v; pos++);
     const unsigned resPos = (unsigned)pos;
@@ -31,6 +43,7 @@
       Buf[pos] = Buf[pos - 1];
     Buf[0] = v;
     return resPos;
+#endif
   }
 };
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Crypto/MyAes.cpp 7zip-25.00+dfsg/CPP/7zip/Crypto/MyAes.cpp
--- 7zip-24.09+dfsg/CPP/7zip/Crypto/MyAes.cpp	2024-03-01 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Crypto/MyAes.cpp	2024-12-10 09:00:00.000000000 +0100
@@ -153,7 +153,26 @@
 #ifndef Z7_EXTRACT_ONLY
 
 #ifdef MY_CPU_X86_OR_AMD64
-  #define USE_HW_AES
+
+  #if defined(__INTEL_COMPILER)
+    #if (__INTEL_COMPILER >= 1110)
+      #define USE_HW_AES
+      #if (__INTEL_COMPILER >= 1900)
+        #define USE_HW_VAES
+      #endif
+    #endif
+  #elif defined(Z7_CLANG_VERSION) && (Z7_CLANG_VERSION >= 30800) \
+     || defined(Z7_GCC_VERSION)   && (Z7_GCC_VERSION   >= 40400)
+    #define USE_HW_AES
+      #if defined(__clang__) && (__clang_major__ >= 8) \
+          || defined(__GNUC__) && (__GNUC__ >= 8)
+        #define USE_HW_VAES
+      #endif
+  #elif defined(_MSC_VER)
+    #define USE_HW_AES
+    #define USE_HW_VAES
+  #endif
+
 #elif defined(MY_CPU_ARM_OR_ARM64) && defined(MY_CPU_LE)
   
   #if   defined(__ARM_FEATURE_AES) \
@@ -186,15 +205,15 @@
     #define SET_AES_FUNC_2(f2) \
       if (algo == 2) if (g_Aes_SupportedFunctions_Flags & k_Aes_SupportedFunctions_HW) \
       { f = f2; }
-  #ifdef MY_CPU_X86_OR_AMD64
+  #ifdef USE_HW_VAES
     #define SET_AES_FUNC_23(f2, f3) \
       SET_AES_FUNC_2(f2) \
       if (algo == 3) if (g_Aes_SupportedFunctions_Flags & k_Aes_SupportedFunctions_HW_256) \
       { f = f3; }
-  #else  // MY_CPU_X86_OR_AMD64
+  #else  // USE_HW_VAES
     #define SET_AES_FUNC_23(f2, f3) \
       SET_AES_FUNC_2(f2)
-  #endif // MY_CPU_X86_OR_AMD64
+  #endif // USE_HW_VAES
 #else  // USE_HW_AES
     #define SET_AES_FUNC_23(f2, f3)
 #endif // USE_HW_AES
diff -Nru 7zip-24.09+dfsg/CPP/7zip/ICoder.h 7zip-25.00+dfsg/CPP/7zip/ICoder.h
--- 7zip-24.09+dfsg/CPP/7zip/ICoder.h	2023-04-06 12:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/ICoder.h	2024-12-14 20:00:00.000000000 +0100
@@ -136,6 +136,9 @@
     kAffinity,          // VT_UI8
     kBranchOffset,      // VT_UI4
     kHashBits,          // VT_UI4
+    kNumThreadGroups,   // VT_UI4
+    kThreadGroup,       // VT_UI4
+    kAffinityInGroup,   // VT_UI8
     /*
     // kHash3Bits,          // VT_UI4
     // kHash2Bits,          // VT_UI4
diff -Nru 7zip-24.09+dfsg/CPP/7zip/Sort.mak 7zip-25.00+dfsg/CPP/7zip/Sort.mak
--- 7zip-24.09+dfsg/CPP/7zip/Sort.mak	1970-01-01 01:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/Sort.mak	2025-07-02 10:00:00.000000000 +0200
@@ -0,0 +1,6 @@
+!IF defined(USE_NO_ASM) || defined(USE_C_SORT) || "$(PLATFORM)" == "ia64" || "$(PLATFORM)" == "mips" || "$(PLATFORM)" == "arm" || "$(PLATFORM)" == "arm64"
+C_OBJS = $(C_OBJS) \
+!ELSE
+ASM_OBJS = $(ASM_OBJS) \
+!ENDIF
+  $O\Sort.obj
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp	2024-06-17 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveCommandLine.cpp	2025-07-03 13:00:00.000000000 +0200
@@ -63,17 +63,46 @@
 
 #else
 
-// #define MY_isatty_fileno(x) (isatty(fileno(x)))
-// #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0);
-static inline bool MY_IS_TERMINAL(FILE *x)
-{
-  return (
-    #if defined(_MSC_VER) && (_MSC_VER >= 1400)
-      _isatty(_fileno(x))
-    #else
-      isatty(fileno(x))
-    #endif
-      != 0);
+static bool MY_IS_TERMINAL(FILE *x)
+{
+#ifdef _WIN32
+  /*
+crt/stdio.h:
+typedef struct _iobuf FILE;
+#define stdin  (&_iob[0])
+#define stdout (&_iob[1])
+#define stderr (&_iob[2])
+*/
+  // fprintf(stderr, "\nMY_IS_TERMINAL = %p", x);
+  const int fd = _fileno(x);
+  /* (fd) is 0, 1 or 2 in console program.
+     docs: If stdout or stderr is not associated with
+     an output stream (for example, in a Windows application
+     without a console window), the file descriptor returned is -2.
+     In previous versions, the file descriptor returned was -1.
+  */
+  if (fd < 0) // is not associated with an output stream application (without a console window)
+    return false;
+  // fprintf(stderr, "\n\nstderr _fileno(%p) = %d", x, fd);
+  if (!_isatty(fd))
+    return false;
+  // fprintf(stderr, "\nisatty_val = true");
+  const HANDLE h = (HANDLE)_get_osfhandle(fd);
+  /* _get_osfhandle() returns intptr_t in new SDK, or long in MSVC6.
+     Also it can return (INVALID_HANDLE_VALUE).
+     docs: _get_osfhandle also returns the special value -2 when
+     the file descriptor is not associated with a stream
+     in old msvcrt.dll: it returns (-1) for incorrect value
+  */
+  // fprintf(stderr, "\n_get_osfhandle() = %p", (void *)h);
+  if (h == NULL || h == INVALID_HANDLE_VALUE)
+    return false;
+  DWORD st;
+  // fprintf(stderr, "\nGetConsoleMode() = %u", (unsigned)GetConsoleMode(h, &st));
+  return GetConsoleMode(h, &st) != 0;
+#else
+  return isatty(fileno(x)) != 0;
+#endif
 }
 
 #endif
@@ -1088,7 +1117,7 @@
     const UString &s = parser[NKey::kLargePages].PostStrings[0];
     if (s.IsEmpty())
       slp = 1;
-    else if (s != L"-")
+    else if (!s.IsEqualTo("-"))
     {
       if (!StringToUInt32(s, slp))
         throw CArcCmdLineException("Unsupported switch postfix for -slp", s);
@@ -1338,7 +1367,7 @@
     const UString &s = parser[NKey::kFullPathMode].PostStrings[0];
     if (!s.IsEmpty())
     {
-      if (s == L"2")
+      if (s.IsEqualTo("2"))
         censorPathMode = NWildcard::k_FullPath;
       else
         throw CArcCmdLineException("Unsupported -spf:", s);
@@ -1400,6 +1429,7 @@
   const bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
   const bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList;
   const bool isRename = options.Command.CommandType == NCommandType::kRename;
+  options.UpdateOptions.RenameMode = isRename;
 
   if ((isExtractOrList || isRename) && options.StdInMode)
     thereIsArchiveName = false;
@@ -1516,9 +1546,9 @@
       const UString &s = parser[NKey::kZoneFile].PostStrings[0];
       if (!s.IsEmpty())
       {
-             if (s == L"0") eo.ZoneMode = NExtract::NZoneIdMode::kNone;
-        else if (s == L"1") eo.ZoneMode = NExtract::NZoneIdMode::kAll;
-        else if (s == L"2") eo.ZoneMode = NExtract::NZoneIdMode::kOffice;
+             if (s.IsEqualTo("0")) eo.ZoneMode = NExtract::NZoneIdMode::kNone;
+        else if (s.IsEqualTo("1")) eo.ZoneMode = NExtract::NZoneIdMode::kAll;
+        else if (s.IsEqualTo("2")) eo.ZoneMode = NExtract::NZoneIdMode::kOffice;
         else
           throw CArcCmdLineException("Unsupported -snz:", s);
       }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp	2024-10-11 18:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp	2025-06-29 11:00:00.000000000 +0200
@@ -6,12 +6,10 @@
 #undef printf
 
 // #include <stdio.h>
-// #include "../../../../C/CpuTicks.h"
 
 #include "../../../../C/Alloc.h"
 #include "../../../../C/CpuArch.h"
 
-
 #include "../../../Common/ComTry.h"
 #include "../../../Common/IntToString.h"
 #include "../../../Common/StringConvert.h"
@@ -33,6 +31,8 @@
 #include "../../Common/FilePathAutoRename.h"
 #include "../../Common/StreamUtils.h"
 
+#include "../../Archive/Common/ItemNameUtils.h"
+
 #include "../Common/ExtractingFilePath.h"
 #include "../Common/PropIDUtils.h"
 
@@ -56,6 +56,19 @@
 static const char * const kCantCreateSymLink = "Cannot create symbolic link";
 #endif
 
+static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
+
+#if WCHAR_PATH_SEPARATOR != L'/'
+  // we convert linux slashes to windows slashes for further processing.
+  // also we convert linux backslashes to BackslashReplacement character.
+  #define REPLACE_SLASHES_from_Linux_to_Sys(s) \
+    { NArchive::NItemName::ReplaceToWinSlashes(s, true); }  // useBackslashReplacement
+      // { s.Replace(L'/', WCHAR_PATH_SEPARATOR); }
+#else
+  #define REPLACE_SLASHES_from_Linux_to_Sys(s)
+#endif
+
+
 #ifndef Z7_SFX
 
 Z7_COM7F_IMF(COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize))
@@ -217,7 +230,7 @@
   if (!_arc->Ask_INode)
     return S_OK;
   
-  IInArchive *archive = _arc->Archive;
+  IInArchive * const archive = _arc->Archive;
   CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
 
   {
@@ -574,6 +587,13 @@
   return _extractCallback2->MessageError(s);
 }
 
+HRESULT CArchiveExtractCallback::SendMessageError2_with_LastError(
+    const char *message, const FString &path1, const FString &path2)
+{
+  const HRESULT errorCode = GetLastError_noZero_HRESULT();
+  return SendMessageError2(errorCode, message, path1, path2);
+}
+
 #ifndef Z7_SFX
 
 Z7_CLASS_IMP_COM_1(
@@ -604,36 +624,20 @@
 #endif // Z7_SFX
 
 
-#ifdef SUPPORT_LINKS
-
-static UString GetDirPrefixOf(const UString &src)
-{
-  UString s (src);
-  if (!s.IsEmpty())
-  {
-    if (IsPathSepar(s.Back()))
-      s.DeleteBack();
-    int pos = s.ReverseFind_PathSepar();
-    s.DeleteFrom((unsigned)(pos + 1));
-  }
-  return s;
-}
-
-#endif // SUPPORT_LINKS
-
 struct CLinkLevelsInfo
 {
   bool IsAbsolute;
   int LowLevel;
   int FinalLevel;
 
-  void Parse(const UString &path);
+  void Parse(const UString &path, bool isWSL);
 };
 
-void CLinkLevelsInfo::Parse(const UString &path)
+void CLinkLevelsInfo::Parse(const UString &path, bool isWSL)
 {
-  IsAbsolute = NName::IsAbsolutePath(path);
-
+  IsAbsolute = isWSL ?
+      IS_PATH_SEPAR(path[0]) :
+      NName::IsAbsolutePath(path);
   LowLevel = 0;
   FinalLevel = 0;
 
@@ -650,9 +654,9 @@
         IsAbsolute = true;
       continue;
     }
-    if (s == L".")
+    if (s.IsEqualTo("."))
       continue;
-    if (s == L"..")
+    if (s.IsEqualTo(".."))
     {
       level--;
       if (LowLevel > level)
@@ -666,16 +670,20 @@
 }
 
 
-bool IsSafePath(const UString &path);
-bool IsSafePath(const UString &path)
+static bool IsSafePath(const UString &path, bool isWSL)
 {
   CLinkLevelsInfo levelsInfo;
-  levelsInfo.Parse(path);
+  levelsInfo.Parse(path, isWSL);
   return !levelsInfo.IsAbsolute
       && levelsInfo.LowLevel >= 0
       && levelsInfo.FinalLevel > 0;
 }
 
+bool IsSafePath(const UString &path);
+bool IsSafePath(const UString &path)
+{
+  return IsSafePath(path, false); // isWSL
+}
 
 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
@@ -791,159 +799,113 @@
 
 HRESULT CArchiveExtractCallback::ReadLink()
 {
-  IInArchive *archive = _arc->Archive;
+  IInArchive * const archive = _arc->Archive;
   const UInt32 index = _index;
-  _link.Clear();
-
+  // _link.Clear(); // _link.Clear() was called already.
   {
     NCOM::CPropVariant prop;
     RINOK(archive->GetProperty(index, kpidHardLink, &prop))
     if (prop.vt == VT_BSTR)
     {
-      _link.isHardLink = true;
-      // _link.isCopyLink = false;
+      _link.LinkType = k_LinkType_HardLink;
       _link.isRelative = false; // RAR5, TAR: hard links are from root folder of archive
-      _link.linkPath.SetFromBstr(prop.bstrVal);
+      _link.LinkPath.SetFromBstr(prop.bstrVal);
+      // 7-Zip 24-: tar handler returned original path (with linux slash in most case)
+      // 7-Zip 24-: rar5 handler returned path with system slash.
+      // 7-Zip 25+: tar/rar5 handlers return linux path in most cases.
     }
     else if (prop.vt != VT_EMPTY)
       return E_FAIL;
   }
-  
   /*
   {
     NCOM::CPropVariant prop;
     RINOK(archive->GetProperty(index, kpidCopyLink, &prop));
     if (prop.vt == VT_BSTR)
     {
-      _link.isHardLink = false;
-      _link.isCopyLink = true;
+      _link.LinkType = k_LinkType_CopyLink;
       _link.isRelative = false; // RAR5: copy links are from root folder of archive
-      _link.linkPath.SetFromBstr(prop.bstrVal);
+      _link.LinkPath.SetFromBstr(prop.bstrVal);
     }
     else if (prop.vt != VT_EMPTY)
       return E_FAIL;
   }
   */
-
   {
     NCOM::CPropVariant prop;
     RINOK(archive->GetProperty(index, kpidSymLink, &prop))
     if (prop.vt == VT_BSTR)
     {
-      _link.isHardLink = false;
-      // _link.isCopyLink = false;
-      _link.isRelative = true; // RAR5, TAR: symbolic links can be relative
-      _link.linkPath.SetFromBstr(prop.bstrVal);
+      _link.LinkType = k_LinkType_PureSymLink;
+      _link.isRelative = true; // RAR5, TAR: symbolic links are relative by default
+      _link.LinkPath.SetFromBstr(prop.bstrVal);
+      // 7-Zip 24-: (tar, cpio, xar, ext, iso) handlers returned returned original path (with linux slash in most case)
+      // 7-Zip 24-: rar5 handler returned path with system slash.
+      // 7-Zip 25+: all handlers return linux path in most cases.
     }
     else if (prop.vt != VT_EMPTY)
       return E_FAIL;
   }
 
-  NtReparse_Data = NULL;
-  NtReparse_Size = 0;
-
-  if (_link.linkPath.IsEmpty() && _arc->GetRawProps)
+  // linux path separator in (_link.LinkPath) is expected for most cases,
+  // if new handler code is used, and if data in archive is correct.
+  // NtReparse_Data = NULL;
+  // NtReparse_Size = 0;
+  if (!_link.LinkPath.IsEmpty())
+  {
+    REPLACE_SLASHES_from_Linux_to_Sys(_link.LinkPath)
+  }
+  else if (_arc->GetRawProps)
   {
     const void *data;
-    UInt32 dataSize;
-    UInt32 propType;
-    
-    _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
-    
-    // if (dataSize == 1234567) // for debug: unpacking without reparse
-    if (dataSize != 0)
+    UInt32 dataSize, propType;
+    if (_arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType) == S_OK
+        // && dataSize == 1234567 // for debug: unpacking without reparse
+        && dataSize)
     {
       if (propType != NPropDataType::kRaw)
         return E_FAIL;
-  
       // 21.06: we need kpidNtReparse in linux for wim archives created in Windows
-      // #ifdef _WIN32
-
-      NtReparse_Data = data;
-      NtReparse_Size = dataSize;
-
-      CReparseAttr reparse;
-      bool isOkReparse = reparse.Parse((const Byte *)data, dataSize);
-      if (isOkReparse)
-      {
-        _link.isHardLink = false;
-        // _link.isCopyLink = false;
-        _link.linkPath = reparse.GetPath();
-        _link.isJunction = reparse.IsMountPoint();
-
-        if (reparse.IsSymLink_WSL())
-        {
-          _link.isWSL = true;
-          _link.isRelative = reparse.IsRelative_WSL();
-        }
-        else
-          _link.isRelative = reparse.IsRelative_Win();
-
-        // const AString s = GetAnsiString(_link.linkPath);
-        // printf("\n_link.linkPath: %s\n", s.Ptr());
-
-        #ifndef _WIN32
-        _link.linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
-        #endif
-      }
-      // #endif
+      // NtReparse_Data = data;
+      // NtReparse_Size = dataSize;
+      // we ignore error code here, if there is failure of parsing:
+      _link.Parse_from_WindowsReparseData((const Byte *)data, dataSize);
     }
   }
 
-  if (_link.linkPath.IsEmpty())
+  if (_link.LinkPath.IsEmpty())
     return S_OK;
-
+  // (_link.LinkPath) uses system path separator.
+  // windows: (_link.LinkPath) doesn't contain linux separator (slash).
   {
-    #ifdef _WIN32
-    _link.linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
-    #endif
-
-    // rar5 uses "\??\" prefix for absolute links
-    if (_link.linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
-    {
-      _link.isRelative = false;
-      _link.linkPath.DeleteFrontal(4);
-    }
-    
-    for (;;)
-    // while (NName::IsAbsolutePath(linkPath))
+    // _link.LinkPath = "\\??\\r:\\1\\2"; // for debug
+    // rar5+ returns kpidSymLink absolute link path with "\??\" prefix.
+    // we normalize such prefix:
+    if (_link.LinkPath.IsPrefixedBy(STRING_PATH_SEPARATOR "??" STRING_PATH_SEPARATOR))
     {
-      unsigned n = NName::GetRootPrefixSize(_link.linkPath);
-      if (n == 0)
-        break;
       _link.isRelative = false;
-      _link.linkPath.DeleteFrontal(n);
-    }
-  }
-
-  if (_link.linkPath.IsEmpty())
-    return S_OK;
-
-  if (!_link.isRelative && _removePathParts.Size() != 0)
-  {
-    UStringVector pathParts;
-    SplitPathToParts(_link.linkPath, pathParts);
-    bool badPrefix = false;
-    FOR_VECTOR (i, _removePathParts)
-    {
-      if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
+       // we normalize prefix from "\??\" to "\\?\":
+      _link.LinkPath.ReplaceOneCharAtPos(1, WCHAR_PATH_SEPARATOR);
+      _link.isWindowsPath = true;
+      if (_link.LinkPath.IsPrefixedBy_Ascii_NoCase(
+          STRING_PATH_SEPARATOR
+          STRING_PATH_SEPARATOR "?"
+          STRING_PATH_SEPARATOR "UNC"
+          STRING_PATH_SEPARATOR))
+      {
+         // we normalize prefix from "\\?\UNC\path" to "\\path":
+        _link.LinkPath.DeleteFrontal(6);
+        _link.LinkPath.ReplaceOneCharAtPos(0, WCHAR_PATH_SEPARATOR);
+      }
+      else
       {
-        badPrefix = true;
-        break;
+        const unsigned k_prefix_Size = 4;
+        if (NName::IsDrivePath(_link.LinkPath.Ptr(k_prefix_Size)))
+          _link.LinkPath.DeleteFrontal(k_prefix_Size);
       }
     }
-    if (!badPrefix)
-      pathParts.DeleteFrontal(_removePathParts.Size());
-    _link.linkPath = MakePathFromParts(pathParts);
   }
-
-  /*
-  if (!_link.linkPath.IsEmpty())
-  {
-    printf("\n_link %s to -> %s\n", GetOemString(_item.Path).Ptr(), GetOemString(_link.linkPath).Ptr());
-  }
-  */
-
+  _link.Normalize_to_RelativeSafe(_removePathParts);
   return S_OK;
 }
 
@@ -961,7 +923,7 @@
     if (prop.vt == VT_UI4)
     {
       res.Id_Defined = true;
-      res.Id = prop.ulVal; // for debug
+      res.Id = prop.ulVal;
       // res.Id++; // for debug
       // if (pidId == kpidGroupId) res.Id += 7; // for debug
       // res.Id = 0; // for debug
@@ -993,7 +955,7 @@
 
 HRESULT CArchiveExtractCallback::Read_fi_Props()
 {
-  IInArchive *archive = _arc->Archive;
+  IInArchive * const archive = _arc->Archive;
   const UInt32 index = _index;
 
   _fi.Attrib_Defined = false;
@@ -1134,7 +1096,7 @@
     if (!_item.IsDir
         #ifdef SUPPORT_LINKS
         #ifndef WIN32
-          || !_link.linkPath.IsEmpty()
+          || !_link.LinkPath.IsEmpty()
         #endif
         #endif
        )
@@ -1273,8 +1235,7 @@
       // MyMoveFile can rename folders. So it's OK to use it for folders too
       if (!MyMoveFile(fullProcessedPath, existPath))
       {
-        HRESULT errorCode = GetLastError_noZero_HRESULT();
-        RINOK(SendMessageError2(errorCode, kCantRenameFile, existPath, fullProcessedPath))
+        RINOK(SendMessageError2_with_LastError(kCantRenameFile, existPath, fullProcessedPath))
         return E_FAIL;
       }
     }
@@ -1341,7 +1302,7 @@
   RINOK(Read_fi_Props())
 
   #ifdef SUPPORT_LINKS
-  IInArchive *archive = _arc->Archive;
+  IInArchive * const archive = _arc->Archive;
   #endif
 
   const UInt32 index = _index;
@@ -1387,7 +1348,7 @@
     if (isAnti)
       RemoveDir(_diskFilePath);
     #ifdef SUPPORT_LINKS
-    if (_link.linkPath.IsEmpty())
+    if (_link.LinkPath.IsEmpty())
     #endif
     {
       if (!isAnti)
@@ -1416,15 +1377,15 @@
 
   #ifdef SUPPORT_LINKS
   
-  if (!_link.linkPath.IsEmpty())
+  if (!_link.LinkPath.IsEmpty())
   {
     #ifndef UNDER_CE
     {
       bool linkWasSet = false;
-      RINOK(SetFromLinkPath(fullProcessedPath, _link, linkWasSet))
+      RINOK(SetLink(fullProcessedPath, _link, linkWasSet))
       if (linkWasSet)
       {
-        _isSymLinkCreated = _link.IsSymLink();
+        _isSymLinkCreated = _link.Is_AnySymLink();
         SetAttrib();
         // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath));
       }
@@ -1454,12 +1415,7 @@
         else
         {
           if (!MyCreateHardLink(fullProcessedPath, hl))
-          {
-            const HRESULT errorCode = GetLastError_noZero_HRESULT();
-            RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, hl))
-            return S_OK;
-          }
-          
+            return SendMessageError2_with_LastError(kCantCreateHardLink, fullProcessedPath, hl);
           // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath));
           // _needSetAttrib = true; // do we need to set attribute ?
           SetAttrib();
@@ -1491,7 +1447,7 @@
 
   bool is_SymLink_in_Data = false;
 
-  if (_curSize_Defined && _curSize > 0 && _curSize < (1 << 12))
+  if (_curSize_Defined && _curSize && _curSize < k_LinkDataSize_LIMIT)
   {
     if (_fi.IsLinuxSymLink())
     {
@@ -1513,7 +1469,7 @@
     _bufPtrSeqOutStream_Spec->Init(_outMemBuf, _outMemBuf.Size());
     outStreamLoc = _bufPtrSeqOutStream;
   }
-  else // not reprase
+  else // not reparse
   {
     if (_ntOptions.PreAllocateOutFile && !_isSplit && _curSize_Defined && _curSize > (1 << 12))
     {
@@ -1568,7 +1524,7 @@
       RINOK(outFileStream_Loc->Seek((Int64)_position, STREAM_SEEK_SET, NULL))
     }
     outStreamLoc = outFileStream_Loc;
-  } // if not reprase
+  } // if not reparse
 
   _outFileStream = outFileStream_Loc;
       
@@ -1620,8 +1576,7 @@
   _fileLength_WasSet = false;
   _isRenamed = false;
   // _fi.Clear();
- _extractMode = false;
-  // _is_SymLink_in_Data = false;
+  _extractMode = false;
   _is_SymLink_in_Data_Linux = false;
   _needSetAttrib = false;
   _isSymLinkCreated = false;
@@ -1661,7 +1616,7 @@
   }
 
 
-  IInArchive *archive = _arc->Archive;
+  IInArchive * const archive = _arc->Archive;
 
   RINOK(GetItem(index))
 
@@ -1677,10 +1632,9 @@
     }
   }
 
-  #ifdef SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
   RINOK(ReadLink())
-  #endif // SUPPORT_LINKS
-  
+#endif
   
   RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted))
 
@@ -2016,63 +1970,80 @@
 
 #ifdef SUPPORT_LINKS
 
+/*
+in:
+  link.LinkPath : must be relative (non-absolute) path in any case !!!
+  link.isRelative / target path that must stored as created link:
+           == false   / _dirPathPrefix_Full + link.LinkPath
+           == true    / link.LinkPath
+*/
 
-HRESULT CArchiveExtractCallback::SetFromLinkPath(
-    const FString &fullProcessedPath,
-    const CLinkInfo &linkInfo,
+HRESULT CArchiveExtractCallback::SetLink(
+    const FString &fullProcessedPath_from,
+    const CLinkInfo &link,
     bool &linkWasSet)
 {
   linkWasSet = false;
-  if (!_ntOptions.SymLinks.Val && !linkInfo.isHardLink)
+  if (link.LinkPath.IsEmpty())
+    return S_OK;
+  if (!_ntOptions.SymLinks.Val && link.Is_AnySymLink())
     return S_OK;
-
-  UString relatPath;
-
-  /* if (linkInfo.isRelative)
-       linkInfo.linkPath is final link path that must be stored to file link field
-     else
-       linkInfo.linkPath is path from root of archive. So we must add _dirPathPrefix_Full before linkPath.
-  */
-     
-  if (linkInfo.isRelative)
-    relatPath = GetDirPrefixOf(_item.Path);
-  relatPath += linkInfo.linkPath;
-  
-  if (!IsSafePath(relatPath))
   {
-    return SendMessageError2(
-          0, // errorCode
+    UString path;
+    if (link.isRelative)
+    {
+      // _item.PathParts : parts that will be created in output folder.
+      // we want to get directory prefix of link item.
+      // so we remove file name (last non-empty part) from PathParts:
+      UStringVector v = _item.PathParts;
+      while (!v.IsEmpty())
+      {
+        const unsigned len = v.Back().Len();
+        v.DeleteBack();
+        if (len)
+          break;
+      }
+      path = MakePathFromParts(v);
+      NName::NormalizeDirPathPrefix(path);
+    }
+    path += link.LinkPath;
+    /*
+    path is calculated virtual target path of link
+    path is relative to root folder of extracted items
+    if (!link.isRelative), then (path == link.LinkPath)
+    */
+    if (!IsSafePath(path, link.Is_WSL()))
+      return SendMessageError2(0, // errorCode
           "Dangerous link path was ignored",
-          us2fs(_item.Path),
-          us2fs(linkInfo.linkPath)); // us2fs(relatPath)
+          us2fs(_item.Path), us2fs(link.LinkPath));
   }
 
-  FString existPath;
-  if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */ || !linkInfo.isRelative)
+  FString target; // target path that will be stored to link field
+  if (link.Is_HardLink() /* || link.IsCopyLink */ || !link.isRelative)
   {
-    if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
-    {
-      RINOK(SendMessageError("Incorrect path", us2fs(relatPath)))
-    }
+    // isRelative == false
+    // all hard links and absolute symbolic links
+    // relatPath == link.LinkPath
+    // we get absolute link path for target:
+    if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(link.LinkPath), target))
+      return SendMessageError("Incorrect link path", us2fs(link.LinkPath));
+    // (target) is (_dirPathPrefix_Full + relatPath)
   }
   else
   {
-    existPath = us2fs(linkInfo.linkPath);
-    // printf("\nlinkPath = : %s\n", GetOemString(linkInfo.linkPath).Ptr());
+    // link.isRelative == true
+    // relative symbolic links only
+    target = us2fs(link.LinkPath);
   }
-    
-  if (existPath.IsEmpty())
-    return SendMessageError("Empty link", fullProcessedPath);
+  if (target.IsEmpty())
+    return SendMessageError("Empty link", fullProcessedPath_from);
 
-  if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */)
+  if (link.Is_HardLink() /* || link.IsCopyLink */)
   {
-    // if (linkInfo.isHardLink)
+    // if (link.isHardLink)
     {
-      if (!MyCreateHardLink(fullProcessedPath, existPath))
-      {
-        const HRESULT errorCode = GetLastError_noZero_HRESULT();
-        RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, existPath))
-      }
+      if (!MyCreateHardLink(fullProcessedPath_from, target))
+        return SendMessageError2_with_LastError(kCantCreateHardLink, fullProcessedPath_from, target);
       /*
       RINOK(PrepareOperation(NArchive::NExtract::NAskMode::kExtract))
       _op_WasReported = true;
@@ -2085,19 +2056,19 @@
     // IsCopyLink
     {
       NFind::CFileInfo fi;
-      if (!fi.Find(existPath))
+      if (!fi.Find(target))
       {
-        RINOK(SendMessageError2("Cannot find the file for copying", existPath, fullProcessedPath));
+        RINOK(SendMessageError2("Cannot find the file for copying", target, fullProcessedPath));
       }
       else
       {
         if (_curSize_Defined && _curSize == fi.Size)
-          _copyFile_Path = existPath;
+          _copyFile_Path = target;
         else
         {
-          RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
+          RINOK(SendMessageError2("File size collision for file copying", target, fullProcessedPath));
         }
-        // RINOK(MyCopyFile(existPath, fullProcessedPath));
+        // RINOK(MyCopyFile(target, fullProcessedPath));
       }
     }
     */
@@ -2111,127 +2082,249 @@
     // Windows before Vista doesn't support symbolic links.
     // we could convert such symbolic links to Junction Points
     // isJunction = true;
-    // convertToAbs = true;
   }
   */
 
-  if (!_ntOptions.SymLinks_AllowDangerous.Val)
+#ifdef _WIN32
+  const bool isDir = (_item.IsDir || link.LinkType == k_LinkType_Junction);
+#endif
+
+  if (!_ntOptions.SymLinks_AllowDangerous.Val && link.isRelative)
   {
-    #ifdef _WIN32
-    if (_item.IsDir)
-    #endif
-    if (linkInfo.isRelative)
-      {
-        CLinkLevelsInfo levelsInfo;
-        levelsInfo.Parse(linkInfo.linkPath);
-        if (levelsInfo.FinalLevel < 1 || levelsInfo.IsAbsolute)
-        {
-          return SendMessageError2(
-            0, // errorCode
-            "Dangerous symbolic link path was ignored",
-            us2fs(_item.Path),
-            us2fs(linkInfo.linkPath));
-        }
-      }
+    /*
+    We want to use additional check for links that can link to directory.
+      - linux: all symbolic links are files.
+      - windows: we can have file/directory symbolic link,
+        but file symbolic link works like directory link in windows.
+    So we use additional check for all relative links.
+
+    We don't allow decreasing of final level of link.
+    So if some another extracted file will use this link,
+    then number of real path parts (after link redirection) cannot be
+    smaller than number of requested path parts from archive records.
+    
+    Now we check only (link.LinkPath) without (_item.PathParts).
+    */
+    CLinkLevelsInfo levelsInfo;
+    levelsInfo.Parse(link.LinkPath, link.Is_WSL());
+    if (levelsInfo.FinalLevel < 1
+      // || levelsInfo.LowLevel < 0 // we allow negative temporary levels
+        || levelsInfo.IsAbsolute)
+      return SendMessageError2(0, // errorCode
+          "Dangerous symbolic link path was ignored",
+          us2fs(_item.Path), us2fs(link.LinkPath));
   }
 
   
-  #ifdef _WIN32
-  
+#ifdef _WIN32
   CByteBuffer data;
-  // printf("\nFillLinkData(): %s\n", GetOemString(existPath).Ptr());
-  if (!FillLinkData(data, fs2us(existPath), !linkInfo.isJunction, linkInfo.isWSL))
+  // printf("\nFillLinkData(): %s\n", GetOemString(target).Ptr());
+  if (link.Is_WSL())
+  {
+    Convert_WinPath_to_WslLinuxPath(target, !link.isRelative);
+    FillLinkData_WslLink(data, fs2us(target));
+  }
+  else
+    FillLinkData_WinLink(data, fs2us(target), link.LinkType != k_LinkType_Junction);
+  if (data.Size() == 0)
     return SendMessageError("Cannot fill link data", us2fs(_item.Path));
-
   /*
   if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0)
-  {
-    SendMessageError("reconstructed Reparse is different", fs2us(existPath));
-  }
+    SendMessageError("reconstructed Reparse is different", fs2us(target));
   */
-  
-  CReparseAttr attr;
-  if (!attr.Parse(data, data.Size()))
   {
-    RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)))
-    return S_OK;
-  }
-  if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
+    // we check that reparse data is correct, but we ignore attr.MinorError.
+    CReparseAttr attr;
+    if (!attr.Parse(data, data.Size()))
+      return SendMessageError("Internal error for symbolic link file", us2fs(_item.Path));
+  }
+  if (!NFile::NIO::SetReparseData(fullProcessedPath_from, isDir, data, (DWORD)data.Size()))
+#else // ! _WIN32
+  if (!NFile::NIO::SetSymLink(fullProcessedPath_from, target))
+#endif // ! _WIN32
   {
-    RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath))
-    return S_OK;
+    return SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath_from);
   }
   linkWasSet = true;
-
   return S_OK;
-  
-  
-  #else // ! _WIN32
+}
+
+
 
-  if (!NFile::NIO::SetSymLink(fullProcessedPath, existPath))
+bool CLinkInfo::Parse_from_WindowsReparseData(const Byte *data, size_t dataSize)
+{
+  CReparseAttr reparse;
+  if (!reparse.Parse(data, dataSize))
+    return false;
+  // const AString s = GetAnsiString(LinkPath);
+  // printf("\nlinkPath: %s\n", s.Ptr());
+  LinkPath = reparse.GetPath();
+  if (reparse.IsSymLink_WSL())
   {
-    RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath))
-    return S_OK;
+    LinkType = k_LinkType_WSL;
+    isRelative = reparse.IsRelative_WSL(); // detected from LinkPath[0]
+    // LinkPath is original raw name converted to UString from AString
+    // Linux separator '/' is expected here.
+    REPLACE_SLASHES_from_Linux_to_Sys(LinkPath)
   }
-  linkWasSet = true;
+  else
+  {
+    LinkType = reparse.IsMountPoint() ? k_LinkType_Junction : k_LinkType_PureSymLink;
+    isRelative = reparse.IsRelative_Win(); // detected by (Flags == Z7_WIN_SYMLINK_FLAG_RELATIVE)
+    isWindowsPath = true;
+    // LinkPath is original windows link path from raparse data with \??\ prefix removed.
+    // windows '\\' separator is expected here.
+    // linux '/' separator is not expected here.
+    // we translate both types of separators to system separator.
+    LinkPath.Replace(
+#if WCHAR_PATH_SEPARATOR == L'\\'
+        L'/'
+#else
+        L'\\'
+#endif
+        , WCHAR_PATH_SEPARATOR);
+  }
+  // (LinkPath) uses system path separator.
+  // windows: (LinkPath) doesn't contain linux separator (slash).
+  return true;
+}
 
-  return S_OK;
 
-  #endif // ! _WIN32
+bool CLinkInfo::Parse_from_LinuxData(const Byte *data, size_t dataSize)
+{
+  // Clear(); // *this object was cleared by constructor already.
+  LinkType = k_LinkType_PureSymLink;
+  AString utf;
+  if (dataSize >= k_LinkDataSize_LIMIT)
+    return false;
+  utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize);
+  UString u;
+  if (!ConvertUTF8ToUnicode(utf, u))
+    return false;
+  if (u.IsEmpty())
+    return false;
+  const wchar_t c = u[0];
+  isRelative = (c != L'/');
+  // linux path separator is expected
+  REPLACE_SLASHES_from_Linux_to_Sys(u)
+  LinkPath = u;
+  // (LinkPath) uses system path separator.
+  // windows: (LinkPath) doesn't contain linux separator (slash).
+  return true;
 }
+    
 
+// in/out:          (LinkPath) uses system path separator
+// in/out: windows: (LinkPath) doesn't contain linux separator (slash).
+// out: (LinkPath) is relative path, and LinkPath[0] is not path separator
+// out: isRelative changed to false, if any prefix was removed.
+// note: absolute windows links "c:\" to root will be reduced to empty string:
+void CLinkInfo::Remove_AbsPathPrefixes()
+{
+  while (!LinkPath.IsEmpty())
+  {
+    unsigned n = 0;
+    if (!Is_WSL())
+    {
+      n =
+#ifndef _WIN32
+      isWindowsPath ?
+        NName::GetRootPrefixSize_WINDOWS(LinkPath) :
+#endif
+        NName::GetRootPrefixSize(LinkPath);
+/*
+      // "c:path" will be ignored later as "Dangerous absolute path"
+      // so check is not required
+      if (n == 0
+#ifndef _WIN32
+          && isWindowsPath
+#endif
+          && NName::IsDrivePath2(LinkPath))
+        n = 2;
+*/
+    }
+    if (n == 0)
+    {
+      if (!IS_PATH_SEPAR(LinkPath[0]))
+        break;
+      n = 1;
+    }
+    isRelative = false; // (LinkPath) will be treated as relative to root folder of archive
+    LinkPath.DeleteFrontal(n);
+  }
+}
 
-bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData)
+
+/*
+  it removes redundant separators, if there are double separators,
+  but it keeps double separators at start of string //name/.
+  in/out:    system path separator is used
+    windows: slash character (linux separator) is not treated as separator
+    windows: (path) doesn't contain linux separator (slash).
+*/
+static void RemoveRedundantPathSeparators(UString &path)
 {
-  Clear();
-  // this->isLinux = isLinuxData;
-  
-  if (isLinuxData)
+  wchar_t *dest = path.GetBuf();
+  const wchar_t * const start = dest;
+  const wchar_t *src = dest;
+  for (;;)
   {
-    isJunction = false;
-    isHardLink = false;
-    AString utf;
-    if (dataSize >= (1 << 12))
-      return false;
-    utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize);
-    UString u;
-    if (!ConvertUTF8ToUnicode(utf, u))
-      return false;
-    linkPath = u;
-    
-    // in linux symbolic data: we expect that linux separator '/' is used
-    // if windows link was created, then we also must use linux separator
-    if (u.IsEmpty())
-      return false;
-    const wchar_t c = u[0];
-    isRelative = !IS_PATH_SEPAR(c);
-    return true;
+    wchar_t c = *src++;
+    if (c == 0)
+      break;
+    // if (IS_PATH_SEPAR(c)) // for Windows: we can change (/) to (\).
+    if (c == WCHAR_PATH_SEPARATOR)
+    {
+      if (dest - start >= 2 && dest[-1] == WCHAR_PATH_SEPARATOR)
+        continue;
+      // c = WCHAR_PATH_SEPARATOR; // for Windows: we can change (/) to (\).
+    }
+    *dest++ = c;
   }
+  *dest = 0;
+  path.ReleaseBuf_SetLen((unsigned)(dest - path.Ptr()));
+}
 
-  CReparseAttr reparse;
-  if (!reparse.Parse(data, dataSize))
-    return false;
-  isHardLink = false;
-  // isCopyLink = false;
-  linkPath = reparse.GetPath();
-  isJunction = reparse.IsMountPoint();
-  
-  if (reparse.IsSymLink_WSL())
+
+// in/out: (LinkPath) uses system path separator
+// in/out: windows: (LinkPath) doesn't contain linux separator (slash).
+// out: (LinkPath) is relative path, and LinkPath[0] is not path separator
+void CLinkInfo::Normalize_to_RelativeSafe(UStringVector &removePathParts)
+{
+  // We WILL NOT WRITE original absolute link path from archive to filesystem.
+  // So here we remove all root prefixes from (LinkPath).
+  // If we see any absolute root prefix, then we suppose that this prefix is virtual prefix
+  // that shows that link is relative to root folder of archive
+  RemoveRedundantPathSeparators(LinkPath);
+  // LinkPath = "\\\\?\\r:test\\test2"; // for debug
+  Remove_AbsPathPrefixes();
+  // (LinkPath) now is relative:
+  //  if (isRelative == false), then (LinkPath) is relative to root folder of archive
+  //  if (isRelative == true ), then (LinkPath) is relative to current item
+  if (LinkPath.IsEmpty() || isRelative || removePathParts.Size() == 0)
+    return;
+
+  // if LinkPath is prefixed by _removePathParts, we remove these paths
+  UStringVector pathParts;
+  SplitPathToParts(LinkPath, pathParts);
+  bool badPrefix = false;
   {
-    isWSL = true;
-    isRelative = reparse.IsRelative_WSL();
+    FOR_VECTOR (i, removePathParts)
+    {
+      if (i >= pathParts.Size()
+        || CompareFileNames(removePathParts[i], pathParts[i]) != 0)
+      {
+        badPrefix = true;
+        break;
+      }
+    }
   }
-  else
-    isRelative = reparse.IsRelative_Win();
-    
-  // FIXME !!!
-  #ifndef _WIN32
-  linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
-  #endif
-  
-  return true;
+  if (!badPrefix)
+    pathParts.DeleteFrontal(removePathParts.Size());
+  LinkPath = MakePathFromParts(pathParts);
+  Remove_AbsPathPrefixes();
 }
-    
+
 #endif // SUPPORT_LINKS
 
 
@@ -2239,12 +2332,12 @@
 {
   HRESULT res = S_OK;
 
-  #ifdef SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
 
   size_t reparseSize = 0;
   bool repraseMode = false;
   bool needSetReparse = false;
-  CLinkInfo linkInfo;
+  CLinkInfo link;
   
   if (_bufPtrSeqOutStream)
   {
@@ -2258,15 +2351,19 @@
       needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode);
       if (needSetReparse)
       {
-        UString linkPath = reparse.GetPath();
+        UString LinkPath = reparse.GetPath();
         #ifndef _WIN32
-        linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
+        LinkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
         #endif
       }
       */
-      needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux);
+      needSetReparse = _is_SymLink_in_Data_Linux ?
+          link.Parse_from_LinuxData(_outMemBuf, reparseSize) :
+          link.Parse_from_WindowsReparseData(_outMemBuf, reparseSize);
       if (!needSetReparse)
         res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path));
+      // (link.LinkPath) uses system path separator.
+      // windows: (link.LinkPath) doesn't contain linux separator (slash).
     }
     else
     {
@@ -2281,23 +2378,18 @@
     _bufPtrSeqOutStream.Release();
   }
 
-  #endif // SUPPORT_LINKS
-
+#endif // SUPPORT_LINKS
 
   const HRESULT res2 = CloseFile();
-
   if (res == S_OK)
     res = res2;
-
   RINOK(res)
 
-  #ifdef SUPPORT_LINKS
+#ifdef SUPPORT_LINKS
   if (repraseMode)
   {
     _curSize = reparseSize;
     _curSize_Defined = true;
-    
-    #ifdef SUPPORT_LINKS
     if (needSetReparse)
     {
       // in Linux   : we must delete empty file before symbolic link creation
@@ -2307,31 +2399,19 @@
         RINOK(SendMessageError_with_LastError("can't delete file", _diskFilePath))
       }
       {
-        /*
-        // for DEBUG ONLY: we can extract sym links as WSL links
-        // to eliminate (non-admin) errors for sym links.
-        #ifdef _WIN32
-        if (!linkInfo.isHardLink && !linkInfo.isJunction)
-          linkInfo.isWSL = true;
-        #endif
-        */
         bool linkWasSet = false;
-        RINOK(SetFromLinkPath(_diskFilePath, linkInfo, linkWasSet))
+        // link.LinkPath = "r:\\1\\2"; // for debug
+        // link.isJunction = true; // for debug
+        link.Normalize_to_RelativeSafe(_removePathParts);
+        RINOK(SetLink(_diskFilePath, link, linkWasSet))
         if (linkWasSet)
-          _isSymLinkCreated = linkInfo.IsSymLink();
+          _isSymLinkCreated = true; // link.IsSymLink();
         else
           _needSetAttrib = false;
       }
-      /*
-      if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, ))
-      {
-        res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath);
-      }
-      */
     }
-    #endif
   }
-  #endif
+#endif // SUPPORT_LINKS
   return res;
 }
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h	2024-10-11 17:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/ArchiveExtractCallback.h	2025-06-16 13:00:00.000000000 +0200
@@ -178,36 +178,50 @@
 
 #ifdef SUPPORT_LINKS
 
+
+enum ELinkType
+{
+  k_LinkType_HardLink,
+  k_LinkType_PureSymLink,
+  k_LinkType_Junction,
+  k_LinkType_WSL
+  // , k_LinkType_CopyLink;
+};
+
+
 struct CLinkInfo
 {
-  // bool isCopyLink;
-  bool isHardLink;
-  bool isJunction;
+  ELinkType LinkType;
   bool isRelative;
-  bool isWSL;
-  UString linkPath;
+    //  if (isRelative == false), then (LinkPath) is relative to root folder of archive
+    //  if (isRelative == true ), then (LinkPath) is relative to current item
+  bool isWindowsPath;
+  UString LinkPath;
+
+  bool Is_HardLink() const { return LinkType == k_LinkType_HardLink; }
+  bool Is_AnySymLink() const { return LinkType != k_LinkType_HardLink; }
 
-  bool IsSymLink() const { return !isHardLink; }
+  bool Is_WSL() const { return LinkType == k_LinkType_WSL; }
 
   CLinkInfo():
-    // IsCopyLink(false),
-    isHardLink(false),
-    isJunction(false),
+    LinkType(k_LinkType_PureSymLink),
     isRelative(false),
-    isWSL(false)
+    isWindowsPath(false)
     {}
 
   void Clear()
   {
-    // IsCopyLink = false;
-    isHardLink = false;
-    isJunction = false;
+    LinkType = k_LinkType_PureSymLink;
     isRelative = false;
-    isWSL = false;
-    linkPath.Empty();
+    isWindowsPath = false;
+    LinkPath.Empty();
   }
 
-  bool Parse(const Byte *data, size_t dataSize, bool isLinuxData);
+  bool Parse_from_WindowsReparseData(const Byte *data, size_t dataSize);
+  bool Parse_from_LinuxData(const Byte *data, size_t dataSize);
+  void Normalize_to_RelativeSafe(UStringVector &removePathParts);
+private:
+  void Remove_AbsPathPrefixes();
 };
 
 #endif // SUPPORT_LINKS
@@ -287,8 +301,8 @@
 
   bool _isRenamed;
   bool _extractMode;
-  // bool _is_SymLink_in_Data;
-  bool _is_SymLink_in_Data_Linux; // false = WIN32, true = LINUX
+  bool _is_SymLink_in_Data_Linux; // false = WIN32, true = LINUX.
+      // _is_SymLink_in_Data_Linux is detected from Windows/Linux part of attributes of file.
   bool _needSetAttrib;
   bool _isSymLinkCreated;
   bool _itemFailure;
@@ -420,6 +434,7 @@
   HRESULT SendMessageError_with_Error(HRESULT errorCode, const char *message, const FString &path);
   HRESULT SendMessageError_with_LastError(const char *message, const FString &path);
   HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2);
+  HRESULT SendMessageError2_with_LastError(const char *message, const FString &path1, const FString &path2);
 
 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX)
   NExtract::NZoneIdMode::EEnum ZoneMode;
@@ -487,11 +502,16 @@
 private:
   CHardLinks _hardLinks;
   CLinkInfo _link;
+  // const void *NtReparse_Data;
+  // UInt32 NtReparse_Size;
 
   // FString _copyFile_Path;
   // HRESULT MyCopyFile(ISequentialOutStream *outStream);
-  HRESULT Link(const FString &fullProcessedPath);
   HRESULT ReadLink();
+  HRESULT SetLink(
+      const FString &fullProcessedPath_from,
+      const CLinkInfo &linkInfo,
+      bool &linkWasSet);
 
 public:
   // call PrepareHardLinks() after Init()
@@ -538,16 +558,6 @@
   HRESULT CloseReparseAndFile();
   HRESULT CloseReparseAndFile2();
   HRESULT SetDirsTimes();
-
-  const void *NtReparse_Data;
-  UInt32 NtReparse_Size;
-
-  #ifdef SUPPORT_LINKS
-  HRESULT SetFromLinkPath(
-      const FString &fullProcessedPath,
-      const CLinkInfo &linkInfo,
-      bool &linkWasSet);
-  #endif
 };
 
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Bench.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/Bench.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Bench.cpp	2024-11-25 15:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/Bench.cpp	2025-06-30 18:00:00.000000000 +0200
@@ -871,14 +871,27 @@
   unsigned NumCoreThreads;
   unsigned NumCores;
   // unsigned DivideNum;
+
+#ifdef _WIN32
+  unsigned NumGroups;
+#endif
+
   UInt32 Sizes[NUM_CPU_LEVELS_MAX];
 
   void SetLevels(unsigned numCores, unsigned numCoreThreads);
   DWORD_PTR GetAffinityMask(UInt32 bundleIndex, CCpuSet *cpuSet) const;
   bool NeedAffinity() const { return NumBundleThreads != 0; }
 
+#ifdef _WIN32
+  bool NeedGroupsMode() const { return NumGroups > 1; }
+#endif
+
   WRes CreateThread_WithAffinity(NWindows::CThread &thread, THREAD_FUNC_TYPE startAddress, LPVOID parameter, UInt32 bundleIndex) const
   {
+#ifdef _WIN32
+    if (NeedGroupsMode()) // we need fix for bundleIndex usage
+      return thread.Create_With_Group(startAddress, parameter, bundleIndex % NumGroups);
+#endif
     if (NeedAffinity())
     {
       CCpuSet cpuSet;
@@ -892,6 +905,9 @@
     NumBundleThreads(0),
     NumLevels(0),
     NumCoreThreads(1)
+#ifdef _WIN32
+    , NumGroups(0)
+#endif
     // DivideNum(1)
     {}
 };
@@ -1288,22 +1304,28 @@
     if (scp)
     {
       const UInt64 reduceSize = kBufferSize;
-      
-      /* in posix new thread uses same affinity as parent thread,
+      /* in posix : new thread uses same affinity as parent thread,
          so we don't need to send affinity to coder in posix */
-      UInt64 affMask;
-      #if !defined(Z7_ST) && defined(_WIN32)
+      UInt64 affMask = 0;
+      UInt32 affinityGroup = (UInt32)(Int32)-1;
+      // UInt64 affinityInGroup = 0;
+#if !defined(Z7_ST) && defined(_WIN32)
       {
         CCpuSet cpuSet;
-        affMask = AffinityMode.GetAffinityMask(EncoderIndex, &cpuSet);
+        if (AffinityMode.NeedGroupsMode()) // we need fix for affinityInGroup also
+          affinityGroup = EncoderIndex % AffinityMode.NumGroups;
+        else
+          affMask = AffinityMode.GetAffinityMask(EncoderIndex, &cpuSet);
       }
-      #else
-        affMask = 0;
-      #endif
-      // affMask <<= 3; // debug line: to test no affinity in coder;
-      // affMask = 0;
-
-      RINOK(method.SetCoderProps_DSReduce_Aff(scp, &reduceSize, (affMask != 0 ? &affMask : NULL)))
+#endif
+      // affMask <<= 3; // debug line: to test no affinity in coder
+      // affMask = 0; // for debug
+      // affinityGroup = 0; // for debug
+      // affinityInGroup = 1; // for debug
+      RINOK(method.SetCoderProps_DSReduce_Aff(scp, &reduceSize,
+          affMask != 0 ? &affMask : NULL,
+          affinityGroup != (UInt32)(Int32)-1 ? &affinityGroup : NULL,
+          /* affinityInGroup != 0 ? &affinityInGroup : */ NULL))
     }
     else
     {
@@ -2962,7 +2984,7 @@
 {
   AString s;
   // s.Add_UInt32(ti.numProcessThreads);
-  unsigned numSysThreads = ti.GetNumSystemThreads();
+  const unsigned numSysThreads = ti.GetNumSystemThreads();
   if (ti.GetNumProcessThreads() != numSysThreads)
   {
     // if (ti.numProcessThreads != ti.numSysThreads)
@@ -2992,6 +3014,35 @@
     }
     #endif
   }
+#ifdef _WIN32
+  if (ti.Groups.GroupSizes.Size() > 1 ||
+      (ti.Groups.GroupSizes.Size() == 1
+       && ti.Groups.NumThreadsTotal != numSysThreads))
+  {
+    s += " : ";
+    s.Add_UInt32(ti.Groups.GroupSizes.Size());
+    s += " groups : ";
+    if (ti.Groups.NumThreadsTotal == numSysThreads)
+    {
+      s.Add_UInt32(ti.Groups.NumThreadsTotal);
+      s += " c : ";
+    }
+    UInt32 minSize, maxSize;
+    ti.Groups.Get_GroupSize_Min_Max(minSize, maxSize);
+    if (minSize == maxSize)
+    {
+      s.Add_UInt32(ti.Groups.GroupSizes[0]);
+      s += " c/g";
+    }
+    else
+    FOR_VECTOR (i, ti.Groups.GroupSizes)
+    {
+      if (i != 0)
+        s.Add_Char(' ');
+      s.Add_UInt32(ti.Groups.GroupSizes[i]);
+    }
+  }
+#endif
   return s;
 }
 
@@ -3753,9 +3804,13 @@
   UInt64 complexInCommands = kComplexInCommands;
   UInt32 numThreads_Start = 1;
   
-  #ifndef Z7_ST
+#ifndef Z7_ST
   CAffinityMode affinityMode;
-  #endif
+#ifdef _WIN32
+  if (threadsInfo.IsGroupMode && threadsInfo.Groups.GroupSizes.Size() > 1)
+    affinityMode.NumGroups = threadsInfo.Groups.GroupSizes.Size();
+#endif
+#endif
 
 
   COneMethodInfo method;
@@ -4861,7 +4916,7 @@
         if (AreSameMethodNames(benchMethod, methodName))
         {
           if (benchProps.IsEmpty()
-              || (benchProps == "x5" && method.PropsString.IsEmpty())
+              || (benchProps.IsEqualTo("x5") && method.PropsString.IsEmpty())
               || method.PropsString.IsPrefixedBy_Ascii_NoCase(benchProps))
           {
             callback.BenchProps.EncComplex = h.EncComplex;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp	2024-10-06 11:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/EnumDirItems.cpp	2025-06-20 12:00:00.000000000 +0200
@@ -1213,11 +1213,13 @@
       // continue; // for debug
       if (!item.Has_Attrib_ReparsePoint())
         continue;
-
+      /*
+      We want to get properties of target file instead of properies of symbolic link.
+      Probably this code is unused, because
+      CFileInfo::Find(with followLink = true) called Fill_From_ByHandleFileInfo() already.
+      */
       // if (item.IsDir()) continue;
-
       const FString phyPath = GetPhyPath(i);
-
       NFind::CFileInfo fi;
       if (fi.Fill_From_ByHandleFileInfo(phyPath)) // item.IsDir()
       {
@@ -1228,38 +1230,13 @@
         item.Attrib = fi.Attrib;
         continue;
       }
-
-      /*
-      // we request properties of target file instead of properies of symbolic link
-      // here we also can manually parse unsupported links (like WSL links)
-      NIO::CInFile inFile;
-      if (inFile.Open(phyPath))
-      {
-        BY_HANDLE_FILE_INFORMATION info;
-        if (inFile.GetFileInformation(&info))
-        {
-          // Stat.FilesSize doesn't contain item.Size already
-          // Stat.FilesSize -= item.Size;
-          item.Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
-          Stat.FilesSize += item.Size;
-          item.CTime = info.ftCreationTime;
-          item.ATime = info.ftLastAccessTime;
-          item.MTime = info.ftLastWriteTime;
-          item.Attrib = info.dwFileAttributes;
-          continue;
-        }
-      }
-      */
-
       RINOK(AddError(phyPath))
       continue;
     }
 
-    // (SymLinks == true) here
-
+    // (SymLinks == true)
     if (item.ReparseData.Size() == 0)
       continue;
-
     // if (item.Size == 0)
     {
       // 20.03: we use Reparse Data instead of real data
@@ -1277,7 +1254,7 @@
     /* imagex/WIM reduces absolute paths in links (raparse data),
        if we archive non root folder. We do same thing here */
 
-    bool isWSL = false;
+    // bool isWSL = false;
     if (attr.IsSymLink_WSL())
     {
       // isWSL = true;
@@ -1314,21 +1291,27 @@
       continue;
     if (rootPrefixSize == prefix.Len())
       continue; // simple case: paths are from root
-
     if (link.Len() <= prefix.Len())
       continue;
-
     if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0)
       continue;
 
     UString newLink = prefix.Left(rootPrefixSize);
     newLink += link.Ptr(prefix.Len());
 
-    CByteBuffer data;
-    bool isSymLink = !attr.IsMountPoint();
-    if (!FillLinkData(data, newLink, isSymLink, isWSL))
+    CByteBuffer &data = item.ReparseData2;
+/*
+    if (isWSL)
+    {
+      Convert_WinPath_to_WslLinuxPath(newLink, true); // is absolute : change it
+      FillLinkData_WslLink(data, newLink);
+    }
+    else
+*/
+      FillLinkData_WinLink(data, newLink, !attr.IsMountPoint());
+    if (data.Size() == 0)
       continue;
-    item.ReparseData2 = data;
+    // item.ReparseData2 = data;
   }
   return S_OK;
 }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Extract.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/Extract.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Extract.cpp	2024-02-10 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/Extract.cpp	2025-06-16 10:00:00.000000000 +0200
@@ -389,7 +389,7 @@
       {
         UString s = arcPath.Ptr(pos + 1);
         int index = codecs->FindFormatForExtension(s);
-        if (index >= 0 && s == L"001")
+        if (index >= 0 && s.IsEqualTo("001"))
         {
           s = arcPath.Left(pos);
           pos = s.ReverseFind(L'.');
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp	2023-03-06 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/ExtractingFilePath.cpp	2025-06-16 08:00:00.000000000 +0200
@@ -208,7 +208,7 @@
       if (parts.Size() > 1 && parts[1].IsEmpty())
       {
         i = 2;
-        if (parts.Size() > 2 && parts[2] == L"?")
+        if (parts.Size() > 2 && parts[2].IsEqualTo("?"))
         {
           i = 3;
           if (parts.Size() > 3 && NWindows::NFile::NName::IsDrivePath2(parts[3]))
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/HashCalc.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.cpp	2024-11-12 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/HashCalc.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -62,7 +62,7 @@
     if (m.MethodName.IsEmpty())
       m.MethodName = k_DefaultHashMethod;
     
-    if (m.MethodName == "*")
+    if (m.MethodName.IsEqualTo("*"))
     {
       CRecordVector<CMethodId> tempMethods;
       GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods);
@@ -431,6 +431,19 @@
 }
 
 
+static void Convert_TagName_to_MethodName(AString &method)
+{
+  // we need to convert at least SHA512/256 to SHA512-256, and SHA512/224 to SHA512-224
+  // but we convert any '/' to '-'.
+  method.Replace('/', '-');
+}
+
+static void Convert_MethodName_to_TagName(AString &method)
+{
+  if (method.IsPrefixedBy_Ascii_NoCase("SHA512-2"))
+    method.ReplaceOneCharAtPos(6, '/');
+}
+
 
 static void WriteLine(CDynLimBuf &hashFileString,
     const CHashOptionsLocal &options,
@@ -440,8 +453,10 @@
 {
   AString methodName;
   if (!hb.Hashers.IsEmpty())
+  {
     methodName = hb.Hashers[0].Name;
-  
+    Convert_MethodName_to_TagName(methodName);
+  }
   AString hashesString;
   AddHashResultLine(hashesString, hb.Hashers);
   WriteLine(hashFileString, options, path, isDir, methodName, hashesString);
@@ -752,7 +767,7 @@
   Name = end;
   
   Hash.Alloc(4);
-  SetBe32(Hash, crc)
+  SetBe32a(Hash, crc)
 
   Size_from_Arc = size;
   Size_from_Arc_Defined = true;
@@ -773,56 +788,87 @@
 {
     "sha256"
   , "sha224"
-//  , "sha512-224"
-//  , "sha512-256"
+  , "sha512-224"
+  , "sha512-256"
   , "sha384"
   , "sha512"
-//  , "sha3-224"
+  , "sha3-224"
   , "sha3-256"
-//  , "sha3-384"
-//  , "sha3-512"
+  , "sha3-384"
+  , "sha3-512"
 //  , "shake128"
 //  , "shake256"
   , "sha1"
+  , "sha2"
+  , "sha3"
+  , "sha"
   , "md5"
-  , "blake2sp"
+  , "blake2s"
   , "blake2b"
+  , "blake2sp"
   , "xxh64"
-  , "crc64"
   , "crc32"
+  , "crc64"
   , "cksum"
 };
 
-static UString GetMethod_from_FileName(const UString &name)
+
+// returns true, if (method) is known hash method or hash method group name.
+static bool GetMethod_from_FileName(const UString &name, AString &method)
 {
+  method.Empty();
   AString s;
   ConvertUnicodeToUTF8(name, s);
   const int dotPos = s.ReverseFind_Dot();
-  const char *src = s.Ptr();
-  bool isExtension = false;
   if (dotPos >= 0)
   {
-    isExtension = true;
-    src = s.Ptr(dotPos + 1);
+    method = s.Ptr(dotPos + 1);
+    if (method.IsEqualTo_Ascii_NoCase("txt") ||
+        method.IsEqualTo_Ascii_NoCase("asc"))
+    {
+      method.Empty();
+      const int dotPos2 = s.Find('.');
+      if (dotPos2 >= 0)
+        s.DeleteFrom(dotPos2);
+    }
+  }
+  if (method.IsEmpty())
+  {
+    // we support file names with "sum" and "sums" postfixes: "sha256sum", "sha256sums"
+    unsigned size;
+    if (s.Len() > 4 && StringsAreEqualNoCase_Ascii(s.RightPtr(4), "sums"))
+      size = 4;
+    else if (s.Len() > 3 && StringsAreEqualNoCase_Ascii(s.RightPtr(3), "sum"))
+      size = 3;
+    else
+      return false;
+    method = s;
+    method.DeleteFrom(s.Len() - size);
   }
-  const char *m = "";
+
   unsigned i;
   for (i = 0; i < Z7_ARRAY_SIZE(k_CsumMethodNames); i++)
   {
-    m = k_CsumMethodNames[i];
-    if (isExtension)
+    const char *m = k_CsumMethodNames[i];
+    if (method.IsEqualTo_Ascii_NoCase(m))
     {
-      if (StringsAreEqual_Ascii(src, m))
-        break;
+      // method = m; // we can get lowcase
+      return true;
+    }
+  }
+
+/*
+  for (i = 0; i < Z7_ARRAY_SIZE(k_CsumMethodNames); i++)
+  {
+    const char *m = k_CsumMethodNames[i];
+    if (method.IsPrefixedBy_Ascii_NoCase(m))
+    {
+      method = m; // we get lowcase
+      return true;
     }
-    else if (IsString1PrefixedByString2_NoCase_Ascii(src, m))
-      if (StringsAreEqual_Ascii(src + strlen(m), "sums"))
-        break;
   }
-  UString res;
-  if (i != Z7_ARRAY_SIZE(k_CsumMethodNames))
-    res = m;
-  return res;
+*/
+  return false;
 }
 
 
@@ -1047,7 +1093,7 @@
   if (propID == kpidChecksum)
   {
     const CHashPair &hp = HashPairs[index];
-    if (hp.Hash.Size() > 0)
+    if (hp.Hash.Size() != 0)
     {
       *data = hp.Hash;
       *dataSize = (UInt32)hp.Hash.Size();
@@ -1100,11 +1146,6 @@
         s.Add_UInt32(_hashSize * 8);
         s += "-bit";
       }
-      if (!_nameExtenstion.IsEmpty())
-      {
-        s.Add_Space_if_NotEmpty();
-        s += _nameExtenstion;
-      }
       if (_is_PgpMethod)
       {
         Add_OptSpace_String(s, "PGP");
@@ -1120,6 +1161,18 @@
         Add_OptSpace_String(s, "TAG");
       if (_are_there_Dirs)
         Add_OptSpace_String(s, "DIRS");
+      if (!_method_from_FileName.IsEmpty())
+      {
+        Add_OptSpace_String(s, "filename_method:");
+        s += _method_from_FileName;
+        if (!_is_KnownMethod_in_FileName)
+          s += ":UNKNOWN";
+      }
+      if (!_methods.IsEmpty())
+      {
+        Add_OptSpace_String(s, "cmd_method:");
+        s += _methods[0];
+      }
       prop = s;
       break;
     }
@@ -1228,6 +1281,15 @@
 }
 
 
+static bool isThere_Zero_Byte(const Byte *data, size_t size)
+{
+  for (size_t i = 0; i < size; i++)
+    if (data[i] == 0)
+      return true;
+  return false;
+}
+
+
 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openCallback))
 {
   COM_TRY_BEGIN
@@ -1239,17 +1301,9 @@
 
     CObjectVector<CHashPair> &pairs = HashPairs;
 
-    bool zeroMode = false;
-    bool cr_lf_Mode = false;
-    {
-      for (size_t i = 0; i < buf.Size(); i++)
-        if (buf.ConstData()[i] == 0)
-        {
-          zeroMode = true;
-          break;
-        }
-    }
+    const bool zeroMode = isThere_Zero_Byte(buf, buf.Size());
     _is_ZeroMode = zeroMode;
+    bool cr_lf_Mode = false;
     if (!zeroMode)
       cr_lf_Mode = Is_CR_LF_Data(buf, buf.Size());
 
@@ -1263,13 +1317,21 @@
         NCOM::CPropVariant prop;
         RINOK(openVolumeCallback->GetProperty(kpidName, &prop))
         if (prop.vt == VT_BSTR)
-          _nameExtenstion = GetMethod_from_FileName(prop.bstrVal);
+          _is_KnownMethod_in_FileName = GetMethod_from_FileName(prop.bstrVal, _method_from_FileName);
       }
     }
 
-    bool cksumMode = false;
-    if (_nameExtenstion.IsEqualTo_Ascii_NoCase("cksum"))
-      cksumMode = true;
+    if (!_methods.IsEmpty())
+    {
+      ConvertUnicodeToUTF8(_methods[0], _method_for_Extraction);
+    }
+    if (_method_for_Extraction.IsEmpty())
+    {
+      // if (_is_KnownMethod_in_FileName)
+      _method_for_Extraction = _method_from_FileName;
+    }
+
+    const bool cksumMode = _method_for_Extraction.IsEqualTo_Ascii_NoCase("cksum");
     _is_CksumMode = cksumMode;
 
     size_t pos = 0;
@@ -1366,6 +1428,7 @@
   _is_ZeroMode = false;
   _are_there_Tags = false;
   _are_there_Dirs = false;
+  _is_KnownMethod_in_FileName = false;
   _hashSize_Defined = false;
   _hashSize = 0;
 }
@@ -1374,7 +1437,8 @@
 Z7_COM7F_IMF(CHandler::Close())
 {
   ClearVars();
-  _nameExtenstion.Empty();
+  _method_from_FileName.Empty();
+  _method_for_Extraction.Empty();
   _pgpMethod.Empty();
   HashPairs.Clear();
   return S_OK;
@@ -1401,19 +1465,73 @@
 }
 
 
-static void AddDefaultMethod(UStringVector &methods, unsigned size)
+static void AddDefaultMethod(UStringVector &methods,
+    const char *name, unsigned size)
 {
+  int shaVersion = -1;
+  if (name)
+  {
+    if (StringsAreEqualNoCase_Ascii(name, "sha"))
+    {
+      shaVersion = 0;
+      if (size == 0)
+        size = 32;
+    }
+    else if (StringsAreEqualNoCase_Ascii(name, "sha1"))
+    {
+      shaVersion = 1;
+      if (size == 0)
+        size = 20;
+    }
+    else if (StringsAreEqualNoCase_Ascii(name, "sha2"))
+    {
+      shaVersion = 2;
+      if (size == 0)
+        size = 32;
+    }
+    else if (StringsAreEqualNoCase_Ascii(name, "sha3"))
+    {
+      if (size == 0 ||
+               size == 32) name = "sha3-256";
+      else if (size == 28) name = "sha3-224";
+      else if (size == 48) name = "sha3-384";
+      else if (size == 64) name = "sha3-512";
+    }
+    else if (StringsAreEqualNoCase_Ascii(name, "sha512"))
+    {
+      // we allow any sha512 derived hash inside .sha512 file:
+           if (size == 48) name = "sha384";
+      else if (size == 32) name = "sha512-256";
+      else if (size == 28) name = "sha512-224";
+    }
+    if (shaVersion >= 0)
+      name = NULL;
+  }
+  
   const char *m = NULL;
-       if (size == 32) m = "sha256";
-  else if (size == 20) m = "sha1";
-  else if (size == 16) m = "md5";
-  else if (size ==  8) m = "crc64";
-  else if (size ==  4) m = "crc32";
+  if (name)
+    m = name;
   else
+  {
+         if (size == 64) m = "sha512";
+    else if (size == 48) m = "sha384";
+    else if (size == 32) m = "sha256";
+    else if (size == 28) m = "sha224";
+    else if (size == 20) m = "sha1";
+    else if (shaVersion < 0)
+    {
+           if (size == 16) m = "md5";
+      else if (size ==  8) m = "crc64";
+      else if (size ==  4) m = "crc32";
+    }
+  }
+
+  if (!m)
     return;
-  #ifdef Z7_EXTERNAL_CODECS
+
+#ifdef Z7_EXTERNAL_CODECS
   const CExternalCodecs *_externalCodecs = g_ExternalCodecs_Ptr;
-  #endif
+#endif
   CMethodId id;
   if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS
       AString(m), id))
@@ -1444,15 +1562,15 @@
   CHashBundle hb_Glob;
   // UStringVector methods = options.Methods;
   UStringVector methods;
-  
-  if (methods.IsEmpty() && !_nameExtenstion.IsEmpty())
+
+/*
+  if (methods.IsEmpty() && !utf_nameExtenstion.IsEmpty() && !_hashSize_Defined)
   {
-    AString utf;
-    ConvertUnicodeToUTF8(_nameExtenstion, utf);
     CMethodId id;
-    if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS utf, id))
+    if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS utf_nameExtenstion, id))
       methods.Add(_nameExtenstion);
   }
+*/
   
   if (methods.IsEmpty() && !_pgpMethod.IsEmpty())
   {
@@ -1461,12 +1579,21 @@
       methods.Add(UString(_pgpMethod));
   }
 
+/*
   if (methods.IsEmpty() && _pgpMethod.IsEmpty() && _hashSize_Defined)
-    AddDefaultMethod(methods, _hashSize);
+  {
+    AddDefaultMethod(methods,
+        utf_nameExtenstion.IsEmpty() ? NULL : utf_nameExtenstion.Ptr(),
+        _hashSize);
+  }
+*/
 
-  RINOK(hb_Glob.SetMethods(
+  if (!methods.IsEmpty())
+  {
+    RINOK(hb_Glob.SetMethods(
       EXTERNAL_CODECS_LOC_VARS
       methods))
+  }
 
   Z7_DECL_CMyComPtr_QI_FROM(
       IArchiveUpdateCallbackFile,
@@ -1561,9 +1688,11 @@
     {
       hb_Use = &hb_Loc;
       CMethodId id;
-      if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS hp.Method, id))
+      AString methodName = hp.Method;
+      Convert_TagName_to_MethodName(methodName);
+      if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS methodName, id))
       {
-        methods_loc.Add(UString(hp.Method));
+        methods_loc.Add(UString(methodName));
         RINOK(hb_Loc.SetMethods(
             EXTERNAL_CODECS_LOC_VARS
             methods_loc))
@@ -1573,7 +1702,10 @@
     }
     else if (methods.IsEmpty())
     {
-      AddDefaultMethod(methods_loc, (unsigned)hp.Hash.Size());
+      AddDefaultMethod(methods_loc,
+          _method_for_Extraction.IsEmpty() ? NULL :
+          _method_for_Extraction.Ptr(),
+          (unsigned)hp.Hash.Size());
       if (!methods_loc.IsEmpty())
       {
         hb_Use = &hb_Loc;
@@ -1621,7 +1753,7 @@
     Int32 opRes = NArchive::NExtract::NOperationResult::kUnsupportedMethod;
     if (isSupportedMode
         && res_SetMethods != E_NOTIMPL
-        && hb_Use->Hashers.Size() > 0
+        && !hb_Use->Hashers.IsEmpty()
         )
     {
       const CHasherState &hs = hb_Use->Hashers[0];
@@ -1774,10 +1906,6 @@
       methods.Add(_methods[k]);
     }
   }
-  else if (_crcSize_WasSet)
-  {
-    AddDefaultMethod(methods, _crcSize);
-  }
   else
   {
     Z7_DECL_CMyComPtr_QI_FROM(
@@ -1789,12 +1917,23 @@
       RINOK(getRootProps->GetRootProp(kpidArcFileName, &prop))
       if (prop.vt == VT_BSTR)
       {
-        const UString method = GetMethod_from_FileName(prop.bstrVal);
+        AString method;
+        /* const bool isKnownMethod = */ GetMethod_from_FileName(prop.bstrVal, method);
         if (!method.IsEmpty())
-          methods.Add(method);
+        {
+          AddDefaultMethod(methods, method, _crcSize_WasSet ? _crcSize : 0);
+          if (methods.IsEmpty())
+            return E_NOTIMPL;
+        }
       }
     }
   }
+  if (methods.IsEmpty() && _crcSize_WasSet)
+  {
+    AddDefaultMethod(methods,
+        NULL, // name
+        _crcSize);
+  }
 
   RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS methods))
 
@@ -2038,6 +2177,15 @@
 }
 
 
+void CHandler::InitProps()
+{
+  _supportWindowsBackslash = true;
+  _crcSize_WasSet = false;
+  _crcSize = 4;
+  _methods.Clear();
+  _options.Init_HashOptionsLocal();
+}
+
 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
 {
   COM_TRY_BEGIN
@@ -2088,22 +2236,27 @@
         " sha512"
         " sha384"
         " sha224"
-        // " sha512-224"
-        // " sha512-256"
-        // " sha3-224"
+        " sha512-224"
+        " sha512-256"
+        " sha3-224"
         " sha3-256"
-        // " sha3-384"
-        // " sha3-512"
+        " sha3-384"
+        " sha3-512"
         // " shake128"
         // " shake256"
         " sha1"
+        " sha2"
+        " sha3"
         " sha"
         " md5"
+        " blake2s"
+        " blake2b"
         " blake2sp"
         " xxh64"
-        " crc32 crc64"
-        " asc"
+        " crc32"
+        " crc64"
         " cksum"
+        " asc"
         // " b2sum"
         ),
         UString());
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.h 7zip-25.00+dfsg/CPP/7zip/UI/Common/HashCalc.h
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/HashCalc.h	2024-02-09 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/HashCalc.h	2024-12-09 10:00:00.000000000 +0100
@@ -279,32 +279,25 @@
   bool _isArc;
   bool _supportWindowsBackslash;
   bool _crcSize_WasSet;
-  UInt64 _phySize;
-  CObjectVector<CHashPair> HashPairs;
-  UString _nameExtenstion;
-  // UString _method_fromName;
-  AString _pgpMethod;
   bool _is_CksumMode;
   bool _is_PgpMethod;
   bool _is_ZeroMode;
   bool _are_there_Tags;
   bool _are_there_Dirs;
+  bool _is_KnownMethod_in_FileName;
   bool _hashSize_Defined;
   unsigned _hashSize;
   UInt32 _crcSize;
+  UInt64 _phySize;
+  CObjectVector<CHashPair> HashPairs;
   UStringVector _methods;
+  AString _method_from_FileName;
+  AString _pgpMethod;
+  AString _method_for_Extraction;
   CHashOptionsLocal _options;
 
   void ClearVars();
-
-  void InitProps()
-  {
-    _supportWindowsBackslash = true;
-    _crcSize_WasSet = false;
-    _crcSize = 4;
-    _methods.Clear();
-    _options.Init_HashOptionsLocal();
-  }
+  void InitProps();
 
   bool CanUpdate() const
   {
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp	2023-12-03 19:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/LoadCodecs.cpp	2025-06-16 08:00:00.000000000 +0200
@@ -170,7 +170,7 @@
     if (i < addExts.Size())
     {
       extInfo.AddExt = addExts[i];
-      if (extInfo.AddExt == L"*")
+      if (extInfo.AddExt.IsEqualTo("*"))
         extInfo.AddExt.Empty();
     }
     Exts.Add(extInfo);
@@ -931,8 +931,8 @@
     const UString name = arcType.Mid(pos, (unsigned)pos2 - pos);
     if (name.IsEmpty())
       return false;
-    int index = FindFormatForArchiveType(name);
-    if (index < 0 && name != L"*")
+    const int index = FindFormatForArchiveType(name);
+    if (index < 0 && !name.IsEqualTo("*"))
     {
       formatIndices.Clear();
       return false;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp	2024-03-03 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/UpdateCallback.cpp	2025-06-18 14:00:00.000000000 +0200
@@ -32,6 +32,7 @@
 #include "../../../Windows/PropVariant.h"
 
 #include "../../Common/StreamObjects.h"
+#include "../../Archive/Common/ItemNameUtils.h"
 
 #include "UpdateCallback.h"
 
@@ -306,7 +307,7 @@
 
 #if defined(_WIN32) && !defined(UNDER_CE)
 
-static UString GetRelativePath(const UString &to, const UString &from)
+static UString GetRelativePath(const UString &to, const UString &from, bool isWSL)
 {
   UStringVector partsTo, partsFrom;
   SplitPathToParts(to, partsTo);
@@ -324,11 +325,12 @@
 
   if (i == 0)
   {
-    #ifdef _WIN32
-    if (NName::IsDrivePath(to) ||
-        NName::IsDrivePath(from))
+#ifdef _WIN32
+    if (isWSL ||
+       (NName::IsDrivePath(to) ||
+        NName::IsDrivePath(from)))
       return to;
-    #endif
+#endif
   }
 
   UString s;
@@ -373,54 +375,87 @@
         return S_OK;
       }
       
-      #if !defined(UNDER_CE)
-
+#if !defined(UNDER_CE)
       if (up.DirIndex >= 0)
       {
         const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
-        
-        #ifdef _WIN32
-        // if (di.IsDir())
+        if (di.ReparseData.Size())
         {
+#ifdef _WIN32
           CReparseAttr attr;
           if (attr.Parse(di.ReparseData, di.ReparseData.Size()))
           {
-            const UString simpleName = attr.GetPath();
-            if (!attr.IsSymLink_WSL() && attr.IsRelative_Win())
-              prop = simpleName;
-            else
+            UString path = attr.GetPath();
+            if (!path.IsEmpty())
             {
-              const FString phyPath = DirItems->GetPhyPath((unsigned)up.DirIndex);
-              FString fullPath;
-              if (NDir::MyGetFullPathName(phyPath, fullPath))
+              bool isWSL = attr.IsSymLink_WSL();
+              if (isWSL)
+                NArchive::NItemName::ReplaceToWinSlashes(path, true); // useBackslashReplacement
+              // it's expected that (path) now uses windows slashes.
+              // CReparseAttr::IsRelative_Win() returns true if FLAG_RELATIVE is set
+              // CReparseAttr::IsRelative_Win() returns true for "\dir1\path"
+              // but we want to store real relative paths without "\" root prefix.
+              // so we parse path instead of IsRelative_Win() calling.
+              if (// attr.IsRelative_Win() ||
+                  (isWSL ?
+                   IS_PATH_SEPAR(path[0]) :
+                   NName::IsAbsolutePath(path)))
               {
-                prop = GetRelativePath(simpleName, fs2us(fullPath));
+                // (path) is abolute path or relative to root: "\path"
+                // we try to convert (path) to relative path for writing to archive.
+                const FString phyPath = DirItems->GetPhyPath((unsigned)up.DirIndex);
+                FString fullPath;
+                if (NDir::MyGetFullPathName(phyPath, fullPath))
+                {
+                  if (IS_PATH_SEPAR(path[0]) &&
+                      !IS_PATH_SEPAR(path[1]))
+                  {
+                    // path is relative to root of (fullPath): "\path"
+                    const unsigned prefixSize = NName::GetRootPrefixSize(fullPath);
+                    if (prefixSize)
+                    {
+                      path.DeleteFrontal(1);
+                      path.Insert(0, fs2us(fullPath.Left(prefixSize)));
+                      // we have changed "\" prefix to drive prefix "c:\" in (path).
+                      // (path) is Windows path now.
+                      isWSL = false;
+                    }
+                  }
+                }
+                path = GetRelativePath(path, fs2us(fullPath), isWSL);
               }
+#if WCHAR_PATH_SEPARATOR != L'/'
+              // 7-Zip's TAR handler in Windows replaces windows slashes to linux slashes.
+              // so we can return any slashes to TAR handler.
+              // or we can convert to linux slashes here,
+              // because input IInArchive handler uses linux slashes for kpidSymLink.
+              // path.Replace(WCHAR_PATH_SEPARATOR, L'/');
+#endif
+              if (!path.IsEmpty())
+                prop = path;
             }
-            prop.Detach(value);
-            return S_OK;
           }
-        }
-        
-        #else // _WIN32
-
-        if (di.ReparseData.Size() != 0)
-        {
+#else // ! _WIN32
           AString utf;
           utf.SetFrom_CalcLen((const char *)(const Byte *)di.ReparseData, (unsigned)di.ReparseData.Size());
-
+    #if 0 // 0 - for debug
+          // it's expected that link data uses system codepage.
+          // fs2us() ignores conversion errors. But we want correct path
+          UString us (fs2us(utf));
+    #else
           UString us;
           if (ConvertUTF8ToUnicode(utf, us))
+    #endif
           {
-            prop = us;
-            prop.Detach(value);
-            return S_OK;
+            if (!us.IsEmpty())
+              prop = us;
           }
+#endif // ! _WIN32
         }
-
-        #endif // _WIN32
+        prop.Detach(value);
+        return S_OK;
       }
-      #endif // !defined(UNDER_CE)
+#endif // !defined(UNDER_CE)
     }
     else if (propID == kpidHardLink)
     {
@@ -428,7 +463,12 @@
       {
         const CKeyKeyValPair &pair = _map[_hardIndex_To];
         const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
-        prop = DirItems->GetLogPath((unsigned)up2.DirIndex);
+        const UString path = DirItems->GetLogPath((unsigned)up2.DirIndex);
+#if WCHAR_PATH_SEPARATOR != L'/'
+        // 7-Zip's TAR handler in Windows replaces windows slashes to linux slashes.
+        // path.Replace(WCHAR_PATH_SEPARATOR, L'/');
+#endif
+        prop = path;
         prop.Detach(value);
         return S_OK;
       }
@@ -438,7 +478,7 @@
         return S_OK;
       }
     }
-  }
+  } // if (up.NewData)
   
   if (up.IsAnti
       && propID != kpidIsDir
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Common/Update.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.cpp	2024-10-22 11:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/Update.cpp	2024-12-30 14:00:00.000000000 +0100
@@ -474,7 +474,7 @@
 
   CArcToDoStat stat2;
 
-  if (options.RenamePairs.Size() != 0)
+  if (options.RenameMode || options.RenamePairs.Size() != 0)
   {
     FOR_VECTOR (i, arcItems)
     {
@@ -1920,7 +1920,7 @@
       if (NFind::DoesDirExist(phyPath))
       {
         RINOK(callback->DeletingAfterArchiving(phyPath, true))
-        RemoveDir(phyPath);
+        RemoveDirAlways_if_Empty(phyPath);
       }
     }
 
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.h 7zip-25.00+dfsg/CPP/7zip/UI/Common/Update.h
--- 7zip-24.09+dfsg/CPP/7zip/UI/Common/Update.h	2024-10-22 11:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Common/Update.h	2024-12-30 14:00:00.000000000 +0100
@@ -94,6 +94,7 @@
 
   bool DeleteAfterCompressing;
   bool SetArcMTime;
+  bool RenameMode;
 
   CBoolPair NtSecurity;
   CBoolPair AltStreams;
@@ -139,6 +140,7 @@
     
     DeleteAfterCompressing(false),
     SetArcMTime(false),
+    RenameMode(false),
 
     ArcNameMode(k_ArcNameMode_Smart),
     PathMode(NWildcard::k_RelatPath)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Console/Main.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Console/Main.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Console/Main.cpp	2024-06-17 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Console/Main.cpp	2025-01-24 08:00:00.000000000 +0100
@@ -908,9 +908,12 @@
 
   if (options.EnableHeaders)
   {
-    ShowCopyrightAndHelp(g_StdStream, false);
-    if (!parser.Parse1Log.IsEmpty())
-      *g_StdStream << parser.Parse1Log;
+    if (g_StdStream)
+    {
+      ShowCopyrightAndHelp(g_StdStream, false);
+      if (!parser.Parse1Log.IsEmpty())
+        *g_StdStream << parser.Parse1Log;
+    }
   }
 
   parser.Parse2(options);
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Console/makefile 7zip-25.00+dfsg/CPP/7zip/UI/Console/makefile
--- 7zip-24.09+dfsg/CPP/7zip/UI/Console/makefile	2023-01-29 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Console/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -59,10 +59,10 @@
 C_OBJS = $(C_OBJS) \
   $O\Alloc.obj \
   $O\CpuArch.obj \
-  $O\Sort.obj \
   $O\Threads.obj \
 
 !include "../../Crc.mak"
+!include "../../Sort.mak"
 !include "Console.mak"
 
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Explorer/makefile 7zip-25.00+dfsg/CPP/7zip/UI/Explorer/makefile
--- 7zip-24.09+dfsg/CPP/7zip/UI/Explorer/makefile	2024-03-20 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Explorer/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -72,7 +72,7 @@
 
 C_OBJS = \
   $O\CpuArch.obj \
-  $O\Sort.obj \
   $O\Threads.obj \
 
+!include "../../Sort.mak"
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Far/makefile 7zip-25.00+dfsg/CPP/7zip/UI/Far/makefile
--- 7zip-24.09+dfsg/CPP/7zip/UI/Far/makefile	2024-01-27 11:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Far/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -99,9 +99,9 @@
 C_OBJS = \
   $O\Alloc.obj \
   $O\CpuArch.obj \
-  $O\Sort.obj \
   $O\Threads.obj \
 
 !include "../../Crc.mak"
+!include "../../Sort.mak"
 
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/Far/Plugin.cpp 7zip-25.00+dfsg/CPP/7zip/UI/Far/Plugin.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/Far/Plugin.cpp	2024-01-23 21:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/Far/Plugin.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -61,7 +61,6 @@
 }
 
 #define kDotsReplaceString "[[..]]"
-#define kDotsReplaceStringU L"[[..]]"
   
 static void CopyStrLimited(char *dest, const AString &src, unsigned len)
 {
@@ -84,7 +83,7 @@
     throw 272340;
 
   AString oemString (UnicodeStringToMultiByte(prop.bstrVal, CP_OEMCP));
-  if (oemString == "..")
+  if (oemString.IsEqualTo(".."))
     oemString = kDotsReplaceString;
 
   COPY_STR_LIMITED(panelItem.FindData.cFileName, oemString);
@@ -193,7 +192,7 @@
 {
   CMyComPtr<IFolderFolder> newFolder;
   UString s = dirName;
-  if (dirName == kDotsReplaceStringU)
+  if (dirName.IsEqualTo(kDotsReplaceString))
     s = "..";
   _folder->BindToFolder(s, &newFolder);
   if (!newFolder)
@@ -209,12 +208,12 @@
 int CPlugin::SetDirectory(const char *aszDir, int /* opMode */)
 {
   UString path = MultiByteToUnicodeString(aszDir, CP_OEMCP);
-  if (path == WSTRING_PATH_SEPARATOR)
+  if (path.IsEqualTo(STRING_PATH_SEPARATOR))
   {
     _folder.Release();
     m_ArchiveHandler->BindToRootFolder(&_folder);
   }
-  else if (path == L"..")
+  else if (path.IsEqualTo(".."))
   {
     CMyComPtr<IFolderFolder> newFolder;
     _folder->BindToParentFolder(&newFolder);
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/FM.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/FM.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/FM.cpp	2024-10-21 09:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/FM.cpp	2025-06-15 10:00:00.000000000 +0200
@@ -651,7 +651,7 @@
   SplitStringToTwoStrings(commandsString, paramString, tailString);
   paramString.Trim();
   tailString.Trim();
-  if (tailString.IsPrefixedBy(L"-t"))
+  if (tailString.IsPrefixedBy("-t"))
     g_ArcFormat = tailString.Ptr(2);
 
   /*
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp	2024-03-15 08:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/LangUtils.cpp	2025-01-07 21:00:00.000000000 +0100
@@ -309,15 +309,13 @@
 {
   g_Lang.Clear();
   ReadRegLang(g_LangID);
-  #ifndef _UNICODE
-  if (g_IsNT)
-  #endif
+  if (g_LangID.IsEmpty())
   {
-    if (g_LangID.IsEmpty())
-    {
+#ifndef _UNICODE
+    if (g_IsNT)
+#endif
       OpenDefaultLang();
-      return;
-    }
+    return;
   }
   if (g_LangID.Len() > 1 || g_LangID[0] != L'-')
   {
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp	2023-03-19 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/LinkDialog.cpp	2025-06-20 12:00:00.000000000 +0200
@@ -45,28 +45,24 @@
   CByteBuffer buf;
   if (!NIO::GetReparseData(path, buf, NULL))
     return false;
-  
   if (!attr.Parse(buf, buf.Size()))
   {
     SetLastError(attr.ErrorCode);
     return false;
   }
-
   CByteBuffer data2;
-  if (!FillLinkData(data2, attr.GetPath(),
-      !attr.IsMountPoint(), attr.IsSymLink_WSL()))
+  FillLinkData(data2, attr.GetPath(),
+      !attr.IsMountPoint(), attr.IsSymLink_WSL());
+  if (data2.Size() == 0)
   {
     errorMessage = "Cannot reproduce reparse point";
     return false;
   }
-    
-  if (data2.Size() != buf.Size() ||
-      memcmp(data2, buf, buf.Size()) != 0)
+  if (data2 != buf)
   {
     errorMessage = "mismatch for reproduced reparse point";
     return false;
   }
-
   return true;
 }
 
@@ -113,8 +109,8 @@
         const bool res = GetSymLink(us2fs(FilePath), attr, error);
         if (!res && error.IsEmpty())
         {
-          DWORD lastError = GetLastError();
-          if (lastError != 0)
+          const DWORD lastError = GetLastError();
+          if (lastError)
             error = NError::MyFormatMessage(lastError);
         }
         
@@ -319,10 +315,10 @@
       return;
     }
 
-    const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION);
-    
     CByteBuffer data;
-    if (!FillLinkData(data, to, isSymLink, isWSL))
+    const bool isSymLink = (idb != IDR_LINK_TYPE_JUNCTION);
+    FillLinkData(data, to, isSymLink, isWSL);
+    if (data.Size() == 0)
     {
       ShowError(L"Incorrect link");
       return;
@@ -386,6 +382,9 @@
         path = destPanel.GetFsPath();
   }
 
+  CSelectedState srcSelState;
+  srcPanel.SaveSelectedState(srcSelState);
+
   CLinkDialog dlg;
   dlg.CurDirPrefix = fsPrefix;
   dlg.FilePath = srcPath + itemName;
@@ -394,7 +393,10 @@
   if (dlg.Create(srcPanel.GetParent()) != IDOK)
     return;
 
-  // fix it: we should refresh panel with changed link
+  // we refresh srcPanel to show changes in "Link" (kpidNtReparse) column.
+  // maybe we should refresh another panel also?
+  if (srcPanel._visibleColumns.FindItem_for_PropID(kpidNtReparse) >= 0)
+    srcPanel.RefreshListCtrl(srcSelState);
 
   RefreshTitleAlways();
 }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/makefile 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/makefile
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/makefile	2024-01-27 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -104,7 +104,7 @@
 C_OBJS = $(C_OBJS) \
   $O\Alloc.obj \
   $O\CpuArch.obj \
-  $O\Sort.obj \
   $O\Threads.obj \
 
+!include "../../Sort.mak"
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp	2024-10-22 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/PanelCopy.cpp	2025-06-16 08:00:00.000000000 +0200
@@ -284,7 +284,7 @@
       if (options.hashMethods.Size() == 1)
       {
         const UString &s = options.hashMethods[0];
-        if (s != L"*")
+        if (!s.IsEqualTo("*"))
           title = s;
       }
     }
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp	2024-07-09 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/PanelFolderChange.cpp	2025-06-16 10:00:00.000000000 +0200
@@ -428,7 +428,7 @@
       UString name_Computer = RootFolder_GetName_Computer(iconIndex);
       name_Computer.Add_PathSepar();
       if (path == name_Computer
-          || path == L"\\\\?\\")
+          || path.IsEqualTo("\\\\?\\"))
         item.iImage = iconIndex;
       else
       {
@@ -639,7 +639,7 @@
       unsigned indent = 0;
       {
         UString path = _currentFolderPrefix;
-        // path = L"\\\\.\\y:\\"; // for debug
+        // path = "\\\\.\\y:\\"; // for debug
         UString prefix0;
         if (path.IsPrefixedBy_Ascii_NoCase("\\\\"))
         {
@@ -702,7 +702,7 @@
       int iconIndex_Computer;
       const UString name_Computer = RootFolder_GetName_Computer(iconIndex_Computer);
 
-      // const bool is_devicePrefix = (sumPath == L"\\\\.\\");
+      // const bool is_devicePrefix = (sumPath.IsEqualTo("\\\\.\\"));
 
       if (pathParts.Size() > 1)
       if (!sumPath.IsEmpty()
@@ -901,8 +901,8 @@
     {
       s = _currentFolderPrefix;
       s.DeleteBack();
-      if (s != L"\\\\." &&
-          s != L"\\\\?")
+      if (!s.IsEqualTo("\\\\.") &&
+          !s.IsEqualTo("\\\\?"))
       {
         int pos = s.ReverseFind_PathSepar();
         if (pos >= 0)
@@ -935,8 +935,8 @@
       }
       else
       */
-      if (focusedName != L"\\\\." &&
-          focusedName != L"\\\\?")
+      if (!focusedName.IsEqualTo("\\\\.") &&
+          !focusedName.IsEqualTo("\\\\?"))
       {
         const int pos = focusedName.ReverseFind_PathSepar();
         if (pos >= 0)
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/Panel.h 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/Panel.h
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/Panel.h	2024-10-13 12:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/Panel.h	2025-06-16 08:00:00.000000000 +0200
@@ -711,8 +711,8 @@
   }
 
   // bool IsFsOrDrivesFolder() const { return IsFSFolder() || IsFSDrivesFolder(); }
-  bool IsDeviceDrivesPrefix() const { return _currentFolderPrefix == L"\\\\.\\"; }
-  bool IsSuperDrivesPrefix() const { return _currentFolderPrefix == L"\\\\?\\"; }
+  bool IsDeviceDrivesPrefix() const { return _currentFolderPrefix.IsEqualTo("\\\\.\\"); }
+  bool IsSuperDrivesPrefix() const { return _currentFolderPrefix.IsEqualTo("\\\\?\\"); }
   
   /*
     c:\Dir
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp	2024-10-04 19:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/PanelOperations.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -275,8 +275,8 @@
 {
   const UString lastPart = name.Ptr((unsigned)(name.ReverseFind_PathSepar() + 1));
   return
-      lastPart != L"." &&
-      lastPart != L"..";
+      !lastPart.IsEqualTo(".") &&
+      !lastPart.IsEqualTo("..");
 }
 
 bool CorrectFsPath(const UString &relBase, const UString &path, UString &result);
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp	2024-07-09 13:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/FileManager/RootFolder.cpp	2025-06-16 08:00:00.000000000 +0200
@@ -249,7 +249,7 @@
       AreEqualNames(name2, L"Documents"))
     return BindToFolder((UInt32)ROOT_INDEX_DOCUMENTS, resultFolder);
   #else
-  if (name2 == WSTRING_PATH_SEPARATOR)
+  if (name2.IsEqualTo(STRING_PATH_SEPARATOR))
     return BindToFolder((UInt32)ROOT_INDEX_COMPUTER, resultFolder);
   #endif
   
@@ -257,7 +257,7 @@
       AreEqualNames(name2, L"Computer"))
     return BindToFolder((UInt32)ROOT_INDEX_COMPUTER, resultFolder);
   
-  if (name2 == WSTRING_PATH_SEPARATOR)
+  if (name2.IsEqualTo(STRING_PATH_SEPARATOR))
   {
     CMyComPtr<IFolderFolder> subFolder = this;
     *resultFolder = subFolder.Detach();
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp 7zip-25.00+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp	2024-11-08 18:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.cpp	2025-06-16 09:00:00.000000000 +0200
@@ -1856,7 +1856,7 @@
     const CProperty &prop = props[i];
     UString name = prop.Name;
     name.MakeLower_Ascii();
-    if (name.IsEqualTo_Ascii_NoCase("m") && prop.Value == L"*")
+    if (name.IsEqualTo_Ascii_NoCase("m") && prop.Value.IsEqualTo("*"))
     {
       bd.TotalMode = true;
       continue;
@@ -1865,7 +1865,7 @@
     NCOM::CPropVariant propVariant;
     if (!prop.Value.IsEmpty())
       ParseNumberString(prop.Value, propVariant);
-    if (name.IsPrefixedBy(L"mt"))
+    if (name.IsPrefixedBy("mt"))
     {
       #ifndef Z7_ST
       RINOK(ParseMtProp(name.Ptr(2), propVariant, numCPUs, numThreads))
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc 7zip-25.00+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc
--- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc	2021-05-26 13:06:11.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/7zip/UI/GUI/BenchmarkDialog.rc	2025-07-03 14:00:00.000000000 +0200
@@ -81,7 +81,7 @@
 
   LTEXT  "&Number of CPU threads:", IDT_BENCH_NUM_THREADS, m, 30, g0xs, 8
   COMBOBOX  IDC_BENCH_NUM_THREADS, g1x, 29, g1xs, 140, MY_COMBO
-  LTEXT  "", IDT_BENCH_HARDWARE_THREADS, gc2x, 30, g7xs, MY_TEXT_NOPREFIX
+  LTEXT  "", IDT_BENCH_HARDWARE_THREADS, gc2x, 30, g7xs, 24, SS_NOPREFIX
 
   RTEXT  "Size", IDT_BENCH_SIZE,                xSize,   54, sSize,   MY_TEXT_NOPREFIX
   RTEXT  "CPU Usage", IDT_BENCH_USAGE_LABEL,    xUsage,  54, sUsage,  MY_TEXT_NOPREFIX
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp 7zip-25.00+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp
--- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp	2024-11-29 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/GUI/CompressDialog.cpp	2025-07-04 11:00:00.000000000 +0200
@@ -2600,11 +2600,17 @@
   UInt32 numAlgoThreadsMax = numHardwareThreads * 2;
   const int methodID = GetMethodID();
 
-  switch (methodID)
+  const bool isZip = IsZipFormat();
+  if (isZip)
+    numAlgoThreadsMax =
+        8 << (sizeof(size_t) / 2); // 32 threads for 32-bit : 128 threads for 64-bit
+  else if (IsXzFormat())
+    numAlgoThreadsMax = 256 * 2;
+  else switch (methodID)
   {
     case kLZMA: numAlgoThreadsMax = 2; break;
     case kLZMA2: numAlgoThreadsMax = 256; break;
-    case kBZip2: numAlgoThreadsMax = 32; break;
+    case kBZip2: numAlgoThreadsMax = 64; break;
     // case kZSTD: numAlgoThreadsMax = num_ZSTD_threads_MAX; break;
     case kCopy:
     case kPPMd:
@@ -2613,17 +2619,6 @@
     case kPPMdZip:
       numAlgoThreadsMax = 1;
   }
-  const bool isZip = IsZipFormat();
-  if (isZip)
-  {
-    numAlgoThreadsMax =
-      #ifdef _WIN32
-        64; // _WIN32 supports only 64 threads in one group. So no need for more threads here
-      #else
-        128;
-      #endif
-  }
-
   UInt32 autoThreads = numHardwareThreads;
   if (autoThreads > numAlgoThreadsMax)
     autoThreads = numAlgoThreadsMax;
@@ -3008,7 +3003,7 @@
       else
       {
         size += numBlockThreads * (size1 + chunkSize);
-        UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1;
+        const UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1;
         if (chunkSize < ((UInt32)1 << 26)) numBlockThreads++;
         if (chunkSize < ((UInt32)1 << 24)) numBlockThreads++;
         if (chunkSize < ((UInt32)1 << 22)) numBlockThreads++;
diff -Nru 7zip-24.09+dfsg/CPP/7zip/UI/GUI/makefile 7zip-25.00+dfsg/CPP/7zip/UI/GUI/makefile
--- 7zip-24.09+dfsg/CPP/7zip/UI/GUI/makefile	2024-03-14 12:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/7zip/UI/GUI/makefile	2025-07-01 17:00:00.000000000 +0200
@@ -141,10 +141,9 @@
   $O\Alloc.obj \
   $O\CpuArch.obj \
   $O\DllSecur.obj \
-  $O\Sort.obj \
   $O\Threads.obj \
 
 !include "../../Crc.mak"
-
+!include "../../Sort.mak"
 
 !include "../../7zip.mak"
diff -Nru 7zip-24.09+dfsg/CPP/Build.mak 7zip-25.00+dfsg/CPP/Build.mak
--- 7zip-24.09+dfsg/CPP/Build.mak	2024-05-02 14:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Build.mak	2025-07-02 07:00:00.000000000 +0200
@@ -111,7 +111,13 @@
 
 !IFNDEF UNDER_CE
 !IF "$(CC)" != "clang-cl"
-CFLAGS = $(CFLAGS) -MP4
+MP_NPROC = 16
+!IFDEF NUMBER_OF_PROCESSORS
+!IF $(NUMBER_OF_PROCESSORS) < $(MP_NPROC)
+MP_NPROC = $(NUMBER_OF_PROCESSORS)
+!ENDIF
+!ENDIF
+CFLAGS = $(CFLAGS) -MP$(MP_NPROC)
 !ENDIF
 !IFNDEF PLATFORM
 # CFLAGS = $(CFLAGS) -arch:IA32
diff -Nru 7zip-24.09+dfsg/CPP/Common/MyString.cpp 7zip-25.00+dfsg/CPP/Common/MyString.cpp
--- 7zip-24.09+dfsg/CPP/Common/MyString.cpp	2024-02-19 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Common/MyString.cpp	2025-06-15 10:00:00.000000000 +0200
@@ -208,35 +208,6 @@
 
 // ---------- ASCII ----------
 
-bool AString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw()
-{
-  const char *s1 = _chars;
-  for (;;)
-  {
-    const char c2 = *s++;
-    if (c2 == 0)
-      return true;
-    const char c1 = *s1++;
-    if (MyCharLower_Ascii(c1) !=
-        MyCharLower_Ascii(c2))
-      return false;
-  }
-}
-
-bool UString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw()
-{
-  const wchar_t *s1 = _chars;
-  for (;;)
-  {
-    const char c2 = *s++;
-    if (c2 == 0)
-      return true;
-    const wchar_t c1 = *s1++;
-    if (MyCharLower_Ascii(c1) != (unsigned char)MyCharLower_Ascii(c2))
-      return false;
-  }
-}
-
 bool StringsAreEqual_Ascii(const char *u, const char *a) throw()
 {
   for (;;)
diff -Nru 7zip-24.09+dfsg/CPP/Common/MyString.h 7zip-25.00+dfsg/CPP/Common/MyString.h
--- 7zip-24.09+dfsg/CPP/Common/MyString.h	2024-01-22 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Common/MyString.h	2025-07-01 12:00:00.000000000 +0200
@@ -429,11 +429,11 @@
   // int CompareNoCase(const char *s) const { return MyStringCompareNoCase(_chars, s); }
   // int CompareNoCase(const AString &s) const { return MyStringCompareNoCase(_chars, s._chars); }
   bool IsPrefixedBy(const char *s) const { return IsString1PrefixedByString2(_chars, s); }
-  bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw();
+  bool IsPrefixedBy_Ascii_NoCase(const char *s) const { return IsString1PrefixedByString2_NoCase_Ascii(_chars, s); }
  
   bool IsAscii() const
   {
-    unsigned len = Len();
+    const unsigned len = Len();
     const char *s = _chars;
     for (unsigned i = 0; i < len; i++)
       if ((unsigned char)s[i] >= 0x80)
@@ -727,22 +727,23 @@
   // int CompareNoCase(const wchar_t *s) const { return MyStringCompareNoCase(_chars, s); }
   // int CompareNoCase(const UString &s) const { return MyStringCompareNoCase(_chars, s._chars); }
   bool IsPrefixedBy(const wchar_t *s) const { return IsString1PrefixedByString2(_chars, s); }
+  bool IsPrefixedBy(const char *s) const { return IsString1PrefixedByString2(_chars, s); }
   bool IsPrefixedBy_NoCase(const wchar_t *s) const { return IsString1PrefixedByString2_NoCase(_chars, s); }
-  bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw();
+  bool IsPrefixedBy_Ascii_NoCase(const char *s) const { return IsString1PrefixedByString2_NoCase_Ascii(_chars, s); }
 
   bool IsAscii() const
   {
-    unsigned len = Len();
+    const unsigned len = Len();
     const wchar_t *s = _chars;
     for (unsigned i = 0; i < len; i++)
-      if (s[i] >= 0x80)
+      if ((unsigned)(int)s[i] >= 0x80)
         return false;
     return true;
   }
   int Find(wchar_t c) const { return FindCharPosInString(_chars, c); }
   int Find(wchar_t c, unsigned startIndex) const
   {
-    int pos = FindCharPosInString(_chars + startIndex, c);
+    const int pos = FindCharPosInString(_chars + startIndex, c);
     return pos < 0 ? -1 : (int)startIndex + pos;
   }
 
diff -Nru 7zip-24.09+dfsg/CPP/Common/MyXml.cpp 7zip-25.00+dfsg/CPP/Common/MyXml.cpp
--- 7zip-24.09+dfsg/CPP/Common/MyXml.cpp	2024-01-21 21:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Common/MyXml.cpp	2025-06-16 11:00:00.000000000 +0200
@@ -24,7 +24,7 @@
 int CXmlItem::FindProp(const char *propName) const throw()
 {
   FOR_VECTOR (i, Props)
-    if (Props[i].Name == propName)
+    if (Props[i].Name.IsEqualTo(propName))
       return (int)i;
   return -1;
 }
@@ -39,7 +39,7 @@
 
 bool CXmlItem::IsTagged(const char *tag) const throw()
 {
-  return (IsTag && Name == tag);
+  return (IsTag && Name.IsEqualTo(tag));
 }
 
 int CXmlItem::FindSubTag(const char *tag) const throw()
diff -Nru 7zip-24.09+dfsg/CPP/Common/Sha3Reg.cpp 7zip-25.00+dfsg/CPP/Common/Sha3Reg.cpp
--- 7zip-24.09+dfsg/CPP/Common/Sha3Reg.cpp	2024-11-26 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Common/Sha3Reg.cpp	2024-12-01 10:00:00.000000000 +0100
@@ -58,7 +58,7 @@
   static IHasher *CreateHasherSpec() \
     { return new CSha3Hasher(digestSize / 8, isShake, \
         SHA3_BLOCK_SIZE_FROM_DIGEST_SIZE(digestSize_for_blockSize / 8)); } \
-  static const CHasherInfo g_HasherInfo = { CreateHasherSpec, id, name, digestSize }; \
+  static const CHasherInfo g_HasherInfo = { CreateHasherSpec, id, name, digestSize / 8 }; \
   struct REGISTER_HASHER_NAME(cls) { REGISTER_HASHER_NAME(cls)() { RegisterHasher(&g_HasherInfo); }}; \
   static REGISTER_HASHER_NAME(cls) g_RegisterHasher; }
 
diff -Nru 7zip-24.09+dfsg/CPP/Common/Wildcard.cpp 7zip-25.00+dfsg/CPP/Common/Wildcard.cpp
--- 7zip-24.09+dfsg/CPP/Common/Wildcard.cpp	2022-12-30 16:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Common/Wildcard.cpp	2025-06-16 12:00:00.000000000 +0200
@@ -255,7 +255,8 @@
 
 bool CItem::AreAllAllowed() const
 {
-  return ForFile && ForDir && WildcardMatching && PathParts.Size() == 1 && PathParts.Front() == L"*";
+  return ForFile && ForDir && WildcardMatching
+      && PathParts.Size() == 1 && PathParts.Front().IsEqualTo("*");
 }
 
 bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const
@@ -542,7 +543,7 @@
   {
     if (pathParts.Size() < 4
         || !pathParts[1].IsEmpty()
-        || pathParts[2] != L"?")
+        || !pathParts[2].IsEqualTo("?"))
       return 0;
     testIndex = 3;
   }
@@ -574,11 +575,11 @@
     return 1;
   if (pathParts.Size() == 2)
     return 2;
-  if (pathParts[2] == L".")
+  if (pathParts[2].IsEqualTo("."))
     return 3;
 
   unsigned networkParts = 2;
-  if (pathParts[2] == L"?")
+  if (pathParts[2].IsEqualTo("?"))
   {
     if (pathParts.Size() == 3)
       return 3;
@@ -642,7 +643,7 @@
   if (pathParts.Size() >= 3
       && pathParts[0].IsEmpty()
       && pathParts[1].IsEmpty()
-      && pathParts[2] == L"?")
+      && pathParts[2].IsEqualTo("?"))
     ignoreWildcardIndex = 2;
   // #endif
 
@@ -665,7 +666,7 @@
       for (unsigned i = numPrefixParts; i < pathParts.Size(); i++)
       {
         const UString &part = pathParts[i];
-        if (part == L".." || part == L".")
+        if (part.IsEqualTo("..") || part.IsEqualTo("."))
           dotsIndex = (int)i;
       }
 
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileDir.cpp 7zip-25.00+dfsg/CPP/Windows/FileDir.cpp
--- 7zip-24.09+dfsg/CPP/Windows/FileDir.cpp	2024-10-24 07:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/FileDir.cpp	2025-06-20 13:00:00.000000000 +0200
@@ -651,6 +651,35 @@
   return RemoveDir(path);
 }
 
+bool RemoveDirAlways_if_Empty(const FString &path)
+{
+  const DWORD attrib = NFind::GetFileAttrib(path);
+  if (attrib != INVALID_FILE_ATTRIBUTES
+      && (attrib & FILE_ATTRIBUTE_READONLY))
+  {
+    bool need_ClearAttrib = true;
+    if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+    {
+      FString s (path);
+      s.Add_PathSepar();
+      NFind::CEnumerator enumerator;
+      enumerator.SetDirPrefix(s);
+      NFind::CDirEntry fi;
+      if (enumerator.Next(fi))
+      {
+        // we don't want to change attributes, if there are files
+        // in directory, because RemoveDir(path) will fail.
+        need_ClearAttrib = false;
+        // SetLastError(ERROR_DIR_NOT_EMPTY);
+        // return false;
+      }
+    }
+    if (need_ClearAttrib)
+      SetFileAttrib(path, 0); // we clear read-only attrib to remove read-only dir
+  }
+  return RemoveDir(path);
+}
+
 #endif // _WIN32
 
 #ifdef UNDER_CE
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileDir.h 7zip-25.00+dfsg/CPP/Windows/FileDir.h
--- 7zip-24.09+dfsg/CPP/Windows/FileDir.h	2024-10-17 09:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/FileDir.h	2024-11-30 21:00:00.000000000 +0100
@@ -78,6 +78,11 @@
 
 bool DeleteFileAlways(CFSTR name);
 bool RemoveDirWithSubItems(const FString &path);
+#ifdef _WIN32
+bool RemoveDirAlways_if_Empty(const FString &path);
+#else
+#define RemoveDirAlways_if_Empty RemoveDir
+#endif
 
 bool MyGetFullPathName(CFSTR path, FString &resFullPath);
 bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName);
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileFind.cpp 7zip-25.00+dfsg/CPP/Windows/FileFind.cpp
--- 7zip-24.09+dfsg/CPP/Windows/FileFind.cpp	2024-02-12 11:47:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Windows/FileFind.cpp	2025-06-20 13:00:00.000000000 +0200
@@ -731,7 +731,7 @@
             bool isOK = false;
             if (finder.FindFirst(s, *this))
             {
-              if (Name == FTEXT("."))
+              if (Name.IsEqualTo("."))
               {
                 Name = path + prefixSize;
                 return true;
@@ -769,6 +769,13 @@
 
   // return FollowReparse(path, IsDir());
   return Fill_From_ByHandleFileInfo(path);
+/*
+  // Fill_From_ByHandleFileInfo returns false (with Access Denied error),
+  // if there is reparse link file (not directory reparse item).
+  if (Fill_From_ByHandleFileInfo(path))
+    return true;
+  return HasReparsePoint();
+*/
 }
 
 bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path)
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileIO.h 7zip-25.00+dfsg/CPP/Windows/FileIO.h
--- 7zip-24.09+dfsg/CPP/Windows/FileIO.h	2024-02-10 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Windows/FileIO.h	2025-06-20 13:15:00.000000000 +0200
@@ -11,8 +11,7 @@
 
 #define Z7_WIN_SYMLINK_FLAG_RELATIVE 1
 
-// what the meaning of that FLAG or field (2)?
-#define Z7_WIN_LX_SYMLINK_FLAG 2
+#define Z7_WIN_LX_SYMLINK_VERSION_2 2
 
 #ifdef _WIN32
 
@@ -44,7 +43,33 @@
 namespace NFile {
 
 #if defined(_WIN32) && !defined(UNDER_CE)
-bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL);
+/*
+  in:  (CByteBuffer &dest) is empty
+  in:  (path) uses Windows path separator (\).
+  out: (path) uses   Linux path separator (/).
+       if (isAbsPath == true), then "c:\\" prefix is replaced to "/mnt/c/" prefix
+*/
+void Convert_WinPath_to_WslLinuxPath(FString &path, bool convertDrivePath);
+// (path) must use Linux path separator (/).
+void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path);
+
+/*
+  in:  (CByteBuffer &dest) is empty
+  if (isSymLink == false) : MOUNT_POINT : (path) must be absolute.
+  if (isSymLink == true)  : SYMLINK : Windows
+  (path) must use Windows path separator (\).
+  (path) must be without link "\\??\\" prefix.
+  link "\\??\\" prefix will be added inside FillLinkData(), if path is absolute.
+*/
+void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink);
+// in: (CByteBuffer &dest) is empty
+inline void FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL)
+{
+  if (isWSL)
+    FillLinkData_WslLink(dest, path);
+  else
+    FillLinkData_WinLink(dest, path, isSymLink);
+}
 #endif
 
 struct CReparseShortInfo
@@ -61,7 +86,6 @@
   UInt32 Flags;
   UString SubsName;
   UString PrintName;
-
   AString WslName;
 
   bool HeaderError;
@@ -71,8 +95,7 @@
 
   CReparseAttr(): Tag(0), Flags(0) {}
 
-  // Parse()
-  // returns (true) and (ErrorCode = 0), if (it'a correct known link)
+  // returns (true) and (ErrorCode = 0), if (it's correct known link)
   // returns (false) and (ErrorCode = ERROR_REPARSE_TAG_INVALID), if unknown tag
   bool Parse(const Byte *p, size_t size);
 
@@ -80,18 +103,14 @@
   bool IsSymLink_Win() const { return Tag == Z7_WIN_IO_REPARSE_TAG_SYMLINK; }
   bool IsSymLink_WSL() const { return Tag == Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK; }
 
+  // note: "/dir1/path" is marked as relative.
   bool IsRelative_Win() const { return Flags == Z7_WIN_SYMLINK_FLAG_RELATIVE; }
 
   bool IsRelative_WSL() const
   {
-    if (WslName.IsEmpty())
-      return true;
-    char c = WslName[0];
-    return !IS_PATH_SEPAR(c);
+    return WslName[0] != '/'; // WSL uses unix path separator
   }
 
-  // bool IsVolume() const;
-
   bool IsOkNamePair() const;
   UString GetPath() const;
 };
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileLink.cpp 7zip-25.00+dfsg/CPP/Windows/FileLink.cpp
--- 7zip-24.09+dfsg/CPP/Windows/FileLink.cpp	2023-09-05 08:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/FileLink.cpp	2025-06-20 13:00:00.000000000 +0200
@@ -39,12 +39,24 @@
 using namespace NName;
 
 /*
+Win10 Junctions/SymLinks:
+  - (/) slash doesn't work as path separator
+  - Win10 preinstalled junctions don't use tail backslash, but tail backslashes also work.
+  - double backslash works only after drive prefix "c:\\dir1\dir2\",
+    and doesn't work in another places.
+  - absolute path without \??\ prefix doesn't work
+  - absolute path "c:" doesn't work
+*/
+
+/*
   Reparse Points (Junctions and Symbolic Links):
   struct
   {
     UInt32 Tag;
     UInt16 Size;     // not including starting 8 bytes
-    UInt16 Reserved; // = 0
+    UInt16 Reserved; // = 0, DOCs: // Length, in bytes, of the unparsed portion of
+       // the file name pointed to by the FileName member of the associated file object.
+       // This member is only valid for create operations when the I/O fails with STATUS_REPARSE.
     
     UInt16 SubstituteOffset; // offset in bytes from  start of namesChars
     UInt16 SubstituteLen;    // size in bytes, it doesn't include tailed NUL
@@ -68,6 +80,16 @@
     2) Default Order in table:
          Print Path
          Substitute Path
+
+DOCS:
+  The print name SHOULD be an informative pathname, suitable for display
+  to a user, that also identifies the target of the mount point.
+  Neither of these pathnames can contain dot directory names.
+
+reparse tags, with the exception of IO_REPARSE_TAG_SYMLINK,
+are processed on the server and are not processed by a client
+after transmission over the wire.
+Clients SHOULD treat associated reparse data as opaque data.
 */
 
 /*
@@ -93,7 +115,8 @@
 #define Get16(p) GetUi16(p)
 #define Get32(p) GetUi32(p)
 
-static const wchar_t * const k_LinkPrefix = L"\\??\\";
+static const char * const k_LinkPrefix = "\\??\\";
+static const char * const k_LinkPrefix_UNC = "\\??\\UNC\\";
 static const unsigned k_LinkPrefix_Size = 4;
 
 static bool IsLinkPrefix(const wchar_t *s)
@@ -102,7 +125,7 @@
 }
 
 /*
-static const wchar_t * const k_VolumePrefix = L"Volume{";
+static const char * const k_VolumePrefix = "Volume{";
 static const bool IsVolumeName(const wchar_t *s)
 {
   return IsString1PrefixedByString2(s, k_VolumePrefix);
@@ -118,7 +141,7 @@
 {
   for (;;)
   {
-    wchar_t c = *path++;
+    const wchar_t c = *path++;
     if (c == 0)
       return;
     Set16(dest, (UInt16)c)
@@ -126,62 +149,103 @@
   }
 }
 
-bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL)
+#ifdef _WIN32
+void Convert_WinPath_to_WslLinuxPath(FString &s, bool convertDrivePath)
 {
-  bool isAbs = IsAbsolutePath(path);
-  if (!isAbs && !isSymLink)
-    return false;
-
-  if (isWSL)
+  if (convertDrivePath && IsDrivePath(s))
   {
-    // unsupported characters probably use Replacement Character UTF-16 0xFFFD
-    AString utf;
-    ConvertUnicodeToUTF8(path, utf);
-    const size_t size = 4 + utf.Len();
-    if (size != (UInt16)size)
-      return false;
-    dest.Alloc(8 + size);
-    Byte *p = dest;
-    Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK)
-    Set16(p + 4, (UInt16)(size))
-    Set16(p + 6, 0)
-    Set32(p + 8, Z7_WIN_LX_SYMLINK_FLAG)
-    memcpy(p + 12, utf.Ptr(), utf.Len());
-    return true;
+    FChar c = s[0];
+    c = MyCharLower_Ascii(c);
+    s.DeleteFrontal(2);
+    s.InsertAtFront(c);
+    s.Insert(0, FTEXT("/mnt/"));
   }
+  s.Replace(FCHAR_PATH_SEPARATOR, FTEXT('/'));
+}
+#endif
 
-  // usual symbolic LINK (NOT WSL)
 
-  bool needPrintName = true;
+static const unsigned k_Link_Size_Limit = 1u << 16; // 16-bit field is used for size.
+
+void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path)
+{
+  // dest.Free(); // it's empty already
+  // WSL probably uses Replacement Character UTF-16 0xFFFD for unsupported characters?
+  AString utf;
+  ConvertUnicodeToUTF8(path, utf);
+  const unsigned size = 4 + utf.Len();
+  if (size >= k_Link_Size_Limit)
+    return;
+  dest.Alloc(8 + size);
+  Byte *p = dest;
+  Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK)
+  // Set32(p + 4, (UInt32)size)
+  Set16(p + 4, (UInt16)size)
+  Set16(p + 6, 0)
+  Set32(p + 8, Z7_WIN_LX_SYMLINK_VERSION_2)
+  memcpy(p + 12, utf.Ptr(), utf.Len());
+}
+
 
-  if (IsSuperPath(path))
+void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink)
+{
+  // dest.Free(); // it's empty already
+  bool isAbs = false;
+  if (IS_PATH_SEPAR(path[0]))
+  {
+    // root paths "\dir1\path" are marked as relative
+    if (IS_PATH_SEPAR(path[1]))
+      isAbs = true;
+  }
+  else
+    isAbs = IsAbsolutePath(path);
+  if (!isAbs && !isSymLink)
   {
-    path += kSuperPathPrefixSize;
-    if (!IsDrivePath(path))
-      needPrintName = false;
+    // Win10 allows us to create relative MOUNT_POINT.
+    // But relative MOUNT_POINT will not work when accessing it.
+    // So we prevent useless creation of a relative MOUNT_POINT.
+    return;
   }
 
-  const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0;
-    
+  bool needPrintName = true;
+  UString subs (path);
+  if (isAbs)
+  {
+    const bool isSuperPath = IsSuperPath(path);
+    if (!isSuperPath && NName::IsNetworkPath(us2fs(path)))
+    {
+      subs = k_LinkPrefix_UNC;
+      subs += (path + 2);
+    }
+    else
+    {
+      if (isSuperPath)
+      {
+        // we remove super prefix:
+        path += kSuperPathPrefixSize;
+        // we want to get correct abolute path in PrintName still.
+        if (!IsDrivePath(path))
+          needPrintName = false; // we need "\\server\path" for print name.
+      }
+      subs = k_LinkPrefix;
+      subs += path;
+    }
+  }
+  const size_t len1 = subs.Len() * 2;
   size_t len2 = (size_t)MyStringLen(path) * 2;
-  const size_t len1 = len2 + add_Prefix_Len * 2;
   if (!needPrintName)
     len2 = 0;
-
-  size_t totalNamesSize = (len1 + len2);
-
+  size_t totalNamesSize = len1 + len2;
   /* some WIM imagex software uses old scheme for symbolic links.
-     so we can old scheme for byte to byte compatibility */
-
-  bool newOrderScheme = isSymLink;
+     so we can use old scheme for byte to byte compatibility */
+  const bool newOrderScheme = isSymLink;
   // newOrderScheme = false;
-
   if (!newOrderScheme)
-    totalNamesSize += 2 * 2;
+    totalNamesSize += 2 * 2; // we use NULL terminators in old scheme.
 
   const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize;
-  if (size != (UInt16)size)
-    return false;
+  if (size >= k_Link_Size_Limit)
+    return;
   dest.Alloc(size);
   memset(dest, 0, size);
   const UInt32 tag = isSymLink ?
@@ -189,6 +253,7 @@
       Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT;
   Byte *p = dest;
   Set32(p, tag)
+  // Set32(p + 4, (UInt32)(size - 8))
   Set16(p + 4, (UInt16)(size - 8))
   Set16(p + 6, 0)
   p += 8;
@@ -204,21 +269,16 @@
   Set16(p + 2, (UInt16)len1)
   Set16(p + 4, (UInt16)printOffs)
   Set16(p + 6, (UInt16)len2)
-
   p += 8;
   if (isSymLink)
   {
-    UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE;
+    const UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE;
     Set32(p, flags)
     p += 4;
   }
-
-  if (add_Prefix_Len != 0)
-    WriteString(p + subOffs, k_LinkPrefix);
-  WriteString(p + subOffs + add_Prefix_Len * 2, path);
+  WriteString(p + subOffs, subs);
   if (needPrintName)
     WriteString(p + printOffs, path);
-  return true;
 }
 
 #endif // defined(_WIN32) && !defined(UNDER_CE)
@@ -230,7 +290,7 @@
   unsigned i;
   for (i = 0; i < len; i++)
   {
-    wchar_t c = Get16(p + i * 2);
+    const wchar_t c = Get16(p + (size_t)i * 2);
     if (c == 0)
       break;
     s[i] = c;
@@ -239,6 +299,7 @@
   res.ReleaseBuf_SetLen(i);
 }
 
+
 bool CReparseAttr::Parse(const Byte *p, size_t size)
 {
   ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA;
@@ -250,7 +311,12 @@
     return false;
   Tag = Get32(p);
   if (Get16(p + 6) != 0) // padding
-    return false;
+  {
+    // DOCs: Reserved : the field SHOULD be set to 0
+    // and MUST be ignored (by parser).
+    // Win10 ignores it.
+    MinorError = true; // optional
+  }
   unsigned len = Get16(p + 4);
   p += 8;
   size -= 8;
@@ -262,8 +328,6 @@
       (type & kReparseFlags_Microsoft) == 0 ||
       (type & 0xFFFF) != 3)
   */
-
-
   HeaderError = false;
 
   if (   Tag != Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT
@@ -282,8 +346,7 @@
   {
     if (len < 4)
       return false;
-    Flags = Get32(p); // maybe it's not Flags
-    if (Flags != Z7_WIN_LX_SYMLINK_FLAG)
+    if (Get32(p) != Z7_WIN_LX_SYMLINK_VERSION_2)
       return false;
     len -= 4;
     p += 4;
@@ -291,12 +354,13 @@
     unsigned i;
     for (i = 0; i < len; i++)
     {
-      char c = (char)p[i];
+      const char c = (char)p[i];
       s[i] = c;
       if (c == 0)
         break;
     }
-    WslName.ReleaseBuf_SetEnd(i);
+    s[i] = 0;
+    WslName.ReleaseBuf_SetLen(i);
     MinorError = (i != len);
     ErrorCode = 0;
     return true;
@@ -304,10 +368,10 @@
   
   if (len < 8)
     return false;
-  unsigned subOffs = Get16(p);
-  unsigned subLen = Get16(p + 2);
-  unsigned printOffs = Get16(p + 4);
-  unsigned printLen = Get16(p + 6);
+  const unsigned subOffs = Get16(p);
+  const unsigned subLen = Get16(p + 2);
+  const unsigned printOffs = Get16(p + 4);
+  const unsigned printLen = Get16(p + 6);
   len -= 8;
   p += 8;
 
@@ -335,15 +399,17 @@
 
 bool CReparseShortInfo::Parse(const Byte *p, size_t size)
 {
-  const Byte *start = p;
-  Offset= 0;
+  const Byte * const start = p;
+  Offset = 0;
   Size = 0;
   if (size < 8)
     return false;
-  UInt32 Tag = Get32(p);
+  const UInt32 Tag = Get32(p);
   UInt32 len = Get16(p + 4);
+  /*
   if (len + 8 > size)
     return false;
+  */
   /*
   if ((type & kReparseFlags_Alias) == 0 ||
       (type & kReparseFlags_Microsoft) == 0 ||
@@ -353,16 +419,14 @@
       Tag != Z7_WIN_IO_REPARSE_TAG_SYMLINK)
     // return true;
     return false;
-
+  /*
   if (Get16(p + 6) != 0) // padding
     return false;
-  
+  */
   p += 8;
   size -= 8;
-  
   if (len != size) // do we need that check?
     return false;
-  
   if (len < 8)
     return false;
   unsigned subOffs = Get16(p);
@@ -396,10 +460,14 @@
 {
   if (IsLinkPrefix(SubsName))
   {
+    if (PrintName == GetPath())
+      return true;
+/*
     if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size)))
       return PrintName.IsEmpty();
     if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0)
       return true;
+*/
   }
   return wcscmp(SubsName, PrintName) == 0;
 }
@@ -415,21 +483,26 @@
 
 UString CReparseAttr::GetPath() const
 {
+  UString s (SubsName);
   if (IsSymLink_WSL())
   {
-    UString u;
     // if (CheckUTF8(attr.WslName)
-    if (!ConvertUTF8ToUnicode(WslName, u))
-      MultiByteToUnicodeString2(u, WslName);
-    return u;
+    if (!ConvertUTF8ToUnicode(WslName, s))
+      MultiByteToUnicodeString2(s, WslName);
   }
-
-  UString s (SubsName);
-  if (IsLinkPrefix(s))
+  else if (IsLinkPrefix(s))
   {
-    s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\"
-    if (IsDrivePath(s.Ptr(k_LinkPrefix_Size)))
-      s.DeleteFrontal(k_LinkPrefix_Size);
+    if (IsString1PrefixedByString2_NoCase_Ascii(s.Ptr(), k_LinkPrefix_UNC))
+    {
+      s.DeleteFrontal(6);
+      s.ReplaceOneCharAtPos(0, '\\');
+    }
+    else
+    {
+      s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\"
+      if (IsDrivePath(s.Ptr(k_LinkPrefix_Size)))
+        s.DeleteFrontal(k_LinkPrefix_Size);
+    }
   }
   return s;
 }
@@ -468,7 +541,7 @@
 static bool CreatePrefixDirOfFile(CFSTR path)
 {
   FString path2 (path);
-  int pos = path2.ReverseFind_PathSepar();
+  const int pos = path2.ReverseFind_PathSepar();
   if (pos < 0)
     return true;
   #ifdef _WIN32
@@ -494,6 +567,8 @@
 }
 
 
+// MOUNT_POINT (Junction Point) and LX_SYMLINK (WSL) can be written without administrator rights.
+// SYMLINK requires administrator rights.
 // If there is Reparse data already, it still writes new Reparse data
 bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size)
 {
@@ -540,10 +615,11 @@
     SetLastError(ERROR_INVALID_REPARSE_DATA);
     return false;
   }
-  BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE];
-  memset(buf, 0, sizeof(buf));
-  memcpy(buf, reparseData, 4); // tag
-  return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, buf, sizeof(buf));
+  // BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE];
+  // memset(buf, 0, sizeof(buf));
+  // memcpy(buf, reparseData, 4); // tag
+  memset(reparseData + 4, 0, my_REPARSE_DATA_BUFFER_HEADER_SIZE - 4);
+  return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, reparseData, my_REPARSE_DATA_BUFFER_HEADER_SIZE);
 }
 
 }
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileName.cpp 7zip-25.00+dfsg/CPP/Windows/FileName.cpp
--- 7zip-24.09+dfsg/CPP/Windows/FileName.cpp	2024-10-06 12:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/FileName.cpp	2025-06-14 15:39:00.000000000 +0200
@@ -65,8 +65,15 @@
     dirPath.Add_PathSepar();
 }
 
+
+#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a')))
+bool IsDrivePath (const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); }
+// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; }
+
 #ifdef _WIN32
 
+bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; }
+
 #ifndef USE_UNICODE_FSTRING
 #ifdef Z7_LONG_PATH
 static void NormalizeDirSeparators(UString &s)
@@ -87,13 +94,6 @@
       s.ReplaceOneCharAtPos(i, FCHAR_PATH_SEPARATOR);
 }
 
-#endif
-
-
-#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a')))
-
-bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); }
-
 bool IsAltPathPrefix(CFSTR s) throw()
 {
   unsigned len = MyStringLen(s);
@@ -117,16 +117,23 @@
   return true;
 }
 
-#if defined(_WIN32) && !defined(UNDER_CE)
+#endif // _WIN32
+
 
-const char * const kSuperPathPrefix = "\\\\?\\";
+const char * const kSuperPathPrefix =
+    STRING_PATH_SEPARATOR
+    STRING_PATH_SEPARATOR "?"
+    STRING_PATH_SEPARATOR;
 #ifdef Z7_LONG_PATH
-static const char * const kSuperUncPrefix = "\\\\?\\UNC\\";
+static const char * const kSuperUncPrefix =
+    STRING_PATH_SEPARATOR
+    STRING_PATH_SEPARATOR "?"
+    STRING_PATH_SEPARATOR "UNC"
+    STRING_PATH_SEPARATOR;
 #endif
 
 #define IS_DEVICE_PATH(s)          (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '.' && IS_SEPAR((s)[3]))
 #define IS_SUPER_PREFIX(s)         (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '?' && IS_SEPAR((s)[3]))
-#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3]))
 
 #define IS_UNC_WITH_SLASH(s) ( \
      ((s)[0] == 'U' || (s)[0] == 'u') \
@@ -134,6 +141,16 @@
   && ((s)[2] == 'C' || (s)[2] == 'c') \
   && IS_SEPAR((s)[3]))
 
+static const unsigned kDrivePrefixSize = 3; /* c:\ */
+
+bool IsSuperPath(const wchar_t *s) throw();
+bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); }
+// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
+
+#if defined(_WIN32) && !defined(UNDER_CE)
+
+#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3]))
+bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
 bool IsDevicePath(CFSTR s) throw()
 {
   #ifdef UNDER_CE
@@ -154,7 +171,7 @@
   
   if (!IS_DEVICE_PATH(s))
     return false;
-  unsigned len = MyStringLen(s);
+  const unsigned len = MyStringLen(s);
   if (len == 6 && s[5] == ':')
     return true;
   if (len < 18 || len > 22 || !IsString1PrefixedByString2(s + kDevicePathPrefixSize, "PhysicalDrive"))
@@ -174,7 +191,7 @@
     return false;
   if (IsSuperUncPath(s))
     return true;
-  FChar c = s[2];
+  const FChar c = s[2];
   return (c != '.' && c != '?');
 }
 
@@ -187,7 +204,7 @@
     prefixSize = kSuperUncPathPrefixSize;
   else
   {
-    FChar c = s[2];
+    const FChar c = s[2];
     if (c == '.' || c == '?')
       return 0;
   }
@@ -209,14 +226,6 @@
   return s[(unsigned)pos + 1] == 0;
 }
 
-static const unsigned kDrivePrefixSize = 3; /* c:\ */
-
-bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; }
-// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; }
-bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); }
-bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
-// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
-
 bool IsAltStreamPrefixWithColon(const UString &s) throw()
 {
   if (s.IsEmpty())
@@ -349,14 +358,16 @@
 }
 
 #endif // USE_UNICODE_FSTRING
+#endif // _WIN32
+
 
 static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw()
 {
   // Network path: we look "server\path\" as root prefix
-  int pos = FindSepar(s);
+  const int pos = FindSepar(s);
   if (pos < 0)
     return 0;
-  int pos2 = FindSepar(s + (unsigned)pos + 1);
+  const int pos2 = FindSepar(s + (unsigned)pos + 1);
   if (pos2 < 0)
     return 0;
   return (unsigned)(pos + pos2 + 2);
@@ -370,7 +381,7 @@
     return 0;
   if (s[1] == 0 || !IS_SEPAR(s[1]))
     return 1;
-  unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
+  const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
   return (size == 0) ? 0 : 2 + size;
 }
 
@@ -378,17 +389,21 @@
 {
   if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize))
   {
-    unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
+    const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
     return (size == 0) ? 0 : kSuperUncPathPrefixSize + size;
   }
   // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\"
-  int pos = FindSepar(s + kSuperPathPrefixSize);
+  const int pos = FindSepar(s + kSuperPathPrefixSize);
   if (pos < 0)
     return 0;
   return kSuperPathPrefixSize + (unsigned)(pos + 1);
 }
 
+#ifdef _WIN32
 unsigned GetRootPrefixSize(const wchar_t *s) throw()
+#else
+unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw()
+#endif
 {
   if (IS_DEVICE_PATH(s))
     return kDevicePathPrefixSize;
@@ -397,7 +412,7 @@
   return GetRootPrefixSize_Of_SimplePath(s);
 }
 
-#else // _WIN32
+#ifndef _WIN32
 
 bool IsAbsolutePath(const wchar_t *s) throw() { return IS_SEPAR(s[0]); }
 
diff -Nru 7zip-24.09+dfsg/CPP/Windows/FileName.h 7zip-25.00+dfsg/CPP/Windows/FileName.h
--- 7zip-24.09+dfsg/CPP/Windows/FileName.h	2023-04-24 20:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/FileName.h	2025-06-20 13:00:00.000000000 +0200
@@ -25,13 +25,13 @@
 
 bool IsAltPathPrefix(CFSTR s) throw(); /* name: */
 
-#if defined(_WIN32) && !defined(UNDER_CE)
-
 extern const char * const kSuperPathPrefix; /* \\?\ */
 const unsigned kDevicePathPrefixSize = 4;
 const unsigned kSuperPathPrefixSize = 4;
 const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4;
 
+#if defined(_WIN32) && !defined(UNDER_CE)
+
 bool IsDevicePath(CFSTR s) throw();   /* \\.\ */
 bool IsSuperUncPath(CFSTR s) throw(); /* \\?\UNC\ */
 bool IsNetworkPath(CFSTR s) throw();  /* \\?\UNC\ or \\SERVER */
@@ -86,6 +86,15 @@
 bool IsAbsolutePath(const wchar_t *s) throw();
 unsigned GetRootPrefixSize(const wchar_t *s) throw();
 
+#ifndef _WIN32
+/* GetRootPrefixSize_WINDOWS() is called in linux, but it parses path by windows rules.
+   It supports only paths system (linux) slash separators (STRING_PATH_SEPARATOR),
+   It doesn't parses paths with backslash (windows) separators.
+   "c:/dir/file" is supported.
+*/
+unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw();
+#endif
+
 #ifdef Z7_LONG_PATH
 
 const int kSuperPathType_UseOnlyMain = 0;
diff -Nru 7zip-24.09+dfsg/CPP/Windows/System.cpp 7zip-25.00+dfsg/CPP/Windows/System.cpp
--- 7zip-24.09+dfsg/CPP/Windows/System.cpp	2024-10-24 07:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/System.cpp	2025-06-30 17:00:00.000000000 +0200
@@ -25,6 +25,69 @@
 
 #ifdef _WIN32
 
+/*
+note: returned value in 32-bit version can be limited by value 32.
+      while 64-bit version returns full value.
+GetMaximumProcessorCount(groupNumber) can return higher value than
+GetActiveProcessorCount(groupNumber) in some cases, because CPUs can be added.
+*/
+// typedef DWORD (WINAPI *Func_GetMaximumProcessorCount)(WORD GroupNumber);
+typedef DWORD (WINAPI *Func_GetActiveProcessorCount)(WORD GroupNumber);
+typedef WORD (WINAPI *Func_GetActiveProcessorGroupCount)(VOID);
+/*
+#if 0 && defined(ALL_PROCESSOR_GROUPS)
+#define MY_ALL_PROCESSOR_GROUPS   ALL_PROCESSOR_GROUPS
+#else
+#define MY_ALL_PROCESSOR_GROUPS   0xffff
+#endif
+*/
+
+Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
+
+bool CCpuGroups::Load()
+{
+  NumThreadsTotal = 0;
+  GroupSizes.Clear();
+  const HMODULE hmodule = ::GetModuleHandleA("kernel32.dll");
+  // Is_Win11_Groups = GetProcAddress(hmodule, "SetThreadSelectedCpuSetMasks") != NULL;
+  const
+      Func_GetActiveProcessorGroupCount
+        fn_GetActiveProcessorGroupCount = Z7_GET_PROC_ADDRESS(
+      Func_GetActiveProcessorGroupCount, hmodule,
+          "GetActiveProcessorGroupCount");
+  const
+      Func_GetActiveProcessorCount
+        fn_GetActiveProcessorCount = Z7_GET_PROC_ADDRESS(
+      Func_GetActiveProcessorCount, hmodule,
+          "GetActiveProcessorCount");
+  if (!fn_GetActiveProcessorGroupCount ||
+      !fn_GetActiveProcessorCount)
+    return false;
+
+  const unsigned numGroups = fn_GetActiveProcessorGroupCount();
+  if (numGroups == 0)
+    return false;
+  UInt32 sum = 0;
+  for (unsigned i = 0; i < numGroups; i++)
+  {
+    const UInt32 num = fn_GetActiveProcessorCount((WORD)i);
+    /*
+    if (num == 0)
+    {
+      // it means error
+      // but is it possible that some group is empty by some reason?
+      // GroupSizes.Clear();
+      // return false;
+    }
+    */
+    sum += num;
+    GroupSizes.Add(num);
+  }
+  NumThreadsTotal = sum;
+  // NumThreadsTotal = fn_GetActiveProcessorCount(MY_ALL_PROCESSOR_GROUPS);
+  return true;
+}
+
 UInt32 CountAffinity(DWORD_PTR mask)
 {
   UInt32 num = 0;
@@ -38,31 +101,62 @@
 
 BOOL CProcessAffinity::Get()
 {
-  #ifndef UNDER_CE
-  return GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask);
-  #else
-  return FALSE;
-  #endif
+  IsGroupMode = false;
+  Groups.Load();
+  // SetThreadAffinityMask(GetCurrentThread(), 1);
+  // SetProcessAffinityMask(GetCurrentProcess(), 1);
+  BOOL res = GetProcessAffinityMask(GetCurrentProcess(),
+      &processAffinityMask, &systemAffinityMask);
+  /* DOCs: On a system with more than 64 processors, if the threads
+     of the calling process  are in a single processor group, the
+     function sets the variables pointed to by lpProcessAffinityMask
+     and lpSystemAffinityMask to the process affinity mask and the
+     processor mask of active logical processors for that group.
+     If the calling process contains threads in multiple groups,
+     the function returns zero for both affinity masks
+
+     note: tested in Win10: GetProcessAffinityMask() doesn't return 0
+           in (processAffinityMask) and (systemAffinityMask) masks.
+     We need to test it in Win11: how to get mask==0 from GetProcessAffinityMask()?
+  */
+  if (!res)
+  {
+    processAffinityMask = 0;
+    systemAffinityMask = 0;
+  }
+  if (Groups.GroupSizes.Size() > 1 && Groups.NumThreadsTotal)
+    if (// !res ||
+        processAffinityMask == 0 || // to support case described in DOCs and for (!res) case
+        processAffinityMask == systemAffinityMask) // for default nonchanged affinity
+    {
+      // we set IsGroupMode only if processAffinity is default (not changed).
+      res = TRUE;
+      IsGroupMode = true;
+    }
+  return res;
 }
 
 
+UInt32 CProcessAffinity::Load_and_GetNumberOfThreads()
+{
+  if (Get())
+  {
+    const UInt32 numProcessors = GetNumProcessThreads();
+    if (numProcessors)
+      return numProcessors;
+  }
+  SYSTEM_INFO systemInfo;
+  GetSystemInfo(&systemInfo);
+  // the number of logical processors in the current group
+  return systemInfo.dwNumberOfProcessors;
+}
+
 UInt32 GetNumberOfProcessors()
 {
   // We need to know how many threads we can use.
   // By default the process is assigned to one group.
-  // So we get the number of logical processors (threads)
-  // assigned to current process in the current group.
-  // Group size can be smaller than total number logical processors, for exammple, 2x36
-
   CProcessAffinity pa;
-
-  if (pa.Get() && pa.processAffinityMask != 0)
-    return pa.GetNumProcessThreads();
-
-  SYSTEM_INFO systemInfo;
-  GetSystemInfo(&systemInfo);
-  // the number of logical processors in the current group
-  return (UInt32)systemInfo.dwNumberOfProcessors;
+  return pa.Load_and_GetNumberOfThreads();
 }
 
 #else
diff -Nru 7zip-24.09+dfsg/CPP/Windows/System.h 7zip-25.00+dfsg/CPP/Windows/System.h
--- 7zip-24.09+dfsg/CPP/Windows/System.h	2024-10-02 10:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/System.h	2025-06-30 12:00:00.000000000 +0200
@@ -9,6 +9,7 @@
 #endif
 
 #include "../Common/MyTypes.h"
+#include "../Common/MyVector.h"
 #include "../Common/MyWindows.h"
 
 namespace NWindows {
@@ -16,6 +17,34 @@
 
 #ifdef _WIN32
 
+struct CCpuGroups
+{
+  CRecordVector<UInt32> GroupSizes;
+  UInt32 NumThreadsTotal; // sum of threads in all groups
+  // bool Is_Win11_Groups; // useless
+  
+  void Get_GroupSize_Min_Max(UInt32 &minSize, UInt32 &maxSize) const
+  {
+    unsigned num = GroupSizes.Size();
+    UInt32 minSize2 = 0, maxSize2 = 0;
+    if (num)
+    {
+      minSize2 = (UInt32)0 - 1;
+      do
+      {
+        const UInt32 v = GroupSizes[--num];
+        if (minSize2 > v) minSize2 = v;
+        if (maxSize2 < v) maxSize2 = v;
+      }
+      while (num);
+    }
+    minSize = minSize2;
+    maxSize = maxSize2;
+  }
+  bool Load();
+  CCpuGroups(): NumThreadsTotal(0) {}
+};
+
 UInt32 CountAffinity(DWORD_PTR mask);
 
 struct CProcessAffinity
@@ -25,14 +54,28 @@
   DWORD_PTR processAffinityMask;
   DWORD_PTR systemAffinityMask;
 
+  CCpuGroups Groups;
+  bool IsGroupMode;
+    /*
+      IsGroupMode == true, if
+          Groups.GroupSizes.Size() > 1) && { dafalt affinity was not changed }
+      IsGroupMode == false, if single group or affinity was changed
+    */
+  
+  UInt32 Load_and_GetNumberOfThreads();
+
   void InitST()
   {
     // numProcessThreads = 1;
     // numSysThreads = 1;
     processAffinityMask = 1;
     systemAffinityMask = 1;
+    IsGroupMode = false;
+    // Groups.NumThreadsTotal = 0;
+    // Groups.Is_Win11_Groups = false;
   }
 
+/*
   void CpuZero()
   {
     processAffinityMask = 0;
@@ -42,9 +85,23 @@
   {
     processAffinityMask |= ((DWORD_PTR)1 << cpuIndex);
   }
+*/
 
-  UInt32 GetNumProcessThreads() const { return CountAffinity(processAffinityMask); }
-  UInt32 GetNumSystemThreads() const { return CountAffinity(systemAffinityMask); }
+  UInt32 GetNumProcessThreads() const
+  {
+    if (IsGroupMode)
+      return Groups.NumThreadsTotal;
+    // IsGroupMode == false
+    // so we don't want to use groups
+    // we return number of threads in default primary group:
+    return CountAffinity(processAffinityMask);
+  }
+  UInt32 GetNumSystemThreads() const
+  {
+    if (Groups.GroupSizes.Size() > 1 && Groups.NumThreadsTotal)
+      return Groups.NumThreadsTotal;
+    return CountAffinity(systemAffinityMask);
+  }
 
   BOOL Get();
 
diff -Nru 7zip-24.09+dfsg/CPP/Windows/Thread.h 7zip-25.00+dfsg/CPP/Windows/Thread.h
--- 7zip-24.09+dfsg/CPP/Windows/Thread.h	2023-03-04 10:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/CPP/Windows/Thread.h	2025-06-30 17:00:00.000000000 +0200
@@ -26,8 +26,10 @@
     { return Thread_Create_With_Affinity(&thread, startAddress, param, affinity); }
   WRes Create_With_CpuSet(THREAD_FUNC_TYPE startAddress, LPVOID param, const CCpuSet *cpuSet)
     { return Thread_Create_With_CpuSet(&thread, startAddress, param, cpuSet); }
-  
-  #ifdef _WIN32
+ 
+#ifdef _WIN32
+  WRes Create_With_Group(THREAD_FUNC_TYPE startAddress, LPVOID param, unsigned group, CAffinityMask affinity = 0)
+    { return Thread_Create_With_Group(&thread, startAddress, param, group, affinity); }
   operator HANDLE() { return thread; }
   void Attach(HANDLE handle) { thread = handle; }
   HANDLE Detach() { HANDLE h = thread; thread = NULL; return h; }
@@ -36,7 +38,7 @@
   bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread, exitCode)); }
   int GetPriority() { return ::GetThreadPriority(thread); }
   bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread, priority)); }
-  #endif
+#endif
 };
 
 }
diff -Nru 7zip-24.09+dfsg/CPP/Windows/TimeUtils.cpp 7zip-25.00+dfsg/CPP/Windows/TimeUtils.cpp
--- 7zip-24.09+dfsg/CPP/Windows/TimeUtils.cpp	2024-04-13 11:00:00.000000000 +0200
+++ 7zip-25.00+dfsg/CPP/Windows/TimeUtils.cpp	2025-06-12 10:00:00.000000000 +0200
@@ -258,8 +258,9 @@
       FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0,
       Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.3,
       Cygwin 2.9, mingw, MSVC 14, Android 9.0.
+  Android NDK defines TIME_UTC but doesn't have the timespec_get().
 */
-#if defined(TIME_UTC)
+#if defined(TIME_UTC) && !defined(__ANDROID__)
 #define ZIP7_USE_timespec_get
 // #pragma message("ZIP7_USE_timespec_get")
 #elif defined(CLOCK_REALTIME)
diff -Nru 7zip-24.09+dfsg/debian/changelog 7zip-25.00+dfsg/debian/changelog
--- 7zip-24.09+dfsg/debian/changelog	2025-05-08 12:46:16.000000000 +0200
+++ 7zip-25.00+dfsg/debian/changelog	2025-07-07 17:16:13.000000000 +0200
@@ -1,3 +1,13 @@
+7zip (25.00+dfsg-1) unstable; urgency=medium
+
+  * New upstream version 25.00+dfsg
+  * Rediff patches
+  * Drop unused macro while building SFX stub
+  * Disable CI for upstream codes branch
+  * Enable cross build test in CI
+
+ -- YOKOTA Hiroshi <yokota.hgml@gmail.com>  Tue, 08 Jul 2025 00:16:13 +0900
+
 7zip (24.09+dfsg-8) unstable; urgency=medium
 
   [ Roger Shimizu ]
diff -Nru 7zip-24.09+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch 7zip-25.00+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch
--- 7zip-24.09+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch	2025-02-26 06:57:23.000000000 +0100
+++ 7zip-25.00+dfsg/debian/patches/0001-Accept-Debian-build-flags.patch	2025-07-07 17:16:13.000000000 +0200
@@ -8,7 +8,7 @@
  1 file changed, 5 insertions(+), 5 deletions(-)
 
 diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak
-index fcb580a..51c73b5 100644
+index 8fbef14..2756ba4 100644
 --- a/CPP/7zip/7zip_gcc.mak
 +++ b/CPP/7zip/7zip_gcc.mak
 @@ -45,7 +45,7 @@ CFLAGS_DEBUG = -g
diff -Nru 7zip-24.09+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch 7zip-25.00+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch
--- 7zip-24.09+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch	2025-02-26 06:57:23.000000000 +0100
+++ 7zip-25.00+dfsg/debian/patches/0002-Use-getcwd-3-POSIX-extension-to-avoid-PATH_MAX-macro.patch	2025-07-07 17:16:13.000000000 +0200
@@ -10,10 +10,10 @@
  1 file changed, 1 insertion(+), 12 deletions(-)
 
 diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp
-index 5063568..f1efd80 100644
+index 2e36cc2..1bd2443 100644
 --- a/CPP/Windows/FileDir.cpp
 +++ b/CPP/Windows/FileDir.cpp
-@@ -1103,22 +1103,11 @@ bool GetCurrentDir(FString &path)
+@@ -1132,22 +1132,11 @@ bool GetCurrentDir(FString &path)
  {
    path.Empty();
  
diff -Nru 7zip-24.09+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch 7zip-25.00+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch
--- 7zip-24.09+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch	2025-03-31 17:09:57.000000000 +0200
+++ 7zip-25.00+dfsg/debian/patches/0005-Add-note-for-unexpected-recursive-operations-behavio.patch	2025-07-07 17:16:13.000000000 +0200
@@ -9,7 +9,7 @@
  1 file changed, 4 insertions(+)
 
 diff --git a/CPP/7zip/UI/Console/Main.cpp b/CPP/7zip/UI/Console/Main.cpp
-index 4fa5c35..86cffb3 100644
+index 5094452..7511322 100644
 --- a/CPP/7zip/UI/Console/Main.cpp
 +++ b/CPP/7zip/UI/Console/Main.cpp
 @@ -133,6 +133,10 @@ static const char * const kHelpString =
diff -Nru 7zip-24.09+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch 7zip-25.00+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch
--- 7zip-24.09+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch	2025-03-31 17:09:57.000000000 +0200
+++ 7zip-25.00+dfsg/debian/patches/0006-Use-c-flags-for-asmc.patch	2025-07-07 17:16:13.000000000 +0200
@@ -2,13 +2,15 @@
 Date: Thu, 26 Dec 2024 23:27:17 +0900
 Subject: Use "-c" flags for asmc
 
+Bug: https://github.com/nidud/asmc/issues/15#issuecomment-2553310674
+
 No needs to build executable files while in assembling.
 ---
  CPP/7zip/7zip_gcc.mak | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak
-index 51c73b5..4b0cc27 100644
+index 2756ba4..5d05cf0 100644
 --- a/CPP/7zip/7zip_gcc.mak
 +++ b/CPP/7zip/7zip_gcc.mak
 @@ -194,7 +194,7 @@ AFLAGS_ABI = -elf -DABI_LINUX -DABI_CDECL
diff -Nru 7zip-24.09+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch 7zip-25.00+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch
--- 7zip-24.09+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch	2025-03-31 17:09:57.000000000 +0200
+++ 7zip-25.00+dfsg/debian/patches/0007-Add-fpic-for-Asmc-options.patch	2025-07-07 17:16:13.000000000 +0200
@@ -2,14 +2,18 @@
 Date: Fri, 27 Dec 2024 12:15:55 +0900
 Subject: Add "-fpic" for Asmc options
 
+Bug: https://github.com/nidud/asmc/issues/15#issuecomment-2554348661
+Bug: https://github.com/nidud/asmc/issues/15#issuecomment-2554659070
+
 Asmc author suggests this option:
 * https://github.com/nidud/asmc/issues/15#issuecomment-2554348661
+* https://github.com/nidud/asmc/issues/15#issuecomment-2554659070
 ---
  CPP/7zip/7zip_gcc.mak | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak
-index 4b0cc27..792ad13 100644
+index 5d05cf0..3fada36 100644
 --- a/CPP/7zip/7zip_gcc.mak
 +++ b/CPP/7zip/7zip_gcc.mak
 @@ -194,7 +194,7 @@ AFLAGS_ABI = -elf -DABI_LINUX -DABI_CDECL
diff -Nru 7zip-24.09+dfsg/debian/patches/0008-Drop-unused-macro-while-building-SFX-stub.patch 7zip-25.00+dfsg/debian/patches/0008-Drop-unused-macro-while-building-SFX-stub.patch
--- 7zip-24.09+dfsg/debian/patches/0008-Drop-unused-macro-while-building-SFX-stub.patch	1970-01-01 01:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/debian/patches/0008-Drop-unused-macro-while-building-SFX-stub.patch	2025-07-07 17:16:13.000000000 +0200
@@ -0,0 +1,32 @@
+From: YOKOTA Hiroshi <yokota.hgml@gmail.com>
+Date: Mon, 7 Jul 2025 11:53:15 +0900
+Subject: Drop unused macro while building SFX stub
+
+> ../../UI/Common/ArchiveExtractCallback.cpp:68:11: error: macro "REPLACE_SLASHES_from_Linux_to_Sys" is not used [-Werror=unused-macros]
+>    68 |   #define REPLACE_SLASHES_from_Linux_to_Sys(s)
+>       |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+> cc1plus: all warnings being treated as errors
+---
+ CPP/7zip/UI/Common/ArchiveExtractCallback.cpp | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
+index 88c3cf8..45ac1f5 100644
+--- a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
++++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
+@@ -58,6 +58,7 @@ static const char * const kCantCreateSymLink = "Cannot create symbolic link";
+ 
+ static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
+ 
++#ifndef Z7_SFX
+ #if WCHAR_PATH_SEPARATOR != L'/'
+   // we convert linux slashes to windows slashes for further processing.
+   // also we convert linux backslashes to BackslashReplacement character.
+@@ -67,6 +68,7 @@ static const unsigned k_LinkDataSize_LIMIT = 1 << 12;
+ #else
+   #define REPLACE_SLASHES_from_Linux_to_Sys(s)
+ #endif
++#endif // Z7_SFX
+ 
+ 
+ #ifndef Z7_SFX
diff -Nru 7zip-24.09+dfsg/debian/patches/series 7zip-25.00+dfsg/debian/patches/series
--- 7zip-24.09+dfsg/debian/patches/series	2025-03-31 17:09:57.000000000 +0200
+++ 7zip-25.00+dfsg/debian/patches/series	2025-07-07 17:16:13.000000000 +0200
@@ -5,3 +5,4 @@
 0005-Add-note-for-unexpected-recursive-operations-behavio.patch
 0006-Use-c-flags-for-asmc.patch
 0007-Add-fpic-for-Asmc-options.patch
+0008-Drop-unused-macro-while-building-SFX-stub.patch
diff -Nru 7zip-24.09+dfsg/debian/salsa-ci.yml 7zip-25.00+dfsg/debian/salsa-ci.yml
--- 7zip-24.09+dfsg/debian/salsa-ci.yml	1970-01-01 01:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/debian/salsa-ci.yml	2025-07-07 17:16:13.000000000 +0200
@@ -0,0 +1,7 @@
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml
+
+variables:
+  SALSA_CI_IGNORED_BRANCHES: 'upstream|pristine-tar'
+  SALSA_CI_DISABLE_CROSSBUILD_ARM64: 0
diff -Nru 7zip-24.09+dfsg/DOC/7zip.wxs 7zip-25.00+dfsg/DOC/7zip.wxs
--- 7zip-24.09+dfsg/DOC/7zip.wxs	2024-11-28 16:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/DOC/7zip.wxs	2025-07-05 15:00:00.000000000 +0200
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 
-<?define VerMajor = "24" ?>
-<?define VerMinor = "09" ?>
+<?define VerMajor = "25" ?>
+<?define VerMinor = "00" ?>
 <?define VerBuild = "00" ?>
 <?define MmVer = "$(var.VerMajor).$(var.VerMinor)" ?>
 <?define MmHex = "$(var.VerMajor)$(var.VerMinor)" ?>
diff -Nru 7zip-24.09+dfsg/DOC/License.txt 7zip-25.00+dfsg/DOC/License.txt
--- 7zip-24.09+dfsg/DOC/License.txt	2024-01-28 15:22:46.000000000 +0100
+++ 7zip-25.00+dfsg/DOC/License.txt	2025-07-05 15:56:54.000000000 +0200
@@ -3,7 +3,7 @@
   License for use and distribution
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-  7-Zip Copyright (C) 1999-2024 Igor Pavlov.
+  7-Zip Copyright (C) 1999-2025 Igor Pavlov.
 
   The licenses for files are:
 
@@ -58,7 +58,7 @@
 
   Copyright (c) 2015-2016, Apple Inc. All rights reserved.
   Copyright (c) Facebook, Inc. All rights reserved.
-  Copyright (c) 2023-2024 Igor Pavlov.
+  Copyright (c) 2023-2025 Igor Pavlov.
 
 Text of the "BSD 3-clause License"
 ----------------------------------
@@ -102,7 +102,7 @@
   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-2024 Igor Pavlov.
+  Copyright (c) 2023-2025 Igor Pavlov.
 
 Text of the "BSD 2-clause License"
 ----------------------------------
diff -Nru 7zip-24.09+dfsg/DOC/readme.txt 7zip-25.00+dfsg/DOC/readme.txt
--- 7zip-24.09+dfsg/DOC/readme.txt	2024-11-29 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/DOC/readme.txt	2025-07-05 15:00:00.000000000 +0200
@@ -1,9 +1,9 @@
-7-Zip 24.09 Sources
+7-Zip 25.00 Sources
 -------------------
 
 7-Zip is a file archiver for Windows. 
 
-7-Zip Copyright (C) 1999-2024 Igor Pavlov.
+7-Zip Copyright (C) 1999-2025 Igor Pavlov.
 
 
 License Info
@@ -73,8 +73,8 @@
 optimization options.
 
 
-How to compile with makefile
-----------------------------
+How to compile with makefile in Windows
+---------------------------------------
 
 Some macronames can be defined for compiling with makefile:
 
@@ -88,6 +88,23 @@
   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,
+use the following command sequence:
+
+  cd SRC\CPP\7zip
+  %comspec% /k "C:\Program Files\VS2022\VC\Auxiliary\Build\vcvars64.bat"
+  nmake
+
+You can use another "vcvars*.bat" files from "VS2022\VC\Auxiliary\Build" directory
+to compile for other platforms:
+  vcvars64.bat
+  vcvarsamd64_arm64.bat
+  vcvarsamd64_x86.bat
+
+Also you can compile single binary from directory with related project.
+For example, to compile 7za.exe, use the following command sequence:
+  cd SRC\CPP\7zip\Bundles\Alone\
+  nmake
 
 
 Compiling 7-Zip for Unix/Linux
diff -Nru 7zip-24.09+dfsg/DOC/src-history.txt 7zip-25.00+dfsg/DOC/src-history.txt
--- 7zip-24.09+dfsg/DOC/src-history.txt	2024-11-29 17:00:00.000000000 +0100
+++ 7zip-25.00+dfsg/DOC/src-history.txt	2025-07-05 16:00:00.000000000 +0200
@@ -1,6 +1,18 @@
 HISTORY of the 7-Zip source code
 --------------------------------
 
+25.00          2025-07-05
+-------------------------
+- 7-Zip for Windows can now use more than 64 CPU threads for compression
+  to zip/7z/xz archives and for the 7-Zip benchmark.
+  If there are more than one processor group in Windows (on systems with more than
+  64 cpu threads), 7-Zip distributes running CPU threads across different processor groups.
+- 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.
+
+
 24.09          2024-11-29
 -------------------------
 - The default dictionary size values for LZMA/LZMA2 compression methods were increased:

--- End Message ---
--- Begin Message ---
I withdraw this request. The .so interface is the same and upstream
seems to keep it stable enough for such a use case.

--- End Message ---

Reply to: