The next GameFAQs contest is here! Sign in and enter a Best Year in Gaming bracket for a chance at a $1000 grand prize!


hide results

    File Format Guide by VBato

    Version: 4 | Updated: | Search Guide | Bookmark Guide

                     COMMAND & CONQUER FILE FORMATS
    
    Revision 4
    
    by Vladan Bato (bat22@geocities.com)
    
    This document explains the file formats used by Command & Conquer.
    
    Command & Conquer is a tradmark of Westwood Studios, Inc.
    Command & Conquer is Copyright (C)1995 Westwood Studios, Inc.
    
    The information provided here is meant for programmers that want to make
    editor and utilites for Command & Conquer. My explanation might not be
    the best one, but it should be enough.
    
    I can't guarantee that the information in here is correct. If you find any
    errors, please report them to me.
    
    In this document I'll use Pascal notation, and any code samples will be in
    Pascal....
    I wanted to rewrite them in C, but I don't have the time to test the code.
    So, to avoid any risks, I'll use the code from Mix Manager.
    
    In case you don't know, the information contained here has been used to
    make the program Mix Manager, which contains a lot of conversion utilities
    for the various formats. For more info, check my homepage (see the end of 
    the document).
    
    ===================
     1. THE .MIX FILES
    ===================
    
    You probably already know the format of these files, but I will add a
    description here for completeness.
    
    The MIX file consists of two parts :
    -A header including the index of all the files contained within
    -A body containing all the files
    
    It's format is :
    
     Header : record
                NumFiles : word;    {Number of files in MIX}
                DataSize : longint; {Size of body}
                Index    : array [1..NumFiles] of
                  record
                    ID    : longint;  {File ID}
                    Start : longint;  {Offset of file from the start of the 
    body}
                    Size  : longint;  {file size}
                  end;
              end;
    
    The ID field is computed from the original filename, which is not stored in
    the MIX.
    The records are always sorted by the ID field (the numbers are signed
    longints).
    Note that the offsets are relative to the start of the body so to find the 
    actual offset in the MIX you have to add the size of the header which is  
    NumFiles*12+6
    
    ===================
     2. THE .PAL FILES
    ===================
    
    The most easiest files....
    These files contain the palette in the same format used by VGA cards.
    
     Palette : array [0..255] of record
                                   red,green,blue:byte;
                                 end;
    
    Note that only the first 6 bits of each number are used, giving a total of
    262144 possible colors (as opposed to the 8 bits used by .PCX files for
    example).
    
    =================================
     3. THE TEMPLATE AND .BIN FILES
    =================================
    
    The Template files contain the map graphics, and can be found in the
    theater specific MIX files (TEMPERAT.MIX, WINTER.MIX, DESERT.MIX).
    The .BIN files contain the maps for the missions and are used in conjunction
    with the .INI files.
    
    I won't explain them here. They are explained with great detail in the
    document titled "Command & Conquer maps" I wrote some time ago.
    The said document can be found on my homepage.
    
    ===================
     5. THE .SHP FILES
    ===================
    
    The .SHP files contain almost all the graphics : units, structures, 
    trees,...
    The header has the following structure :
    
      Header : record
                 NumImages : word;    {Number of images}
                 A,B       : word;    {Unknown}
                 Width,
                 Height    : word;    {Width and Height of the images}
                 C         : longint; {Unknown}
               end;
    
    If you know something about those unknown fields, please e-mail me.
    Following that there's an array of records, one for each image :
    
      Offsets : array [0..NumImages+1] of
                  record
                    Offset  : longint;  {Offset and format of image in file}
                    RefOffs : longint;  {Offset and format of image on
                                         which it is based}
                  end;
    
    The most significant byte (last) of the Offset and RefOffs fields
    contains the format, while the lower three are used for the offset.
    The format byte can have one of the three values : 80h, 40h, 20h.
    I will call the three image formats Format80, Format40 and Format20.
    
    The Format80 images are compressed with a compression method I'll explain
    later.
    
    The Format40 images must be xor-ed with a Format80 image. That's what the
    RefOffs field is used for. It tells which Format80 image they are
    based upon. The Format40 will be explained in detail later.
    
    The Format20 images use the same format as the Format40, the difference is
    that they are xor-ed with the image that precedes them in the file. That can
    be either in Format20 or in Format40.
    The offset part of the RefOffs field contains the number of the first
    Format40 image in the chain, and the format field is always 48h.
    
    Here's an example :
    
    0) Off0(three bytes) 80h 000000h 00h
    1) Off1(three bytes) 80h 000000h 00h
    2) Off2(three bytes) 40h Off1    80h
    3) Off3(three bytes) 80h 000000h 00h
    4) Off4(three bytes) 40h Off1    80h
    5) Off5(three bytes) 20h 000400h 48h
    6) Off6(three bytes) 20h 000400h 48h
    7) Off7(three bytes) 40h Off3    80h
    
    For example to draw image 7, you have to draw the image 3 first (whose 
    offset
    and format are given) and then xor image 7 over it.
    
    To draw image 6, you have to xor it over the previous image, i.e. 5, which
    is format20 again, that means that it has to be xor-ed over image 4, which
    is in format40, i.e. it must be xor-ed over the image in format80 it has a
    reference to. In this case it's image 1. Thus the chain is 1,4,5,6.
    This is one way to see it, the other could be :
    Image 6 is in Format20, the RefOffs field contains the number of the first
    Format40 image in the chain, in this case image 4. To draw Image 4, the
    Image 1 has to be drawn first, next is image 4, and then all the images
    from the 4th to the 6th have to be xor-ed over the previous.
    
    I made some experiments and found out that you don't have to use the
    Format40 and Format20 images. I tried converting all of them into Format80
    and it worked.
    
    Also, when changing graphics, note that all the unit and structure graphics
    should be drawn using the GDI colors, which will be automatically converted
    for the other sides. 
    The palette you should use is one of those found in DESERT.MIX, WINTER.MIX
    and TEMPERAT.MIX. The GDI colors are colors 0B0h-0BFh. The other colors 
    won't be converted and will remain the same for all the sides (be sure to
    use only the colors that are the same all three palettes).
    
    The above applies only to the graphics that appear in all three theaters
    (the .SHP file found in CONQUER.MIX). The graphics for the structures and
    overlays that appear in a single theater (found inside the theater specific
    MIX) can use the palette entries that are unique for that theater (and will
    be shown with garbled colors in the others).
    
    Also a special color is used for shadows. It's color 04h. In the palettes
    it's bright green, but C&C puts a shadow instead of it. I don't know how
    the shadows are calculated however.
    
    You should've noticed that the array has NumImages+2 elements when only
    NumImages elements are needed. The last one contains zeros, and the one 
    before
    that points to the end of the file. These two can be used to identify the 
    file as a .SHP.
    
    Here's the description of the compression formats : Format80 and Format40.
    
    ----------
     Format80
    ----------
    
    There are several different commands, with different sizes : form 1 to 5
    bytes.
    The positions mentioned below always refer to the destination buffer (i.e.
    the uncompressed image). The relative positions are relative to the current
    position in the destination buffer, which is one byte beyond the last 
    written
    byte.
    
    I will give some sample code at the end.
    
    (1) 1 byte
          +---+---+---+---+---+---+---+---+
          | 1 | 0 |   |   |   |   |   |   |
          +---+---+---+---+---+---+---+---+
                  \_______________________/
                             |
                           Count
    
          This one means : copy next Count bytes as is from Source to Dest.
    
    (2) 2 bytes
      +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
      | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
      +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
          \___________/\__________________________________________________/
                |                             |
             Count-3                    Relative Pos.
    
      This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to
      Current position.
      Note that you have to add 3 to the number you find in the bits 4-6 of the
      first byte to obtain the Count.
      Note that if the Rel. Pos. is 1, that means repeat Count times the 
    previous
      byte.
    
    (3) 3 bytes
      +---+---+---+---+---+---+---+---+   +---------------+---------------+
      | 1 | 1 |   |   |   |   |   |   |   |               |               |
      +---+---+---+---+---+---+---+---+   +---------------+---------------+
              \_______________________/                  Pos
                         |
                     Count-3
    
      Copy Count bytes from Pos, where Pos is absolute from the start of the
      destination buffer. (Pos is a word, that means that the images can't be
      larger than 64K)
    
    (4) 4 bytes
      +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+
      | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |
      +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+
                                                Count          Color
    
      Write Color Count times.
      (Count is a word, color is a byte)
    
    (5) 5 bytes
      +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+
      | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |
      +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+
                                                Count               Pos
    
      Copy Count bytes from Dest. starting at Pos. Pos is absolute from the 
    start of the Destination buffer.
      Both Count and Pos are words.
    
    These are all the commands I found out. Maybe there are other ones, but I
    haven't seen them yet.
    
    All the images end with a 80h command.
    
    To make things more clearer here's a piece of code that will uncompress the
    image.
    
      DP = destination pointer
      SP = source pointer
      Source and Dest are the two buffers
    
    
      SP:=0;
      DP:=0;
      repeat
        Com:=Source[SP];
        inc(SP);
        b7:=Com shr 7;  {b7 is bit 7 of Com}
        case b7 of
          0 : begin  {copy command (2)}
                {Count is bits 4-6 + 3}
                Count:=(Com and $7F) shr 4 + 3;
                {Position is bits 0-3, with bits 0-7 of next byte}
                Posit:=(Com and $0F) shl 8+Source[SP];
                Inc(SP);
                {Starting pos=Cur pos. - calculated value}
                Posit:=DP-Posit;
                for i:=Posit to Posit+Count-1 do
                begin
                  Dest[DP]:=Dest[i];
                  Inc(DP);
                end;
              end;
          1 : begin
                {Check bit 6 of Com}
                b6:=(Com and $40) shr 6;
                case b6 of
                  0 : begin  {Copy as is command (1)}
                        Count:=Com and $3F;  {mask 2 topmost bits}
                        if Count=0 then break; {EOF marker}
                        for i:=1 to Count do
                        begin
                          Dest[DP]:=Source[SP];
                          Inc(DP);
                          Inc(SP);
                        end;
                      end;
                  1 : begin  {large copy, very large copy and fill commands}
                        {Count = (bits 0-5 of Com) +3}
                        {if Com=FEh then fill, if Com=FFh then very large copy}
                        Count:=Com and $3F;
                        if Count<$3E then {large copy (3)}
                        begin
                          Inc(Count,3);
                          {Next word = pos. from start of image}
                          Posit:=Word(Source[SP]);
                          Inc(SP,2);
                          for i:=Posit to Posit+Count-1 do
                          begin
                            Dest[DP]:=Dest[i];
                            Inc(DP);
                          end;
                        end
                        else if Count=$3F then   {very large copy (5)}
                        begin
                          {next 2 words are Count and Pos}
                          Count:=Word(Source[SP]);
                          Posit:=Word(Source[SP+2]);
                          Inc(SP,4);
                          for i:=Posit to Posit+Count-1 do
                          begin
                            Dest[DP]:=Dest[i];
                            Inc(DP);
                          end;
                        end else
                        begin   {Count=$3E, fill (4)}
                          {Next word is count, the byte after is color}
                          Count:=Word(Source[SP]);
                          Inc(SP,2);
                          b:=Source[SP];
                          Inc(SP);
                          for i:=0 to Count-1 do
                          begin
                            Dest[DP]:=b;
                            inc(DP);
                          end;
                        end;
                      end;
                end;
              end;
        end;
      until false;
    
    Note that you won't be able to compile this code, because the typecasting
    won't work. (But I'm sure you'll be able to fix it).
    
    
    ----------
     Format40
    ----------
    
    As I said before the images in Format40 must be xor-ed over a previous 
    image, or against a black screen (as in the .WSA format).
    It is used when there are only minor changes between an image and a 
    following one.
    
    Here I'll assume that the old image is in Dest, and that the Dest pointer is
    set to the beginning of that buffer.
    
    As for the Format80, there are many commands :
    
    
    (1) 1 byte
                   byte
      +---+---+---+---+---+---+---+---+
      | 1 |   |   |   |   |   |   |   |
      +---+---+---+---+---+---+---+---+
          \___________________________/
                       |
                     Count
    
      Skip count bytes in Dest (move the pointer forward).
    
    (2) 3 bytes
                  byte                           word
      +---+---+---+---+---+---+---+---+  +---+-----+-------+
      | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  | 0 | ... |       |
      +---+---+---+---+---+---+---+---+  +---+-----+-------+
                                             \_____________/
                                                    |
                                                  Count
    
      Skip count bytes.
    
    (3) 3 bytes
                    byte                              word
      +---+---+---+---+---+---+---+---+  +---+---+-----+-------+
      | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  | 1 | 0 | ... |       |
      +---+---+---+---+---+---+---+---+  +---+---+-----+-------+
                                                 \_____________/
                                                       |
                                                     Count
    
     Xor next count bytes. That means xor count bytes from Source with bytes
     in Dest.
    
    (4) 4 bytes
                  byte                               word           byte
      +---+---+---+---+---+---+---+---+  +---+---+-----+-------+  +-------+
      | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  | 1 | 1 | ... |       |  |       |
      +---+---+---+---+---+---+---+---+  +---+---+-----+-------+  +-------+
                                                 \_____________/    value
                                                       |
                                                     Count
    
      Xor next count bytes in Dest with value.
    
    5) 1 byte
                   byte
      +---+---+---+---+---+---+---+---+
      | 0 |   |   |   |   |   |   |   |
      +---+---+---+---+---+---+---+---+
          \___________________________/
                       |
                     Count
    
      Xor next count bytes from source with dest.
    
    6) 3 bytes
                  byte                     byte       byte
      +---+---+---+---+---+---+---+---+  +-------+  +-------+
      | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |  |       |  |       |
      +---+---+---+---+---+---+---+---+  +-------+  +-------+
                                           Count      Value
    
      Xor next count bytes with value.
    
    
    All images end with a 80h 00h 00h command.
    
    I think these are all the commands, but there might be some other.
    If you find anything new, please e-mail me.
    
    As before here's some code :
    
      DP = destination pointer
      SP = source pointer
      Source is buffer containing the Format40 data
      Dest   is the buffer containing the image over which the second has
             to be xor-ed
    
    
      SP:=0;
      DP:=0;
      repeat
        Com:=Source[SP];
        Inc(SP);
    
        if (Com and $80)<>0 then {if bit 7 set}
        begin
          if Com<>$80 then  {small skip command (1)}
          begin
            Count:=Com and $7F;
            Inc(DP,Count);
          end
          else  {Big commands}
          begin
            Count:=Word(Source[SP]);
            if Count=0 then break;
            Inc(SP,2);
    
            Tc:=(Count and $C000) shr 14;  {Tc=two topmost bits of count}
    
            case Tc of
              0,1 : begin  {Big skip (2)}
                      Inc(DP,Count);
                    end;
              2 : begin {big xor (3)}
                    Count:=Count and $3FFF;
                    for i:=1 to Count do
                    begin
                      Dest[DP]:=Dest[DP] xor Source[SP];
                      Inc(DP);
                      Inc(SP);
                    end;
                  end;
              3 : begin  {big repeated xor (4)}
                    Count:=Count and $3FFF;
                    b:=Source[SP];
                    Inc(SP);
                    for i:=1 to Count do
                    begin
                      Dest[DP]:=Dest[DP] xor b;
                      Inc(DP);
                    end;
                  end;
            end;
          end;
        end else  {xor command}
        begin
          Count:=Com;
          if Count=0 then
          begin {repeated xor (6)}
            Count:=Source[SP];
            Inc(SP);
            b:=Source[SP];
            Inc(SP);
            for i:=1 to Count do
            begin
              Dest[DP]:=Dest[DP] xor b;
              Inc(DP);
            end;
          end else  {copy xor (5)}
            for i:=1 to Count do
            begin
              Dest[DP]:=Dest[DP] xor Source[SP];
              Inc(DP);
              Inc(SP);
            end;
        end;
      until false;
    
    
    
    ===================
     6. THE .CPS FILES
    ===================
    
    The .CPS files contain 320x200x256 images. The images are compressed with 
    the Format80 compression method. They may or may not contain a palette.
    
    The header has the following structure :
    
      Header : record
                 Size    : word;  {File size - 2}
                 Unknown : word;  {Always 0004h}
                 ImSize  : word;  {Size of uncompressed image (always 0FA00h)}
                 Palette : longint; {Is there a palette ?}
               end;
    
    If Palette is 03000000h then there's a palette after the header, otherwise
    the image follows.  CPS file without palette can be found in the SETUP.MIX 
    file, and they all use the Palette that can be found inside the same .MIX.
    
    The image that follows the palette (or the Header) is in Format80 which is
    explained above.
    
    ===================
     7. THE .WSA FILES
    ===================
    
    
    WSA files contain short animations and can be found in the GENERAL.MIX 
    files.
    They are basically a series of Format40 images, that are then compressed 
    with
    Format80.
    
    The header is :
    
      Header : record
                 NumFrames : word;  {Number of frames}
                 X,Y       : word;  {Position on screen of the upper left 
    corner}
                 W,H       : word;  {Width and height of the images}
                 Delta     : longint; {Frames/Sec = Delta/(2^10)}
               end;
    
    Following that there's an array of offsets :
    
      Offsets : array [0..NumFrames+1] of longint;
    
    The obtain the actual offset, you have to add 300h. That is the size of the
    palette that follows the Offsets array.
    As for .SHP files the two last offsets have a special meaning.
    If the last offset is 0 then the one before it points to the end of file
    (after you added 300h of course).
    If the last one is <>0 then it points to the end of the file, and the
    one before it points to a special frame that gives you the difference 
    between the last and the first frame. This is used when you have to loop the
    animation.
    
    As I said before, the images are in Format40 but are then compressed with
    Format80. That means that you first have to uncompress the Format80 and then
    decode the Format40 image you obtain.
    The first frame should be xor-ed over a black image (filled with zeros), all
    the other are xor-ed over the previous one.
    
    There is a variant of the file without the palette that can be found in
    SETUP.MIX but I wasn't able to decode it (maybe there are some commands I
    don't know about)...
    
    =====================
     8. ADDITIONAL NOTES
    =====================
    
    The VQA files (that contain movies) have been decoded by Aaron Glover
    (arn@ibm.net), and are explained in a document he wrote up.
    You can find the document on my homepage (or ask him directly).
    
    What is still missing are the .AUD files.
    It seems that the AUD files use some kind of lossy sound compression,
    which means that it is almost impossible to decode them.
    However if someone manages to work them out, I'd really appreciate some
    info.
    
    I know my explanations are not very good, but you'll have to bear them,
    unless someone else wants to rewrite this.
    
    ============
     9. CREDITS
    ============
    
    I wish to thank the following people :
    
    -Andrew Griffin (buggy@adam.com.au) for starting it all.
    -Aaron Glover (arn@ibm.net) and
     Denis Moeller (d.moeller@rendsburg.netsurf.de) for their work on .SHP 
    files.
    -Aaron Glover for decoding the VQA files.
    -Carl Kenner (andrew.kenner@unisa.edu.au) for the info on .CPS files.
    
    
    Vladan Bato (bat22@geocities.com)
    http://www.geocities.com/SiliconValley/8682
    
    

    FAQ Display Options: Printable Version