All formats

ConvertLIT (openclit)

LanguageC
LicenseGPL-2.0-or-later
Repositorysource zips on convertlit.com (clit18src.zip); Debian packaging at packages.debian.org/convlit
Documentationclit man page, comments in lib/*.c
Depends onnothing for the library (it includes its own LZX, DES and SHA-1 code); the clit tool also needs LibTomMath for DRM5 keys
Read / writereads all LIT files; writes LIT only to re-seal (downgrade DRM) or inscribe an existing book

ConvertLIT is the program that reverse-engineered the LIT format. It has two parts:

The code dates from 2002-2004 and is stable: LIT hasn’t changed since. It’s not distributed as a shared library and has no installed headers. To use the library you add lib/*.c to your build, or, more practically, run clit as a separate process. The GPL applies to any program that links the library.

Installation#

The clit tool is packaged:

Shell
# Debian / Ubuntu
sudo apt install convlit
# macOS
brew install convertlit

To build from source, get clit18src.zip from convertlit.com:

Shell
unzip clit18src.zip
cd lib && make            # builds the static library openclit.a

The clit18/Makefile builds the clit program and expects the LibTomMath sources in ../libtommath-0.30/; edit the paths to use a system LibTomMath (libtommath-dev) instead.

Basic usage: extract a LIT book#

Shell
# explode book.lit into an OEB package (note the trailing slash)
clit book.lit book/

# result: book/book.opf, book/*.htm, book/*.css, images
ls book/

Zip the folder or open book.opf in calibre to produce an EPUB. Add -d to keep all files in one directory instead of recreating the original subdirectories.

How to open a LIT file with the library#

The library reads through a callback you provide (readat) and reports errors through lit_error, which your program must define. This is how clit itself opens a file:

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "litlib.h"

/* the library calls this on errors; your program must define it */
void lit_error(int what, char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fputc('\n', stderr);
}

/* the library calls this to read 'size' bytes at 'offset'; it frees the buffer */
static U8 *readat(void *v, U32 offset, int size) {
    FILE *f = (FILE *)v;
    U8 *mem = malloc(size);
    if (!mem) return NULL;
    if (fseek(f, offset, SEEK_SET) != 0 || fread(mem, 1, size, f) != (size_t)size) {
        free(mem);
        return NULL;
    }
    return mem;
}

int main(int argc, char **argv) {
    if (argc < 2) return 1;
    FILE *fh = fopen(argv[1], "rb");
    if (!fh) return 1;

    lit_file lit;
    memset(&lit, 0, sizeof(lit));
    lit.file_pointer = fh;
    lit.readat = readat;
    fseek(fh, 0, SEEK_END);
    lit.filesize = ftell(fh);

    int status = lit_read_from_file(&lit);
    if (status && status != E_LIT_DRM_ERROR) {
        lit_close(&lit);
        fclose(fh);
        return 1;
    }
    printf("DRM level: %d\n", lit.drmlevel);
    lit_close(&lit);
    fclose(fh);
    return 0;
}

Build it against the library: cc -funsigned-char main.c -Ilib lib/openclit.a.

How to list the files in a LIT book#

lit_read_manifest returns the mapping from internal names to original file names and MIME types, in 4 groups (HTML in the spine, other HTML, CSS, images):

C
manifest_type manifest;
memset(&manifest, 0, sizeof(manifest));
if (lit_read_manifest(&lit, &manifest) == 0) {
    for (int group = 0; group < NUM_MANIFEST_MAPS; group++) {
        for (int i = 0; i < manifest.num_mappings[group]; i++) {
            name_mapping *m = &manifest.mappings[group][i];
            printf("%s -> %s (%s)\n", m->sInternal, m->sOriginal, m->sType);
        }
    }
    lit_free_manifest(&manifest);
}

How to read one stored file#

lit_get_file decompresses (and decrypts) one internal file. Internal paths are /data/ plus the internal name from the manifest. Images and CSS come out as-is; HTML documents (/data/<name>/content) are still in LIT’s binary tag encoding, which clit18/explode.c converts back to HTML with lit_reconstitute_html:

C
U8 *data = NULL;
int size = 0;
/* m is a name_mapping from the manifest, e.g. an image */
char path[512];
snprintf(path, sizeof(path), "/data/%s", (char *)m->sInternal);
if (lit_get_file(&lit, path, &data, &size) == 0) {
    FILE *out = fopen((char *)m->sOriginal, "wb");
    fwrite(data, 1, size, out);
    fclose(out);
    free(data);
}

Tips#