PCF

The PCF format is an implementation of DMX serialization to store particle data.
== Origin ==
Prior to the Orange Box, particles were handled exclusively in code. To increase flexibility, particles were moved to independent files interpreted dynamically by the engine.
== File Format ==
Currently, there are five known versions: Binary 2 DMX 1 (DoD:S), Binary 2 PCF 1 (Orange Box), Binary 3 PCF 2 (Left 4 Dead), Binary 4 PCF 2 (Left 4 Dead 2), and Binary 5 PCF 2 (Alien Swarm). The serialization of each is basically the same. All strings are null-terminated and other data is little-endian. Version differences only seem to describe what set of particle operators the PCF uses. More specific version differences are described later.
A notable exception is the Source Particle Benchmark tool, where PCF files are plain KeyValues2 files.
A PCF file has three main sections: the string dictionary, the element dictionary, and the data itself.
Fun Fact: Valve's PCFs are five bytes larger than necessary.
==== Header ====
PCF files use a "magic string" for a header:
String 	Version
<!-- dmx encoding binary 2 format dmx 1 -->	Only found with DoD:S. Predecessor to and/or interchangeable with Binary 2 PCF 1?
<!-- dmx encoding binary 2 format pcf 1 -->	Orange Box
<!-- dmx encoding binary 3 format pcf 1 -->	clouds.pcf found in Portal 2.
<!-- dmx encoding binary 3 format pcf 2 -->	Left 4 Dead, various unused Portal 2 particles such as zombie.pcf, paint_fizzler.pcf, and chicken.pcf
<!-- dmx encoding binary 4 format pcf 2 -->	Left 4 Dead 2
<!-- dmx encoding binary 5 format pcf 2 -->	Alien Swarm, Portal 2, Counter-Strike: Global Offensive, Titanfall branch
==== String Dictionary ====
The string dictionary starts with the number of strings to follow. For Binary 4 PCF 2, the number is an (unsigned?) int, otherwise, an (unsigned?) short.
Unlike previous versions, Binary 4 PCF 2 has every string in the dictionary. Strings that would normally appear as-is in the two remaining sections are serialized as offsets.
==== Element Dictionary ====
The element dictionary starts with an (unsigned?) int with the number of element structures to follow.
(In general practice, any int's that count structures and/or elements are unsigned in nature, as it is highly improbable to have a negative count of them, and also prevents any integer-overflow bugs.)
For Binary 2 DMX 1, Binary 2 PCF 1, and Binary 3 PCF 2:
struct CDmxElement
{
	unsigned short typeNameIndex; // String dictionary index
	std::string elementName; // Element name
	unsigned char dataSignature[16]; // Globally unique identifier
};
For Binary 4 PCF 2:
struct CDmxElement
{
	unsigned short typeNameIndex; // String dictionary index
	unsigned short elementNameIndex; // String dictionary index
	unsigned char dataSignature[16]; // Globally unique identifier
};
For Binary 5 PCF 2:
struct CDmxElement
{
	unsigned int typeNameIndex; // String dictionary index
	unsigned int elementNameIndex; // String dictionary index
	unsigned char dataSignature[16]; // Globally unique identifier
};
==== Data ====
Every element owns a list of attributes. An attribute may contain generic data, or reference another element.
Attributes appear in the order of each element, preceded by an integer count. This listing continues through the end of the file.
struct CDmxAttribute
{
	unsigned short typeNameIndex; // String dictionary index (unsigned int for Binary 5)
	unsigned char attributeType; // See below tables
	void *attributeData; // Pointer to data, use attributeType to safely deference
};
Attribute 	Type	Size 	Default Value 	Notes
ATTRIBUTE_ELEMENT 	0x01	4 Bytes 	? 	Integer index into element array
ATTRIBUTE_INTEGER 	0x02	4 Bytes 	0 	int
ATTRIBUTE_FLOAT 	0x03	4 Bytes 	0.0 	float
ATTRIBUTE_BOOLEAN 	0x04	1 Byte 	false 	bool
ATTRIBUTE_STRING 	0x05	1+ Byte(s)	Empty 	C-style string. For Binary 4 PCF 2, unsigned short
ATTRIBUTE_BINARY 	0x06	4+ Bytes 	Empty 	Array of char preceded by integer count
ATTRIBUTE_TIME 	0x07	4 Bytes 	0.0 	Technically float, written as (int)( float * 10000.0 ), read as ( int / 10000.0 )
ATTRIBUTE_COLOR 	0x08	4 Bytes 	0 0 0 0 	unsigned char red , green , blue , alpha;
ATTRIBUTE_VECTOR2 	0x09	8 Bytes 	0.0 0.0 	float x , y;
ATTRIBUTE_VECTOR3 	0x0A	12 Bytes 	0.0 0.0 0.0 	float x , y , z;
ATTRIBUTE_VECTOR4 	0x0B	16 Bytes 	0.0 0.0 0.0 0.0	float x , y , z , w;
ATTRIBUTE_QANGLE 	0x0C	12 Bytes 	0.0 0.0 0.0 	Same as ATTRIBUTE_VECTOR3
ATTRIBUTE_QUATERNION	0x0D	16 Bytes 	0.0 0.0 0.0 1.0	Same as ATTRIBUTE_VECTOR4, but w defaults to 1
ATTRIBUTE_MATRIX 	0x0E	64 Bytes 	Identity matrix	float[4][4];
Each type has an array counterpart. The data is preceded by an integer count. An array may be empty.
Attribute 	Type
ATTRIBUTE_ELEMENT_ARRAY 	0x0F
ATTRIBUTE_INTEGER_ARRAY 	0x10
ATTRIBUTE_FLOAT_ARRAY 	0x11
ATTRIBUTE_BOOLEAN_ARRAY 	0x12
ATTRIBUTE_STRING_ARRAY 	0x13
ATTRIBUTE_BINARY_ARRAY 	0x14
ATTRIBUTE_TIME_ARRAY 	0x15
ATTRIBUTE_COLOR_ARRAY 	0x16
ATTRIBUTE_VECTOR2_ARRAY 	0x17
ATTRIBUTE_VECTOR3_ARRAY 	0x18
ATTRIBUTE_VECTOR4_ARRAY 	0x19
ATTRIBUTE_QANGLE_ARRAY 	0x1A
ATTRIBUTE_QUATERNION_ARRAY	0x1B
ATTRIBUTE_MATRIX_ARRAY 	0x1C
==Hex hacking==
It is possible to load L4D1 Particles into a Source 2009 mod using a simple hex hack. 
Open up the particle file in Notepad and look for this header:
 "<!– dmx encoding binary 3 format pcf 2 –>"
Change it to
 "<!– dmx encoding binary 2 format pcf 1 –>"
then save.
==VIDE==
You can use VIDE to convert newer pcf files to the orangebox pcf version.
== See also ==
* VIDE - Valve Integrated Development Environment (A third-party Source toolkit)
Category:File formats
Category:Particle System