Compression

From FF5 Hacking Wiki
Jump to navigation Jump to search

Compression

FFV have some ROM areas of texts, graphics, data and even code which are compressed. The game includes some functions to decompress that data. One of them read the first byte to check the type of decompression to apply and the other one presuppose decompression "type 02".

Subroutine 7F/C00D

At the beginning of the game, some code is written to bank 7F. The function stored in 7F/C00D is a decompressor which waits for headed data. The pointer to the source to decompress for is stored in $D0 and the pointer to the destination of the decompressed data is stored in $D3. The first byte pointed by $D0 is read and to choose the decompression algorithm. I am going to call them Type 00, Type 01 and Type 02. If the first byte is a number over 2, the function just returns.

Decompression Type 00

No decompression here. The first two bytes after the header are a 16 bit value which represents the number of bytes to read forward from $D0. Every byte which is read from $D0 is stored in $D3.

Decompression Type 01 (RLE)

The data is stored here as packages with one byte heading every package. I'm going to call that byte "action byte". While the decompressing is happening, the "action byte" is taken from $D0 and choose the next action to perform:

 If the action byte is 0:
   The decompression ends.
 If the action byte is 01 to 7F:
   Store action byte as n.
   Read next byte from $D0 and store it as byteToRepeat.
   Write byteToRepeat in $D3 n times.
 If the action byte is 80 to FF:
   Store action byte as n.
   Ignore the higher bit of n. (n is now in the range of 00 to 7F)
   Read n bytes from $D0 and write them in $D3

Decompression Type 02 (LZSS)

The first two bytes after the header are a 16 bit value which represents the number of bytes to write in $D3. The decompression ends when that number of bytes has been written. This algorithm counts with a temporal circular buffer stored in 7F/F7FF, with a size of #$0800 bytes, initially filled with zeroes and with an initial offset of #$07DE. The data is stored as packages with one byte heading every package. I'm going to call that byte "bitmap byte". Every time a bitmap byte is read, it marks the next eight lectures. Beginning with the lesser bit of the bitmap byte and ending with the higher:

 If the bit is 1:
   Read next byte from $D0
   Write it on the circular buffer
   Write it on $D3
 If the bit is 0:
   Read 16 bit value from $D0
   That value contains an 'offset' and a 'length': O07 O06 O05 O04 O03 O02 O01 O00 | O10 O09 O08 L04 L03 L02 L01 L00
   Do (length + 3) times
     Read a byte from the circular buffer with the given offset
     Write the byte on the circular buffer
     Write the byte on $D3
     Increment offset (if offset > #$07FF then offset = #$0000)

After consuming the 8 bits from the bitmap byte, a new bitmap byte is read from $D0.

Subroutine C3/0053

The function addressed at C3/0053 performs a Decompression Type 02 (LZSS). The pointer to the source to decompress for is stored in $D0 and the pointer to the destination of the decompressed data is stored in $D3. The first byte pointed by $D0 is not a header in this case.

Decompression Type Custom Wold Map (Custom + LZS) Subroutine C0/69A1

The function addressed at C0/69A1 performs a custom decompression oriented to load the World Map tilemap data. First of all, it reads the offset of the tileset which is stored at CF/E000. Each offset points to a compressed horizontal row of world map tiles. (2bytes * 256 pointers to each horizontal tile line * 5 worlds) The offset is used in the data table C7/0000-C8/221F to set the point to begin the decompression. The data here is stored as bytes, excepting values, 0x0C, 0x1C, 0x2C and values greater than 0xBF. While the decompressing is happening, one byte is taken from the table and choose the next action to perform:

 If the byte is greater than 0xBF
   Store byte as n.
   Read next byte and store it as byteToRepeat.
   Write byteToRepeat in buffet (n - 0xBF) times.
 Else if the action byte is 0x0C:
   Write 0x0C in the output buffer
   Write 0x0D in the output buffer
   Write 0x0E in the output buffer
 Else if the action byte is 0x1C:
   Write 0x1C in the output buffer
   Write 0x1D in the output buffer
   Write 0x1E in the output buffer
 Else if the action byte is 0x2C:
   Write 0x2C in the output buffer
   Write 0x2D in the output buffer
   Write 0x2E in the output buffer
 Else:
   Write byte in the output buffer
 

Ends when 0x0100 bytes are written in the output buffer