[FF8] .x battle fields

  • Thread starter Thread starter Halfer
  • Start date Start date
Status
Not open for further replies.
Gold shovel for me! :)

After ten hours of extensive work I finally made it to produce my own battle stage from scratch and create working tool to convert OBJ meshes to FF8 battle stage. It's still mess as it's hard as hell (The UVs are messed up, there's also MOD jump that needs extreme amount of testing and also unknown GPU byte that sometimes needs to be 0x24 and sometimes 0x2C or sometimes all it show is just black screen)
Anyway, I'm tired, this is proof:
actual.jpg

It crashes after finishing camera animation in un-reversed function so I don't know what's messed up. I never had such problems earlier with test geometries before.
As it's now it doesn't require any manual tweaking
repack.jpg

max.jpg

I made some further tests and...
actually FF8 supports triangles, quads and two unknown (in wiki I noted it as padding as it's always 00, but in code it's actually get parsed just like triangles and quads, touching that produces glitches)
FF8 doesn't support 24BPP textures
FF8 doesn't support second TIM texture, it's just a binary leftover, the engine never touches this
That might be all

Fast 5k triangle test (It's much more than the most complicated battle stage):
bak.jpg

No problems at export, everything is fine. FF8 loads it without problems but there are a lot of graphical bugs, maybe I should split the meshes like in original FF8?
Anyway that's how it looks before it vanishes at the end of camera animation and soon after crashes(I think I know why, but I need to prove it)
actual.jpg
 
Last edited:
Have a look at psx gpu prims, the other types probably match onto that, POLY_FT4 etc. Perhaps the other types are g/flat/untextured shaded tri and quad?
 
I managed to somewhat break the camera structure. I updated http://wiki.ffrtt.ru/index.php/FF8/FileFormat_X#Camera_data

Case study:
a0stg101.x - balamb plains:

0x5D8 - camera start [via code]
02 00 is skipped
08 00 is pointer to camera settings
20 00 is pointer to camera animation

Let's jump to camera animation as it's the most important part here.

We will end up at 0x32. This is sub-section. We will work with pointers relatively to this position.
You'll get a ushort for number of camera animations - in this example it's 7. [a0stg101.x have 7 sets, therefore 7*8 animations, but a0stg000 or a0stg001 don't remember but they have only one!, so 1*8 animations!]

Now, when the game starts and encounter is loaded it magically resolves which animation to use to show the enemy well. You can of course break at getting camera anim index code and change the register so the camera will present the enemy like it's T-rex showing only one mosquitoe (I don't remember that fly name on Balamb's plain).

So, we are at pointers indicator, the engine now:
EAX + ECX*2 + 02, where:
-EAX - absolute memory pointer to 07 00 or 08 00 as said above;
-ECX - the camera index that it got from from static memory (pointer to pointer to some packed binary values)
02 - size of numberOfAnimations

Let's jump to last animation now:
considering 07 00 in our file is now offset 0x00:
(7-1)*2+2 = 0xE //because 00 means first index, so 7th index is in fact 6*2

Now get ushort pointer at 0xE= 0x1F10;
We will get to 0x1F10. Engine now reads 8 pointers, like in this case first pointer is 08 00 and next is 48 00. Now it calculates it by pointer*2. So first pointer actually starts at 0x16, second at 0x48*2=0x90.

UPDATE3:
grab the useful breakpoint:
0x5035AB (Newest steam english).

UPDATE4:
Engine camera animation parsing at: sub_503C8F
checking for FFFF: 00503C7B:
cmp ax, 0xFFFF, where ax is *(019399E2) (cameraSet 1+ anim 5)

Operands are bit operated...

There:
JPG.jpg
 
Last edited:
The structure that the camera animation is loaded into looks like:
Code: [Select]
Code:
struct CameraStruct { BYTE unkbyte000; //000 BYTE unkbyte001; //001 keyframe count? WORD control_word; //002 WORD unkword004; //004 WORD unkword006; //006 WORD unkword008; //008 WORD unkword00A; //00A WORD unkword00C; //00C WORD unkword00E; //00E total frame count/time? BYTE unk[20] //010 short unkword024[32]; //024 - start frames for each key frame? short unkword064[32]; //064 short unkword0A4[32]; //0A4 short unkword0E4[32]; //0E4 BYTE unkbyte124[32]; //124 short unkword144[32]; //144 short unkword184[32]; //184 short unkword1C4[32]; //1C4 BYTE unkbyte204[32]; //204 BYTE unkbyte224[128]; //224 BYTE unkbyte2A4[128]; //2A4 BYTE unkbyte324[128]; //324 BYTE unkbyte3A4[128]; //3A4 BYTE unkbyte424[128]; //424 BYTE unkbyte4A4[128]; //4A4};
and the function that parses the animation into it looks something like:
Code: [Select]
Code:
short* parse_camera(short* camera_data, CameraStruct* cam) { short* local2C; BYTE keyframecount; WORD totalframecount; short* local1C; short* local18; short* local14; short* local10;  short* current_position = camera_data; if (*current_position == 0xFFFF) {  return NULL; } cam->control_word = *current_position; current_position++; totalframecount = 0; keyframecount = 0; switch ((cam->control_word >> 6) & 3) {  case 1:   cam->unkword004 = 0x200;   cam->unkword006 = 0x200;   break;  case 2:   cam->unkword004 = *current_position;   cam->unkword006 = *current_position;   current_position++;   break;  case 3:   cam->unkword004 = *current_position++;   cam->unkword006 = *current_position++;   break; } switch ((cam->control_word >> 8) & 3) {  case 0:   cam->unkword008 = ff8vars.unkword1D977A2;   cam->unkword00A = ff8vars.unkword1D977A2;   break;  case 1:   cam->unkword008 = 0;   cam->unkword00A = 0;   break;  case 2:   cam->unkword008 = *current_position;   cam->unkword00A = *current_position;   current_position++;   break;  case 3:     cam->unkword008 = *current_position++;   cam->unkword00A = *current_position++;   break; } switch (cam->control_word & 1) {  case 0:   if (*current_position >= 0) {    do {     cam->unkword024[keyframecount] = totalframecount;     totalframecount += *current_position++ * 16;     cam->unkbyte124[keyframecount] = *current_position++;     cam->unkword064[keyframecount] = *current_position++;     cam->unkword0A4[keyframecount] = *current_position++;     cam->unkword0E4[keyframecount] = *current_position++;     cam->unkbyte204[keyframecount] = *current_position++;     cam->unkword144[keyframecount] = *current_position++;     cam->unkword184[keyframecount] = *current_position++;     cam->unkword1C4[keyframecount] = *current_position++;     keyframecount++;    } while (*current_position >= 0);    if (keyframecount > 2) {     ff8funcs.Sub50D010(cam->unkword024, cam->unkword064, cam->unkword0A4, cam->unkword0E4, keyframecount, cam->unkbyte224, cam->unkbyte2A4, cam->unkbyte324);     ff8funcs.Sub50D010(cam->unkword024, cam->unkword144, cam->unkword184, cam->unkword1C4, keyframecount, cam->unkbyte3A4, cam->unkbyte424, cam->unkbyte4A4);    }   }   break;  case 1:   if (*current_position >= 0) {    local14 = current_position + 5;    local10 = current_position + 6;    local2C = current_position + 7;    local18 = current_position + 1;    local1C = current_position + 2;    short* ebx = current_position + 3;    do {     cam->unkword024[keyframecount] = totalframecount;     totalframecount += *current_position++ * 16;     ff8funcs.Sub503AE0(++local18, ++local1C, ++ebx, *(BYTE*)current_position, &cam->unkword064[keyframecount], &cam->unkword0A4[keyframecount], &cam->unkword0E4[keyframecount]);     ff8funcs.Sub503AE0(++local14, ++local10, ++local2C, *(BYTE*)(current_position + 4), &cam->unkword144[keyframecount], &cam->unkword184[keyframecount], &cam->unkword1C4[keyframecount]);     cam->unkbyte204[keyframecount] = 0xFB;     cam->unkbyte124[keyframecount] = 0xFB;     local1C += 8;     local18 += 8;     current_position += 8;     local2C += 8;     ebx += 8;     local10 += 8;     local14 += 8;     keyframecount++;    } while (*current_position >= 0);    if (keyframecount > 2) {     ff8funcs.Sub50D010(cam->unkword024, cam->unkword064, cam->unkword0A4, cam->unkword0E4, keyframecount, cam->unkbyte224, cam->unkbyte2A4, cam->unkbyte324);     ff8funcs.Sub50D010(cam->unkword024, cam->unkword144, cam->unkword184, cam->unkword1C4, keyframecount, cam->unkbyte3A4, cam->unkbyte424, cam->unkbyte4A4);    }   }   break; } if ((cam->control_word & 0x3E) == 0x1E) {  ff8funcs.Sub503300(); } cam->unkbyte001 = keyframecount; cam->unkword00E = totalframecount; cam->unkword00C = 0; return current_position + 1;}
note: adding to a word pointer adds 2 to the value.
The size of the animation depends a lot on the control word and the amount of keyframes.
I haven't verified the code completely but it should be roughly correct with some possible minor errors.
 
Last edited:
Status
Not open for further replies.
Back
Top