Additions and corrections to the format specifications

About PR usage and development, and about the POP format.
Post Reply
David
The Prince of Persia
The Prince of Persia
Posts: 2850
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

Additions and corrections to the format specifications

Post by David »

(Apologies for the very long post.)

In this topic I will post additions and corrections to this document: http://www.popot.org/documentation.php? ... ifications

PoP1 Palettes (corrections)
The first four bytes are marked unknown.
Byte 0 tells how many images are associated with the palette, i.e. how many images should the game load when it loads the palette.
If n is the value of byte 0 and id is the id of the palette, then images from id+1 to id+n belong to this palette.
Byte 3 is always 0x10 (16), perhaps it means the number of colors in the palette, but changing it has no effect.

CGA and EGA palettes: (I already published this, but it seems somebody misunderstood it a bit.)
The specification states that the four entries that belong to the same color are grouped together, but that's not the case.
Instead, first we have the top-left entry of all 16 colors, then the top-right of all, then the bottom-left and bottom-right.
For example, EDUNGEON.DAT contains the following:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
If the palette was stored as the specification says, it would contain this:
0000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF

Also, the EGA palette used by PoP is a bit different from the default. Color 0 is transparent, and color 13 is black.


PoP2 Palettes (additions)
SHPL: SHape PaLette, or perhaps SHaPe Loader?

Code: Select all

2 bytes: the ID of the first image (SHAP)
2 bytes: the number of images
2 bytes: 0
1 byte: number of colors
n*3 bytes: RGB data (same format as in PoP1)
PALS and PALC: PALetteS and PALette Count
These resources always exist together.
PALS stores raw RGB data.
PALC stores the number of palettes in the PALS resource.
For example, KID.DAT/PALS/25001 contains 8 palettes, one for each type of level.

PALT stores cutscene palettes.
The format is raw RGB data, without header.


PoP2 SHAP: images (addition) (This is already in PR)
The PoP2 image format is an extension of the PoP1 image format.

There is a new bit-per-pixel value: 1111 means 256 colors = 8 bpp. (0000 = 2 colors = 1 bpp, 1011 = 16 colors = 4 bpp)

There is a new compression method: LZG+RLEC.
It is signified by setting the last 4 bits of ImageMask to 0011 (LZG_LR) and setting the last bit of the first byte of ImageMask to 1.

The decompression method:

Code: Select all

LZG+RLE:
repeat until you have enough bytes in the output:
	read 2 bytes from the input: TempLength
	decompress with LZG from the input into a temporary buffer until it contains TempLength bytes
	go to the beginning of the temporary buffer
	repeat while you reach the end of the temporary buffer:
		read 2 bytes from the temporary buffer: LineLength
		decompress with RLEC from the next LineLength bytes of the temporary buffer to the output 
	end repeat
end repeat

RLEC:
repeat until you reach the end of the input:
	read 1 byte: C
	if C>=0x80 then:
		// repeat
		read 1 byte: X
		write X into the output (C-0x80)+1 times
	else:
		// copy
		copy C+1 bytes from the input to the output
	endif
end repeat

Note: zero means transparent only if it's encoded in "repeat" mode.

PoP2 STRL: string lists (addition)
The first byte is the number of strings that follow.
Each string is zero-terminated.


PoP2 TXT4: compressed texts (addition)
First, you need the following table from PRINCE.EXE:

Code: Select all

     0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F    0123456789ABCDEF
00: 00 20 61 65  74 6F 6E 69  73 72 64 6C  68 75 67 66      aetonisrdlhugf
10: 63 77 79 70  62 6D 6B 2C  76 53 41 2E  54 27 50 4D    cwypbmk,vSA.T'PM
20: 78 42 43 49  52 47 44 57  48 71 45 2D  7A 4E 46 4B    xBCIRGDWHqE-zNFK
30: 4C 30 6A 3A  35 31 59 4A  38 5C 55 3F  37 33 51 3B    L0j:51YJ8\U?73Q;
40: 32 21 34 36  39 0D 0A 09  4F 56 58 5A  28 29 2A 2B    2!469♪◙○OVXZ()*+
50: 22 23 24 25  26 3C 3D 3E  2F 40 5B 5D  5E 5F 60 07    "#$%&<=>/@[]^_`•
60: 01 02 03 04  05 06 08 0B  0C 0E 0F 10  11 12 13 14    ☺☻♥♦♣♠◘♂♀♫☼►◄↕‼¶
70: 15 16 17 18  19 1A 1B 1C  1D 1E 1F 7B  7C 7D 7E 7F    §▬↨↑↓→←∟↔▲▼{|}~⌂
The first two bytes of a TXT4 resource contain the number of characters in the text, name it n.
The remaining part should be read as bits.
Read the first two bits as a binary number and name it w.
Repeat the following n times:
Read the next four bits, name it c. If c=0 then read the next w+5 bits into c. Use c as an index into the above table, and append the character to the output.

