  db 'VWAD'
  64 bytes: ed25519 signature   ; cannot be all zeroes
  32 bytes: ed25519 public key
auth is done starting from here and to the end of the file.
the following fields are not encrypted, but participate in xor seed creation:
  db author_len
  db title_len
  db 0x0d, 0x0a
  ...author...
  db 0x0d, 0x0a
  ...title...
  db 0x0d, 0x0a, 0x1b, 0x00
everything that follows is encrypted with public key (see the code)
  dd  header_crc32  ; without the crc itself; used to check for correct xor key
                    ; doesn't include comment data
  dw  version       ; 0
  dw  flags         ; bit 0: set if signature is fake, do not check the digest
                    ; bit 1: set if name offsets are actually name lengthes
                    ;        first is offset, others are lengthes
                    ; bit 2: set if we have FAT
from `version` and up to (and including) `comment_crc32` is encrypted as one chunk (nonce is 1)
each chunk is encrypted separately (nonce is 4 + chunkidx)
dir header is encrypted separately, dir contents is encrtypted as one chunk.
  dd  dirofs
  dw  u_comment_size  ; ascii comment size (unpacked)
  dw  p_comment_size  ; ascii comment size (packed)
                      ; if 0, and `u_comment_size` is not 0, the comment is unpacked
  dd  comment_crc32   ; of unpacked data
  ...comment...       ; ascii comment (encrypted as separate chunk) (nonce is 2)
  ...data...
  ...dir...

there should be no gap between data and directory.

dir header:
  dd  pkdir_crc32  ; for packed data
  dd  dir_crc32    ; for unpacked data
  dd  pkdirsize    ; packed dir size; it is always packed
  dd  upkdirsize   ; unpacked dir size

dir is packed as one huge chunk. it is always packed, even if the packed
data is bigger than unpacked.

chunks: crc32 (of unpacked data), then data (packed or unpacked).

file names are with pathes, normalized.


directory:
  uint32_t chunk_count; // never zero
  uint32_t file_count;  // never zero

chunks info:
  uint32_t zero0;  // zero; used in vfs code (chunk offset)
  uint16_t zero1;  // zero; used in vfs code (unpacked chunk size-1)
  uint16_t pksize; // packed chunk size (0 means "unpacked")

files info:  (file time is in UTC)
  uint32_t zero0;      // zero; used in vfs code (first chunk)
  uint32_t zero1;      // zero; used in vfs code (name hash)
  uint32_t zero2;      // zero; used in vfs code (next name in bucket)
  uint32_t gnameofs;   // group name offset, or 0
  uint64_t ftime;      // UTC file time in seconds since Epoch (0 if unknown)
  uint32_t filecrc32;  // for full integrity checks
  uint32_t upksize;    // unpacked file size
  uint32_t chunkCount; // chunk count
  uint32_t nameofs;    // name offset in names array (may be delta)
  ; if we have a FAT:
  ; here is FAT table, which simply holds next chunk numbers for each file
  ; `zero0` then contains the first file chunk in this table
  ; final chunk is 0xffffffffU
  ; each FAT item is `uint32_t`
  ; FAT table size is exactly `chunk_count` items
  ; note that FAT-based vwads may contain orphan chunks, it is legal
  ; WARNING! when saved, this is actually delta from the previous record
  ;          (and it cannot be 0 in this case; 0 means "eof")

  name table size is implicit (from here to the end of the directory)
file names; 0-terminated strings; each name is dword-aligned.
first string is always empty (4 zeroes).
