PPF File Format
Previous  Top  Next

Psychonauts .PPF File Format:
By Bgbennyboy and Benjamin Haisch

4 bytes: Header 'PPAK'

Texture section
Contains .dds files without the standard header.
2 bytes - Number of DDS files

Repeat for each file:
40 bytes - ?
2 bytes - Filename length
X bytes - Filename
44 byte header:
2 bytes - ID number
2 bytes - ID2 number
2 bytes - Texture ID
10 bytes - ?
4 bytes - Texture width
4 bytes - Texture height
4 bytes - Number of mipmaps
4 bytes - ?
2 bytes - New Texture ID (used only if 'animated' image, see below)
10 bytes - ?
X bytes - Texture data

Size of texture data depends on the Texture ID:
0: Size=(Width*Height)*4
6: Size=(Width*Height)
9: Size=(Width * Height) div 2
10: Size=(Width * Height)
11: Size=(Width * Height)
12: Size=(Width*Height)*2
14: Size=(Width*Height) (+1024 if has a palette)

If the file is a cubemap (see the filename) then:
Size=Size*6

However:
The 44 byte header is larger for some files:

If ID2=0 and TextureID=0 then the file is an animation.
It contains multiple dds images within one file:

Number of frames=ID1
Texture ID=New Texture ID
After the 44 byte header:
4 bytes - Texture width
4 bytes - Texture height
4 bytes - Number of mipmaps
16 bytes - ??
Essentially this format is just a container. To parse this format you use
exactly the same methods as below.
After the first block of texture data there follows 'Number of frames' -1
dds files that are set out as normal, with a 44 byte header and then the data.
Also note that the frames within this file do not count as seperate dds files
(in the texture section header).


If Texture ID=14 then:
2 bytes - HasPalette
If HasPalette = 1 or 256 then image has a palette.
Palette has 256 entries so is always 1024 bytes in size.
After the palette data comes the texture data as usual.


The file size also depends on whether the image has mipmaps.
Each mipmap is one-fourth the size of the previous. So the size of each mipmap
has to be worked out and added to the total size. The method of working out
the size depends on the texture id.
So if texture ID was 0 with 8 mipmaps and size of 256*256 then:
262144
65536
16384
4096
1024
256
64
16
total size = 349520


If the TextureID = 9, 10 or 11 things are slightly different:
These use DXT compression so when working out the size:
If TextureID = 9 {DXT1} then minimum mipmaps size = 8
If TextureID = 10 or 11 {DXT3 + DXT5} then minimum mipmaps size = 16

Furthermore if TextureID = 9, 10 or 11 and the image is non-square (eg 256*128) then
the following method must be used at each mipmap level:
Size = max(1,width ÷ 4)x max(1,height ÷ 4) x 8(If ID 9) or x16 (If ID 10 + 11)

Annoyingly there's a further layer of complexity:
If textureID = 0 or 14 then the number of mipmaps reported by the file may be wrong.
So for these 2 ID's the number of mipmaps needs to be corrected:

if (mipmaps=0) or (width <> height) then
begin
temp:=min(width, height);
case temp of
512: mipmaps:=10;
256: mipmaps:=9;
128: mipmaps:=8;
64: mipmaps:=7;
32: mipmaps:=6;
16: mipmaps:=5;
8: mipmaps:=4;
4: mipmaps:=3;
2: mipmaps:=2;
1: mipmaps:=1
else
Mipmaps:=1;
end;
end;

Texture ID's Correspond to:
0: 8:8:8: argb
6: ??
9: DXT1
10: DXT3
11: DXT5
12: 8:8:8: rgb ???
14: ??

Model section.
4 bytes - 'MPAK'
2 bytes - Number of model files
Repeat for each file:
*2 bytes - Filename length
*X bytes - Filename
*2 bytes -??
*4 bytes - Size of model data
*X bytes - Model Data

Named scripts section.
Repeat for each file:
*2 bytes - Filename length
*X bytes - Filename
*4 bytes - Size of script data
*X bytes - Script data

Unnamed scripts section.
*4 bytes - Size of script data
*X bytes - Script data

Level section
Not present in some files (eg common.ppf)
Size = remaining file data