Anonymous

Changes

From Final Fantasy Inside

FF7/Battle/Battle Animation (PC)

1,351 bytes added, 05:10, 23 May 2019
m
14 revisions imported
This file was written by me, L. Spiro, as can be seen by the proper grammar and perfect spelling.
Part I: Structures1.FF7FrameHeader2.FF7FrameMiniHeader3.FF7ShortVec4.FF7FrameBuffer Part II: Functions and Format1.GetBitsFixed()A. Format2.GetDynamicFrameOffsetBits(Wiki-fied by Halkun)3.GetEncryptedRotationBits(Small additions by BordePart III: Putting it All Together1.LoadFrames()2.A Sample Loop Part IV: Qhimm’s Input    
=== Part I: Structures ===
<code><pre>
typedef struct FF7FrameHeader {
DWORD dwBones; // Bones in the model + 1(unless we're dealing with a weapon animation, in which case it's value is always 1). 0x00 // This field is rather unreliable so better use the number of bones provided by the skeleton file.
DWORD dwFrames; // Frames in the animation. 0x04
DWORD dwChunkSize; // Size of the animation set. 0x08
<code><pre>
typedef struct FF7FrameMiniHeader {
//SHORT sBones; // Bones in the animation. 0x00SHORT sFrames;// Apparently, frames in the animation (but sometimes sFrames > dwFrames)
SHORT sSize; // Size of the animation data. 0x02
BYTE bKey; // A key flag used for decoding. 0x04
} * PFF7FrameMiniHeader; // Size = 5 bytes.
</pre></code>
 
NOTE: sBones should provably be called sFrames since it seems to hold a secondary frames counter. Thus, it should be equal to dwFrames. Unfortunately, it usually isn't. In fact, It's hard to say which one should actually be trusted. Apparently dwFrames is a more conservative value, meaning there will always be at least that many frames in the animation. But there can be more of them. I can't help but wonder if this means the rest of the frames are dummied out information or they serve some sort of purpose. On the other hand, sFrames is sometimes higher than the actual number of frames on the animation chunck.
 
Anyway, the actual number of frames can be computed by parsing the whole animation chunck.
 
It's also worth mentioning that there is at least one animation (15th from RSAA, the playable frog) which physically lacks the sFrames field. Instead, sSize is at 0x00 and bKey at 0x02. This animation is more than likely damaged, because FF7 doesn't seems to be able to handle it.
==== FF7FrameMiniHeader ====
This is a basic bit-reading function. It reads “dwTotalBits” from “pbBuffer” starting at the “dwStartBit”’th bit.
==== GetBitsFixed ====
1.
<code><pre>
Now that we can read the bits in the buffer we have made, it’s time to know what we’re doing!
===== A.=====
The animation data begins with one full frame that is uncompressed, but stored in one of 3 ways. Every frame after that is compressed, but compressed in one of three ways; one way can be decoded using the same method as on the first frame, which is why sometimes the second, third, and even fourth frames can be decoded using the same method as was used on the first frame.
First Frame:
==== GetDynamicFrameOffsetBits ====
The first frame is easy.
Remember that all frames after are stored as relative offsets from the frame before it.
</pre></code>
 
==== GetEncryptedRotationBits ====
Now all that is left is to decode the rotations.
Rotations change size in multiple ways.
After the function returns, we must translate the rotational INT values to their FLOAT forms (although the function can be modified to do this part itself).
This function will be called in a loop for every frame in the rotation.
 
==== LoadFrames ====
1.
</pre></code>
2.
 
==== A Sample Loop ====
This is an example loop that could be used to load a full animation.
<code></pre>
FF7FrameHeader fhHeader;
ReadFile( hFile, &fhHeader, sizeof( fhHeader ), &ulBytesRead,
// to dynamically make the model point at its target
// or face different directions during battle.
// UPDATE: Although the value of this field is 0,0,0 for most animations, some actually store a base rotation here
// so it shouldn't be ignored.
for ( DWORD I = 0; I < fhHeader.dwBones - 1; I++ ) {
fbFrameBuffer.psvRots[I+1].fX = (FLOAT)fbFrameBuffer.psvRots[I+1].iX / 4096.0f * 360.0f;
delete [] baData;
</pre></code>
 
 
 
== Part IV: Qhimm’s Input ==