Technical Notes on the Puzznic Level Format (PC/MS-DOS)
This post contains some technical details regarding the file format used by the MS-DOS version of the Taito puzzle game Puzznic.
Compression
Headerless LZW with variable length code words ranging from 9 to 12 bits. The string table can contain a maximum of 2^12 (4096) entries.
The initial table for codes 0 through 255 contains the corresponding octet, as expected.
Code 256 is reserved for a clear (or reset) symbol.
Code 257 is reserved for EOF.
The first free code is thus 258.
The clear code resets the state to the inital one, with 9-bit codes, and the next available code being 258. The compressor will signal this when it runs out of space in the string table.
The current code length is increased once the last code we can express has been written to the table. I.e once the 512:th string is written, the current code width is expanded from 9 to 10 bits, and then again at 1024 to 11 bits and 2048 to 12 bits. Since 12-bits is the upper limit, it's a decoding error if we ever have to expand to 13 bits and the next symbol is not the clear symbol.
The files start with a reset symbol, and end properly with the EOF symbol.
LSB-first packing is used. For instance, if a file starts with the octets 00, 05 and 08, after shifting in 16 bits we're holding the value 0x500. Masking off the lowest 9 bits (0x500 & 0x1FF) we get 0x100, the reset symbol (256). After shifting out the previous code word, we're holding 2. Having read 16 bits and shifted out 9 bits, we shift in the next octet by 7 bits, holding 2 + (8 << 7) or 0x0402, masking 0x1FF bits gives the code word 2, and so on.
While I have not tested it, it's possible that the LZW code from a GIF decompressor would work, if properly initialized.
Reference data
The level files I have decompressed are:
$ sha256sum LEVELMP1.MAP EXTRA.PUZ
8b57ee1e373c926182e47afd3d97477c07f98ad6dde076cdf3c3f703f250d46c LEVELMP1.MAP
f397e1e1b58d02ca6f469c8af0f5e50620621f267f48cb71af545f77d550607a EXTRA.PUZ
They are 5596 and 6227 bytes respectively, and decompress to 18432 bytes.
Level format
Each file contains a number of playfields, where each occupy 128 bytes; 10x12 bytes for the blocks, and 8 bytes of unknown data. The unknown data seems to have high entropy, and is likely just garbage for padding. While each playfield has a par-time, these seems to be related to the level number and not stored with the playfield data.
The game divides the playfields into levels and problems, such that there are eight levels and each level contains level-number of sets of problems, where each set has four playfields. This gives us the total number of playfields as A046092(8), the 9:th triangular number times 4, i.e 2*8*(8+1), or unrolled: (1*4) + (2*4) + (3*4) + (4*4) + (5*4) + (6*4) + (7*4) + (8*4) = 144.
We can go from a level, set and problem number to the linear playfield number via 2*(level-1)*level + (set-1)*4 + (field-1).
The playfield number for level 3, problem 2-3 is 2*(3-1)*3 + (2-1)*4 + (3-1) = 18, which means the data for that level is at offset 18*128 or 2304 in the file.
The octets maps to blocks as follows:
- 0x00 metal
- 0x01 blank
- 0x02 brick_wall
- 0x03 lift-updown
- 0x04 lift-leftright
- 0x14 red-circle
- 0x15 diamond
- 0x16 pink-cube
- 0x17 triangle
- 0x18 gray-pentagon
- 0x19 green-bar
- 0x1a blue-pyramid
- 0x1b checkered-box
eof
Log in to comment