TMD Format - niemasd/PyFF7 GitHub Wiki

The TMD format contains 3D modeling data which is compatible with the PlayStation expanded graphics library (libgs). The data in a TMD file is a set of graphics primitives⁠—polygons, lines, etc.⁠—that make up a 3D object. A single TMD file can contain data for one or more 3D objects.

This wiki and this code might be a useful reference. Maybe this code too. Actually, the file linked here is the most helpful.

Section 1: File Header

A TMD file's header gives general information about the TMD file. It is 12 bytes in size.

4 bytes: Version

  • This is an unsigned integer and seems to always equal 65 (i.e., 0x41) in Final Fantasy VII

4 bytes: Flags

  • This is an unsigned integer indicating if addresses are explicit (1) or relative to the start (0)
  • This seems to always equal 0 in Final Fantasy VII

4 bytes: Number of Objects

  • This is an unsigned integer denoting the number of objects in this TMD file
  • It seems to always be at most 5,000

Section 2: Object Table

A TMD file's object table stores information about the objects contained within the TMD. Each object has the following structure and is 28 bytes in size:

4 bytes: Vertex List Start Offset

  • This is an unsigned integer indicating the start offset of the vertex list for this object

4 bytes: Vertex List Length

  • This is an unsigned integer indicating the number of vertices in the vertex list for this object

4 bytes: Normal List Start Offset

  • This is an unsigned integer indicating the start offset of the normal list for this object

4 bytes: Normal List Length

  • This is an unsigned integer indicating the number of normals in the normal list for this object

4 bytes: Primitive List Start Offset

  • This is an unsigned integer indicating the start offset of the primitive list for this object

4 bytes: Primitive List Length

  • This is an unsigned integer indicating the number of primitives in the primitive list for this object

4 bytes: Scaling Factor

  • This is a signed integer, and 2 raised to its value is the scale value
    • If this is 0, the scaling value is 20 = 1
    • If this is 2, the scaling value is 22 = 4
    • If this is -1, the scaling value is 2-1 = 0.5
  • It seems to always be 0 in Final Fantasy VII

Section 3: Primitive Data

Each primitive has the following structure and is ??? bytes in size:

1 byte: Size of 2D Drawing Primitives (olen)

  • This is an unsigned integer denoting the size of 2D drawing primitives that are generated by intermediate processing

1 byte: Size of Packet Data Section (ilen)

  • This is an unsigned integer denoting the size of the packet data section

1 byte: Flags

  • This is an unsigned integer whose bits represent various flags in the following format: 00000GFL
    • The 5 left-most (i.e., most significant) bits are all 0
    • The next bit is the GRD flag
      • This is only valid for the polygon not textured, subjected to light source calculation
      • 1 = Gradation polygon
      • 0 = Single-color polygon
    • The next bit is the FCE flag
      • This is only valid when the Code value refers to a polygon (see Primitive Mode section)
      • 1 = Double-faced polygon
      • 0 = Single-faced polygon
    • The right-most (i.e., least significant) bit is the LGT flag
      • 1 = Light source calculation not carried out
      • 0 = Light source calculation carried out

1 byte: Mode

  • This is an unsigned integer whose bits are in the following format: CCCOOOOO
    • The 3 left-most (i.e., most significant) bits represent this primitive's Code
      • 000 = 0 = UNKNOWN!!! Final Fantasy VII has primitives like this, though!!! Not in Sony documentation
      • 001 = 1 = Polygon (triangle, quadrilateral)
      • 010 = 2 = Straight line
      • 011 = 3 = Sprite
    • The 5 right-most (i.e., least significant) bits represent this primitive's Option
      • See each primitive type's description for information of what's in the Option bits

Section 4: Vertex Data

Each vertex has the following structure and is 8 bytes in size:

2 bytes: X Coordinate

  • This is a signed integer denoting the X coordinate of this vertex

2 bytes: Y Coordinate

  • This is a signed integer denoting the Y coordinate of this vertex

2 bytes: Z Coordinate

  • This is a signed integer denoting the Z coordinate of this vertex

2 bytes: Padding

  • This is unused padding

Section 5: Normal Data

Each normal has the following structure and is 8 bytes in size:

