github fcorbelli/zpaqfranz 65.14
Just about everything

pre-release4 hours ago

zpaqfranz 65.14

DO NOT USE IN PRODUCTION: IT IS STILL TO BE STABILIZED

The quickest way to check a backup: the same line, with t

zpaqfranz a z:\backup.zpaq c:\data -not "*.log"
zpaqfranz t z:\backup.zpaq c:\data -not "*.log" -ssd

The line of the backup typed again, with t in place of a and -ssd at
the end. t with a path compares every file of c:\data with the archive:
the SHA-1 of every fragment of the file on the disk against the ones stored
in the archive, and its CRC-32. Nothing is decompressed: the time is the
time to read the files. With -ssd (65.14f) many files are read at once,
one thread each (-t, default all the cores), and a file over 256 MB is
cut in pieces read by more threads (65.14g).
BTW you can use -ssd -t1 (to get the nice dashboard) on HDD

t with a path, 116,725 files, 6.5 GB (warm cache, 32 threads) 65.14e 65.14n
t z:\msys.zpaq c:\msys64 12.0 s 9.5 s
t z:\msys.zpaq c:\msys64 -ssd - 2.0 s
t z:\msys.zpaq c:\msys64 -verify (the hash of every whole file too) 12.6 s
t z:\msys.zpaq c:\msys64 -verify -ssd - 2.0 s

One file of 2 GB with -ssd: 1.8 s read by one thread (65.14f), 0.63 s in
pieces with -t4, 0.42 s with -t8 (65.14g).

The report is the one of t without -ssd, line by line, and so is the
exit code: 0 if every file is the same, 2 if one is not.

zpaqfranz t z:/demo14m/backup.zpaq z:/demo14m/data -ssd

SHA-1-chunked verify
Comparing archive content to folder(s)
<<z:/demo14m/backup.zpaq>>:
1 versions, 845 files, 51.018.575 bytes (48.65 MB)
----------------------------------------------------------------------------
      845 in <<z:/demo14m/data>>
----------------------------------------------------------------------------
Total files found: 845
100%  129.79 MB/ 129.79 MB @  636.11 MB/s ETA 00:00:00  parts 823/823  different 0
00000845 =  same (files 823 and folders 22)
Total different file size: 0 bytes
0.219s (00:00:00,1.23MB) (all OK)

One file changed, one deleted, one new:

# 2026-10-23 16:33:37               4.376 z:/demo14m/data/doc/bash/README
! 2026-10-23 16:34:43               4.376 z:/demo14m/data/doc/bash/README
+ 2026-10-23 16:34:43                   6 z:/demo14m/data/doc/nuovo.txt
- 2026-10-23 16:33:37              30.174 z:/demo14m/data/bin/cygpath.exe
00000843 =  same (files 821 and folders 22)
00000001 #different
00000001 +external (file missing in ZPAQ)
00000001 -internal (file in ZPAQ but not on disk)
Total different file size: 38.932 bytes
0.218s (00:00:00,1.23MB) (with errors)

exit code 2.

On a console, -ssd shows a bar for every thread (more threads on one row
if the console has fewer rows), as long as what that thread reads, that
fills as it goes: green while all is the same, red once a file is not.
The pieces of the big files are a lighter grey. A sketch:

Comparing 116.725 files (6.50 GB) with 32 threads (-ssd)
T01-02 ██████████████████████████████░░░░░░░░░░░░░░░░
T03-04 ████████████████████████████████████░░░░░░░░░░
...
T31-32 ███████████████████████████░░░░░░░░░░░░░░░░░░░
 61%    3.97 GB/   6.50 GB @    3.25 GB/s ETA 00:00:00  parts 70.112/116.725  different 0

Redirected (a log, a script), the last line alone, once a second.

The same in one line: a ... -test -ssd adds, then compares the folder
with the archive in the same way.

zpaqfranz a z:\backup.zpaq c:\data -test -ssd

The switches of the backup, without typing them again: -writeini
saves the switches of a line in a file, -ini reads them back (65.14a):

