Pics

Back to the Game FIles =allpicsX= The files allpics1 and allpics2 hold the encounter graphics which are displayed if you are in a fight, in a shop, in a hospital or at the ranger center for example. All these graphics have the size 96x84 pixels and are animated. The animation support is quite impressive. It's not just one looping animation. Instead each graphic can have multiple animations. They are updating different parts auf the base frame and so they are totally independent from each other and can loop at different rates. Take the lizard (picture #26 in allpics1) as an example: It has two animations. The tongue and the eye. Both animations are played independent from each other and have their own different delays. Other pictures (like the robots) have more different animations. They have rotating dishes, blinking lights, firing lasers, spinning wheels, and so on. This results in a total animation which has lots of different animation combinations. Seems to be impossible to create an animated GIF out of it. But it's possible to put each animation set into a separate transparent animated GIF and layer them in a HTML file. A full export into PNG (Base frame), GIF (animated layers) and HTML (Which displays the files) can be found here: The allpics files contains lots of MSQ blocks which are all huffman compressed. The blocks must be read in pairs. The first block contains the base frame (Standard vertical xor encoded PIC format) and the second block contains the animation data (not vertical xor encoded). The encounter picture animations works totally different from the end.cpa animation. While the end.cpa animation //copies// new pixels onto the current picture the encounter animations //changes// the existing pixels with XOR values. This is a pretty nice idea because an animation update block can just be applied again to reconstruct the original picture. This makes the animation data smaller because no update block is needed to reconstruct the original picture in easy animations.

Animation block
The animation block starts with a 16 bit long header size. This size defines how many following bytes are part of the animation header. The animation header contains the delays and the update block indices. So the header defines in which order and with which delays the frames are played. The header can contain multiple animation descriptions (The game uses up to four different animations. Currently it's unknown if more are supported by the game). Each animation description is terminated by a 0xff byte. So if there is only one animation (like the Ranger Center animation with the rotating dish) then there is exactly one 0xff byte and it's always the last byte in the header. A single animation description consists of byte pairs. The first byte is the delay and the second byte is the index of the animation update block to be applied to get the next animation frame.

Example header: 0c 00  Size of the header (12 bytes) 05     Wait five time units 00     Apply update block 0 to get frame 1 03     Wait three time units 00     Apply update block 0 again to get frame 0 again ff     End of first animation 01     Wait one time unit 01     Apply update block 1 to get frame 1 09     Wait nine time units 02     Apply update block 2 to get frame 2 05     Wait five time units 03     Apply update block 3 to get frame 0 again ff     End of second animation

The first animation in this example uses just one update block to loop the animation. The update is applied (changing the original picture) and after that applied again (Reconstructing the original picture). Result is an animation with two frames. The second animation instead uses three update blocks where the last one is responsible for reconstructing the original image. Instead of doing it that way it would also be possible to apply update 2 again and then update 1 again to reconstruct the original picture but this is not always intended by the animation.

Following the header comes another size information (again 16 bit). This time its the size of the following data block containing all the update blocks.

Each update block consists of multiple update instructions. Each update instruction starts with an offset address. Unlike the strange offset address of the end.cpa this offset address is logically encoded because it has nothing to do with then screen size. It only depends on the picture size. So the offset is simply the byte offset in the picture which is (y*96+x)/2 (Division by 2 because each byte holds two pixels).

Well... If you are trying to decode the offset you may be surprised because it nearly always points at a pixel outside of the picture. This is because the programmers have cleverly put another information into these two bytes. Only 12 bits are needed for the offset so four bits can be used for something different. These four bits are used to tell the game how many update bytes are following the offset. The minimum number of update bytes is 1 (represented by a length of 0x00) so the maximum is 16 (represented by a length of 0x0f) because only four bits are available for storing the size. So the maximum number of pixels which can be updated by a single update instruction is 32. The minimum number is 2. So if just one pixel needs to be updated one nibble of the byte must be zero so the other pixel is not changed (xor'd with 0). Example: Think of the offset 0x34c9 (which is stored as 0xc9 0x34). The real offset is 0x04c9 (which is x=50, y=25) and the number of update bytes is 3+1. So as expected the next four bytes in this example are update bytes. These bytes are simply XOR values which must be applied at the current state of the picture at the given offset. Following these update bytes comes another offset and more update bytes and so on until an offset address is 0xffff. This marks the end of an update block. The next update block follows this end marker. There is no end-of-animation-marker so you have to continue reading update blocks until you have reached the data size which was read after the animation header.

MSQ block offsets
The offsets of the base frame MSQ blocks are hardcoded in wl.exe. There are no pointers to the animation data MSQ blocks. The game always expects them right after the base frames. So if you have changed an allpics file you most likely need to correct the hardcoded offset addresses because your changes may have affected the efficiency of the huffman compression. The offsets for allpics1 are located at seg2:ba90 in the unpacked wl.exe. Each offset is a 32 bit value. The offsets for allpics2 are located at seg2:bb18. There is one speciality for allpics1: There are 34 offsets in the wl.exe but there are only 33 pictures. The offset to the 25th picture is found two times in the wl.exe. If this is a bug or if there are really 34 possible pictures and only 33 are used is currently unclear.

How this was found
Decoding the base frame was quite easy because this MSQ block is encoded exactly in the same way as the base frame in the end.cpa animation. That the animation data is huffman encoded is also a logical conclusion because the MSQ block contains a memory allcation size hint. So there was only one thing left: Playing with the bytes in the animation data one by one and finding out what they do.

Picture mapping
Pictures are referenced in the game files with a special ID which don't match the index of the pictures in the allpics files. There are two mapping tables in the EXE to map a picture ID (Which is used as an index in this mapping table) to the picture index used in the picture overviews shown above. If the game map is in the file game1 then the first mapping table is used to map to pictures in allpics1. If the game map is in the file game2 then the second mapping table is used to map to pictures in allpics2. The first mapping table is at offset seg2:be2a and has 80 entries. The second mapping table is at offset seg2:be7a and also has 80 entries. Not all entries in these tables are used. Unused entries have the value 0x80. Here is a small CLI PHP script which can be used to dump the picture mapping tables. First parameter must point to the unpacked EXE file and the second parameter must be 1 for the first mapping table and 2 for the second mapping table: <?php // Check which mapping is requested if ($_SERVER["argc"] != 3) die("SYNTAX: dumppicmap EXE 1|2\nEXAMPLE: dumppicmap wlu.exe 1\n"); $exe = $_SERVER["argv"][1]; $game = $_SERVER["argv"][2]; // Check EXE size if (!file_exists($exe)) die("EXE file $exe not found\n"); $exesize = filesize($exe); if ($exesize == 62549) die("ERROR: $exe is packed\n"); // Calculate SEG2 offset $seg2 = $exesize - 116544; // Open wl.exe, seek to mapping and output it. 0x80 entries are ignored $file = fopen($exe, "rb"); fseek($file, $seg2 + ($game == 1 ? 0xbe2a : 0xbe7a)); for ($i = 0; $i < 80; $i++) {   $p = ord(fread($file, 1)); if ($p != 0x80) echo "$i => $p\n"; } fclose($file); ?> Or simply use the following mapping tables:
 * 1) !/usr/bin/php