Skip to content

Latest commit

 

History

History
242 lines (198 loc) · 10.8 KB

File metadata and controls

242 lines (198 loc) · 10.8 KB

Twenty-four quests, one board, and the index that names them

python tools/quest.py _work/hero-quest

Twenty-four files, 43,744 bytes, 1,648 to 2,086 each, mean 1,822.7. Diffing them against each other before parsing any one of them gives the layout almost for free, because the same structure repeats twenty-four times with different contents.

Layout

0x0000 .. 0x01EE   494 bytes   26 x 19 grid of region ids
0x01EE .. 0x0200    18 bytes   zero padding to 512
0x0200 .. 0x03EE   494 bytes   26 x 19 grid of wall and door flags
0x03EE .. 0x0400    18 bytes   zero padding to 1024
0x0400 .. 0x0460    96 bytes   fixed-size header: four 3-byte records, then zero
0x0460 .. ~0x0470             six 16-bit fields
~0x0470 ..                    ten-byte placement records, then three-byte ones
then                          the quest title and briefing, as text

Two 494-byte layers, each padded up to the next 512-byte boundary. 26 × 19 = 494 exactly, and 26 × 19 is the HeroQuest board.

The board, reconstructed from all twenty-four files

Every byte of the first layer is either 0 or one region id, and the id at a given square is the same in every quest that uses it. A quest turns regions on and off; it does not move them. So taking the union across all twenty-four files reconstructs the whole board:

01 01 01 01 02 02 02 02 02 02 03 03 03 03 03 03 04 04 04 04 04 04 05 05 05 05
01 15 15 15 15 16 16 16 16 17 17 17 03 03 18 18 18 19 19 19 19 1A 1A 1A 1A 05
01 15 15 15 15 16 16 16 16 17 17 17 03 03 18 18 18 19 19 19 19 1A 1A 1A 1A 05
01 15 15 15 15 16 16 16 16 17 17 17 03 03 18 18 18 19 19 19 19 1A 1A 1A 1A 05
01 1B 1B 1B 1B 1C 1C 1C 1C 17 17 17 03 03 18 18 18 19 19 19 19 1A 1A 1A 1A 05
06 1B 1B 1B 1B 1C 1C 1C 1C 17 17 17 08 08 18 18 18 1D 1D 1D 1D 1E 1E 1E 1E 0A
06 1B 1B 1B 1B 1C 1C 1C 1C 07 08 08 08 08 08 08 09 1D 1D 1D 1D 1E 1E 1E 1E 0A
06 1B 1B 1B 1B 1C 1C 1C 1C 07 1F 1F 1F 1F 1F 1F 09 1D 1D 1D 1D 1E 1E 1E 1E 0A
06 1B 1B 1B 1B 1C 1C 1C 1C 07 1F 1F 1F 1F 1F 1F 09 1D 1D 1D 1D 1E 1E 1E 1E 0A
06 06 06 06 07 07 07 07 07 07 1F 1F 1F 1F 1F 1F 09 09 09 09 09 09 0A 0A 0A 0A
0B 20 20 20 20 21 21 22 22 0C 1F 1F 1F 1F 1F 1F 0E 23 23 23 23 24 24 24 24 0F
0B 20 20 20 20 21 21 22 22 0C 1F 1F 1F 1F 1F 1F 0E 23 23 23 23 24 24 24 24 0F
0B 20 20 20 20 21 21 22 22 0C 0D 0D 0D 0D 0D 0D 0E 23 23 23 23 24 24 24 24 0F
0B 20 20 20 20 26 26 26 26 27 27 27 0D 0D 28 28 28 28 23 23 23 24 24 24 24 0F
0B 25 25 25 25 26 26 26 26 27 27 27 0D 0D 28 28 28 28 29 29 29 2A 2A 2A 2A 0F
10 25 25 25 25 26 26 26 26 27 27 27 12 12 28 28 28 28 29 29 29 2A 2A 2A 2A 14
10 25 25 25 25 26 26 26 26 27 27 27 12 12 28 28 28 28 29 29 29 2A 2A 2A 2A 14
10 25 25 25 25 26 26 26 26 27 27 27 12 12 28 28 28 28 29 29 29 2A 2A 2A 2A 14
10 10 10 10 11 11 11 11 11 11 12 12 12 12 12 12 13 13 13 13 13 13 14 14 14 14