zpaqfranz a z:\backup.zpaq c:\data -not "*.log" -m1 -ssd -writeini z:\backup.ini
-writeini <<z:/backup.ini>>: 3 switch(es) written

zpaqfranz t z:\backup.zpaq c:\data -ini z:\backup.ini
Switches from <<z:/backup.ini>>: 3 switch(es) loaded

What t with a path says, and what it does not. It says that the
archive holds every file of the folder as the file is on the disk now:
none missing (+external), none different (#), none only in the
archive (-internal). It does not decompress the archive: that the data
blocks decompress well is t without a path (every block, the CRC-32 of
every file), or w ... -test -verify (below: every file decompressed in
RAM and compared with the one on the disk). Not on spinning drives: many
threads on one disk that turns are slower than one. On an SSD, often -t4
is the limit (measured: 143 GB, 31 big files, -t16 49 s,
-t4 35 s).


From 65.13n to 65.14n: about 5,850 new lines (256,458 to 262,305); most of
them are the help, now data, in English, Italian and Russian.

This document has three parts:

  • Part one: what is new, in short, for everybody
  • Part two: the same things in more detail, for power users
  • Part three: "lo spiegone", how it works inside, and why, for developers

Part one, what is new, for the user

  1. t with a path and -ssd
  2. w -test -verify: the archive decompressed in RAM against the disk
  3. -ini and -writeini
  4. The help: search, and in Italian and Russian
  5. ac from Windows PE
  6. v and the empty files
  7. Smaller things

1. t with a path and -ssd

See the top of this document.
In short:

  • t archive path -ssd: many files compared at once, the same report,
    the same exit code. Without -ssd: one file at a time, as before.
  • A file over 256 MB: in pieces, read by more threads (not with
    -verify, -checksum, -paranoid: they want the hash of the whole
    file, read in one go).
  • Every file is read in blocks of 1 MB (it was 4 KB: on Windows one
    ReadFile every 4 KB, 1.6 million of them for 6.5 GB). Without -ssd
    too, and for x -force, that compares a file before writing it again:
    12.0 s became 9.5 s.
  • a ... -test -ssd: the add, then the same comparison.

2. w -test -verify: the archive decompressed in RAM against the disk

zpaqfranz w z:\1.zpaq -test -verify -ssd
zpaqfranz w z:\1.zpaq -to z:\copy -test -verify -ssd

The other side of t with a path (65.14i). Every file is decompressed in
RAM (nothing is written on the disk) and compared with the file on the
disk: where it was saved from, or, with -to, where x -to would write
it. The hash of the whole file in RAM, the hash of the file on the disk and
the hash stored in the archive must be the same. An archive without hashes
(zpaq 7.15, -nochecksum): RAM and disk byte by byte. The bars of t -ssd
(a lighter grey for the work in RAM, darker for the disk), the names of
what differs, exit code 2.

-test -verify: -ramdisk on (the files are compared from RAM)
...
Stage VEF 0001 : errors  0 (0=good)
============================================================================
Verify against the filesystem: 823 files (129.77 MB) all the same

t with a path is quicker (it decompresses nothing); w -test -verify
decompresses everything, so it checks the archive too.

3. -ini and -writeini

zpaqfranz a z:\1.zpaq c:\data -not "*.tmp" -key pippo -writeini z:\my.ini
zpaqfranz a z:\1.zpaq c:\data -ini z:\my.ini

-writeini X writes the switches of the line in X, and the command goes on.
-ini X puts the switches of X on the line, as if typed (65.14a). More
-ini: in order. In X: #, ::, rem comments, "a b" for blanks.

  • The same switch typed on the line and in X: the one on the line wins.
  • -not, -only, -always: they add up (the ones of X and the ones
    typed).
  • A password in X (-key, -franzen) is in clear text: zpaqfranz says it
    and shows how to protect the file (icacls / chmod 600), or to leave the
    password out (-key alone asks for it; FRANZKEY / FRANZFRANZEN).
  • In cyan, after the banner: Switches from <<X>>: N switch(es) loaded
    (65.14e), -verbose every switch (passwords hidden).

4. The help: search, and in Italian and Russian

zpaqfranz h -ssd -n          where -ssd is, in every command
zpaqfranz h a -n             the switches of a
zpaqfranz h t -it            the help of t, in Italian
zpaqfranz h t -ru            in Russian
  • The help is data now (65.14b): one table, every line with its
    number. h -X -n finds a switch in the help of every command.
  • Italian and Russian (65.14l, 65.14m): -eng, -it, -ru on the
    line; else ZPAQFRANZ_LANG (en, it, ru); else the language of the
    system. Only the help: the messages stay in English. The Russian is
    automatic only on a UTF-8 console.
  • The usage (zpaqfranz with nothing) has two new rows:
Language : zpaqfranz -eng English  -it Italian  -ru Russian
Search   : zpaqfranz h -key -n (switches in every command)  zpaqfranz h a -n
  • A comparison of the help with the parser (every switch of the help is a
    switch, and the other way round) found switches with wrong names in the
    help (-checkspace is -checksize, -noonedrive is -onedrive,
    -blake 3, -find/replace), and help lines about switches *nix does not
    have (f -gui, f -ntfs, z -longpath): fixed, or gone from the *nix
    help.

**The Russian translation is to be reviewed by people who speak it :-) **

