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

Bug#1109782: unblock: 7zip/25.00+dfsg-1



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:

Reply to: