Falcon 4.0 Forum, Falcon 4 Home, Falcon 4 Campaign, Falcon 4 Cockpits, Falcon 4 Database, Falcon 4 File Formats, Falcon 4 SRTM Terrain, Falcon 4 Terrain, Falcon 4 Textures, Falcon 4 Tools
Falcon 4 File Formats .CAM, .TRN and .TAC
Campaign files are similar in structure to training and TE files. Generally they do not have flights allocated though, leaving them to the mission planner to generate.
Campaign files end in the suffix .CAM and often have associated other files, such as:
Trigger files, to trigger events (.TRI)
Force Ratio files (.FRC)
History files (.HIS)
Tactical files are similar in structure to training and campaign files. Generally they do not have flights allocated though, leaving them to the mission planner to generate.
Tactical files end in the suffix .TAC.
Training missions files end in the suffix .TRN.
Training files are similar in structure to campaign and TE files. Generally they have preplanned missions already edited, and do not generate debriefing screens.
The various files in Falcon that end with .CAM, .TRN, and .TAC extensions are just container files for storing various other campaign files (kind of like a .ZIP file).
.CAM, .TRN, and .TAC files come with an embedded “directory” of their contents, typically located at the end of the file, although there is no requirement that the directory must come at the end. The only requirement is that the header must point to the beginning of the directory information, and the directory information must conform to the directory layout standard.
To read a .CAM/.TRN/.TAC file properly, you first need to read the embedded directory, and then using the information from the directory, you can extract any or all of the embedded files. Note that Some of the files embedded in a .CAM/.TRN/.TAC file have their contents compressed using LZSS compression. Other files are uncompressed binaries, and yet others are uncompressed plain text files.
The header consists merely of the first 4 bytes of the file, which are a DWORD (unsigned 32-bit integer) that indicates the exact position (offset) in the file at which the DIRECTORY section starts.
The directory section lists all of the embedded file names, their offsets, and their sizes.
The directory section begins with 4 bytes (a DWORD), which, when taken as an unsigned 32-bit integer, specifies the number of embedded files that are contained in this .CAM/.TRN/.TAC file.
DWORD NumEmbeddedFiles
After those initial 4 bytes in the DIRECTORY section, the following layout repeats once per embedded file:
for each embedded file:
1 byte, whose integer value indicates how long this embedded file's name is, in bytes //n// bytes (per above), containing the actual file name of this embedded file, ASCII-encoded 4 bytes (DWORD), taken as an unsigned 32-bit integer, whose value specifies the exact offset in the .CAM/.TRN/.TAC file where this embedded file's contents begin 4 bytes (DWORD), taken as an unsigned 32-bit integer, whose value specifies the length (in bytes), of this embedded file's contents
The rest of the .CAM/.TRN/.TAC file contents contain the actual binary representations of the embedded files, per the layout defined in the directory.
A.CAM/.TRN/.TAC file typically contains several of the following types of embedded files. Clicking any of the files listed below will take you to a Wiki article that describes that file's content and file-format structure.
.CMP file (basic campaign information)
.OBJ file (campaign objectives list)
.OBD file (campaign objective deltas)
.UNI file (campaign units list)
.TEA file (campaign teams list)
.EVT file (campaign events list)
.POL file (campaign primary objectives list)
.PLT file (campaign pilots list)
.PST file (persistent objects list)
.WTH file (weather)
.VER file (version information)
.TE file (Victory conditions)
The following example code provides a C# class which can be used to enumerate and read the embedded files from within a .CAM/.TRN/.TAC file, per the above specification. The code below does not actually uncompress any of the embedded files, but it extracts their (raw) uncompressed bytes, which can then be processed according to the specifications for that individual file.
using System; using System.Text; using System.IO; public struct EmbeddedFileInfo { public string FileName; public uint FileOffset; public uint FileSizeBytes; } public class F4CampaignFileBundleReader { protected EmbeddedFileInfo[] _embeddedFileDirectory; protected byte[] _rawBytes; public F4CampaignFileBundleReader() : base() { } public F4CampaignFileBundleReader(string campaignFileBundleFileName):this() { Load(campaignFileBundleFileName); } public void Load(string campaignFileBundleFileName) { FileInfo fi = new FileInfo(campaignFileBundleFileName); if (!fi.Exists) throw new FileNotFoundException(campaignFileBundleFileName); _rawBytes=new byte[fi.Length]; using (FileStream fs = new FileStream(campaignFileBundleFileName, FileMode.Open)) { fs.Seek(0, SeekOrigin.Begin); fs.Read(_rawBytes, 0, (int)fi.Length); } uint directoryStartOffset = BitConverter.ToUInt32(_rawBytes, 0); uint numEmbeddedFiles = BitConverter.ToUInt32(_rawBytes, (int)directoryStartOffset); _embeddedFileDirectory = new EmbeddedFileInfo[numEmbeddedFiles]; int curLoc = (int)directoryStartOffset + 4; for (int i = 0; i < numEmbeddedFiles; i++) { EmbeddedFileInfo thisFileResourceInfo = new EmbeddedFileInfo(); byte thisFileNameLength = (byte)(_rawBytes[curLoc] & 0xFF); curLoc++; string thisFileName = Encoding.ASCII.GetString(_rawBytes, curLoc, thisFileNameLength); thisFileResourceInfo.FileName = thisFileName; curLoc += thisFileNameLength; thisFileResourceInfo.FileOffset = BitConverter.ToUInt32(_rawBytes, curLoc); curLoc += 4; thisFileResourceInfo.FileSizeBytes = BitConverter.ToUInt32(_rawBytes, curLoc); curLoc += 4; _embeddedFileDirectory[i] = thisFileResourceInfo; } } public EmbeddedFileInfo[] GetEmbeddedFileDirectory() { if (_embeddedFileDirectory ==null || _rawBytes == null || _rawBytes.Length ==0) throw new InvalidOperationException("Campaign bundle file not loaded yet."); return _embeddedFileDirectory; } public byte[] GetEmbeddedFileContents(string embeddedFileName) { if (_embeddedFileDirectory ==null || _rawBytes == null || _rawBytes.Length ==0) throw new InvalidOperationException("Campaign bundle file not loaded yet."); for (int i = 0; i < _embeddedFileDirectory.Length; i++) { EmbeddedFileInfo thisFile= _embeddedFileDirectory[i]; if (thisFile.FileName.ToLowerInvariant() == embeddedFileName.ToLowerInvariant()) { byte[] toReturn = new byte[thisFile.FileSizeBytes]; Array.Copy(_rawBytes, thisFile.FileOffset, toReturn, 0, thisFile.FileSizeBytes); return toReturn; } } throw new FileNotFoundException(embeddedFileName); } }