Hey, everyone. I've been visiting this forum for some time now without registering or posting. But today I decided I should go ahead and appear out of nowhere.
You guys are incredible! On my own, I would never have been able to figure out LZS decompression, let alone the compression (which I still know nothing about). I must thank Ficedula immensely for his helpful documents. However, I felt like a leech by reading all these threads without adding any of my own contributions. I probably don't have a lot to add that isn't already known, but whatever. I think it's better to give out well-known information than to not give out unknown information.
These are the functions I wrote for getting the size of a decompressed LZS file and actually decompressing the file. If you see errors in these, please inform me, but they have worked fine for me, and the file sizes match those from Fice's LZS decompressor.
DWORD LZSG(BYTE *lzs, DWORD li)
{
BYTE cb=0;
DWORD cs=0;
for(DWORD ci=0;ci<li;){
cb=lzs[ci++];
for(BYTE bt=0;bt<8&&ci<li;bt++,cb>>=1){
if(cb&1){cs++;ci++;}
else{ci++;cs+=(lzs[ci++]&0xF)+3;}}}
return(cs);
}
void LZSD(BYTE *dec, BYTE *lzs, DWORD li)
{
BYTE cb=0,cl;
DWORD co=0;
for(DWORD ci=0;ci<li;){
cb=lzs[ci++];
for(BYTE bt=0;bt<8&&ci<li;bt++,cb>>=1){
if(cb&1)dec[co++]=lzs[ci++];
else{
int o1;
cl=(lzs[++ci]&0xF)+3;
o1=((lzs[ci]&0xF0)<<4)+lzs[(ci++)-1];
o1=co-((co-o1-18)&0xFFF);
for(;o1<0&&(cl--);o1++)dec[co++]=0;
for(;cl--;)dec[co++]=dec[o1++];}}}
}
First of all, the only game I'm focusing on is FF7. I only have the PSX US version of the game, and that's what I've been working on. I suppose the only real files I've been concentrating on are the FIELD directory files. As I found out, there are differences between this version of the game and the well-documented PC version. This is what I know about the decompressed files.
BSX - I have no idea about these files. Haven't looked all that much.
MIM - I've looked at these guys a lot, and it seems to me that all they contain is an 8BPP TIM of the "unconstructed" tiled picture for the field backgrounds. I call this the raw background TIM. Anyway, the MIM skips the ID tag DWORDs for TIM and 8BPP. That is, if you look at the 8BPP TIM file structure starting with the DWORD containing the size of the CLUT section, that right there is the structure of the MIM file. However, I always saw 4 extra 00 bytes at the end of the file for some reason. Now, it seems that there are subsections of the raw background TIM. The pixels with an x lower than 256 are all related and share a CLUT. The pixels with an x from 256 to 512 share the next CLUT, etc. However, there is a problem. It's not always 256. Sometimes the sections are only 128 pixels wide. I cannot find in the DAT where it says how wide each section of the raw TIM is!
DAT - I got frustrated with these guys for a long time. After probably a week of staring at the file in a hex editor, I figured out how to always get the exact location of where the text starts. There is a word at offset 0x20 that stores an offset to the text section header, but you have to add 0x1C to it. (Yes, I found this out independently somehow, but am now aware it is already written somewhere.) It seems like this will be easier to describe in code.
ifstream datfile;
WORD offset, numstr, headsize;
datfile.open(".\\FILE.DAT", ios::in | ios::binary);
datfile.seekg(0x20);
datfile.read((char *)&offset, 2);
datfile.seekg(offset + 0x1C);
datfile.read((char *)&numstr, 2);
datfile.read((char *)&headsize, 2);
datfile.seekg(offset + 0x1C + headsize);
//You may now begin reading text. The number of strings should be numstr.
More recently, I figured out what the first 0x1C bytes are for. They're offsets to the sections of the DAT (seems PSX has different sections than the PC; I only see 7 sections here) but there's a couple of really weird things going on. First, there's a useless 0x8011 WORD every other WORD. Second, looking at the even WORDs instead, you have to subtract 0x5000 from their value and then add 0x1C to get correct offsets in the file. I think the background construction section where it tells you how to load the background picture using the MIM is the third such offset, the WORD starting at offset 0x8 in the file.
ifstream datfile;
WORD offset;
DWORD headsize;
datfile.open(".\\FILE.DAT", ios::in | ios::binary);
datfile.seekg(0x8);
datfile.read((char *)&offset, 2);
datfile.seekg(offset - 0x5000 + 0x1C);
datfile.read((char *)&headsize, 4);
datfile.seekg(offset - 0x5000 + 0x1C + headsize);
//This is where the first construction section starts.
I don't know what the header before the image construction does. There seems to be more than one DWORD besides headsize. If you read the one after that instead and add it like you did there, it takes you to a differently structured section, but it still seems to be image construction. Anyway, for the first image construction section you should see this repetition... (Note that like in the PC version, the destination's (0, 0) is the middle of the screen.)
short dstx;
short dsty;
BYTE srcx;
BYTE srcy;
WORD section;
If you take (section - 0x7800) / 64 you should get which section to get the tile from. I haven't figured out any more image construction sections. As of now, the only way I can figure to get the final background width is to look at every dstx in the image construction section and look for the largest x and smallest x, then find (lrgx - smlx + 16) as the width. Same method for height. But this isn't wide/high enough sometimes. I guess sprites can stick out past the bottom layer sometimes? If there is an easier way to find the image dimensions, please let me know!
I think that's it for this post. If you see errors anywhere, correct them as you wish in a reply. If you can help me with something I didn't know about, by all means, go ahead. I am not too proud to accept help. If this post was long AND useless, I apologize, but I know I couldn't find anything this detailed about these files for the PSX version when I searched through this forum. I hope this helps people.