User Tools

Site Tools


falcon4:file_formats:cam_trn_tac

Falcon 4 .CAM/.TRN/.TAC File Overview

.CAM Files

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)

.TAC Files

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.

.TRN Files

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.

Basic Structure

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.

HEADER SECTION

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.

DIRECTORY SECTION

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.

Typical directory contents

Example Code in C#

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);
    }
}
falcon4/file_formats/cam_trn_tac.txt · Last modified: 2024/07/31 08:34 by snakeman

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

All PMC web site download services are temporarily suspended until web site yearly fees have been recovered, want to download addons/mods? Then Support PMC.

If you are grateful for all the work PMC has done in the past 25 years, use Support PMC page.