KaTAR (.ktar) packs a directory tree into one file: file contents first, then a table of paths and byte ranges, then a trailer that locates the table. Directories are not stored; they are implied by the / separators in the paths. This document specifies the format as Extras/KaTAR implements it:
| File | Role |
|---|---|
Extras/KaTAR/KTCompress.k |
writer: ktcompress <input_folder> <output_file> |
Extras/KaTAR/KTDecompress.k |
reader: ktextract <archive_file> <output_folder> |
Extras/KaTAR/TableEntry.k |
TableEntry { filename, offset, length } |
Extras/KaTAR/UTF8.k |
wstring to UTF-8, without <codecvt> or locales |
Extras/KaTAR/Encode.k |
usize to decimal ASCII |
The format has no magic number and no version field, and stores no compression despite the writer's name.
[data] for each file: its bytes, then one '\n'
[table] for each file: <namelen>'\n' <name> <offset>'\n' <length>'\n'
[trailer] <entry_count>'\n' <table_offset>'\n'
Every number is unsigned decimal ASCII, newline-terminated.
Starts at byte 0. Each regular file's bytes are written as they are, followed by one \n separator that no table entry counts. The data section therefore ends at table_offset, which equals the sum of every file's length plus one byte per file.
One entry per file, in the order the writer met them:
| Field | Encoding |
|---|---|
namelen |
decimal byte length of name, then \n |
name |
namelen bytes of UTF-8, no terminator |
offset |
decimal byte offset of the file's first byte from the start of the archive, then \n |
length |
decimal byte length of the file's contents, then \n |
name is the file's path relative to the input folder in generic form (/ separators), converted from the platform's wide string to UTF-8. It is length-prefixed, so a name may contain \n.
entry_count then table_offset, each a decimal line. Both are variable-width text, so the trailer has no fixed size: a reader finds it by scanning back from the end of the file.
KTDecompress reads an archive as follows:
- Scan backwards from the last byte for the third
\n. The trailer starts after it. (The last two newlines end the trailer's two lines; the third ends the table's last entry, or the data section's last separator.) - Parse
entry_countandtable_offset. - Seek to
table_offsetand parse exactlyentry_countentries: read thenamelenline, readnamelenraw bytes, then theoffsetandlengthlines. - For each entry, create the parent directories of
<output_folder>/<name>and copylengthbytes fromoffset.
KTCompress walks the input folder with std::filesystem::recursive_directory_iterator and archives every entry for which is_regular_file() holds (a symlink to a regular file is followed and stored as a file). Directories, empty directories included, and special files are not stored. Entry order is the iteration order of the host's file system, so the archive of one tree is not byte-reproducible across machines.
- No validation on read: a
namethat is absolute or contains..is written outside the output folder, and a malformed trailer or table throws fromstoullrather than producing a diagnostic. - No file metadata (permissions, timestamps) is stored.
- The writer holds the whole data section in memory before writing it.