5. ac from Windows PE

zpaqfranz ac e:\pc.zpaq
zpaqfranz ac e:\pc.zpaq d:

From a Windows PE (booted from a stick, the one on SourceForge or any
other), ac stopped at once: "cannot open disk -1" (it took the X: of the
PE, a RAM disk, for the Windows to save). Now (65.14j, an issue on GitHub):

  • In Windows PE ac finds the Windows to save by itself (a volume with
    \Windows\System32\config\SYSTEM, not the X: of the PE). That Windows is
    not running: no shadow copy, no SystemPartition from the registry.
  • More than one: ac archive D: says which one.
  • A volume with a Windows and no letter (a GPT disk in a PE can come
    so) gets the first free letter (65.14k): "the volume of 63.00 GB with a
    Windows (\?\Volume{..}) had no letter: now it is E:". It was the first
    real try: the Windows of disk 0 had no letter.
  • Out of Windows PE, ac archive C: is not refused: the letter is ignored
    (66009, a warning) and ac saves the disk of the Windows running, as
    before.

6. v and the empty files

v (and a -verify) did not check the empty files (65.14c, an issue). Now
an empty file still empty is OK, one written after the archive is a FAIL
(exit code 2), one gone is "not found" (exit code 1).

7. Smaller things

  • -distinct (pakka): every version of a file on its own line. pakka
    used it, but no switch turned it on: catpaq showed "?" in place of the
    name of the second version of a file (65.14d).
  • On *nix -sfx, -sfxto, -sfxnot, -sfxonly, -sfxuntil, -sfxforce,
    -sfxall were taken and ignored: now they are unknown switches there
    ("00562! Unknown option ignored"), as they are in the help (Windows only).
  • The line "Scan ... block/s" of the reading of a big archive could stay
    half under the totals: "(30.93 GB)block/s)" (65.14h).
  • t with a path, "N = same (files X and folders Y)": X counted a
    file different twice (its line of the archive and its line of the disk)
    and the files on one side only: 843 = same (files 825 and folders 22)
    with 821 files the same. Now X + Y = N (65.14n). It was so long before
    65.14, with or without -ssd; the verdicts and the exit code were right.

Part two, the details, for power users

  1. t -ssd: the rules
  2. w -test -verify: the rules
  3. -ini: the rules
  4. The language: the rules
  5. ac from Windows PE: the rules
  6. What can change for a script

