Welcome to Data Crystal's new home! Data Crystal is now part of the TCRF family (sort of).
The wiki has recently moved; please report any issues in Discord. Pardon the dust.

Vagrant Story/SEQ files: Difference between revisions

From Data Crystal
Jump to navigation Jump to search
(Created page with "Seq (Sequence) Files are animation files that are used to animate the games 3D models. The file format is not yet understood. Much of what follows was decyphered by Valendian ...")
 
(Added information about skeletal poses with rotations and some other things)
Line 1: Line 1:
Seq (Sequence) Files are animation files that are used to animate the games 3D models. The file format is not yet understood. Much of what follows was decyphered by Valendian and another reverse engineer whose shall remain anonymous until I have their permission to reveal their identity.
Seq (Sequence) Files are animation files that are used to animate the games 3D models. The file format is not yet understood. Much of what follows was decyphered by Valendian and another reverse engineer whose shall remain anonymous until I have their permission to reveal their identity.
It seems that each SeqFrameHeader defines a different skeletal/bone pose. Animations are achieved by interpolating between these poses. But there is more done to the joints with the Opcodes, which still has to be uncovered. Note that, for example in a jump, the different poses are hardcoded. So it seems that not all animation info is stored in the SEQ files.
Every joint is assigned a rotation, but translation vectors, except for the complete model, have not been found yet.


  typedef struct tSeqHeader {
  typedef struct tSeqHeader {
Line 21: Line 25:
  typedef struct tSeqFrameHeader {    // seq Frame Header
  typedef struct tSeqFrameHeader {    // seq Frame Header
     uhalf Unknown1;                  // [1]
     uhalf Unknown1;                  // [1]
     ubyte Unknown2;                 // [1]
     ubyte idOtherPose;               // [1]
     ubyte Unknown3;                  // [1][2]
     ubyte Unknown3;                  // [1][2]
     uhalf PtrNextFrame;              // [1]
     uhalf PtrNextFrame;              // [1]
Line 30: Line 34:
  } SEQ_FRAMEHDR[NumSequences];
  } SEQ_FRAMEHDR[NumSequences];
  // FrameHdr.Unknown1                  #
  // FrameHdr.Unknown1                  #
  // FrameHdr.Unknown2                  #
  // FrameHdr.idOtherPose                # see below
  // FrameHdr.Unknown3                  #
  // FrameHdr.Unknown3                  #
  // FrameHdr.PtrNextFrame              #
  // FrameHdr.PtrNextFrame              #
  // FrameHdr.PtrThisFrame              #
  // FrameHdr.PtrThisFrame              #
  // FrameHdr.PtrUnknownFrame            #
  // FrameHdr.PtrUnknownFrame            #
  // FrameHdr.JointPtr0[Hdr.NumJoints]  # points to animation data for each joint per frame
  // FrameHdr.JointPtr0[Hdr.NumJoints]  # points to rotation and animation data for each joint per frame
  // FrameHdr.JointPtr1[Hdr.NumJoints]  # points to animation data for each joint per frame
  // FrameHdr.JointPtr1[Hdr.NumJoints]  # points to animation data for each joint per frame
// if FrameHdr.idOtherPose is not 0xFF = -1, then it's an id of a
// different pose, and the FrameHdr.JointPtr0/1 of the OTHER pose is
// used instead!
   
   
  ubyte Section2[Hdr.NumFrames];      // [1]
  ubyte Section2[Hdr.NumFrames];      // [1]
Line 49: Line 57:
  // FrameDatahdr.Unknown1  #
  // FrameDatahdr.Unknown1  #
  // FrameDatahdr.Unknown2  #
  // FrameDatahdr.Unknown2  #
// if FrameHdr.idOtherPose is 0xFF, this is the first thing that
// appears at any offset given by FrameHdr.JointPtr[0]
// use this instead of SEQ_FrameDataHDR
typedef struct tSeqFrameDataRotation { // seq Frame Data Rotation
    ubyte x1;
    ubyte x2;
    ubyte y1;
    ubyte y2
    ubyte z1;
    ubyte z2;
} SEQ_FrameDataRotation;
// rotation is represented by euler angles
// stored in big endian!
// angles are computed like this:
// x = ((x1 << 8) | x2) << 1; // rotation around x axis
// 0x0000 is 0 degrees, 0x0400 is 90 degrees, 0x0800 is 180 degrees and 0x0c00 is -90 degrees
// the bone is first rotated around the x axis, then y, then z
   
   
  typedef struct tSeqFrameOpcode {
  typedef struct tSeqFrameOpcode {
Line 81: Line 108:
  SEQ_FRAMEHDR FrameHdr[NumSequences];
  SEQ_FRAMEHDR FrameHdr[NumSequences];
  ubyte        Section2[Hdr.NumFrames];
  ubyte        Section2[Hdr.NumFrames];
// probably not for each sequence
  for (NumSequences) {
  for (NumSequences) {
     SEQ_FrameDataHDR FrameDataHdr;
     SEQ_FrameDataHDR FrameDataHdr;

Revision as of 19:06, 6 April 2013

Seq (Sequence) Files are animation files that are used to animate the games 3D models. The file format is not yet understood. Much of what follows was decyphered by Valendian and another reverse engineer whose shall remain anonymous until I have their permission to reveal their identity.

It seems that each SeqFrameHeader defines a different skeletal/bone pose. Animations are achieved by interpolating between these poses. But there is more done to the joints with the Opcodes, which still has to be uncovered. Note that, for example in a jump, the different poses are hardcoded. So it seems that not all animation info is stored in the SEQ files.

Every joint is assigned a rotation, but translation vectors, except for the complete model, have not been found yet.

typedef struct tSeqHeader {
    ubyte NumFrames;
    ubyte padding;
    uhalf NumJoints;
    word  FileSize;
} SEQ_HEADER;
// Hdr.NumFrames   # number of frames of animation
// Hdr.NumJoints   # number of joints in the target skeleton (must match that of the shp file)
// Hdr.FileSize    # size of file data (minus this header) in bytes aligned to a 4 byte boundary
// the number of sequences must be calculated at runtime. its not actually needed but nice to know

typedef struct tSeqPointerTable {    // seq Pointer Table
    word  PtrSection3;               // [1][2][3][4]
    word  PtrSection2;               // [1][2][3]
} SEQ_PTRTBL;
// SeqPointerTable.PtrSection3   # pointer to the frame data section
// SeqPointerTable.PtrSection2   # pointer to the next sequence section

typedef struct tSeqFrameHeader {     // seq Frame Header
   uhalf Unknown1;                  // [1]
   ubyte idOtherPose;               // [1]
   ubyte Unknown3;                  // [1][2]
   uhalf PtrNextFrame;              // [1]
   uhalf PtrThisFrame;              // [1][2]
   uhalf PtrUnknownFrame;           // [1][2]
   uhalf JointPtr0[Hdr.NumJoints];  // [1][2]
   uhalf JointPtr1[Hdr.NumJoints];  // [1]
} SEQ_FRAMEHDR[NumSequences];
// FrameHdr.Unknown1                   #
// FrameHdr.idOtherPose                # see below
// FrameHdr.Unknown3                   #
// FrameHdr.PtrNextFrame               #
// FrameHdr.PtrThisFrame               #
// FrameHdr.PtrUnknownFrame            #
// FrameHdr.JointPtr0[Hdr.NumJoints]   # points to rotation and animation data for each joint per frame
// FrameHdr.JointPtr1[Hdr.NumJoints]   # points to animation data for each joint per frame

// if FrameHdr.idOtherPose is not 0xFF = -1, then it's an id of a
// different pose, and the FrameHdr.JointPtr0/1 of the OTHER pose is
// used instead!

ubyte Section2[Hdr.NumFrames];       // [1]
// specifies the sequence that follows on from this one (or looping)

typedef struct tSeqFrameDataHeader { // seq Frame Data Header
    ubyte Unknown0;                  // [1][2] # (unaligned half's are read as two bytes)
    ubyte Unknown1;                  // [1][2] # (unaligned half's are read as two bytes)
    ubyte Unknown2;                  // [1][2] # (unaligned half's are read as two bytes)
} SEQ_FrameDataHDR;
// FrameDatahdr.Unknown0   #
// FrameDatahdr.Unknown1   #
// FrameDatahdr.Unknown2   #

// if FrameHdr.idOtherPose is 0xFF, this is the first thing that
// appears at any offset given by FrameHdr.JointPtr[0]
// use this instead of SEQ_FrameDataHDR

typedef struct tSeqFrameDataRotation { // seq Frame Data Rotation
    ubyte x1;
    ubyte x2;
    ubyte y1;
    ubyte y2
    ubyte z1;
    ubyte z2;
} SEQ_FrameDataRotation;
// rotation is represented by euler angles
// stored in big endian!
// angles are computed like this:
// x = ((x1 << 8) | x2) << 1; // rotation around x axis
// 0x0000 is 0 degrees, 0x0400 is 90 degrees, 0x0800 is 180 degrees and 0x0c00 is -90 degrees
// the bone is first rotated around the x axis, then y, then z

typedef struct tSeqFrameOpcode {
    ubyte Opcode;
    ubyte Param1;
    ubyte Param2;
    ubyte Param3;
    ubyte Param4;
    uhalf Param5;
} SEQ_FrameOpcode;
// FrameOpcode.Opcode   # the 3 most significant bits flag the existence of parameters
// FrameOpcode.Param1   # present if  ((FrameOpcode.Opcode & 0x80) == 0x80)
// FrameOpcode.Param2   # present if  ((FrameOpcode.Opcode & 0x40) == 0x40)
// FrameOpcode.Param3   # present if  ((FrameOpcode.Opcode & 0x20) == 0x20)
// FrameOpcode.Param4   # present if (((FrameOpcode.Opcode & 0xE0) == 0x00)
//                                &&  ((FrameOpcode.Opcode & 0x03) != 0x03))
// FrameOpcode.Param5   # present if  ((FrameOpcode.Opcode & 0xE0) == 0x00)

typedef struct tSeqSequenceEnd { // appears to control looping animations
    uhalf Unknown1;                  // [1][2] # (unaligned half's are read as two bytes)
    uhalf Unknown2;                  // [1][2] # (unaligned half's are read as two bytes)
    uhalf Unknown3;                  // [1][2] # (unaligned half's are read as two bytes)
} SEQ_SequenceEnd;
// SequenceEnd.Unknown1   # sometimes first byte is read as an opcode
// SequenceEnd.Unknown2   #
// SequenceEnd.Unknown3   #

// putting it all together the file has the following layout
runtime word NumSequences = (PtrTbl.PtrSection2 - &FrameHdr[0]) / (0x0A + 4*Hdr.NumJoints);
SEQ_HEADER   Hdr;
SEQ_PTRTBL   PtrTbl;
SEQ_FRAMEHDR FrameHdr[NumSequences];
ubyte        Section2[Hdr.NumFrames];

// probably not for each sequence
for (NumSequences) {
   SEQ_FrameDataHDR FrameDataHdr;
   for (?) {
   	SEQ_FrameOpcode Opcode;
   }
}