CSF File Format
CSF files hold stringtables for RA2/YR (also for Generals/ZH and probably others).
For more information about what a CSF file is, go to the CSF page.
On this page you will find a guide to how the format is built up.
The Header
The header of a CSF file is 0x18 bytes long.
It is built up like this:
Offset | Type | Description |
---|---|---|
0x0 | char[4] |
" FSC" |
0x4 | DWORD |
Unknown |
0x8 | DWORD |
NumLabels |
0xC | DWORD |
NumLabels2 |
0x10 | DWORD |
(nothing) |
0x14 | DWORD |
Language |
Language
The language DWORD can have the following values (others will be recognized as "Unknown"):
0 = US (English)* 1 = UK (English) 2 = German* 3 = French* 4 = Spanish 5 = Italian 6 = Japanese 7 = Jabberwockie 8 = Korean* 9 = Chinese* >9 = Unknown
* RA2/YR has been released in this language.
Labels
After the header, the label data follows.
A label can be considered an entry in the stringtable (e.g. "GUI:OK" is a label).
Each label can have a name (e.g. "NAME:MTNK"), a value (e.g. "Grizzly Tank") and an extra value (no example in the original ra2.csf/ra2md.csf).
While the name and the extra value are ASCII strings, the value is a Unicode string (in order to support Korean, Chinese, etc).
Now let's come to how the data is stored in the CSF file:
Label header
The label data begins with a label header, which is built up like this:
Offset | Type | Description |
---|---|---|
0x0 | char[4] |
" LBL" |
0x4 | DWORD |
Unknown |
0x8 | DWORD |
LabelNameLength |
0xC | char[LabelNameLength] |
LabelName |
The first label in ra2md.csf can be found at 0x18.
Values
Directly after the label header, the value data follows.
This is how it is built up:
Offset | Type | Description |
---|---|---|
0x0 | char[4] |
" RTS or "WRTS" |
0x4 | DWORD |
ValueLength |
0x8 | byte[ValueLength*2] |
Value |
0x8+ValueLength*2 | DWORD |
ExtraValueLength |
0x8+ValueLength*2+0x4 | char[ExtraValueLength] |
ExtraValue |
Decoding the value
To decode the value to a Unicode string, not every byte of the value data (or substract it from 0xFF).
An example in C/C++:
int ValueDataLength=ValueLength<<1 for(register int i=0;i<ValueDataLen;i++) { ValueData[i]=~ValueData[i] }