2 bytes: X Coordinate

  • This is a signed integer denoting the X coordinate of this normal

2 bytes: Y Coordinate

  • This is a signed integer denoting the Y coordinate of this normal

2 bytes: Z Coordinate

  • This is a signed integer denoting the Z coordinate of this normal

2 bytes: Padding

  • This is unused padding

Primitive Types

There are many different types of primitives, and each must be handled in its own way. Recall that the Code byte of a primitive is in the format CCCOOOOO, where the 3 left-most (i.e., most significant) bits, CCC, are the Code, and the 5 right-most (i.e., least significant) bits, OOOOO, are the Option.

Code 000 = 0: Unknown

Some primitives in Final Fantasy VII have Code = 000 = 0, but this is not documented in the Sony PlayStation TMD format description. Need to figure out how to decode these primitives.

Code 001 = 1: Polygon (Triangle or Quadrilateral)

When Code = 001 = 1, the primitive is a polygon (i.e., triangle or quadrilateral). Polygons have the following bits in their Mode byte:

  • The 3 left-most (i.e., most significant) bits are the Code (001)
  • The next bit is the Shading Mode (IIP) flag
    • 0 = Flat Shading
    • 1 = Gouraud Shading
  • The next bit determines if this is a 3-vertex (i.e., triangle) or 4-vertex (i.e., quadrilateral) polygon
    • 0 = 3-vertex (i.e., triangle)
    • 1 = 4-vertex (i.e., quadrilateral)
  • The next bit is the Texture Specification (TME) flag
    • 0 = Off
    • 1 = On
  • The next bit is the Translucency Processing (ABE) flag
    • 0 = Off
    • 1 = On
  • The right-most (i.e., least significant) bit is the Brightness Calculation (TGE) flag
    • 0 = On = Brightness is calculated at the time of texture mapping
    • 1 = Off = Draw texture as-is

Code 010 = 2: Straight Line

When Code = 010 = 2, the primitive is a straight line. Straight lines have the following bits in their Mode byte:

  • The 3 left-most (i.e., most significant) bits are the Code (010)
  • The next bit is the Gradation (IIP) flag
    • 0 = Gradation Off (Monochrome)
    • 1 = Gradation On
  • The next 2 bits are 0
  • The next bit is the Translucency Processing (ABE) flag
    • 0 = Off
    • 1 = On
  • The right-most (i.e., least significant) bit is 0

Code 011 = 3: Sprite

When Code = 011 = 3, the primitive is a sprite. Sprites have the following bits in their Mode byte:

  • The 3 left-most (i.e., most significant) bits are the Code (011)
  • The next 2 bits denote the size of the sprite
    • 00 = Free Size (specified in the primitive data by W and H)
    • 01 = 1 by 1
    • 10 = 8 by 8
    • 11 = 16 by 16
  • The next bit is 1
  • The next bit is the Translucency Processing (ABE) flag
    • 0 = Off
    • 1 = On
  • The right-most (i.e., least significant) bit is 0

Summary

  • File Header
    • 4 bytes (Version)
    • 4 bytes (Flags)
    • 4 bytes (Number of Objects, nO)
  • Object Table (nO entries)
    • Object 1
      • 4 bytes (Vertex List Start)
      • 4 bytes (Vertex List Length, nV)
      • 4 bytes (Normal List Start)
      • 4 bytes (Normal List Length, nN)
      • 4 bytes (Primitive List Start)
      • 4 bytes (Primitive List Length, nP)
      • 4 bytes (Scaling Factor)
    • ...
    • Object nO
      • ...
  • Primitive Data
    • Primitive 1
      • 1 byte (olen)
      • 1 byte (ilen)
      • 1 byte (Flag)
      • 1 byte (Mode)
    • ...
  • Vertex Data
    • Vertex 1
      • 2 bytes (X Coordinate)
      • 2 bytes (Y Coordinate)
      • 2 bytes (Z Coordinate)
      • 2 bytes (Padding)
    • ...
  • Normal Data
    • Normal 1
      • 2 bytes (X Coordinate)
      • 2 bytes (Y Coordinate)
      • 2 bytes (Z Coordinate)
      • 2 bytes (Padding)
    • ...
⚠️ **GitHub.com Fallback** ⚠️