After the text records a MOBI file holds indexes, images and a few records that start with a four-letter tag. Their record numbers are given in the MOBI header. All numbers are big-endian. None of this is documented by Amazon; it follows the MobileRead Wiki and the readers in calibre and KindleUnpack, and fields marked unknown are not understood.
Index records (INDX)
An index is a sorted table of entries, each with a text key and a set of numbered
values. MOBI uses indexes for the table of contents (the ncxIndex) and for
dictionaries (the orthographic and inflection indexes). An index takes several
consecutive records:
- The first
INDXrecord, the meta record: a header, the TAGX table that says how entries are coded, and usually a summary of the last key in each following record. countfurtherINDXrecords holding the entries, each with an IDXT table of entry offsets.ncncxCNCX records holding the strings that entries refer to, such as the titles of table of contents entries.
INDX header
Offsets are from the start of the record. The names are those of calibre’s reader; sources disagree on some of them.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | char[4] | identifier | INDX. |
4 | 4 | UInt32 | headerLength | The length of this header. The TAGX section, in the meta record, follows it. |
8 | 4 | UInt32 | unknown | The MobileRead Wiki reads the index type here; calibre treats it as unused. |
12 | 4 | UInt32 | type | The index type: 0 a normal index, 2 an inflection index (per calibre; see the row above). |
16 | 4 | UInt32 | unknown | Unknown. |
20 | 4 | UInt32 | idxtStart | The offset of the IDXT section in this record. |
24 | 4 | UInt32 | count | In the meta record, the number of index records that follow it; in an entry record, the number of entries in it. |
28 | 4 | UInt32 | encoding | 1252 or 65001, as in the MOBI header. 65002 is seen in some dictionaries; their keys are then codes mapped to characters through the ORDT tables. |
32 | 4 | UInt32 | language | A Windows language code, as in the MOBI header’s locale. |
36 | 4 | UInt32 | totalEntries | In the meta record, the number of entries in the whole index. |
40 | 4 | UInt32 | ordtStart | The offset of an ORDT section, or 0. |
44 | 4 | UInt32 | ligtStart | The offset of a LIGT (ligature) section, or 0. |
48 | 4 | UInt32 | ligtCount | The number of LIGT entries. |
52 | 4 | UInt32 | cncxCount | The number of CNCX records that follow the index records. |
56–163 | 27 × 4 | UInt32 | unknown | Unknown. |
164 | 4 | UInt32 | ordtType | Unknown; relates to ORDT. |
168 | 4 | UInt32 | ordtEntries | The number of ORDT entries. |
172 | 4 | UInt32 | ordt1 | Offset of the first ORDT table: one byte per entry. |
176 | 4 | UInt32 | ordt2 | Offset of the second ORDT table: two bytes per entry, the character each key code stands for. |
180 | 4 | UInt32 | tagxStart | The offset of the TAGX section, in headers long enough to have it; otherwise TAGX follows the header. |
TAGX
The meta record’s TAGX section says which numbered tags an entry can carry and how they are packed.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | char[4] | identifier | TAGX. |
4 | 4 | UInt32 | length | The length of the section, including these 12 bytes. |
8 | 4 | UInt32 | controlByteCount | How many control bytes start each entry’s data. |
12 | 4 each | entries | tags | (length − 12) / 4 tag entries. |
Each tag entry is four bytes:
| Offset | Size | Name | Description |
|---|---|---|---|
0 | 1 | tag | The tag number, such as 1 for a text position. |
1 | 1 | valuesPerEntry | How many numbers make one value of this tag (2 for a start and length pair). |
2 | 1 | mask | The bits of the current control byte that belong to this tag. |
3 | 1 | endFlag | 1: this entry only marks the end of a control byte, and the next tags use the next control byte; the other three bytes are then 0. |
Reading an entry
In an entry record, the IDXT gives where each entry starts; it ends where the next starts. An entry is:
- One byte, the length of the key, then the key itself: the text the index is sorted on. For a table of contents it is a short identifier, usually the entry’s number written as digits; for a dictionary, the headword.
controlByteCountcontrol bytes.- The values, as forward-encoded integers.
For each TAGX entry in order, take the current control byte AND the tag’s mask. If it
is 0, the tag is absent. If it equals the mask and the mask has more than one bit set,
a variable-width integer follows giving the number of bytes of values for this tag.
Otherwise shift it right until the mask’s lowest bit reaches bit 0: the result is the
number of values, and the tag has that number × valuesPerEntry integers.
An end-flag entry moves on to the next control byte. The values are then read in the
order of the tags.
IDXT
At idxtStart in each entry record: the four bytes IDXT,
then one UInt16 per entry giving the entry’s offset in the record. The meta record has
an IDXT too, pointing at one summary entry per entry record, whose key is the last key
in that record, so a reader can find the record for a key without reading them all.
CNCX
CNCX records hold the strings of an index, packed one after another: each is a forward-encoded length, then that many bytes of text. A tag that points at a string gives its offset as record number × 0x10000 + offset in the record, counting the CNCX records from 0.
Table of contents tags
The index at ncxIndex holds the book’s table of contents, built from
its NCX file. Its entries use these tags:
| Tag | Values | Name | Meaning |
|---|---|---|---|
| 1 | 1 | offset | Where the entry’s target starts in the uncompressed text. |
| 2 | 1 | length | How many bytes of text the entry covers. |
| 3 | 1 | label | The title, as a CNCX offset. |
| 4 | 1 | depth | The nesting level, 0 for the top. |
| 5 | 1 | class | A class name as a CNCX offset; in periodicals periodical, section or article. |
| 6 | 2 | position | KF8 only: a fragment number and offset, as in kindle:pos:fid links. |
| 21 | 1 | parent | The entry number of the parent. |
| 22 | 1 | first child | The entry number of the first child. |
| 23 | 1 | last child | The entry number of the last child. |
| 69 | 1 | image | Periodicals: the image for an article, as an image number. |
| 70 | 1 | description | Periodicals: a summary, as a CNCX offset. |
| 71 | 1 | author | Periodicals: the author, as a CNCX offset. |
| 72 | 1 | image caption | Periodicals: a caption, as a CNCX offset. |
| 73 | 1 | image attribution | Periodicals: a credit, as a CNCX offset. |
The tags of dictionary indexes are not publicly documented beyond the headword itself and the text position of its entry.
Image records
From firstImageRecord on, each record holds one image file exactly as
it would be on disk: JPEG, GIF, PNG or BMP, recognised by its own signature. The
4,096-byte limit of text records does not apply. Images are referred to by their number
counting from 1 at firstImageRecord (in recindex
attributes) or from 0 (in EXTH 201 and 202). Kindles limited images in MOBI books
to 127 KB each.
FLIS
A 36-byte record whose content never changes. KindleGen writes it and Kindles may expect it; its purpose is unknown.
| Offset | Size | Value |
|---|---|---|
0 | 4 | FLIS |
4 | 4 | 8 |
8 | 2 | 65 |
10 | 2 | 0 |
12 | 4 | 0 |
16 | 4 | 0xFFFFFFFF |
20 | 2 | 1 |
22 | 2 | 3 |
24 | 4 | 3 |
28 | 4 | 1 |
32 | 4 | 0xFFFFFFFF |
FCIS
A 44-byte record, fixed except for the text length; its purpose is unknown.
| Offset | Size | Value |
|---|---|---|
0 | 4 | FCIS |
4 | 4 | 20 |
8 | 4 | 16 |
12 | 4 | 1 |
16 | 4 | 0 |
20 | 4 | The text length, as in the PalmDOC header |
24 | 4 | 0 |
28 | 4 | 32 |
32 | 4 | 8 |
36 | 2 | 1 |
38 | 2 | 1 |
40 | 4 | 0 |
SRCS and CMET
SRCS: the source files the book was built from, as a
ZIP archive. The record starts with
SRCS and three UInt32 values of unknown meaning; the ZIP data runs from
offset 16 to the end of the record. KindleGen adds it by default (an option to leave it
out was removed in 2010); the MOBI header’s srcsRecord and srcsCount point to it.
CMET: KindleGen’s build log. CMET, a UInt32 that is 12, a
UInt32 text length, then the log text from offset 12, with CRLF line ends, then data of
unknown meaning.
AUDI and VIDE
Embedded audio and video, one clip per record: AUDI or
VIDE, two UInt32 values of unknown meaning, then the media file from
offset 12. They appear only in books made for Kindle apps that play media.
DATP
A record starting DATP, pointed to by the MOBI header at
0x100. Its contents are not documented.
End-of-file record
The last record is four bytes: E9 8E 0D 0A. The last two are a CR LF;
the meaning of the first two is unknown. Readers do not need it, but KindleGen and
calibre always write it.