Author Topic: FF7 .p Format Summary  (Read 3602 times)

Alhexx

  • *
  • Posts: 1894
    • View Profile
    • http://www.alhexx.com
FF7 .p Format Summary
« on: 2005-06-29 19:40:15 »
I know there are already several  topics about this around here in tech-related, however, I don't want to resurrect one of them. And I think someone might have made some progress here.

As most of you surely know, the FF7 .p model format has been decoded quite far, but not to 100%.

Qhimm has started hacking the .p format, ficedula did another step, and after some time, I wrote the first FF7 .p Editor, Ultima. This was 4 years ago.
There has been much progress made, especially in texturing.
I don't have the latest copy of gears here, but I think the .p description was taken from my last description, and mirex's latest description.

In the last time, some FF7 viewers and even editors have appeared. Yes, I talk about Kimera.
However, AFAIK, I'm the only one who was able to generate a .p file from scratch that FF7 accepted (unfortunately, this model was just a coloured triangle without textures)

Okay, the aim of this topic is to collect as much info as possible about the .p Format to be able to build "real" .p models from scratch.
I already have an idea how to do this, however, there is still one part of the file which I know almost nothing about - the hundrets thingy...

This post is going to be a big quote from my .p File Description, however, I have tried to correct some minor bugs here.

Notation:
All offsets are hex-decimal, all sizes are decimal.

First, here comes the table of contents of this topic:

Chapter 0: Table of Contents
Code: [Select]
.p-File
    |
    +- Header
    |
    +- Vertices[]
    |
   (+- Normals[]) ( Field-Files only)
    |
   (+- Texture Coords[]) (Textured models only)
    |
    +- Vertice Colors[]
    |
    +- Polygon Colors[]
    |
    +- Edges[]
    |
    +- Polygons[]
    |
    +- Hundrets[]
    |
    +- Groups[]
    |
    +- BoundingBox
    |
    +- Normal Index Table[]



Chapter I.i: The File Header
The structure:
Code: [Select]
typedef struct
{
long off00;
long off04;
long VertexColor;
long NumVerts;
long NumNormals;
long off14;
long NumTexCs;
long NumNormInds;
long NumEdges;
long NumPolys;
long off28;
long off2c;
long mirex_h;
long NumGroups;
long mirex_g;
long off3c;
} t_p_header; // 64 Bytes

Interpretation:
VertexColor   specifies if Vertex-Colors are used
      (0=no,1=yes; default: 1)
NumVerts   Count of Vertices
NumNormals   Count of Normals (always 0 in Battle files)
NumTexCs   Count of Texture Coords
NumNormInds   Count of Normal Indices
NumEdges   Count of Lines for WireFrame-Model
NumPolys   Count of Polygons
mirex_h      Count of Hundrets
NumGroups   Count of Groups
mirex_g      ? (sometimes 0 or 1)(but usually 1)


Chapter I.ii: The Extended Header
Misterious name for something maybe banally simple.
This is just an array of 16 32-Bit Integers. We have never been able to decode this 64 byted, however, I suppose this is some kind of checksum for the single parts of the .p file.
I don't remember what happens when you change all those values to 0, but IIRC, FF7 *should* accept those files without crashing... but I'm not sure.


Chapter II: The Vertices
The vertex structure:
Code: [Select]
typedef struct
{
float x;
float y;
float z;
} t_p_vertex; // 12 bytes


Offset: 0x80
Number of Entries: NumVerts;

I don't think that I have to add anything here. It's trivial.


Chapter III. The Normals (Field Files only)
The normal structure is exactly the same as the vertex structure.

Offset: 0x80 + (NumVerts * 12)
Number Of Entries: NumNormals;

I don't remember what they're normal to, but I think they're normal to the vertices...


Chapter IV: The Texture Coordinates
Structure:
Code: [Select]
typedef struct
{
float x;
float y;
} t_p_texturecoord; // 8 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12
Number Of Entries: NumVerts

x and y are the offsets on the texture relative to the texture size.
The values can between 0.0 and 1.0. So if a TexCoord has x = 0 and x = 0, it is in the upper left corner, if it has x = 1 and y = 1 it is in the
lower right corner (or at least I hope so...)

