The ENG text format
All of a game's text lives in .eng files: one for strings, one ending _mm.eng for the pop-up messages, which carries dialog geometry alongside the words. Anyone translating one of these games, or reading its text out, works here.
Used by Caesar III Pharaoh Zeus Emperor
Two related files, both in three parts: a short header, a fixed-size index, and a data section of null-separated strings.
The wiki documents Caesar III as its worked example — its files identify themselves with a descriptor string like "C3 textfile." — and the later titles use the same structure with their own descriptor and their own quirks.
Text file header
28 bytes.
| Offset | Size | Type | Field | Description |
|---|---|---|---|---|
| 0 | 16 | char[] | descriptor | Identifies the file and the game, e.g. "C3 textfile." |
| 16 | 4 | int32 | num_groups | Highest group ID in use, plus one. |
| 20 | 4 | int32 | num_strings | Total strings in the file. |
| 24 | 4 | int32 | num_words | Word count. Not reliable — do not use it to size a buffer. |
Text file index
1,000 entries of 8 bytes each, following the header.
| Offset | Size | Type | Field | Description |
|---|---|---|---|---|
| 0 | 4 | int32 | offset | Where this group's strings begin in the data section. |
| 4 | 4 | int32 | in_use | In the earlier games a flag, 0 or 1. In the later ones, the number of strings in the group. |
Text data and encoding
Strings run consecutively, separated by null bytes, and a string must contain at least one printable character — a byte of 32 or above — to count.
There is no encoding declared anywhere in the file; it follows from the language of the release. Western European editions use Windows-1252, Polish uses Windows-1250, Russian Windows-1251, and Korean Windows-950. Traditional Chinese uses an encoding of its own.
Message file header
24 bytes. Message files are the ones whose names end in _mm.eng.
| Offset | Size | Type | Field | Description |
|---|---|---|---|---|
| 0 | 16 | char[] | descriptor | Identifies the file, e.g. "C3 MMfile". |
| 16 | 4 | int32 | num_entries | Index entries present — typically 1,000. |
| 20 | 4 | int32 | num_used | Highest index in use, plus one. |
Message index entry
80 bytes each. This is where the format stops being a string table: each entry lays out the dialog the message appears in.
| Offset | Size | Type | Field | Description |
|---|---|---|---|---|
| 0 | 2 | int16 | type | Message type. |
| 2 | 2 | int16 | message_type | Message subtype. |
| 6 | 2 | int16 | x | Dialog X position. |
| 8 | 2 | int16 | y | Dialog Y position. |
| 10 | 2 | int16 | width_blocks | Dialog width, in 16-pixel blocks. |
| 12 | 2 | int16 | height_blocks | Dialog height, in 16-pixel blocks. |
| 14 | 2 | int16 | image1_id | Primary image ID. |
| 16 | 2 | int16 | image1_x | Primary image X. |
| 18 | 2 | int16 | image1_y | Primary image Y. |
| 20 | 2 | int16 | image2_id | Secondary image ID. |
| 22 | 2 | int16 | image2_x | Secondary image X. |
| 24 | 2 | int16 | image2_y | Secondary image Y. |
| 26 | 2 | int16 | title_x | Title X. |
| 28 | 2 | int16 | title_y | Title Y. |
| 30 | 2 | int16 | subtitle_x | Subtitle X. |
| 32 | 2 | int16 | subtitle_y | Subtitle Y. |
| 38 | 2 | int16 | video_x | Video X. |
| 40 | 2 | int16 | video_y | Video Y. |
| 56 | 4 | int32 | urgent | 0 for a normal message, 1 for an urgent one. |
| 60 | 4 | int32 | video_offset | Offset of the video filename string. |
| 64 | 4 | int32 | unknown_offset | Offset of a string whose purpose is not established. |
| 68 | 4 | int32 | title_offset | Offset of the title string. |
| 72 | 4 | int32 | subtitle_offset | Offset of the subtitle string. |
| 76 | 4 | int32 | content_offset | Offset of the body text, which may contain formatting. |