For example, PRINCE.DAT/TXT4/8010 contains the following:
05 00 42 D2 05 4C 00
05 00 tells that the text will contain five characters.
42 D2 05 4C 00 converted to binary: 01000010 11010010 00000101 01001100 00000000
If we split it up according to the above algorithm: 01|0000|101101|0010|0000|010101|0011|0000|000000
The first two bits are 01, so w+5 will be 6.
First we read 0000, so we read another 6 bits, which is 101101 = 0x2D. Row 2, column D contains 0x4E = 'N'.
Next we read 0010=0x02. Row 0, column 2 contains 0x61 = 'a'.
Third: 0000, read another 6 bits, 010101 = 0x15. Row 1, column 5 contains 0x6D='m'.
Fourth: 0011 = 0x03. Row 0, column 3 contains 0x65 = 'e'.
Fifth: 0000, read another 6 bits, 000000 = 0x00. Row 0, column 0 contains 0x00, which marks the end of strings in C.
Reading them together, we get "Name". This is one of the headers in the Hall of fame.

The following texts are contained in TXT4 resources: (in PRINCE.DAT)
The cheat activation word (resource 10)
The credits (resources 1000..1011) (*)
The text at the end of the demo version (resources 1020..1026) (*)
The texts on the options, save/load game, hall of fame, copy protection screens (8000..8016)
(*): After decompression, you'll get a string list. (Same format as STRL.)


PoP2 FONT: bitmap fonts (addition)
This format is also used for the hardcoded fonts of PoP1 and PoP2.
Header:

Code: Select all

1 byte: code of first character that has an image
1 byte: code of last character that has an image
2 byte: height above baseline
2 byte: height below baseline
2 byte: distance between lines
2 byte: spacing between characters
(last-first+1)*2 byte: addresses
The image of each character is stored this way: (the storage is similar to the storage of images)

Code: Select all

2 byte: height in pixels
2 byte: width in pixels (width in bytes=(width+7) div 8)
2 byte: 0
(width in bytes)*height bytes: data, each pixel row is padded to an integer number of bytes.

Sounds: (correction)

PoP1:

Code: Select all

1 byte: type of sound, 0=pcspeaker, 1=wave, 2=midi, +0x80 if looped (level door opening, 10015)
if pcspeaker:
	2 bytes: ticks per second
	for each sound:
		2 byte: frequency in hertz
			0=silence
			1=silence at the beginning
			2=cue point (marker) (used for triggering events when the music reaches this point)
		1 byte: length in ticks
			for cue points, this is the marker id, usually a lowercase letter
	2 bytes: 0x0012
if wave:
	2 bytes: sample rate (Hz)
	2 bytes: number of samples, or 0 if compressed
	2 bytes: always 0
	1 byte: sample size, 8 if not compressed, 255 if compressed (PoP1 demo)
	The following appears only if compressed:
		2 bytes: number of samples after decompression
if midi:
	The full contents of a MIDI file follow.
PoP2:

Code: Select all

1 byte: type of sound, 0=pcspeaker, 1=wave, 2=midi, +0x80 if looped
if pcspeaker:
	Same as for PoP1.
if wave:
	2 bytes: sample rate (Hz)
	1 byte: sample size, 8 if not compressed, 255 if compressed (NISDIGI.DAT)
	2 bytes: number of samples, or 0 if compressed
	2 bytes: 0 for most sounds (*)
	2 bytes: 0 for most sounds (*)
	The following appears only if compressed:
		2 bytes: number of samples after decompression
if midi:
	Same as for PoP1.
	(FRAGSND.DAT resources store a 15-byte something with a "MSeq" header.)

(*) Some looped sounds in DIGISND.DAT store offsets here. Their IDs are: 10007, 10047, 10054, 10258.

The decompression method for compressed waves:

Code: Select all

read 1 byte: Sample
write Sample
repeat until there are not enough bytes in the output:
	read 3 bits: B
	if B==7 then:
		Sample := Sample xor 0x80
		write Sample
	else:
		repeat:
			read B+2 bits (signed) : Delta
			if Delta is the smallest negative number encodable in B+2 bits then: exit repeat
			Sample := Sample + Delta
			write Sample
		end repeat
	endif
end repeat
User avatar
Norbert
The Prince of Persia
The Prince of Persia
Posts: 5749
Joined: April 9th, 2009, 10:58 pm

Re: Additions and corrections to the format specifications

Post by Norbert »

Wow, that's a lot of information.

A while back when I created this, poirot asked me if I wanted the source for the format specifications PDF file.
I didn't feel like updating it (too much work, not really necessary).
Do you want me to ask him about the source file (you could, of course, also do that yourself, if you want) so you can update it, or do you too prefer not to update it and is it preferable if I simply link to this thread from the Documentation section of the Princed download page and the popot.org page about the specifications document?
Post Reply