Simetimes, the values may be even higher than 1.0. In that case, i.e. 1.3
is the same as 0.3. So if a value exceeds 1.0, simply subtract 1.0 from
it.


Chapter V: Vertice Colors
Structure:
Code: [Select]
typedef struct
{
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char reserved;
} t_p_color; // 4 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8)
Number Of Entries: NumVerts

Every Vertex has its own color stored here.


Chapter VI: Polygon Colors
The structure is the same as the vertex color one.

Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + NumVerts * 4
Number Of Entries: NumPolys

Every Polygon has its color stored here.


Chapter VII: Edges
Structure:
Code: [Select]
typedef struct
{
unsigned short Vertex[2];
} t_p_edge; // 4 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + (NumVerts + NumPolys) * 4
Number Of Entries: NumEdges

The edges build up the wireframe of the model. Every edge connects 2 vertices of the model.


Chapter VIII: Polygons
Structure:
Code: [Select]
typedef struct
{
unsigned short Tag1;
unsigned short Vertex[3];
unsigned short Normal[3];
unsigned short Edge[3];
unsigned long Tag2;
} t_p_polygon; // 24 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + (NumVerts + NumPolys + NumEdges) * 4
Number Of Entries: NumPolys

Interpretation:
Tag      Unknown, but always 0
Vertex[*]   Index of Vertex * for the polygon
Normal[*]   Index of Normal* for the polygon (always 0 in Battle Files)
Edge[*]      Index of Edge * for WireFrame-Model
Tag2      Unknown, but always 0x0CFCEA00

Note: The Normal Indices are absolute, not relative to the Group!


Chapter IX: Hundrets
A very ugly Structure:
Code: [Select]
typedef struct
{ // 1st 2nd 3rd
long off00; // 0x00000001
long off04; // 0x00000001
char off08; // 0x00
char off09; // 0x82 0x86 0x86
short off0a; // 0x0003
char off0c; // 0x02
char off0d; // 0x00 0x04 0x04
short off0e; // 0x0002
long off10; // 0x00000000 0x00000000 0x00000001
long off14; // 0x00000000
long off18; // 0x00000000 0x00000001 0x00000001
long off1c; // 0x00000000 0x00000025 0x00000025
char off20; // 0x00 x00 x40 x80 x00 x40 x80
char off21; // 0x00 0x78 0x78
short off22; // 0x0000
long off24; // 0x00000002 0x00000001 0x00000001
long off28; // 0xFFFFFFFF
long off2c; // 0x00000000
long off30; // 0x00000000
long off34; // 0x00000002 0x00000005 0x00000005
long off38; // 0x00000001 0x00000006 0x00000006
long off3c; // 0x00000002
long off40; // 0x00000000
long off44; // 0x00000004 0x00000000 0x00000000
long off48; // 0x00000000
long off4c; // 0x00000000
long off50; // 0x00000000
long off54; // 0x00000000
long off58; // 0x00000000
long off5c; // 0x000000FF 0x00000080 0x00000080
long off60; // 0x00000000
} t_p_hundrets; // 100 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + (NumVerts + NumPolys + NumEdges) * 4 + (NumPolys * 24)
Number Of Entries: Mirex-H

I don't really know what is stored here, but I suppose it has to do with
the textures... well, dunno.
In the C/C++ Struct, there is the info how this struct is usually filled.
So if you want to create a new Hundrets Entry, you could use these
values.

I could really need some help of this part, if someone has any ideas...


Chapter X: Groups
Structure:
Code: [Select]
typedef struct
{
long polyType;
long offPoly;
long numPoly;
long offVert;
long numVert;
long offEdge;
long numEdge;
long off18; // 0
long off1c; // 0
long off20; // 0
long off24; // 0
long offTex;
long texFlag;
long texID;
} t_p_group; // 56 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + (NumVerts + NumPolys + NumEdges) * 4 + (NumPolys * 24) +

Mirex-H * 100
Number Of Entries: NumGroups

Interpretation:
polyType:   Specifies the Polygon Type for this Group:
      1 - nontextured Polygons
      2 - textured Polygons with normals
      3 - textured Polygons without normals
