The current URL is datacrystal.tcrf.net.
Final Fantasy Tactics Advance/Compression Formats: Difference between revisions
Darthatron (talk | contribs) mNo edit summary |
m (Xkeeper moved page Final Fantasy Tactics Advance:Compression Formats to Final Fantasy Tactics Advance/Compression Formats: normalize subpages and titles) |
||
(6 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{subpage|game=Final Fantasy Tactics Advance}} | |||
[[Final Fantasy Tactics Advance]] employs several compression formats. Several have been reverse engineered, and information is below: | [[Final Fantasy Tactics Advance]] employs several compression formats. Several have been reverse engineered, and information is below: | ||
=Main compression methods= | =Main compression methods= | ||
Line 13: | Line 13: | ||
The destination is then filled with 0x1000 words of value 0. | The destination is then filled with 0x1000 words of value 0. | ||
The following bytes are GBA LZ77 format. | The following bytes are GBA LZ77 format. | ||
=LZSS= | =LZSS= | ||
Several places in the FFTA ROM, a derivative of the LZSS compression scheme is used. In this format, literals (uncompressed data) and compressed data is mixed. The compressed data comes in several forms, and is identified by the highest bit that is set. Upon reading one compressed byte (and additionally however more are specified by that form), the next byte is read as compressed as well. Bytes are only read as uncompressed when specified by a special byte. The following is the list of all of the compressed data forms: | Several places in the FFTA ROM, a derivative of the LZSS compression scheme is used. In this format, literals (uncompressed data) and compressed data is mixed. The compressed data comes in several forms, and is identified by the highest bit that is set. Upon reading one compressed byte (and additionally however more are specified by that form), the next byte is read as compressed as well. Bytes are only read as uncompressed when specified by a special byte. The following is the list of all of the compressed data forms: | ||
*Bit 7 set: 0b1XXXXYYY 0bYYYYYYYY | *Bit 7 set: 0b1XXXXYYY 0bYYYYYYYY | ||
:Track back Y bytes from outputted data and copy X+3 bytes. | :Track back Y bytes from outputted data and copy X + 3 bytes. | ||
*Bit 6 set: 0b01XXXXXX | *Bit 6 set: 0b01XXXXXX | ||
:The next compressed byte is in X+1 bytes | :The next compressed byte is in X + 1 bytes | ||
*Bit 5 set: 0b001XXXXX | *Bit 5 set: 0b001XXXXX | ||
:Output X+2 zeros | :Output X + 2 zeros | ||
*Bit 4 set: 0b0001XXXX 0bYYZZZZZZ 0bZZZZZZZZ | *Bit 4 set: 0b0001XXXX 0bYYZZZZZZ 0bZZZZZZZZ | ||
: | :Track back Z bytes from outputted data and copy 4 + 0b00YYXXXX bytes. | ||
*Bit 3 set: 0b00001??? | *Bit 3 set: 0b00001??? | ||
:''' | :'''Ignored.''' | ||
*Bit 2 set: 0b000001?? | *Bit 2 set: 0b000001?? | ||
:''' | :'''Ignored.''' | ||
*Bit 1 set: | *Bit 1 set: 0b00000010 0bXXXXXXXX | ||
:Output | :Output 0x00 byte X + 3 times. | ||
*Bit 0 set: 0b00000001 0bXXXXXXXX | |||
*Bit 0 set: 0b00000001 0bXXXXXXXX | :Output 0xFF byte X + 3 times. | ||
:Output X+3 | *No bits set: 0b00000000 0bXXXXXXXX 0bYYYYYYYY 0xZZZZZZZZ | ||
*No bits set: 0b00000000{{ | :Track back 0bYYYYYYYYZZZZZZZZ bytes from outputted data and copy X + 5 bytes. | ||
==C# LZSS Decompression Function== | |||
public int LZSSDecompress(byte[] source, ref byte[] dest) | |||
{ | |||
int retlen = source[3] | (source[2] << 0x08) | (source[1] << 0x10) | (source[0] << 0x18); | |||
int xIn = 4; | |||
int xOut = 0; | |||
int tmp = 0; | |||
int i = 0; | |||
int j = 0; | |||
while (xOut < retlen) | |||
{ | |||
if ((source[xIn] & 0x80) == 0x80) | |||
{ | |||
tmp = (xOut - ((source[xIn] & 0x07) << 8)) - source[xIn + 1] - 1; | |||
for (i = ((source[xIn] >> 3) & 0x0F) + 3; i > 0; i--) | |||
{ | |||
dest[xOut] = dest[tmp]; | |||
xOut++; | |||
tmp++; | |||
} | |||
xIn++; | |||
} | |||
else if ((source[xIn] & 0x40) == 0x40) | |||
{ | |||
for (i = (source[xIn] & 0x3F) + 1; i > 0; i--) | |||
{ | |||
xIn++; | |||
dest[xOut] = source[xIn]; | |||
xOut++; | |||
} | |||
} | |||
else if ((source[xIn] & 0x20) == 0x20) | |||
{ | |||
for (i = (source[xIn] & 0x1F) + 2; i > 0; i--) | |||
{ | |||
dest[xOut] = 0x00; | |||
xOut++; | |||
} | |||
} | |||
else if ((source[xIn] & 0x10) == 0x10) | |||
{ | |||
j = ((source[xIn + 1] & 0x3F) << 8) | source[xIn + 2]; | |||
tmp = (xOut - j) - 1; | |||
if (tmp < 0) tmp = 0; | |||
for (i = (((source[xIn + 1] >> 2) & 0x30) | (source[xIn] & 0x0F)) + 4; i > 0; i--) | |||
{ | |||
dest[xOut] = dest[tmp]; | |||
tmp++; | |||
xOut++; | |||
} | |||
xIn += 2; | |||
} | |||
else if (source[xIn] == 0x01) | |||
{ | |||
for (i = source[xIn + 1] + 3; i > 0; i--) | |||
{ | |||
dest[xOut] = 0xFF; | |||
xOut++; | |||
} | |||
xIn += 1; | |||
} | |||
else if (source[xIn] == 0x02) | |||
{ | |||
for (i = source[xIn + 1] + 3; i > 0; i--) | |||
{ | |||
dest[xOut] = 0x00; | |||
xOut++; | |||
} | |||
xIn += 1; | |||
} | |||
else if (source[xIn] == 0x00) | |||
{ | |||
j = source[xIn + 3] | (source[xIn + 2] << 0x08); | |||
tmp = xOut - j - 1; | |||
if (tmp < 0) tmp = 0; | |||
for (i = source[xIn + 1] + 5; i > 0; i--) | |||
{ | |||
dest[xOut] = dest[tmp]; | |||
xOut++; | |||
tmp++; | |||
} | |||
xIn += 3; | |||
} | |||
xIn++; | |||
} | |||
return retlen; | |||
} |
Latest revision as of 02:41, 24 January 2024
This is a sub-page of Final Fantasy Tactics Advance.
Final Fantasy Tactics Advance employs several compression formats. Several have been reverse engineered, and information is below:
Main compression methods
Starting around 0x0801F100 in the code is a routine for decompressing some data. The first byte seems to indicate type. Bytes checked for are, in order:
- 0x10
- 0x01
- 0x11
If none of these are matched, it prepares three arguments and then branches with link to 0x08141868.
Mode 0x11
In mode 0x11, the first three bytes after the identifier are compared against the value at 0x0801F26C (0x00FFFFFF in the original ROM). If the bytes match, it jumps to 0x0801F274. The destination is then filled with 0x1000 words of value 0. The following bytes are GBA LZ77 format.
LZSS
Several places in the FFTA ROM, a derivative of the LZSS compression scheme is used. In this format, literals (uncompressed data) and compressed data is mixed. The compressed data comes in several forms, and is identified by the highest bit that is set. Upon reading one compressed byte (and additionally however more are specified by that form), the next byte is read as compressed as well. Bytes are only read as uncompressed when specified by a special byte. The following is the list of all of the compressed data forms:
- Bit 7 set: 0b1XXXXYYY 0bYYYYYYYY
- Track back Y bytes from outputted data and copy X + 3 bytes.
- Bit 6 set: 0b01XXXXXX
- The next compressed byte is in X + 1 bytes
- Bit 5 set: 0b001XXXXX
- Output X + 2 zeros
- Bit 4 set: 0b0001XXXX 0bYYZZZZZZ 0bZZZZZZZZ
- Track back Z bytes from outputted data and copy 4 + 0b00YYXXXX bytes.
- Bit 3 set: 0b00001???
- Ignored.
- Bit 2 set: 0b000001??
- Ignored.
- Bit 1 set: 0b00000010 0bXXXXXXXX
- Output 0x00 byte X + 3 times.
- Bit 0 set: 0b00000001 0bXXXXXXXX
- Output 0xFF byte X + 3 times.
- No bits set: 0b00000000 0bXXXXXXXX 0bYYYYYYYY 0xZZZZZZZZ
- Track back 0bYYYYYYYYZZZZZZZZ bytes from outputted data and copy X + 5 bytes.
C# LZSS Decompression Function
public int LZSSDecompress(byte[] source, ref byte[] dest) { int retlen = source[3] | (source[2] << 0x08) | (source[1] << 0x10) | (source[0] << 0x18); int xIn = 4; int xOut = 0; int tmp = 0; int i = 0; int j = 0; while (xOut < retlen) { if ((source[xIn] & 0x80) == 0x80) { tmp = (xOut - ((source[xIn] & 0x07) << 8)) - source[xIn + 1] - 1; for (i = ((source[xIn] >> 3) & 0x0F) + 3; i > 0; i--) { dest[xOut] = dest[tmp]; xOut++; tmp++; } xIn++; } else if ((source[xIn] & 0x40) == 0x40) { for (i = (source[xIn] & 0x3F) + 1; i > 0; i--) { xIn++; dest[xOut] = source[xIn]; xOut++; } } else if ((source[xIn] & 0x20) == 0x20) { for (i = (source[xIn] & 0x1F) + 2; i > 0; i--) { dest[xOut] = 0x00; xOut++; } } else if ((source[xIn] & 0x10) == 0x10) { j = ((source[xIn + 1] & 0x3F) << 8) | source[xIn + 2]; tmp = (xOut - j) - 1; if (tmp < 0) tmp = 0; for (i = (((source[xIn + 1] >> 2) & 0x30) | (source[xIn] & 0x0F)) + 4; i > 0; i--) { dest[xOut] = dest[tmp]; tmp++; xOut++; } xIn += 2; } else if (source[xIn] == 0x01) { for (i = source[xIn + 1] + 3; i > 0; i--) { dest[xOut] = 0xFF; xOut++; } xIn += 1; } else if (source[xIn] == 0x02) { for (i = source[xIn + 1] + 3; i > 0; i--) { dest[xOut] = 0x00; xOut++; } xIn += 1; } else if (source[xIn] == 0x00) { j = source[xIn + 3] | (source[xIn + 2] << 0x08); tmp = xOut - j - 1; if (tmp < 0) tmp = 0; for (i = source[xIn + 1] + 5; i > 0; i--) { dest[xOut] = dest[tmp]; xOut++; tmp++; } xIn += 3; } xIn++; } return retlen; }