The first record of the Palm database describes the whole book. It holds, in order: the 16-byte PalmDOC header, the MOBI header, the EXTH metadata when there is any, the full title, and padding. Offsets on this page are from the start of record 0, not of the file, as is usual for MOBI. All numbers are big-endian. The field names follow the MobileRead Wiki and the KindleUnpack and calibre source code; many fields are still not understood, and those are marked unknown.
PalmDOC header
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 2 | UInt16 | compression | How the text records are compressed: see compression. |
2 | 2 | UInt16 | unused | Always 0. |
4 | 4 | UInt32 | textLength | The length of the whole book text once uncompressed, in bytes. |
8 | 2 | UInt16 | textRecordCount | The number of text records. They are records 1 to this number. |
10 | 2 | UInt16 | textRecordSize | The most text one record holds once uncompressed: always 4,096. |
12 | 2 | UInt16 | encryption | The DRM scheme: see encryption. In a plain PalmDOC file, bytes 12 to 15 are instead a 4-byte current reading position. |
14 | 2 | UInt16 | unknown | Usually 0. |
Compression
| Value | As bytes | Meaning |
|---|---|---|
| 1 | 00 01 | No compression. |
| 2 | 00 02 | PalmDOC compression, a simple LZ77. Most books. |
| 17480 | 44 48 (DH) | HUFF/CDIC Huffman compression with a phrase dictionary, used by Mobipocket Creator’s high-compression setting and by Amazon for many store books. |
Encryption
| Value | Meaning |
|---|---|
| 0 | None. |
| 1 | Old Mobipocket encryption, used by early Mobipocket Reader versions. |
| 2 | Mobipocket encryption, also used by Kindle books (AZW) sold with DRM. The keys are in the DRM block that the MOBI header points to; the text records are encrypted, the headers and metadata are not. |
How the DRM keys are derived is not documented by Amazon and is not described on this site.
MOBI header
The MOBI header starts at offset 16, directly after the PalmDOC header. Its length is
in the header itself; it has grown over the format’s versions, so a reader must check
that length before reading any field past offset 0x84. Common lengths are 0xE4 (228),
0xE8 (232) and 0x108 (264), counted from the MOBI identifier. Fields that
point at a record hold its record number, counting from 0, and 0xFFFFFFFF where there
is none.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x10 | 4 | char[4] | identifier | Always MOBI. |
0x14 | 4 | UInt32 | headerLength | The length of the MOBI header, counted from identifier. The EXTH block starts at 16 + this value. |
0x18 | 4 | UInt32 | mobiType | What kind of publication this is: see MOBI type. |
0x1C | 4 | UInt32 | textEncoding | The character set of the text: 1252 for Windows-1252 (Western European) or 65001 for UTF-8. |
0x20 | 4 | UInt32 | uniqueID | An ID for the book, apparently random. |
0x24 | 4 | UInt32 | fileVersion | The MOBI format version the file uses: see versions. Kindle books have 6, or 8 for the KF8 part of an AZW3 file. |
0x28 | 4 | UInt32 | orthographicIndex | Record of the orthographic meta index, used by dictionaries for headword lookup, or 0xFFFFFFFF. |
0x2C | 4 | UInt32 | inflectionIndex | Record of the inflection meta index, used by dictionaries for word forms, or 0xFFFFFFFF. |
0x30 | 4 | UInt32 | indexNames | Unknown; 0xFFFFFFFF when absent. |
0x34 | 4 | UInt32 | indexKeys | Unknown; 0xFFFFFFFF when absent. |
0x38–0x4C | 6 × 4 | UInt32 | extraIndex0–extraIndex5 | Records of six further indexes, or 0xFFFFFFFF each. Their use is unknown. |
0x50 | 4 | UInt32 | firstNonBookRecord | The first record after the text records (and any padding record). |
0x54 | 4 | UInt32 | fullNameOffset | Where the full title starts, from the start of record 0. |
0x58 | 4 | UInt32 | fullNameLength | The length of the full title in bytes, not counting its terminator. |
0x5C | 4 | UInt32 | locale | The book’s language as a Windows language code: see locale. |
0x60 | 4 | UInt32 | inputLanguage | For a dictionary, the language of the headwords, coded as locale; 0 otherwise. |
0x64 | 4 | UInt32 | outputLanguage | For a dictionary, the language of the definitions; 0 otherwise. |
0x68 | 4 | UInt32 | minVersion | The lowest MOBI version a reader must support to open the book. |
0x6C | 4 | UInt32 | firstImageRecord | The record of the first image, or more generally the first resource; images follow one per record. EXTH cover and thumbnail offsets count from here. |
0x70 | 4 | UInt32 | huffmanRecordOffset | The record of the HUFF record when compression is 17480. |
0x74 | 4 | UInt32 | huffmanRecordCount | How many records the Huffman data takes: one HUFF and the CDIC records after it. |
0x78 | 4 | UInt32 | huffmanTableOffset | Unknown use; usually 0. |
0x7C | 4 | UInt32 | huffmanTableLength | Unknown use; usually 0. |
0x80 | 4 | UInt32 | exthFlags | Bit 6 (0x40) set: an EXTH block follows the MOBI header. Other bits are not understood; calibre writes 0x50. |
0x84 | 32 | bytes | unknown | 32 bytes of unknown use, usually 0. |
0xA4 | 4 | UInt32 | unknown | Usually 0xFFFFFFFF. |
0xA8 | 4 | UInt32 | drmOffset | Where the DRM key information starts in record 0, or 0xFFFFFFFF with no DRM. |
0xAC | 4 | UInt32 | drmCount | The number of DRM key entries, or 0xFFFFFFFF with no DRM. |
0xB0 | 4 | UInt32 | drmSize | The size of the DRM information in bytes. |
0xB4 | 4 | UInt32 | drmFlags | Flags for the DRM; their meaning is not public. |
0xB8 | 8 | bytes | unknown | Usually 0. |
0xC0 | 2 | UInt16 | firstContentRecord | The first text record: normally 1. |
0xC2 | 2 | UInt16 | lastContentRecord | The last content record: the last image, or the last text record when there are no images. Huffman and DRM records are counted as content. |
0xC4 | 4 | UInt32 | unknown | Usually 1. |
0xC8 | 4 | UInt32 | fcisRecord | The record of the FCIS record. |
0xCC | 4 | UInt32 | fcisCount | Probably the number of FCIS records; always 1. |
0xD0 | 4 | UInt32 | flisRecord | The record of the FLIS record. |
0xD4 | 4 | UInt32 | flisCount | Probably the number of FLIS records; always 1. |
0xD8 | 8 | bytes | unknown | Usually 0. |
0xE0 | 4 | UInt32 | srcsRecord | The first SRCS record, holding the zipped source files, or 0xFFFFFFFF. |
0xE4 | 4 | UInt32 | srcsCount | The number of SRCS records, or 0. |
0xE8 | 4 | UInt32 | unknown | Usually 0xFFFFFFFF. |
0xEC | 4 | UInt32 | unknown | Usually 0xFFFFFFFF. |
0xF0 | 2 | UInt16 | unknown | Usually 0; the two bytes before the flags. |
0xF2 | 2 | UInt16 | extraDataFlags | What is appended to the end of each text record: see extra data flags. Only valid when headerLength is at least 0xE4; older files have no trailing data. |
0xF4 | 4 | UInt32 | ncxIndex | The record of the first INDX record of the table of contents (from the book’s NCX), or 0xFFFFFFFF. |
0xF8 | 4 | UInt32 | unknown | 0xFFFFFFFF in version 6 files. (Version 8 uses it for the fragment index: see AZW3.) |
0xFC | 4 | UInt32 | unknown | 0xFFFFFFFF in version 6 files. |
0x100 | 4 | UInt32 | datpRecord | The record of a DATP record, of unknown purpose, or 0xFFFFFFFF. |
0x104 | 4 | UInt32 | unknown | 0xFFFFFFFF in version 6 files. |
0x108–0x117 | 4 × 4 | UInt32 | unknown | Present when the header is 264 bytes long; 0xFFFFFFFF, except the last, which is often 0. |
MOBI type
| Value | Meaning |
|---|---|
| 2 | Mobipocket book. Most books. |
| 3 | PalmDOC book. |
| 4 | Audio. |
| 232 | Mobipocket book generated by KindleGen 1.2. |
| 248 | KF8 book generated by KindleGen 2. |
| 257 | News. |
| 258 | News feed. |
| 259 | News magazine. |
| 513 | PICS: pictures. |
| 514 | WORD: a Word document. |
| 515 | XLS: a spreadsheet. |
| 516 | PPT: a presentation. |
| 517 | TEXT: plain text. |
| 518 | HTML. |
The 5xx values come from Mobipocket’s tools for converting office documents; they are rare in practice. Amazon’s own document type, which a Kindle uses to file a book as a book, a sample or a personal document, is EXTH 501, not this field.
Versions
The fileVersion and minVersion fields hold the Mobipocket
format version. What changed between versions is not documented; what can be seen in
files is:
| Version | What is seen |
|---|---|
| 1 to 4 | Early Mobipocket books, with shorter MOBI headers and no trailing data on text records. |
| 5 | Mobipocket Creator books with a 0xE4 or 0xE8 header; trailing data may be present. |
| 6 | Kindle books from Mobipocket Creator and KindleGen 1, and the old part of joint KindleGen 2 files. Header usually 0xE8 bytes, with EXTH, indexes and trailing entries. |
| 8 | KF8: the new part of a KindleGen 2 file, or a stand-alone AZW3. |
A reader should use headerLength to decide which fields exist, not the
version.
Locale
The language fields use Windows language identifiers. The low byte is the language and the next byte the sub-language (country or dialect); the upper two bytes are 0.
| Value | Hex | Language |
|---|---|---|
| 1033 | 0x0409 | English (United States) |
| 2057 | 0x0809 | English (United Kingdom) |
| 1031 | 0x0407 | German (Germany) |
| 1036 | 0x040C | French (France) |
| 1034 | 0x040A | Spanish (traditional sort) |
| 1040 | 0x0410 | Italian (Italy) |
| 1041 | 0x0411 | Japanese |
| 2052 | 0x0804 | Chinese (simplified, PRC) |
| 9 | 0x0009 | English, with no country |
Any Windows language identifier may appear; the full list is Microsoft’s [MS-LCID]. Newer books also carry an IETF language tag in EXTH 524, which readers prefer.
Extra data flags
Each set bit means one more entry is appended to the end of every text record, after the compressed text. A reader must remove them before decompressing: see trailing entries.
| Bit | Value | Entry |
|---|---|---|
| 0 | 0x0001 | Multibyte overlap: the bytes of a multibyte character that runs over the end of the record. |
| 1 | 0x0002 | Indexing data (TBS, trailing byte sequence) linking the record to entries of the NCX index. |
| 2 | 0x0004 | Uncrossable breaks; contents not documented. |
| 3–15 | Not identified; each is an entry of the same <data><size> form and can be skipped. |
Full title and padding
After the EXTH block (or the MOBI header when there is none), some files have bytes of
unknown use. The full title sits at fullNameOffset, encoded in
textEncoding, followed by two zero bytes and then zeros up to a multiple of
4 bytes. Record 0 often goes on with zeros: calibre, for example, adds 8 KB of them, so
that DRM information or longer metadata can be added later without moving the other
records. The DRM block, when there
is one, also lies in this area, at drmOffset.
Example
The start of record 0 of a KindleGen book: PalmDOC compression, 245,302 bytes of text in 60 records, no DRM, and a MOBI header of 232 bytes for a version 6 Mobipocket book in UTF-8.
00000000 00 02 00 00 00 03 BE 36 00 3C 10 00 00 00 00 00 .......6.<......
00000010 4D 4F 42 49 00 00 00 E8 00 00 00 02 00 00 FD E9 MOBI............
00000020 6A 3B 91 0C 00 00 00 06 FF FF FF FF FF FF FF FF j;..............
| Bytes | Field | Value |
|---|---|---|
00 02 | compression | PalmDOC |
00 03 BE 36 | textLength | 245,302 |
00 3C, 10 00 | textRecordCount, textRecordSize | 60 records of up to 4,096 bytes |
00 00 | encryption | none |
4D 4F 42 49 | identifier | MOBI |
00 00 00 E8 | headerLength | 232, so EXTH starts at 16 + 232 = 248 |
00 00 00 02 | mobiType | Mobipocket book |
00 00 FD E9 | textEncoding | 65001, UTF-8 |
6A 3B 91 0C | uniqueID | random |
00 00 00 06 | fileVersion | 6 |
FF FF FF FF × 2 | orthographic and inflection indexes | none: not a dictionary |
60 records × 4,096 = 245,760, so 60 records are enough for 245,302 bytes. The example is built to show the layout.