offPoly:   The First Polygon used in this Group
numPoly:   Number of Polygons used in this Group
offVert:   see above
numVert:   see above
offEdge:   The First Edge used in this Group
numEdge:   see below
offTex:      The first Texture Coord used in this Group
texFlag:   Texture Flag:
      0 - No texture on this Group
      1 - Textured
texID:      Index of Texture (see below)

Now it's gettin' more complicated... The numEdge value is usually 0.
So if you want to know how many Edges are used in Chunk i, then you'll
have to see the offEdge in Chunk i+1 and get the difference ... blabla ...
ahhh, take a look at this:
t_p_group.numEdge = t_p_group[i+1].offEdge - t_p_group.offEdge

BUT: If you want to get the numEdge for the LAST Group, then, of course,
you'll have to do this:

t_p_group.numEdge = t_header.NumEdges - t_p_group.offEdge

I hope you know what I mean.
BTW: If you're going to generate you own Groups, you CAN save the numEdge
values to the struct; FF7 won't crash.

As for the texID entry:
In Field files, this is the Index used by the RSD Files.
In Battle files it *should* be like this:
(Example: Yuffie: RX**)
1 - rxac
2 - rxad
3 - rxae
...


Chapter XI: Bounding Box
Structure:
Code: [Select]
typedef struct
{
float max_x;
float max_y;
float max_z;
float min_x;
float min_y;
float min_z;
} t_p_boundingbox; // 24 Bytes


Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + (NumVerts + NumPolys + NumEdges) * 4 + (NumPolys * 24) +

(Mirex-H * 100) + (NumGroups * 56) + 4
Number of Entries: 1


Chapter XII: Normal Index Table
The entries in the Normal index table are just 32-bit unsigned integers.

Offset: 0x80 + (NumVerts + NumNormals) * 12 + (NumTexCs * 8) + (NumVerts + NumPolys + NumEdges) * 4 + (NumPolys * 24) +  

(Mirex-H * 100) + (NumGroups * 56) + 4 + 24
Number Of Entries: NumNormInds

Interpretation:
Heh, it took me a long time top hack these values, too!
But it's quite simple! This is just a normal index table!
It's just an array of 32Bit integers.
That means if you want to know which normal is used by vertex *, you
will have to take a look into that table.

So let's say:
NormalIndex[0] = 4
NormalIndex[1] = 7
NormalIndex[2] = 2
...

So the Vertex 0 uses Normal 4,
...    Vertex 1 uses Normal 7,
...    Vertex 2 uses Normal 2,
and so on...






That's it. If you have any corrections or additions to make, please let me know. I'll update this post then ASAP.


 - Alhexx

_Ombra_

  • *
  • Posts: 110
    • View Profile
    • http://www.sadnescity.it
FF7 .p Format Summary
« Reply #1 on: 2005-09-01 16:56:21 »
Hey Alhexx,

why, instead of a all new editor, you don't write a plugin for an exiting software? Take MilkShape for example (http://www.swissquake.ch/chumbalum-soft/ms3d/index.html). It comes with an SDK and a pretty good editing enviroment.

This way everybody can work on it. When somebody finds out something he just has to update it.

What do you think?

mirex

  • *
  • Posts: 1645
    • View Profile
    • http://mirex.mypage.sk
FF7 .p Format Summary
« Reply #2 on: 2005-09-02 08:43:05 »
_Ombra_: Milkshape is not good enough, it does not support vertex colours, the feature which FF7 models have as a base idea.

Cyberman

  • *
  • Posts: 1572
    • View Profile
FF7 .p Format Summary
« Reply #3 on: 2005-09-02 15:15:22 »
Rephrasing:
Because FF7's engine uses colored vertices which Milkshape does not support, it won't be useable for making FF7 models.

I do believe gmax does, I was going to try to make a plugin script for it, save that it's scripting doesn't allow one to directly read binary files (only ascii). So that was a wash.  I'll have to see if max script will allow one to call a program to generate intermediate files too load into it.  In any case it's fairly dubious to make work in the end.

The other option is to make a plugin for milk shape that does the following.  Adds Texture data for each vertex of the triangles in the regions of the textures not used in the FF7 model and convert these to textured polys whose UV coords are just the vertex color of the pixels in the texture. It might be complicated but it wouldn't actually increase the size of the model (in fact it might shrink it). It wouldn't work with the PS1 variant I believe however.

Cyb