User Tools

Site Tools


falcon4:file_formats:cam_trn_tac

This is an old revision of the document!


.CAM/.TRN/.TAC File Overview

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, but without the built-in data compression – note that Falcon compresses most of the contents of the majority of the individual files inside a .CAM/.TRN/.TAC file using LZSS compression)

.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.

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.

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.1234683182.txt.gz · Last modified: 2009-02-15 07:33 by lightning