1. t -ssd: the rules

  • In parallel only t (and a -test) with a path, with -ssd, and two
    files or more
    to compare. Else, one at a time, as before.
  • Threads: -t N; default all the cores; never more than the jobs (the
    files, and the pieces of the big ones).
  • The work goes to the threads from the biggest file down, every file to
    the thread with fewer bytes so far: the bars are about as long.
  • A file over 256 MB (ZPAQ_TPEZZO, a #define): pieces of about that
    size, cut on the borders of its fragments, in the same queue. The file is
    the same if every piece is; its CRC-32 is put together from the CRC-32 of
    the pieces. Before reading: a file whose size or fragments do not match
    the archive is different without reading it. Not with -verify,
    -checksum, -paranoid, -quick
    : those big files are read whole by
    one thread.
  • The report: the one of t without -ssd, the same lines in the same
    order. Exit code: the same.
  • The bars: on a console. Redirected: the info line (parts done/all,
    different N) once a second; -noeta: nothing.
  • -n and -find/-replace: as without -ssd.

2. w -test -verify: the rules

  • Which file on the disk: the path stored in the archive; with -to, the
    one x -to would write (one -to at most: 02570, exit code 2). The
    -to folder need not be empty, and nothing is asked: nothing is
    written there.
  • The hash: of the whole file, with the algorithm stored. Disk = RAM =
    stored. No hash stored (7.15, -nochecksum, -crc32 only): RAM and disk
    byte by byte.
  • Without -ramdisk: turned on by itself (02444).
  • -test -paranoid: refused (02443, exit code 1: -paranoid writes on the
    disk).
  • Exit code 2: a file different, missing, that cannot be read, or that in
    RAM is not as stored ("DECOMPRESSED NOT AS STORED": the archive). 1: other
    errors.
  • A file bigger than the RAM window: its hash goes from one window to the
    next, the verdict at the last one; an extraction that stops: "NOT
    CHECKED".
  • Empty files: they must be there and empty. Folders: not compared. Files
    deleted in that version: left out. *nix: regular files only.
  • -verify without -test, and -paranoid: as before.

3. -ini: the rules

  • The switches of X go on the line after the command, the archive and the
    files, before the first switch typed.
  • A switch that is there twice is kept once, by its meaning: -out and
    -output are one, -m1 and -m5 are one (the method), -t4 and -t8
    are one, -turbo and -noturbo are one, every hash is one. The one typed wins; between two -ini, or
    inside one, the last.
  • They add up: -not, -only, -always (and -sfxnot, -sfxonly).
    Replaced: -to, -mailattach, -mailzip.
  • In X: switches only (no command, archive, file: 03621); no -ini or
    -writeini (03620). -ini and -writeini on the same line: refused
    (03618).
  • Comments: # :: rem at the start of a line (after blanks); # at
    the start of a word: the rest of the line. "a b": one word with a
    blank; "" inside the quotes: one quote. No backslash escapes
    ("c:\program files\" is fine). A quote not closed: an error with its
    line number. UTF-8, with or without BOM, \r\n too.
  • -writeini: the line as the parser read it, on one line; without the
    switches zpaqfranz adds when it starts itself again elevated (-elevated
    and so on). Refused if X is the archive (03622). A write that fails:
    exit code 2, the command does not start.
  • Codes 03616 - 03628, 03643.

4. The language: the rules

  • -eng, -it, -ru anywhere on the line (and in a -ini): the last
    wins; the one typed wins over the one of an ini.
  • Else ZPAQFRANZ_LANG = en, it, ru.
  • Else the system: Windows GetUserDefaultUILanguage, *nix LC_ALL,
    LC_MESSAGES, LANG. Russian only if the console is UTF-8 (on Windows a
    console, not -noconsole; on *nix a UTF-8 locale). Else English.
  • A line whose English changed after it was translated comes out in
    English (every translation keeps a fingerprint of its English).
  • -helpid: the number of every line of the help (for who translates).
  • Italian: with the apostrophe (e', piu'), no accented letters.

5. ac from Windows PE: the rules

  • Windows PE: the key HKLM\SYSTEM\CurrentControlSet\Control\MiniNT.
  • The Windows: the fixed and removable volumes with
    \Windows\System32\config\SYSTEM, not the one of the PE. One: taken.
    None: 66022 (BitLocker: manage-bde -unlock first). More: 66023 "which
    one? ... ac archive E:". A letter given without a Windows: 66024.
  • A volume without a letter and with a Windows (only in the search by
    itself, not with a letter given): the first free letter from C: (it
    stays until the PE is turned off), 66028; none free or it fails: 66027.
  • From the PE: no shadow copy (with -vss: a warning), no
    SystemPartition; the metafile says windows=not running, on E: (saved from Windows PE) and winpe=yes.
  • A volume on no physical disk: 66026 (it was "cannot open disk -1").
  • Out of the PE, a letter: 66009, ignored.

6. What can change for a script

  • The help is in the language of the system: on an Italian or Russian
    computer h, the usage and the help come out translated. A script that
    reads them: ZPAQFRANZ_LANG=en (or -eng). The messages are not
    translated.
  • v / a -verify on an empty file written after the archive: 2 (it was
    0); an empty file gone: 1 (it was 0).
  • ac archive C: out of Windows PE: it goes on (it was refused, 66000).
  • *nix: the -sfx... switches are unknown (ignored, with 00562).
  • -distinct is a switch (it was "Unknown option ignored").
  • t -ssd with a path: the same report and exit code; the info line
    (03645) is new when redirected.
  • t with a path, when something differs: the numbers in "same (files X
    and folders Y)" are smaller (only what is the same).

Part three, lo spiegone, for developers

  1. testverify_ssd
  2. The big files in pieces
  3. equal() by 1 MB
  4. franzbarre
  5. w -test -verify: wconfronta
  6. franzini and ini_espandi
  7. The help as data
  8. The translations
  9. ac in Windows PE
  10. Known limits and open points

1. testverify_ssd

testverify() builds its filelist as before (every file of the disk
after its file of the archive, then the files of the archive only). With
-ssd, before its loop, testverify_ssd(filelist, esiti) takes the pairs
archive/disk that are files, decodes their franz_block on the main thread
(hash type, hash, CRC-32), orders them from the biggest down and gives each
one to the thread with fewer bytes (LPT). The threads (tverifica_thread,
a friend of Jidac: equal() is private) call equal() and write the
result in tconfronto, by the index of filelist. Then the loop of
testverify() runs as ever, and where a thread did the work it takes its
result in place of calling equal(): the output is the one without
-ssd, by construction. The main thread looks at the counters every 200
ms (the bars, the info line).

2. The big files in pieces

A file over ZPAQ_TPEZZO (256 MB) is cut in pieces of about that size on
the borders of its fragments (tlavoro.grosso, primo, ultimo,
inizio), in the same queue as the files. Jidac::testverify_grosso
first: a regular file, the size, the fragments there and as long as the
file (else: different, nothing read). Jidac::equalpezzo: the SHA-1 of the
fragments of its piece and the CRC-32 of the piece, never past it. The
CRC-32 of the file: crc32_combine of the pieces in order. The counters
are volatile, x= x + 1 (++ on a volatile is a warning of gcc 16).
Measured with a build with -DZPAQ_TPEZZO=1048576 too (every file in
pieces).

3. equal() by 1 MB

Jidac::equal() read the file 4 KB at a time; now 1 MB, and the fragments
are hashed from memory. int64_t *o_fatti is the counter of the thread
(the bars); with it, no percentage per file. Every caller has it: t, x -force (that skips the identical files), 205402.

4. franzbarre

The area is printed once, then vai() as franzdash (gotoxy on Windows,
relative moves on *nix). The bars: U+2588 / U+2591 / U+2592 (or #, X,
. without UTF-8), usb_colore 1 green, 3 red, 5 grey, \x1b[37m the
lighter grey. More threads than rows: threads grouped on a row (T01-02).
usb_colore was inside the #ifndef ANCIENT of the command f: ESX and
the NAS builds (ANCIENT) did not link from 65.14f to 65.14h. Moved before
franzbarre (65.14i).

5. w -test -verify: wconfronta

struct twfile, twconti, g_wconti and the wconfronta_* before
Jidac::extractw; wconfronta() (threads and bars) after
testverify_ssd. Every file is two jobs (RAM, disk) in the LPT queue.
extractqueue2 has a twfile *i_grande for the files bigger than a
window: the hashes go on from one window to the next (wconfronta_inizia,
the verdict at the last window, wconfronta_lascia if the extraction
stops). The thread of the disk compares the hash of the disk with the
stored one at once (wconfronta_discodiverso): red bars, different N.

Found and fixed on the way:

  • -checksum on files bigger than a window of RAM checked nothing
    (extractqueue2 went out before): now it does, in a real extraction too.
  • scriviramtodisk: franz_do_hash::update(char*, int) with the size of
    the whole file: from 2 GB up, a wrong hash, a false "ERROR hash" and exit
    code 1. Now in blocks of 1 MB.
  • In the loop of the windows, with -test -verify a file different no
    longer stops the others.

6. franzini and ini_espandi

class franzini (near stessoarchivio()): load / save of a file of
switches; its only line of platform is _wfopen. ini_espandi, at the top
of zpaq_main_internal (before mail, tui, franzscegli), puts the words of
the ini on the line; on Windows \ becomes / (as wtou); the doubles go
by key (ini_chiave: the aliases, the short forms, the pairs, the hashes).
ini_scrivi: the parser decides what a switch is (iniinizi, where every
piece starts in the loop of loadparameters; inioriginale, the line
before -dummyhash); written right after that loop. Tested apart too:
tortura\ini\banco.cpp (the class alone, -std=c++98 -pedantic, 31 cases
and 20,000 random load(save(x)) == x).

7. The help as data

The 94 help_X() and their pieces, converted once (a Perl script) into
g_helprighe[]: macros HU HS HRIGA HSTD HE HESEMPIO HTESTO HESEMPI HINCLUDI HGENERA HBREVE, with the #ifdefs and the NOSFTPSTART/END of
the old code. | is a new line, || a real |. help_stampa() prints as
before (the English byte by byte the same as 65.14a, but for the fixes).
help_cerca (h -X -n) at the top of zpaq_main_internal, before
ini_espandi (so h -key does not ask for a password). The hidden
-helpcheck (help_controlla): the cli_* of the parser register their
names, the help is compared with them. Codes 03629 - 03642.

A trap: flagtest is there twice, a global and a member of Jidac. Moved
into a free function, &flagtest was the global one: x was a -test
(nothing written). Seen by the batteries, fixed passing the address.

8. The translations

Every line has a number (id, 1 - 1791; next free 1792, -helpcheck
says it) and its translations under it: HIT(id, 0xFINGERPRINT, "..."),
HRU(...). The fingerprint is a FNV-1a of the English: if the English
changes, the old translation is not used (English shown). Phrases that are
not lines of a command (the usage, h, h h, the categories,
"Examples:") are found by their English (help_t). lingua_scegli runs
before help_cerca and again after ini_espandi, and takes -eng -it -ru -helpid off the line. moreprint, scrivi() and the columns count
UTF-8 characters, and cut between one and the next. From 65.14m on, the
lines are written by hand (the converter of 65.14l would number them
again). Codes 03648 - 03662 (03660 and 03661, of -lang, gone in
65.14m).

9. ac in Windows PE

g_acpe (the MiniNT key, or the hidden -winpefake for the tests);
ac_windowsspento() looks for the Windows; ac_dailettere()
(FindFirstVolumeW, the volumes without paths, SetVolumeMountPointW)
gives a letter to the one without. Tested on a Windows 11 VM with
-winpefake: the disk restored from it is the same (SHA-256) as the one
from ac -acfake of the same disk (the way already tried with a real
boot).

10. Known limits and open points

  • -ssd on a spinning drive: slower. Many threads on one SSD: often
    -t4 is the limit.
  • The Russian is to be reviewed; a real Windows console in Russian was
    not seen here.

Download zpaqfranz

Don't miss a new zpaqfranz release

NewReleases is sending notifications on new releases.