Difference between revisions of "PC-98 format"
(Added tiles) |
(Added list of files) |
||
Line 140: | Line 140: | ||
TODO: How to make an empty disk. How to add a file. | TODO: How to make an empty disk. How to add a file. | ||
+ | |||
+ | == List of files == | ||
+ | |||
+ | In the order they appear in the [[#Directory]]: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | ! file !! format !! contents | ||
+ | |- | ||
+ | | MUSIC.SYS || program || music player | ||
+ | |- | ||
+ | | MAIN.PRG || program || gameplay, cutscenes | ||
+ | |- | ||
+ | | SUBPROG.PRG || program || menus, intro, ending | ||
+ | |- | ||
+ | | BJLOGO.PRG || program || Broderbund logo animation | ||
+ | |- | ||
+ | | PALET.DAT || [[#Palette|palette]] || the palette used by the game | ||
+ | |- | ||
+ | | FONT.DAT || font || menu font | ||
+ | |- | ||
+ | | FIRE.DAT || sprites || torch flame | ||
+ | |- | ||
+ | | MUSIC1.MUS || music || | ||
+ | |- | ||
+ | | MUSIC2.MUS || music || | ||
+ | |- | ||
+ | | MUSIC3.MUS || music || | ||
+ | |- | ||
+ | | MUSIC4.MUS || music || | ||
+ | |- | ||
+ | | FIGHT.MUS || music || | ||
+ | |- | ||
+ | | FIGHTF.MUS || music || | ||
+ | |- | ||
+ | | FIGHTS.MUS || music || | ||
+ | |- | ||
+ | | FIGHTK.MUS || music || | ||
+ | |- | ||
+ | | VICT1.MUS || music || | ||
+ | |- | ||
+ | | APPEAR.MUS || music || | ||
+ | |- | ||
+ | | APPEAR2.MUS || music || | ||
+ | |- | ||
+ | | PICKUP.MUS || music || | ||
+ | |- | ||
+ | | POWUP.MUS || music || | ||
+ | |- | ||
+ | | KUSURI.MUS || music || | ||
+ | |- | ||
+ | | CLEAR.MUS || music || | ||
+ | |- | ||
+ | | EXIT.MUS || music || | ||
+ | |- | ||
+ | | DEAD.MUS || music || | ||
+ | |- | ||
+ | | DEAD2.MUS || music || | ||
+ | |- | ||
+ | | OPEN.MUS || music || | ||
+ | |- | ||
+ | | ROOM1.MUS || music || | ||
+ | |- | ||
+ | | ROOM2.MUS || music || | ||
+ | |- | ||
+ | | END1.MUS || music || | ||
+ | |- | ||
+ | | END2.MUS || music || | ||
+ | |- | ||
+ | | END3.MUS || music || | ||
+ | |- | ||
+ | | TIMEOUT.MUS || music || | ||
+ | |- | ||
+ | | MENU.MUS || music || | ||
+ | |- | ||
+ | | SOUND.EFC || sound effects || sound effects used when synthesizer hardware is available (in Neko Project 2: Device → Sounds → PC-9801-86) | ||
+ | |- | ||
+ | | BEEP.EFC || sound effects || sound effects used when only a beeper is available (in Neko Project 2: Device → Sounds → Disable boards) | ||
+ | |- | ||
+ | | JAFFER1.DAT || background || Intro: Jaffar's face | ||
+ | |- | ||
+ | | JAFFER2.DAT || background || Intro: Jaffar's hands and top half of crystal ball | ||
+ | |- | ||
+ | | JAFFER3.DAT || background || Intro: bottom half of crystal ball | ||
+ | |- | ||
+ | | OPEN1.DAT || background || Intro: In the crystal ball: the prince and the princess | ||
+ | |- | ||
+ | | OPEN2.DAT || background || Intro: In the crystal ball: the prince and the princess caught | ||
+ | |- | ||
+ | | OPEN3.DAT || background || Intro: In the crystal ball: the prince in dungeon | ||
+ | |- | ||
+ | | OPEN11.DAT || background || Intro: Waving water for OPEN1.DAT | ||
+ | |- | ||
+ | | OPEN12.DAT || background || Intro: Waving water for OPEN1.DAT | ||
+ | |- | ||
+ | | OPEN32.DAT || background || Intro: Torch flame for OPEN3.DAT | ||
+ | |- | ||
+ | | OPEN33.DAT || background || Intro: Torch flame for OPEN3.DAT | ||
+ | |- | ||
+ | | JAF_S1.DAT || background || Intro: Jaffar grinning | ||
+ | |- | ||
+ | | JAF_S2.DAT || background || Intro: Jaffar grinning | ||
+ | |- | ||
+ | | JAF_E1.DAT || background || Intro: Jaffar's eyes glowing | ||
+ | |- | ||
+ | | JAF_E2.DAT || background || Intro: Jaffar's eyes glowing | ||
+ | |- | ||
+ | | JAF_E3.DAT || background || Intro: Jaffar's eyes glowing | ||
+ | |- | ||
+ | | JAF_E4.DAT || background || Intro: Jaffar's eyes glowing | ||
+ | |- | ||
+ | | TITLE.DAT || background || Title screen: Persia at night | ||
+ | |- | ||
+ | | TITLE2.DAT || background || Title screen: game title | ||
+ | |- | ||
+ | | TITLE3.DAT || background || Title screen: copyrights | ||
+ | |- | ||
+ | | TITLE4.DAT || background || Title screen: a game by Jordan Mechner | ||
+ | |- | ||
+ | | PROOM.DAT || background || Princess's room | ||
+ | |- | ||
+ | | VCLIP.DAT || raw 1bpp image || clipping masks? | ||
+ | |- | ||
+ | | MOYOU.DAT || background || menu background | ||
+ | |- | ||
+ | | TRAP.DAT || ([[#Compression|compressed]]) sprites || traps, potions, doors, everything not part of the background / level objects except buttons and loose floors | ||
+ | |- | ||
+ | | PLATE1.DAT || ([[#Compression|compressed]]) sprites || buttons, loose floors for LEV01.CHR | ||
+ | |- | ||
+ | | PLATE4.DAT || ([[#Compression|compressed]]) sprites || buttons, loose floors for LEV04.CHR | ||
+ | |- | ||
+ | | PLATE7.DAT || ([[#Compression|compressed]]) sprites || buttons, loose floors for LEV07.CHR | ||
+ | |- | ||
+ | | PLATEA.DAT || ([[#Compression|compressed]]) sprites || buttons, loose floors for LEV10.CHR | ||
+ | |- | ||
+ | | PLATEC.DAT || ([[#Compression|compressed]]) sprites || buttons, loose floors for LEV12.CHR | ||
+ | |- | ||
+ | | PLATEE.DAT || ([[#Compression|compressed]]) sprites || button for LEV14.CHR | ||
+ | |- | ||
+ | | CHTAB1.DAT || ([[#Compression|compressed]]) sprites || prince | ||
+ | |- | ||
+ | | CHTAB2.DAT || ([[#Compression|compressed]]) sprites || prince and mouse | ||
+ | |- | ||
+ | | CHTAB3.DAT || ([[#Compression|compressed]]) sprites || prince and sword | ||
+ | |- | ||
+ | | CHTAB4F.DAT || ([[#Compression|compressed]]) sprites || fat guard on level 6 | ||
+ | |- | ||
+ | | CHTAB4G.DAT || ([[#Compression|compressed]]) sprites || regular guard | ||
+ | |- | ||
+ | | CHTAB4K.DAT || ([[#Compression|compressed]]) sprites || shadow on level 12 | ||
+ | |- | ||
+ | | CHTAB4S.DAT || ([[#Compression|compressed]]) sprites || skeleton on level 3 | ||
+ | |- | ||
+ | | CHTAB4V.DAT || ([[#Compression|compressed]]) sprites || the boss on level 13 | ||
+ | |- | ||
+ | | CHTAB4X.DAT || ([[#Compression|compressed]]) sprites || Jaffar on level 14, 15 | ||
+ | |- | ||
+ | | CHTAB5.DAT || ([[#Compression|compressed]]) sprites || prince | ||
+ | |- | ||
+ | | CHTAB6.DAT || ([[#Compression|compressed]]) sprites || princess, Jaffar in the intro, hourglass, princess room torch flame | ||
+ | |- | ||
+ | | CHTAB7.DAT || ([[#Compression|compressed]]) sprites || Jaffar in the intro | ||
+ | |- | ||
+ | | LEV01.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for levels 0, 1, 2, 3 | ||
+ | |- | ||
+ | | LEV04.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for levels 4, 5, 6 | ||
+ | |- | ||
+ | | LEV07.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for levels 7, 8, 9 | ||
+ | |- | ||
+ | | LEV10.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for levels 10, 11 | ||
+ | |- | ||
+ | | LEV12.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for levels 12, 13 | ||
+ | |- | ||
+ | | LEV14.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for level 14 | ||
+ | |- | ||
+ | | LEV15.CHR || ([[#Compression|compressed]]) [[#Tiles|tiles]] || graphics for level 15 | ||
+ | |- | ||
+ | | LEV00.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 0 (demo) | ||
+ | |- | ||
+ | | LEV01.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 1 | ||
+ | |- | ||
+ | | LEV02.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 2 | ||
+ | |- | ||
+ | | LEV03.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 3 | ||
+ | |- | ||
+ | | LEV04.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 4 | ||
+ | |- | ||
+ | | LEV05.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 5 | ||
+ | |- | ||
+ | | LEV06.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 6 | ||
+ | |- | ||
+ | | LEV07.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 7 | ||
+ | |- | ||
+ | | LEV08.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 8 | ||
+ | |- | ||
+ | | LEV09.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 9 | ||
+ | |- | ||
+ | | LEV10.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 10 | ||
+ | |- | ||
+ | | LEV11.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 11 | ||
+ | |- | ||
+ | | LEV12.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 12 (12a) (up to the Shadow fight) | ||
+ | |- | ||
+ | | LEV13.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 13 (12b) (a boss, but not Jaffar yet) | ||
+ | |- | ||
+ | | LEV14.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 14 (12c) (the halls leading to the princess) | ||
+ | |- | ||
+ | | LEV15.MAP || ([[#Compression|compressed]]) [[#Levels|level]] || level 15 (12d) (the fight with Jaffar) | ||
+ | |- | ||
+ | | DEMOPLAY.KEY || automatic moves || Automatic moves of the prince for the demo level. | ||
+ | |- | ||
+ | | EFONT.DAT || font || ending credits font | ||
+ | |- | ||
+ | | ENDING1.DAT || background || Ending: the prince before the sultan | ||
+ | |- | ||
+ | | ENDING2.DAT || background || Ending: the prince and the princess wave to the people | ||
+ | |- | ||
+ | | ENDING3.DAT || background || Ending: the prince and the princess together | ||
+ | |- | ||
+ | | ENDING4.DAT || background || Ending: palace garden | ||
+ | |- | ||
+ | | ENDING5.DAT || background || Ending: "The End" text | ||
+ | |- | ||
+ | | ENDING6.DAT || backgrounds || Ending: waving flags | ||
+ | |} | ||
== Compression == | == Compression == | ||
Line 456: | Line 680: | ||
* Hacks for easier playtesting: skipping the Broderbund logo, starting any level. | * Hacks for easier playtesting: skipping the Broderbund logo, starting any level. | ||
* VCLIP.DAT | * VCLIP.DAT | ||
+ | * the hit point images in MAIN.PRG | ||
+ | * the Broderbund logo in BJLOGO.PRG | ||
== External links == | == External links == | ||
* [https://forum.princed.org/viewtopic.php?p=34174#p34174 Documentation and tools by zarazala, and the game itself.] | * [https://forum.princed.org/viewtopic.php?p=34174#p34174 Documentation and tools by zarazala, and the game itself.] |
Revision as of 08:02, 28 December 2021
This document describes the format of data in Prince of Persia 1 for the PC-98.
Contents
General
Byte order: Numbers larger than a byte use little-endian (Intel) byte order, unless noted otherwise.
Number systems: Hexadecimal numbers are prefixed with 0x.
Numbering of bits: The least significant bit of a byte is bit 0, the most significant bit of a byte is bit 7.
Tools: Zarazala has made some tools for working with the files described in this document. You can get them from #External links.
Disk images
Disk images can be either headerless, or have a header.
Online, this game is most commonly found in *.FDI files, these have a header of 0x1000 bytes.
- Tools: datacut.exe to extract files from a (headerless!) disk image, makeimg.exe to make a disk image from files.
How recognize and convert headered images
A headerless game disk starts with the bytes EB 0A. If they appear at offset 0x1000 then you have a headered disk image.
To convert a headered disk image to headerless, go to offset 0x1000 and delete everything before it. You might want to save the new file under a different name.
FDI header
The FDI header contains the following information:
Offset in bytes | Size | Value on PoP disks | Meaning |
---|---|---|---|
0x00 | 4 bytes | 0 | (unknown) |
0x04 | 4 bytes | 0x90 = 144 | (unknown) |
0x08 | 4 bytes | 0x1000 = 4096 | Start offset of the raw disk image (i.e. the size of the FDI header). |
0x0C | 4 bytes | 0x134000 = 1261568 | Size of the raw disk image in bytes. |
0x10 | 4 bytes | 0x400 = 1024 | Size of a sector (or cluster?) in bytes. |
0x14 | 4 bytes | 8 | Number of sectors per track? |
0x18 | 4 bytes | 2 | Number of sides? |
0x1C | 4 bytes | 0x4D = 77 | Number of tracks per side? |
If you multiply the last four numbers, you get the size of the raw disk image: 0x400 * 8 * 2 * 0x4D = 0x134000.
The rest of the header is filled with zeroes.
File system
A disk consists of 0x4D0 sectors, numbered from 0 to 0x4CF. Each sector is 0x400 = 1024 bytes.
The game disk (disk A) contains the following parts:
Offset in bytes | Occupied sectors | Contents |
---|---|---|
0x0000 - 0x03FF | 0 | #Boot sector |
0x0400 - 0x0FFF | 1 - 3 | #File allocation table |
0x1000 - 0x1FFF | 4 - 7 | #Directory |
0x2000 - 0x133FFF | 8 - 0x4CF | Data area |
The offsets in the table assume you have a headerless disk image.
Boot sector
The boot sector contains the code for starting the game.
From offset 2 is the disk label "GAME DISK" on disk A, and "USER DISK" on disk B.
Directory
This area contains 0x100 = 256 entries, of 0x10 = 16 bytes each.
Each entry describes a file in the following format:
Offset in bytes | Size | Meaning |
---|---|---|
0 | 8 bytes | Base name. If it's shorter than 8 characters then it's padded with spaces (0x20). |
8 | 3 bytes | Extension. (The same padding is used here, although it does not occur in the original PoP disk image.) |
0x0B = 11 | 3 bytes | Unknown. Might be file attributes and/or modification date/time? |
0x0E = 14 | 2 bytes | The number of the first sector of this file. |
To get the full file name, concatenate the base name (with padding removed), a dot, and the extension.
Unused entries are filled with 0xFF bytes.
File allocation table
This area contains 2-byte entries for each sector on the disk.
There is space for 0x600 entries, but only the first 0x4D0 entries are used, since the disk has only that many sectors. The unused entries are filled with zeroes.
The possible values of each entry are:
- 0xFFFF : If the sector is before the data area (sectors 0-7).
- 0x0008 - 0x04CF : If this sector is part of a file but not the last sector.
- This whole sector is part of the file.
- The value is the number of the next sector of the file.
- 0xFC00 - 0xFFFF : If this is the last sector of a file.
- Subtract 0xFC00 from the value to get a size. The first size bytes of this sector are part of the file.
- If you get 0 from the subtraction, then the whole sector is part of the file. (This does not occur in the original PoP disk image, though.)
- The unused area of last sectors is filled with 0x00. (makeimg.exe fills it with 0xE5.)
- 0x0000 : If the sector is not used or does not exist (unused entries).
- Unused sectors are filled with 0xE5.
How to read a file
- Find the file in the #Directory.
- Read the number of the first sector into current_sector.
- Repeat until exit:
- current_sector should be a valid sector number pointing into the data area (between 0x0008 and 0x04CF). If it's not then it's an error.
- Read the #File allocation table entry corresponding to current_sector into next_sector. (offset = 0x400 + current_sector * 2, length = 2)
- Read the sector numbered current_sector into sector_data. (offset = current_sector * 0x400, length = 0x400)
- If next_sector < 0xFC00:
- Append sector_data to the output.
- Set current_sector to next_sector.
- Continue the loop.
- else:
- Set size to next_sector - 0xFC00.
- If size = 0 then set size to 0x400.
- Append the first size bytes of sector_data to the output.
- Exit the loop.
- Done.
TODO: How to make an empty disk. How to add a file.
List of files
In the order they appear in the #Directory:
file | format | contents |
---|---|---|
MUSIC.SYS | program | music player |
MAIN.PRG | program | gameplay, cutscenes |
SUBPROG.PRG | program | menus, intro, ending |
BJLOGO.PRG | program | Broderbund logo animation |
PALET.DAT | palette | the palette used by the game |
FONT.DAT | font | menu font |
FIRE.DAT | sprites | torch flame |
MUSIC1.MUS | music | |
MUSIC2.MUS | music | |
MUSIC3.MUS | music | |
MUSIC4.MUS | music | |
FIGHT.MUS | music | |
FIGHTF.MUS | music | |
FIGHTS.MUS | music | |
FIGHTK.MUS | music | |
VICT1.MUS | music | |
APPEAR.MUS | music | |
APPEAR2.MUS | music | |
PICKUP.MUS | music | |
POWUP.MUS | music | |
KUSURI.MUS | music | |
CLEAR.MUS | music | |
EXIT.MUS | music | |
DEAD.MUS | music | |
DEAD2.MUS | music | |
OPEN.MUS | music | |
ROOM1.MUS | music | |
ROOM2.MUS | music | |
END1.MUS | music | |
END2.MUS | music | |
END3.MUS | music | |
TIMEOUT.MUS | music | |
MENU.MUS | music | |
SOUND.EFC | sound effects | sound effects used when synthesizer hardware is available (in Neko Project 2: Device → Sounds → PC-9801-86) |
BEEP.EFC | sound effects | sound effects used when only a beeper is available (in Neko Project 2: Device → Sounds → Disable boards) |
JAFFER1.DAT | background | Intro: Jaffar's face |
JAFFER2.DAT | background | Intro: Jaffar's hands and top half of crystal ball |
JAFFER3.DAT | background | Intro: bottom half of crystal ball |
OPEN1.DAT | background | Intro: In the crystal ball: the prince and the princess |
OPEN2.DAT | background | Intro: In the crystal ball: the prince and the princess caught |
OPEN3.DAT | background | Intro: In the crystal ball: the prince in dungeon |
OPEN11.DAT | background | Intro: Waving water for OPEN1.DAT |
OPEN12.DAT | background | Intro: Waving water for OPEN1.DAT |
OPEN32.DAT | background | Intro: Torch flame for OPEN3.DAT |
OPEN33.DAT | background | Intro: Torch flame for OPEN3.DAT |
JAF_S1.DAT | background | Intro: Jaffar grinning |
JAF_S2.DAT | background | Intro: Jaffar grinning |
JAF_E1.DAT | background | Intro: Jaffar's eyes glowing |
JAF_E2.DAT | background | Intro: Jaffar's eyes glowing |
JAF_E3.DAT | background | Intro: Jaffar's eyes glowing |
JAF_E4.DAT | background | Intro: Jaffar's eyes glowing |
TITLE.DAT | background | Title screen: Persia at night |
TITLE2.DAT | background | Title screen: game title |
TITLE3.DAT | background | Title screen: copyrights |
TITLE4.DAT | background | Title screen: a game by Jordan Mechner |
PROOM.DAT | background | Princess's room |
VCLIP.DAT | raw 1bpp image | clipping masks? |
MOYOU.DAT | background | menu background |
TRAP.DAT | (compressed) sprites | traps, potions, doors, everything not part of the background / level objects except buttons and loose floors |
PLATE1.DAT | (compressed) sprites | buttons, loose floors for LEV01.CHR |
PLATE4.DAT | (compressed) sprites | buttons, loose floors for LEV04.CHR |
PLATE7.DAT | (compressed) sprites | buttons, loose floors for LEV07.CHR |
PLATEA.DAT | (compressed) sprites | buttons, loose floors for LEV10.CHR |
PLATEC.DAT | (compressed) sprites | buttons, loose floors for LEV12.CHR |
PLATEE.DAT | (compressed) sprites | button for LEV14.CHR |
CHTAB1.DAT | (compressed) sprites | prince |
CHTAB2.DAT | (compressed) sprites | prince and mouse |
CHTAB3.DAT | (compressed) sprites | prince and sword |
CHTAB4F.DAT | (compressed) sprites | fat guard on level 6 |
CHTAB4G.DAT | (compressed) sprites | regular guard |
CHTAB4K.DAT | (compressed) sprites | shadow on level 12 |
CHTAB4S.DAT | (compressed) sprites | skeleton on level 3 |
CHTAB4V.DAT | (compressed) sprites | the boss on level 13 |
CHTAB4X.DAT | (compressed) sprites | Jaffar on level 14, 15 |
CHTAB5.DAT | (compressed) sprites | prince |
CHTAB6.DAT | (compressed) sprites | princess, Jaffar in the intro, hourglass, princess room torch flame |
CHTAB7.DAT | (compressed) sprites | Jaffar in the intro |
LEV01.CHR | (compressed) tiles | graphics for levels 0, 1, 2, 3 |
LEV04.CHR | (compressed) tiles | graphics for levels 4, 5, 6 |
LEV07.CHR | (compressed) tiles | graphics for levels 7, 8, 9 |
LEV10.CHR | (compressed) tiles | graphics for levels 10, 11 |
LEV12.CHR | (compressed) tiles | graphics for levels 12, 13 |
LEV14.CHR | (compressed) tiles | graphics for level 14 |
LEV15.CHR | (compressed) tiles | graphics for level 15 |
LEV00.MAP | (compressed) level | level 0 (demo) |
LEV01.MAP | (compressed) level | level 1 |
LEV02.MAP | (compressed) level | level 2 |
LEV03.MAP | (compressed) level | level 3 |
LEV04.MAP | (compressed) level | level 4 |
LEV05.MAP | (compressed) level | level 5 |
LEV06.MAP | (compressed) level | level 6 |
LEV07.MAP | (compressed) level | level 7 |
LEV08.MAP | (compressed) level | level 8 |
LEV09.MAP | (compressed) level | level 9 |
LEV10.MAP | (compressed) level | level 10 |
LEV11.MAP | (compressed) level | level 11 |
LEV12.MAP | (compressed) level | level 12 (12a) (up to the Shadow fight) |
LEV13.MAP | (compressed) level | level 13 (12b) (a boss, but not Jaffar yet) |
LEV14.MAP | (compressed) level | level 14 (12c) (the halls leading to the princess) |
LEV15.MAP | (compressed) level | level 15 (12d) (the fight with Jaffar) |
DEMOPLAY.KEY | automatic moves | Automatic moves of the prince for the demo level. |
EFONT.DAT | font | ending credits font |
ENDING1.DAT | background | Ending: the prince before the sultan |
ENDING2.DAT | background | Ending: the prince and the princess wave to the people |
ENDING3.DAT | background | Ending: the prince and the princess together |
ENDING4.DAT | background | Ending: palace garden |
ENDING5.DAT | background | Ending: "The End" text |
ENDING6.DAT | backgrounds | Ending: waving flags |
Compression
- Tools: compress.exe
The following files are compressed: TRAP.DAT, PLATE*.DAT, CHTAB*.DAT, LEV*.CHR, LEV*.MAP
Compressed data is stored as a series of blocks. Each block starts with a head byte specifying what to do, followed by 0 to 4 argument bytes.
Each block adds 4 bytes to the output, except those with head bytes 0x11, 0x21, and 0x91. Those add multiples of 4 bytes.
In the arguments and the output column, each group of two letters represents a byte.
In the following table, the head bytes are ordered first by their second half, then by their first half.
head byte | arguments | output |
---|---|---|
0x00 | xx yy zz ww | xx yy zz ww (i.e. copy the next 4 bytes to the output) |
0x01 | - | Repeat the last 4 bytes of the output. |
0x11 | nn | Repeat the last 4 bytes of the output, 0xnn + 1 times. |
0x21 | nn mm | Repeat the last 4 bytes of the output, 0xmmnn + 1 times. |
0x81 | - | Repeat the penultimate 4 bytes of the output. |
0x91 | nn | Repeat the penultimate 4 bytes of the output, 0xnn + 1 times.
Phrased differently, start from the 8th last byte of the output, and copy (0xnn + 1) * 4 bytes. |
0x02 | xx | xx xx xx xx (i.e. write the next byte 4 times to the output) |
0x03 | xx yy | xx yy yy yy |
0x13 | xx yy | xx yy xx xx |
0x23 | xx yy | xx xx yy xx |
0x33 | xx yy | xx xx xx yy |
0x04 | xx yy | xx xx yy yy |
0x14 | xx yy | xx yy xx yy |
0x24 | xx yy | xx yy yy xx |
0x44 | xx yy zz | xx xx yy zz |
0x54 | xx yy zz | xx yy xx zz |
0x64 | xx yy zz | xx yy zz xx |
0x74 | xx yy zz | xx yy yy zz |
0x84 | xx yy zz | xx yy zz yy |
0x94 | xx yy zz | xx yy zz zz |
0xN5 (N=0..F) | ba dc | Na Nb Nc Nd (i.e. write bytes whose upper half comes from the head and their lower half comes from the arguments) |
0xN6 (N=0..F) | ba dc | aN bN cN dN (i.e. write bytes whose lower half comes from the head and their upper half comes from the arguments) |
0x07 | xx | 00 00 00 xx |
0x17 | xx | 00 00 xx 00 |
0x27 | xx | 00 xx 00 00 |
0x37 | xx | xx 00 00 00 |
0x47 | xx yy | 00 00 xx yy |
0x57 | xx yy | 00 xx 00 yy |
0x67 | xx yy | 00 xx yy 00 |
0x77 | xx yy | xx 00 00 yy |
0x87 | xx yy | xx 00 yy 00 |
0x97 | xx yy | xx yy 00 00 |
0xA7 | xx yy zz | 00 xx yy zz |
0xB7 | xx yy zz | xx 00 yy zz |
0xC7 | xx yy zz | xx yy 00 zz |
0xD7 | xx yy zz | xx yy zz 00 |
0xF7 | - | 00 00 00 00 |
0xN8 (N=0..D or F) | see above | Like 0xN7, but instead of 00, use FF. |
0xN9 (N=0..D) | see above | Like 0xN7, but instead of 00, use the corresponding byte from the last 4 bytes of the output. |
0xNA (N=0..D) | see above | Like 0xN7, but instead of 00, use the corresponding byte from the penultimate 4 bytes of the output. |
Levels
- Tools: mapedit.exe
The levels are stored in the (compressed) LEV*.MAP files.
After decompression, levels contain the following data:
Offset | Size | Contents |
---|---|---|
0x0000..0x07FF | 2048 (128 halfblocks * 4*4 CHR tiles) | CHR tiles of each halfblock |
0x0800..0x08FF | 256 (128 blocks * 2 halfblocks) | back layer halfblocks of each block |
0x0900..0x09FF | 256 (128 blocks * 2 halfblocks) | front layer halfblocks of each block (The most significant bit is sometimes set, what does it mean?) |
0x0A00..0x0AFF | 256 (128 blocks * 2 bytes) | #Block flags and objects |
0x0B00..0x0DCF | 720 (24 rooms * 30 tiles) | blocks (The most significant bit is sometimes set, what does it mean?) |
The remaining parts correspond to the DOS / Apple II level format from offset 0x2D0. | ||
0x0DD0..0x109F | 720 (24 rooms * 30 tiles) | modifiers |
0x10A0..0x119F | 256 | #Door events 1 |
0x11A0..0x129F | 256 | #Door events 2 |
0x12A0..0x12FF | 96 (24 rooms * 4 directions) | room links (left,right,up,down) (1 to 24, or 0 if there is no adjacent room) |
0x1300 | 1 | number of used rooms |
0x1301..0x1318 | 24 | room row within level map (unused) |
0x1319..0x1330 | 24 | room column within level map (unused) |
0x1331..0x133F | 15 | unused |
0x1340 | 1 | starting room (1 to 24) |
0x1341 | 1 | starting tile position (0 to 29) |
0x1342 | 1 | starting direction (0x00=right, 0xFF=left) |
0x1343..0x1346 | 4 | unused |
0x1347..0x135E | 24 | guard tile position for each room (0 to 29=0x1D, or 30=0x1E if no guard) |
0x135F..0x1376 | 24 | guard direction for each room (0x00=right, 0xFF=left) |
0x1377..0x138E | 24 | unused? |
0x138F..0x13A6 | 24 | unused? |
0x13A7..0x13BE | 24 | guard skill for each room |
0x13BF..0x13D6 | 24 | unused? |
0x13D7..0x13EE | 24 | unused? (DOS PoP stores the guard color here) |
0x13EF..0x13FF | 17 | unused |
Tiles and blocks
Tiles from LEV*.CHR files are 16×16 pixels. 4×4 tiles are combined into a halfblock (64×64 pixels). Then two halfblocks (top and bottom halves) are combined in each layer (back, front) of each block (64×128 pixels).
Blocks changed by the game
(Based on the documentation by zarazala)
When you pick up an item or a loose floor starts falling, the block number of that tile is incremented by 1. Therefore, for each block with a potion, a sword, or a loose floor, the next block must have the same graphics and flags but no object.
TODO: What happens when a loose floor lands? There is no broken floor object. To add a broken floor, the block number has to be changed, but by what logic?
Special events which change tiles similarly increase the block number by 1. The exception is the appearing of the mirror, which decreases the block number by 1.
Block flags and objects
Only the first byte of each item is used, the second bytes are zero.
byte | meaning | notes |
---|---|---|
flags -- these can be combined with objects | ||
0x01 | floor | |
0x02 | wall | |
0x04 | torch | Unlike in DOS PoP, torches appear in the tile where they are placed, not one tile to the right. |
objects | ||
0x00 | none | |
0x08 | loose floor | |
0x10 | open button | The modifier selects the door event to be triggered. |
0x18 | close button | The modifier selects the door event to be triggered. |
0x20 | door | If the modifier is 1 then the door is open, else it is closed. |
0x28 | spike | |
0x30 | heal potion | |
0x38 | hurt potion | |
0x40 | life potion | |
0x48 | upside down potion | |
0x50 | slow fall potion | |
0x58 | chomper | |
0x60 | sword | |
0x68 | mirror | Not used in level data: Placed on level 4 when the exit door opens. |
0x70 | level door | |
0x78 | skeleton | |
0x80 | door top | |
0x88 | balcony stars | Used in the top left corner of balconies. |
Door events
(same format as in the DOS / Apple II version) ------------------------- 7 6 5 4 3 2 1 0 <-bits byte from door events 1 | NX R1 R0 L4 L3 L2 L1 L0 byte from door events 2 | R4 R3 R2 0 0 0 0 0
R4 R3 R2 R1 R0: The number of the room. (1..24)
L4 L3 L2 L1 L0: The location in the room. (0..29)
NX: If zero then trigger next door event, if one then don't.
Palette
The palette is stored in PALET.DAT.
It is used for the whole game, except the following:
- It does not affect the Broderbund logo animation.
- Nor does it affect the flashes of the screen when someone is hurt, etc.
It contains 7 RGB color values, for palette slots 1-7. Slot 0 is not in the file, it is always black.
Each color is encoded in two bytes, with the bits arranged as: rrrrbbbb 0000gggg
That is, in the first byte, bits 0-3 store the blue intensity, bits 4-7 store the red intensity. In the second byte, bits 0-3 store the green intensity, and bits 4-7 are unused.
The default colors are:
index | bytes | HTML | color |
---|---|---|---|
0 | - | #000 | |
1 | 09 00 | #009 | |
2 | A0 00 | #A00 | |
3 | BB 00 | #B0B | |
4 | 00 08 | #080 | |
5 | 0D 0B | #0BD | |
6 | D0 0D | #DD0 | |
7 | EE 0E | #EEE |
Tiles
Tiles are stored in the (compressed) LEV*.CHR files.
After decompression, they contain data for 256 tiles, each is 0x80 bytes long. The data of each tile is further split into four sections (bitplanes) of 0x20 bytes each.
To decode the image of a tile, arrange the bits of each plane in a 16×16 array. The bits should be read from bit 0 to bit 7, and the array should be filled left to right, top to bottom.
The first three planes contain bits 0, 1, and 2 of the color indices for each pixel.
(The fourth plane contains transparency masks, but they are used only for tiles in the foreground layer?)
TODO
- Graphics: backgrounds, sprites
- Fonts
- Music and sounds
- Texts?
- User disk
- Automatic moves (DEMOPLAY.KEY)
- FM Towns version (PRI1.DAT)
- Hacks for easier playtesting: skipping the Broderbund logo, starting any level.
- VCLIP.DAT
- the hit point images in MAIN.PRG
- the Broderbund logo in BJLOGO.PRG