VBM Specification by Heiko Herrmann

From Red Faction Wiki
Revision as of 09:12, 3 October 2019 by Goober (talk | contribs)

I'm posting Heiko Herrmann's VPP and VBM file specifications here to archive so they're not lost to the sands of time.

NOTE: If you're looking for the source code for the VPPBuilder32 software (which utilizes these file specifications), you can find it here: VPPBuilder32 Source Code.


VPP Specification


Introduction VPP files are essentially file archives like *.ZIP or *.RAR files. However VPP files are not compressed. They serve to "package" all the files needed by the game (maps, interface graphics, models, textures, music and all other game data).

The VPP file format is similar but still different from the VP format, that Volition Inc. used in the FreeSpace series. The biggest differences are probably the clustering into 0x0800 blocks (see below) and the missing of a directory structure. As a minor difference you will also note that VPPs do not save timestamps for the files anymore and that filenames' maximum length has been extended from 32 to 60 characters.

Please note that the following information is unconfirmed and was hacked out by myself (Heiko Herrmann) of the Summoner Demo PC V1.0 summoner.vpp file. While it seems to be unchanged in Summoner Full Version (both PC and PlayStation), as well as all versions of Red Faction, I give absolutely no guarantee that the information on this page is correct.

File format The file has the following structure:

//At offset 0x0000: Header
int signature; //0x51890ace
int version; //0x00000001
int num_files;
int filesize; //Size of the complete .VPP file

...2032 unused bytes...

//At offset 0x0800: Directory (64=0x0040 bytes long for each entry)
for(int i=0;i<num_files;i++)
{
    char filename[60];
    int size;
}

The first file starts at the next offset adress after the directory that is dividable by 0x0800. The second file starts again at the next offset adress after (offset of first file+size of first file) that is dividable by 0x0800.

Example: Summoner Demo PC 1.0 has 0x0962 files, so the directory ends at 0x00000800+0x0962*0x0040=0x00026080. So the first file starts at 0x00026800, since that is the next adress that is dividable by 0x0800. The first file is 0x00010515 bytes long, so it ends at 0x00026800+0x00010515=0x00036d15. So the second file starts at 0x00037000.

In other words, Volition uses "clusters" of 0x0800 (=2048 bytes) in Summoner for each file, the directory and even the header. The non-used space is just filled up with "00" bytes. In the worst case this means that 2047 bytes are wasted, which happens when a file is 1 or 2049 or 4097 or... bytes long. I am not sure why they do this, but my guess would be that there are performance reasons reading files in 2048-byte-blocks into memory.

Here is some example code:

	//Load header
	f->Read(&m;_Signature,4); ASSERT(m_Signature==0x51890ace);
	f->Read(&m;_Version,4); ASSERT(m_Version==0x00000001);
	f->Read(&m;_NumFiles,4);
	f->Read(&m;_Filesize,4);
	f->Seek(_2048isize(f->GetPosition()),CFile::begin);
	int offset=_2048isize(2048+64*m_NumFiles);
        
	//Load directory
	for(int i=0;i<m_NumFiles;i++)
	{
		m_Files''.filename=ReadString(f,60);
		f->Read(&m;_Files''.filesize,4);
		m_Files''.offset=offset;
		offset+=_2048isize(m_Files''.filesize);
		//TRACE("File %s, size %i\n",m_Files''.filename,m_Files''.filesize);
	}
        (...)



//Chunks
int _2048isize(int x)
{
	if(x%2048==0)
		return x;
	x-=x%2048;
	x+=2048;
	return x;
}

//Read a null-terminated but fixed-length string
CString ReadString(CFile *f, int len)
{
	char *t=new char[len];
	f->Read(t,len);

	CString ret="";
	BOOL _0passed=FALSE;
	for(int i=0;i


VBM Specification


Introduction The VBM file format, used in Summoner and Red Faction is a very simple non-compressed bitmap animation format with mipmap support (mipmaps are used for lower detail levels). So it is a lot easier to read/generate than the ANI file format, which Volition Inc. used in Conflict/Descent: FreeSpace and its successor FreeSpace 2. VBM supports 16-bit color depth, with an optional alpha (=transparency) channel (see color formats below for details).

These specs were hacked out and written by Heiko Herrmann. They are by no means "official" and so I give no guarantee for the correctness of the information you find here. Of course I would love to get feedback when you find errors or missing information in these specs :).

File format The file begins with a 32-byte header, directly followed by the first frame first mipmap data. If the file has more than one mipmap the second mipmap level of the first frame with half the width and half the height follows next. The second frame (with all mipmaps) starts after all mipmaps of the first frame. And so on. There is no file footer, so the file ends directly after the last mipmap of the last frame. Here are the specs:

#define COLORFORMAT_1555   0
#define COLORFORMAT_4444   1
#define COLORFORMAT_565    2

char signature[4]; //".vbm"
int unknown; //sometimes 1, sometimes 2 - not sure what it means
int width;
int height;
int color_format; //see COLORFORMAT_* constants
int fps;
int num_frames;
int num_mipmaps; //excluding the primary mipmap

for(int frame=0;frame<num_frames;frame++)
{
    for(int mipmap=0;mipmap<num_mipmaps+1;mipmap++)
    {
        unsigned char bitmapdata[width*height*2/pow(4,mipmap)]; //(*)
    }
}

(*) Note: The first mipmap always has the full width*height pixels. The second has half width * half height, so we need to divide it by 4 (=pow(4,1)). Each following has pow(4,x) pixels, since both the width and the height are always divided by 2.

The multiplication with 2 is needed because every pixel needs two bytes (see below).

Color format Each pixel needs two bytes, since they are always stored with 16-bit color depth. The format of the color depth can be one of the following (as specified in color_format):

COLORFORMAT_1555 (=0)

5 bits of color for the RG and B channels, 1 bit for alpha. Use this when you have alpha, but it's only the "on/off" variety. Pixels are either totally opaque or totally transparent. Useful largely for interface art.

 byte 0   byte 1
01234567 01234567
RRRRRGGG GGBBBBBA 

COLORFORMAT_4444 (=1)

4 bits for each RGB channel, 4 bits for alpha. Use this format when you want a full, varied alpha channel with semi-transparency.

 byte 0   byte 1
01234567 01234567
RRRRGGGG BBBBAAAA 

COLORFORMAT_565 (=2)

5 bits of alpha for the R and B channels, 6 bits for Green, and zero for alpha. Use this when the image needs no alpha transparency at all.

 byte 0   byte 1
01234567 01234567
BBBBBGGG GGGRRRRR