Library API (libuc2)

libuc2 provides C99 functions for reading, extracting, and compressing UC2 archives. The library is callback-based: callers supply I/O and memory callbacks, making it suitable for embedded, DOS, and freestanding environments.

Header: <uc2/libuc2.h>

Archive Reading

int uc2_identify(void *magic, unsigned magic_size)

Check whether a buffer contains a UC2 archive header.

Parameters:
  • magic – Pointer to the first 4-21 bytes of the file.

  • magic_size – Number of bytes available.

Returns:

1 if UC2, 0 if not, -1 if more bytes needed.

uc2_handle uc2_open(struct uc2_io *io, void *io_ctx)

Open a UC2 archive. The caller provides I/O callbacks via uc2_io. Returns NULL on allocation failure.

uc2_handle uc2_close(uc2_handle h)

Close the archive and free all resources. Always returns NULL.

Directory Enumeration

int uc2_read_cdir(uc2_handle h, struct uc2_entry *entry)

Read the next central directory entry.

Returns:

  • UC2_End (0): end of directory, entry not filled.

  • UC2_BareEntry (1): entry filled, no tags.

  • UC2_TaggedEntry (3): entry filled, call uc2_get_tag() to read tags (long filename, etc.).

  • Negative value on error.

Directories appear before their contents. Duplicate filenames are listed oldest-first.

int uc2_get_tag(uc2_handle h, struct uc2_entry *entry, char **tag, void **data, unsigned *data_len)

Read a tag from a tagged entry. Call repeatedly until it returns UC2_End.

int uc2_finish_cdir(uc2_handle h, char label[12])

Read the archive tail and retrieve the volume label.

Extraction

int uc2_extract(uc2_handle h, struct uc2_xinfo *xi, unsigned size, int (*write)(void *ctx, const void *ptr, unsigned len), void *ctx)

Decompress a file entry. Call only after the entire central directory has been read. The write callback receives decompressed data in chunks.

Compression

int uc2_compress(int level, int (*read)(void *ctx, void *buf, unsigned len), void *read_ctx, int (*write)(void *ctx, const void *ptr, unsigned len), void *write_ctx, unsigned size, unsigned short *checksum_out, unsigned *compressed_size_out)

Compress raw data into a UC2 bitstream (no archive framing).

Parameters:
  • level – Compression level: 2 = Fast, 3 = Normal, 4 = Tight (default), 5 = Ultra. Levels 6-9 select rANS Fast through Ultra.

  • read – Callback returning bytes read (0 at EOF, <0 on error).

  • write – Callback returning <0 on error.

  • size – Total input size in bytes.

  • checksum_out – Receives the Fletcher checksum of the input.

  • compressed_size_out – Receives the compressed size.

Returns:

0 on success, negative UC2_* error code on failure.

int uc2_compress_ex(int level, const void *master, unsigned master_size, int (*read)(void *ctx, void *buf, unsigned len), void *read_ctx, int (*write)(void *ctx, const void *ptr, unsigned len), void *write_ctx, unsigned size, unsigned short *checksum_out, unsigned *compressed_size_out)

Compress with a master-block dictionary prefix. The master data pre-fills the LZ77 sliding window, allowing back-references into the master for cross-file deduplication. Pass NULL / 0 for no master (equivalent to uc2_compress()).

The CLI builds custom master blocks from input content. Those masters are themselves compressed with the built-in 49,152-byte SuperMaster.

int uc2_get_supermaster(void *buf, unsigned buf_size)

Decompress the built-in SuperMaster into buf (must be at least 49152 bytes). Returns 49152 on success, negative error code on failure.

I/O Callbacks

struct uc2_io
int (*read)(void *io_ctx, unsigned pos, void *buf, unsigned len)

Read len bytes from the archive at offset pos into buf. Return number of bytes read (less if EOF), or negative on error.

void *(*alloc)(void *io_ctx, unsigned size)

Allocate memory. Return NULL on failure.

void (*free)(void *io_ctx, void *ptr)

Free memory.

void (*warn)(void *io_ctx, char *fmt, ...)

Optional warning callback.

Data Structures

struct uc2_entry

A directory entry.

unsigned dirid

Parent directory (0 = root).

unsigned id

Directory index (directories only).

unsigned size

Uncompressed file size.

unsigned csize

Compressed file size.

unsigned dos_time

DOS-format timestamp.

unsigned char attr

DOS file attributes.

char name[300]

Filename (UTF-8, NUL-terminated). Populated after tags are read.

Error Codes

Constant

Meaning

UC2_UserFault (-2)

User callback refused to cooperate

UC2_BadState (-3)

API called in wrong order

UC2_Damaged (-4)

Archive data is corrupt

UC2_Truncated (-5)

Unexpected end of data

UC2_Unimplemented (-6)

Feature not yet implemented

UC2_InternalError (-7)

Internal logic error

Archive Writing

Header: <uc2/uc2_writer.h>. This GPL-3.0-or-later module uses hosted C99 malloc and tmpfile. It is separate from the optional LGPL legacy reader. A writer owns its input spool and metadata; callbacks are used only during the call that receives them. Free it with uc2_writer_free on every exit path.

struct uc2_writer_options options = UC2_WRITER_OPTIONS_INIT;
options.level = 4;
uc2_writer *writer = uc2_writer_create(&options);
if (!writer) return -1;
unsigned directory;
int rc = uc2_writer_add_directory(writer, 0, "docs", 0, 0, &directory);
if (rc == 0)
    rc = uc2_writer_add_file(writer, directory, "notes.txt", 0, 0x20,
                             UC2_WRITER_UNKNOWN_SIZE, read_input, input_ctx);
if (rc == 0)
    rc = uc2_writer_finish(writer, write_output, output_ctx);
/* Copy uc2_writer_error(writer) here if rc < 0. */
uc2_writer_free(writer);
return rc;

read_input(ctx, buffer, length) returns the number of bytes supplied, zero at EOF, or a negative error. Short reads are allowed. A supplied file size must match exactly; UC2_WRITER_UNKNOWN_SIZE reads to EOF. A limit violation can consume one extra byte. write_output(ctx, data, length) must accept the entire buffer and return a nonnegative value, or return a negative error.

The completed archive is assembled before the first output callback. Output callback failure can leave partial output. For atomic destination replacement, write to an exclusively created sibling temporary file, check flush and close, then rename it. The CLI and Python UC2Writer implement this sequence. An error poisons the writer: free it and start a new object. Successful finish is one-shot. Objects must be used serially and callbacks must not reenter them.

Directory IDs come from uc2_writer_add_directory; parent zero is the root. Files with the same name and parent become stored versions in insertion order. Directory conflicts are rejected. Empty archives and directories are supported. Names are single UTF-8 components representable in CP850, limited to 255 CP850 bytes and 299 UTF-8 bytes. Slash, backslash, colon, control characters, empty, dot and dot-dot components are rejected. No silent truncation occurs.

Options default to level 4, 100,000 entries and UINT32_MAX total input bytes. The maximum configurable entry count is 1,000,000. Input and archive size limits are 32-bit; the raw central directory is capped at 64 MiB, directory nesting at 64, and the CDC index at 1,048,576 chunks. Metadata grows with the collection while payload buffers remain fixed. Source and archive temporary files require disk space. The public header defines return values, metadata fields, limits, and uc2_writer_get_stats.

Set options.chunked = 1 to share payload chunks across named files and versions. Python exposes this as UC2Writer(..., chunked=True). This opt-in format requires the full a11 or newer reader. See the chunk-sharing guide for the wire format, reference limits and compressed-size accounting. Rebuild C callers against the matching header when upgrading writer option/stat structures.