Forty-two regions, and zero unassigned squares. Every one of the 494 cells belongs to exactly one region. Ids 0x010x14 (1–20) form the corridor: the outer ring plus the two cross passages that divide the board. Ids 0x150x2A (21–42) are the twenty-two interior rectangles, which is the room count.

There is exactly one square in the whole board where the twenty-four files disagree: offset 483, column 15 of row 18, which is 0x12 in twenty-three quests and 0x13 in one. It is on the bottom corridor row, on the boundary between two corridor segments, and it is the sort of one-cell difference a hand edit leaves behind.

Per quest, the number of regions in play ranges from 10 (quest 10, Castle of Mystery) to 33 (quests 19 and 22), and the number of live squares from 169 to 366 of 494. Quest 1, The Maze, uses 32 regions and 343 squares — the tutorial opens most of the board.

The wall layer

The second 494-byte grid is a per-square flag byte. Its census across quest 1:

0x00  230     0x40   92     0x80  114     0xC0   19
0x10    7     0x41    9     0x81   11     0x20    5
0x18    2     0x60    1     0x90    1     0x21    1
0x29    1     0x68    1

Two bits dominate and their geography settles what they mean. Row 0 is a solid run of 0x80; column 0 is a solid run of 0x40; the right-hand column carries 0x41. So 0x80 is a wall on the square's north edge and 0x40 a wall on its west edge, which is the standard way to store a grid's walls without double-counting them. 0xC0 is a corner.

0x20 occurs only inside runs of 0x80, and 0x10 only inside runs of 0x40 or standing alone in the interior; reading them as doors in the north and west wall respectively fits every occurrence in quest 1 and is the obvious complement to the two wall bits. 0x08 and 0x01 also occur, in combinations like 0x68, 0x29, 0x81, and are not identified here — secret doors and the stairway are the two features the board has that are not yet accounted for. See 12-open-questions.md.

The placement records

At 0x0402, four three-byte records. In quest 1 they read

(00,00,1D)  (19,00,32)  (00,12,35)  (19,12,4A)

0x19 = 25 and 0x12 = 18, which are exactly the maximum column and row of a 26 × 19 board: the four corners. In quest 10 the same four records read (02,0F,1D) (03,10,2F) (03,11,35) (01,0F,47) — not corners, and the third byte changes with them. So the layout is (column, row, id) and these four are a per-quest fixture; the recurring third bytes 0x1D and 0x35 in both quests suggest an id rather than a coordinate.

At 0x0462, six 16-bit values, two of which (0x000C and 0x000F) are the same in every quest examined and look like counts.

From about 0x0470 the file is a run of ten-byte records, each beginning 0x81:

81 07 02 00 00 00 01 12 01 01
81 16 06 00 00 00 01 12 01 01
81 0A 03 00 00 00 01 10 05 05     (quest 10)
81 0D 08 00 00 00 01 11 00 00     (quest 10)
81 0B 0A 00 00 00 01 13 03 03     (quest 10)

Bytes 1 and 2 are a column and row in range; byte 7 takes the values 0x100x13; bytes 8 and 9 covary with byte 7 (0x1201 01, 0x1303 03, 0x1004 04/05 05/06 06, 0x1100 00). A type with a size attached, placed on the board — furniture.

Then a run of three-byte records, (column, row, id). Quest 1:

0C 01 30  0D 01 30  0B 06 30  00 08 30  11 09 30  00 0A 30
09 0A 30  0E 0C 30  0C 11 30  0D 11 30  0A 12 30
0C 08 38  0C 09 39  0D 08 3A  0D 09 3B

Eleven of one id, then four consecutive ids 0x38 0x39 0x3A 0x3B on a 2 × 2 block of squares. Quest 10 does the same thing with a different monster id and a different 2 × 2 block:

01 10 38  01 11 39  02 10 3A  02 11 3B
0E 04 5D  09 02 37  10 02 37  16 04 37  04 06 37  0F 09 37 ...

Four consecutive ids on four adjacent squares, in every quest, is the party: Barbarian, Dwarf, Elf and Wizard, standing in a two-by-two block at the starting position. Everything else in that list is monsters — 0x30 in quest 1, 0x37 in quest 10, with 0x5D appearing once as the quest objective.

The text, and the contradiction it creates

The three-byte records are followed immediately by the same 0x11 row col cursor escape the language files use, and then by plain uppercase English:

