UFO:Aftermath is a trademark of Altar interactive. This document is not a product of Altar interactive, who should not be associated with it. Nor will they provide any support for it. You may use this document freely as long as you dont take out a fee for it. None of the information contained is guaranteed to be correct. The author can not be held responsible for any consequences related to the use or misuse of the information contained herein.
VFS-files are basically raw images of a FAT-style file system. As such they are easy to change dynamically but suffers from fragmentation. That a game uses a file system this way is very unusual.
A VFS consists of these four segements:
Name: | Size: |
header fat table root directory clusters | sizeof(header) clustercount*sizeof(fat_entry) maxrootfiles*sizeof(file_entry) clustersize*clustercount |
The header describes the file system. It contains version information, sizes and limits of the systems and an md5 checksum. The header is followed by the fat table, which has one entry for each cluster. The entry describes if the cluster is used, and at what cluster the file that own it continues. The root directory is a fixed-size area that contains the file entries for the root files/directories. Directories are normal files that contains sequences of file_entry. And last are the clusters.
struct header { float version // 1.0 uint32 clustersize uint32 clustercount uint32 maxrootfiles // 32 in save games, 64 otherwise uint32 zero // could be that maxrootfiles is 64bit uint32 filenamelength // always 64 uint32 windowsize // for compressed data, always 50000 char md5sum[16] // calculated from offset 44 to EOF uint32 versionstringlength // always 256 char versionstring[256] uint32 usedclusters } struct file_entry { char name[64] // padded with '\0' char unknown1[4] uint32 type // 1=file,2=directory,9=compressed file char unknown2[4] // always 0xFFFFFFFF uint32 startcluster // one-based uint32 size uint32 size_uncompressed // 0 if uncompressed } struct fat_entry { uint32 usage // 0=unused,1=used uint32 nextcluster // 0xFFFFFFFF to signal end cluster }
The compressed files, type 9, are series of compressed chunks. The compression library is used is regular zlib. Each uncompressed chunk is at most the size of windowsize from the header. Before each chunk is a marker, uint32, that describes the size of the chunk. Then follows the compressed data. On some files the last marker is repeated after the last chunk, this is probably caused by a bug in the generator tool.
Note that there is often garbage in the blocks that comprise directories, these are the result of reused buffers during the creation of the vfs. Always respect the size given in the entry and dont rely on unused entries being zeroed out. Except in the root directory where there is no other options.
In the type field of file_entry the gap between 2 and 9 could be the result of bit 3 being a flag the signals compression, this way, theoretically there can be compressed directories.
Cluster indices range from 1 and up to and including clustercount
There is still alot of loose ends.