QUEST1   0x04E1  11 02 10  "THE MAZE"  11 04 05  "THIS WILL BE YOUR FINAL TEST..."
QUEST10  0x054F  11 03 0B  "CASTLE OF MYSTERY"  11 05 05  "LONG AGO A..."

Not compressed, not indexed, not localised. 8,500 printable characters across the twenty-four files, 227 to 547 per quest.

There is only one set of twenty-four quest files, so the briefings are English only, in every copy of this build, whatever language the player picks. That is the resolution of the contradiction the file listing raises, and it is discussed further in 06-language-files.md.

The twenty-four titles, as stored inside the quest files:

title file briefing chars
1 THE MAZE 1,656 360
2 THE RESCUE OF SIR RAGNAR 1,792 393
3 LAIR OF THE ORC WARLORD 1,651 268
4 PRINCE MAGNUS' GOLD 1,660 296
5 MELAR'S MAZE 1,878 465
6 LEGACY OF THE ORC WARLORD 1,911 438
7 THE STONE HUNTER 1,688 300
8 THE FIRE MAGE 1,788 388
9 RACE AGAINST TIME 1,855 412
10 CASTLE OF MYSTERY 1,743 343
11 BASTION OF CHAOS 2,086 503
12 BARAK TOR, HOME OF THE WITCHLORD 1,946 547
13 QUEST FOR THE SPIRIT BLADE 1,975 480
14 RETURN TO BARAK TOR 1,773 357
15 THE GATE OF DOOM 1,926 394
16 THE COLD HALLS 1,648 227
17 THE SILENT PASSAGES 1,845 280
18 HALLS OF VISION 1,784 293
19 THE GATE OF BELLTHOR 1,898 326
20 HALLS OF THE DEAD 1,805 236
21 THE FORGOTTEN LEGION 1,831 329
22 THE FORBIDDEN CITY 1,885 369
23 THE LAST GATE 1,896 255
24 THE COURT OF THE WITCH LORD 1,824 241

EQUESTS.BIN is the quest index, and it is multilingual

3,421 bytes, and it is not a twenty-fifth quest. It is the quest-selection screen: a twenty-byte header, ten 16-bit offsets, and then the twenty-four titles with 0x11 row col positioning — five times over, once per language, plus the two navigation strings for each.

python tools/strdump.py _work/hero-quest/EQUESTS.BIN --min 4
0x001E  THE MAZE                       0x028E  LE LABYRINTHE
0x0031  THE RESCUE OF SIR RAGNAR       0x029F  SAUVETAGE DE SIR RAGNARD
...                                    0x0391  BROUETTE DU SORCIER
0x025E  PREVIOUS PAGE                  0x03DB  PAGE SUIVANTE
0x026F  EXIT TO MAIN MENU              0x03EC  ALLEZ AU MENU PRINCIPAL

0x052D  DAS LABYRINTH                  0x05D4  DER STEINJaGER
0x053E  DIE RETTUNG VON SIR RAGNAR     0x0640  HuGELGRAB DES HEXENHERRN
0x055C  LAGER DES KRIEGSHERRN DER ORCS 0x06A2  NaCHSTE SEITE
0x059B  MELARS LABYRINTH               0x06B3  AUSGANG ZUM HAUPTMENu

PREVIOUS PAGE / PAGE SUIVANTE / NaCHSTE SEITE and EXIT TO MAIN MENU tell you the selection screen is paged; the ten 16-bit offsets in the header are the page starts.

The German entries are the cleanest confirmation of the accent mapping in 06-language-files.md, because this file is not compressed: STEINJaGER, HuGELGRAB, NaCHSTE, RuCKKEHR, HAUPTMENua = ä, u = ü, exactly as the font predicts.

And there is a discrepancy worth keeping. Quest 12's title inside QUEST12.BIN is BARAK TOR, HOME OF THE WITCHLORD; the same quest in the English section of EQUESTS.BIN is BARROW OF THE WITCHLORD. The German index calls it HuGELGRAB DES HEXENHERRNburial mound of the witch lord — which follows BARROW, and the French calls it BROUETTE DU SORCIER, which is wheelbarrow of the sorcerer: somebody translated the wrong sense of "barrow". The mistranslation is only possible from the EQUESTS.BIN wording, so the index was localised from the index and not from the quest files, and the two English titles were never reconciled.