git-svn-id: svn://svn.ijw.co.nz/svn/OpenRa@1059 993157c7-ee19-0410-b2c4-bb4e9862e678
This commit is contained in:
988
raff.txt
Normal file
988
raff.txt
Normal file
@@ -0,0 +1,988 @@
|
||||
|
||||
THE RED ALERT FILE FORMATS GUIDE
|
||||
|
||||
Release v1.4
|
||||
Last Updated: 18th April 1997
|
||||
(c) 1997 Gavin Pugh
|
||||
(email - rascenedit@geocities.com)
|
||||
(WWW - http://www.geocities.com/TimesSquare/Arcade/5553)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
The purpose of this guide is to detail the structure of files used
|
||||
in the Game Red Alert, for use to create utilities or editors.
|
||||
|
||||
Command & Conquer : Red Alert is a trademark and copyright of
|
||||
Westwood Studios, and is so acknowledged.
|
||||
Any trademarks not mentioned here are still acknowledged.
|
||||
|
||||
|
||||
|
||||
If you intend to use quite a bit of this guide in your FAQ or similar
|
||||
text then I would quite like to hear from you, as I can create a reference
|
||||
to it in my guide. Also I want be credited if you use parts of this guide in your own.
|
||||
|
||||
Any use of information in this guide should credit the relevant author,
|
||||
(me in most cases), in such programs as utilities and editors.
|
||||
|
||||
If you have a contribution then please email it to the above address, it'll be more than
|
||||
welcome, and of course, you'll be credited for it, any help is appreciated.
|
||||
|
||||
Web Site Authors : You are welcome (and encouraged) to have my guide on your page(s), as
|
||||
long as you do not modify the HTML file in any way whatsoever, and also I request that you include
|
||||
a link on your site to my site at the address at the bottom of this document.
|
||||
|
||||
Enough of the legal crap, enjoy....
|
||||
|
||||
MIX Files
|
||||
|
||||
All MIX files I have seen can be used with the old C&C utilites, with the MIX headers
|
||||
following this format, In Red Alert only the more minor MIX files hidden in the CD
|
||||
directories follow the normal MIX format, the others are RMIX files.
|
||||
|
||||
+--------+------------------------+------+
|
||||
| HEADER | ARRAY OF OFFSETS/SIZES | BODY |
|
||||
+--------+------------------------+------+
|
||||
|
||||
struct MIXheader {
|
||||
WORD NumFiles; //Number Of Files In the MIX
|
||||
LONG DataSize; //Size Of The Body
|
||||
};
|
||||
|
||||
The Array of offsets has one entry for each file:
|
||||
|
||||
struct MIXrec {
|
||||
LONG FileID; //The ID Of The File, derived from the name of the file
|
||||
LONG Offset; //Offset Of The FIle From The Start Of The Body
|
||||
LONG Size; //Size Of The File
|
||||
};
|
||||
|
||||
If you know how the ID is calculated, I'd like to hear it, just for completeness.
|
||||
|
||||
|
||||
RMIX Files
|
||||
|
||||
However, the two which don't though are : MAIN.MIX and REDALERT.MIX. These both contain
|
||||
other MIX files which also can't be read by Mixman.
|
||||
I'll refer to these 'special' MIX files as RMIX from now on, as a few others working
|
||||
on MIX files have done.
|
||||
|
||||
Here is a list of all the RMIX files I have found:
|
||||
MAIN.MIX ;Contains most other MIX files
|
||||
REDALERT.MIX
|
||||
MOVIES1.MIX
|
||||
MOVIES2.MIX
|
||||
GENERAL.MIX
|
||||
SCORES.MIX
|
||||
CONQUER.MIX
|
||||
SPEECH.MIX
|
||||
SOUNDS.MIX
|
||||
RUSSIAN.MIX
|
||||
ALLIES.MIX
|
||||
LOCAL.MIX
|
||||
LORES.MIX
|
||||
HIRES.MIX
|
||||
NCHIRES.MIX
|
||||
EDITOR.MIX
|
||||
EDHI.MIX
|
||||
EDLO.MIX
|
||||
TRANSIT.MIX
|
||||
and SC*.MIX and SS*.MIX, probably used on Counterstrike
|
||||
|
||||
Well, so far I can amount the structure of an RMIX to:
|
||||
|
||||
+--------+------+
|
||||
| HEADER | BODY |
|
||||
+--------+------+
|
||||
|
||||
The header is an encoded representation of where and how long each file is in
|
||||
the body section.
|
||||
The body where all the contained files are, stored one after the other, with
|
||||
nothing inbetween.
|
||||
RMIX Header
|
||||
I can almost definately say that the headers include at least:
|
||||
* The offsets of the files, from the start of the body.
|
||||
* The lengths of the files
|
||||
* And some sort of ID, similar to the C&C one.
|
||||
|
||||
Looking at MAIN.MIX, it is structured like this:
|
||||
|
||||
+--------+-------------+----------+----------- ...
|
||||
| HEADER | CONQUER.MIX | EDHI.MIX | EDLO.MIX .....
|
||||
+--------+-------------+----------+----------- ...
|
||||
OFFSETS: 0 EC 213903 220D42 222D68
|
||||
LENGTHS: EC 213817 D43F 2026
|
||||
|
||||
Here is the header in HEX View:
|
||||
00 00 02 00 6E 3A 77 4B 7A BB E8 57 DB 10 8B 77
|
||||
EC 67 5C 0C D9 8A 6B 50 47 AC 8D A4 31 FD 0A A1
|
||||
EB CF F1 5F 93 19 4D FD C6 49 3F 10 67 A5 7B E2
|
||||
5D 11 98 3C B4 D8 35 40 3B 36 E6 B3 13 37 70 9C
|
||||
3F 3C E0 70 97 47 1F BC CE 1B B0 D1 68 D4 F3 B7
|
||||
E2 F6 8A 32 C2 4B 7D BF 43 87 0B 40 63 27 77 9E
|
||||
9E 94 40 86 9A B6 59 09 15 52 D2 8E FB E6 BA B8
|
||||
6A 15 FC 31 5A 1C 4A 9E 54 B3 F6 98 66 24 DB 5E
|
||||
33 14 82 3D 6E 6B 7C 37 EF 3D 58 83 A5 08 D8 2F
|
||||
9C A0 D0 86 6F C7 65 5F 56 EF E8 7E 13 5F 08 5A
|
||||
1E E3 E1 E7 47 0E 72 34 4C 65 BD DD 71 AC 83 FD
|
||||
4C 4C D2 A3 85 E5 06 C3 55 18 BE FA 70 01 81 F1
|
||||
40 31 5E 71 64 BB 54 04 95 1E 51 10 B6 DF 96 6F
|
||||
71 C4 6A 19 CE 19 DC 3E 85 1C 4B B1 B3 DC 21 79
|
||||
D7 B1 65 C1 B8 73 C9 77 A5 14 7B 0C
|
||||
|
||||
(still in progess, I'm uploading my site now, so I'll be stopping here for the mo)
|
||||
|
||||
|
||||
RMT Files (TMP files in Red Alert) Thanks To Moritz Mertinkat, (and Andrew Griffin for the Xdim/Ydim Info)
|
||||
|
||||
My Note: These RTM files are used in Red Alert for the map tiles, the 3 theater RMIX files
|
||||
contain many of these tiles to put over your maps. These tiles are those you can place
|
||||
using the RA Terrain Editor, but using this method you could also extract the graphics
|
||||
for the INDOOR theater.
|
||||
|
||||
The header (in pascal):
|
||||
|
||||
TRMTHeader = record
|
||||
Width : Word; {Width of images, always 24 (18h)}
|
||||
Height : Word; {Heigth of images, always 24 (18h)}
|
||||
NumTil : Word; {Number of Tiles}
|
||||
Unknown1 : Word;
|
||||
XDim : Word;
|
||||
YDim : Word;
|
||||
Size : LongInt; {Size of file}
|
||||
ImgStart : LongInt; {Start of image-data}
|
||||
Unknown3 : LongInt;
|
||||
Unknown4 : LongInt;
|
||||
Index2 : LongInt;
|
||||
Unknown5 : LongInt;
|
||||
Index1 : LongInt; {Offset of "Index"}
|
||||
end;
|
||||
|
||||
You can use XDim and YDim to determine how the tiles should be used
|
||||
to construct the uber-tile. For example, say XDim = 3 and YDim = 2, then
|
||||
the uber-tile would be a 3x2 tile. This is probably only of use when
|
||||
placing the uber-tile in the first place, as the individual sub-tiles
|
||||
would be placed when the map is being drawn. XDim and YDim give you the
|
||||
dimensions of the uber-tile.
|
||||
|
||||
If XDim and YDim are both equal to 1, then there is no uber-tile. If
|
||||
this RMT file holds more than 1 tile, then they are all individual tiles
|
||||
and have no special relationship to each other in regards to being parts
|
||||
of a larger tile.
|
||||
|
||||
Index2-Index1 should be the number of tiles!
|
||||
|
||||
Index1 is an offset for an array of bytes:
|
||||
Index: ARRAY[1..NumTil] of Byte;
|
||||
|
||||
Every entry in this array which points to a specified tile.
|
||||
If the entry is 255 (FFh) then the tile is empty!
|
||||
An example:
|
||||
If you want to display tile 8 of a RMT-file you have to do
|
||||
the following:
|
||||
- open the file
|
||||
- read the header
|
||||
- seek to pos. INDEX1
|
||||
- read the index
|
||||
- read the 9th byte of it (for tile 8, because the index
|
||||
starts with 0!}
|
||||
- seek to: SizeOf(Header) + Index[9] * (24*24)
|
||||
- read 576 byte (24*24) and display them!
|
||||
- close the file
|
||||
|
||||
All the graphics-data is uncompressed!
|
||||
|
||||
|
||||
PCX-files (Documentation by Moritz Mertinkat)
|
||||
|
||||
My Note: These PCX files are used for 640x400 images in the Windows 95' version of
|
||||
Red Alert, where CPS files are used for the 320x200 ones in DOS.
|
||||
|
||||
PCX-Header (in pascal):
|
||||
-----------------------
|
||||
|
||||
TPCXHeader = RECORD
|
||||
Signature : Byte;
|
||||
Version : Byte;
|
||||
Encoding : Byte;
|
||||
BitsPixel : Byte;
|
||||
XMin : Word;
|
||||
YMin : Word;
|
||||
XMax : Word;
|
||||
YMax : Word;
|
||||
HRes : Word;
|
||||
VRes : Word;
|
||||
Palette16 : ARRAY[1..48] of Byte;
|
||||
Reserved : Byte;
|
||||
NumPlanes : Byte;
|
||||
BytesLine : Word;
|
||||
PalType : Word;
|
||||
Dummy : ARRAY[1..58] of Byte;
|
||||
end;
|
||||
|
||||
Description:
|
||||
------------
|
||||
Signature:
|
||||
10 = ZSoft PCX-files
|
||||
|
||||
Version:
|
||||
0 = PC Paintbrush v2.5
|
||||
2 = Version 2.8 with palette information
|
||||
3 = Version 2.8 without palette information
|
||||
4 = PC Paintbrush for Windows (Plus for Windows uses Ver 5)
|
||||
5 = Version 3.0: Used by Paintbrush in Win3.x and Win95,
|
||||
Can be used for 24-bit images!!
|
||||
>>>> NOTE: Version 5 is standard!!
|
||||
|
||||
PalType:
|
||||
1 = Color/BW (use this one for 256-color images!)
|
||||
2 = Grayscale
|
||||
|
||||
XMin, XMax, YMin and YMax are the image dimensions.
|
||||
HRes and VRes are the horizantal and vertical Resultion
|
||||
of the image in DPI. If you create your own PCX-files, set
|
||||
HRes and VRes to 300.
|
||||
|
||||
The image data is encoded with RLE (Run Length Encoding) and
|
||||
follows directly after the header.
|
||||
|
||||
|
||||
Decoding the PCX-data:
|
||||
----------------------
|
||||
First you have to calculate the X- and Y-dimension of the image.
|
||||
X-dimension:
|
||||
Width:= BytesLine*NumPlanes
|
||||
(But I've seen, that there's another way doing this:
|
||||
Width:= XMax-XMin+1)
|
||||
|
||||
Y-dimension:
|
||||
Height:= YMax-YMin+1
|
||||
|
||||
Now, read a single byte (B1) from the PCX-file. If the top two bits of
|
||||
this byte (B1) are set, then the remaining six bits show how many times
|
||||
you have to duplicate the next byte (after B1) in the file.
|
||||
[That means: If the byte (B1) is > 192, then it is a "Repeat-byte" and
|
||||
you have to duplicate the next byte (after B1) RepeatByte-192 times]
|
||||
|
||||
If the top two bits are not set, the byte (B1) itself is the data with
|
||||
a count of one.
|
||||
[That means: If the byte B1 < 192 then this byte represents a pixel
|
||||
itself]
|
||||
|
||||
Simply do this with all bytes (and don't forget the linebreaks after
|
||||
Width decoded bytes).
|
||||
|
||||
If FileSize(PCXFile)-(768+1) = 12 [the byte before the pal-array],
|
||||
then the following 768 bytes contain the palette information - otherwise
|
||||
there's no palette.
|
||||
|
||||
(If you have any questions, comments, ect. please email me:
|
||||
Moritz Mertinkat)
|
||||
|
||||
Some sample-code (in pascal) at:
|
||||
http://home.t-online.de/home/moehrchen (TP Programmers Page)
|
||||
(Download-area, "PCX-viewer 4.0b for 256-color-bitmaps")
|
||||
|
||||
|
||||
SHP Files
|
||||
The SHP files are stored in the MIX files. They are used for the units, icons in the sidebar,
|
||||
structures and other graphics.
|
||||
|
||||
A 6 image SHP would look like:
|
||||
|
||||
[Header][ImgInfo1][ImgInfo2]....[ImgInfo5][ImgInfo6][ImgInfo7][ImgInfo8][THE SHP DATA]
|
||||
|
||||
Note that the two extra Imginfo parts are special, the last one is all nulls, where
|
||||
the offset in the penultimate one points to the end of the file.
|
||||
|
||||
The header for these SHP files is as follows:
|
||||
|
||||
struct SHPheader {
|
||||
WORD NumImages; //Number Of Images In SHP file
|
||||
WORD A; //Unknown (please email me if you know what they are)
|
||||
WORD B; //Unknown
|
||||
WORD Width; //Width Of The Images, in pixels
|
||||
WORD Height; //Height Of The Images, in pixels
|
||||
LONG C; //Unknown
|
||||
};
|
||||
|
||||
Each Image (and those 2 special bits), are represented by a info part here.
|
||||
|
||||
struct ImgInfo {
|
||||
3 BYTES Offset; //Offset of image in file
|
||||
CHAR Format; //Format Of Image (&h80 is Format80, &h40 is Format40, etc..)
|
||||
WORD RefOff; //Offset of reference image for Format20/40
|
||||
WORD RefForm; //Format of reference image
|
||||
};
|
||||
|
||||
Note that the Offset field is different from C&C, with it being 3 bytes, the RefOff
|
||||
field may also be 3 bytes now as well, but the Refform can be a word, so it is most
|
||||
likely not 3 bytes.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Packed Sections
|
||||
|
||||
First, in relation to C&C1 where each cell is represented by 2 bytes, as the total map size in
|
||||
RA seems to be 128x128 then the binary file for the map which could then be fiddled with and read
|
||||
by third-party editors would be a 128x128x3 byte binary file (=48k). Note that 3 bytes per cell
|
||||
in a map section are used in RA.
|
||||
|
||||
(This would definately be needed if an editor was created allowing the creation of INDOOR maps)
|
||||
To think about.....Maybe putting a 48k SCG01EA.BIN file along with the INI (a'la C&C1), this would
|
||||
be a solution, but unfortunately the RA terrain editor only handles packed maps :(
|
||||
(note that NewINIFormat=1 would be needed for this way to work, and the OVERLAY section used instead
|
||||
of the OverlayPack)
|
||||
|
||||
This would be a 2-part process:
|
||||
|
||||
[MapPack]
|
||||
1=983704304RREREW84790.......
|
||||
2=8743907547054......... > Compressed Binary > 48k Map Binary
|
||||
3=..... 6 8K Chunks In Format80 3 bytes per cell
|
||||
..... And So On
|
||||
..
|
||||
< 70 chars a line >
|
||||
|
||||
|
||||
Aha! Looked in GAME.DAT for the full alphabet and saw:
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
|
||||
(This most probably is the same as Base64, but for completeness, I've decoded it myself)
|
||||
|
||||
Theory:
|
||||
-------
|
||||
The GAME.DAT sequence shows that A would be 0, B=1, C=2..... +=62 and /=63
|
||||
Each character represents 6 bits, arranged as follows
|
||||
|
||||
6 bits [000000][111111][000000][111111]
|
||||
|
||||
So in the compressed Binary form:
|
||||
|
||||
8 bits [00000011][11110000][00111111]
|
||||
|
||||
The '=' character means take off 2 bits from the byte sequence, this means that
|
||||
the whole sequence is MOD 8, so doesn't leave any stray bits behind.
|
||||
|
||||
Now all the data can be handled in normal hex byte form.
|
||||
|
||||
This Process In Reverse
|
||||
-----------------------
|
||||
If you want to get from the byte sequence to the ASCII string again (maybe for a
|
||||
map tile placing utility), use this:
|
||||
|
||||
Convert the hex bytes into a long string of bits: (example only here.)
|
||||
|
||||
09 00 00 20
|
||||
|
||||
00001001 00000000 00000000 00100000
|
||||
|
||||
00001001000000000000000000100000
|
||||
|
||||
Then start pulling out 6 bits at a time and compare with the ASCII table I made
|
||||
earlier until you've got less than 6 bits left.
|
||||
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
|
||||
(Where A=0, B=1, C=2,..... +=62, /=63)
|
||||
|
||||
000010 010000 000000 000000 001000 00
|
||||
|
||||
>>>>>>>> C Q A A I .....
|
||||
|
||||
So after making 5 characters, there are 2 bits left, Red Alert needs this to be 6 bits,
|
||||
so you would need another 4 bits.
|
||||
As you know, each character means 6 bits, and the '=' character means take the last 2
|
||||
bits off.
|
||||
So you would use this idea to pad 4 bits onto the end using 'A=' which means 0000
|
||||
|
||||
The final string therefore would be:
|
||||
CQAAIA=
|
||||
|
||||
And remember that 2 bits could also be left behind, you would use 'A==' in this case.
|
||||
|
||||
How To Decode The OverlayPack in HEX Form
|
||||
|
||||
Well, it will produce a 16k binary, with 1 byte representing each cell
|
||||
|
||||
Here is the format of the packed section
|
||||
|
||||
[aa bb cc] 20 [.....]
|
||||
|
||||
Where ccbbaa is the length of the [...] section
|
||||
|
||||
The &h20 may have a special meaning, but I've no idea yet.
|
||||
|
||||
There are two of these in each [OverlayPack], with each representing 8k.
|
||||
|
||||
After a VERY VERY VERY long time, I was able to work out how the [....]
|
||||
part was coded, just by luck really. Use Format80 to decode this, using
|
||||
an 8K destination buffer in each case, then join both these together to
|
||||
get a 16k file.
|
||||
Look at Appendix A (the Format80 images) for more information on this algorithm.
|
||||
|
||||
This 16k file produced by the algorithm then reads from left to right like this:
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-+
|
||||
+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+ ......
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-+
|
||||
+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+FF+ ...
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
........
|
||||
|
||||
The top left cell would be offset 0, the one below 128, and the one below
|
||||
that 256, (the same as the cell number).
|
||||
|
||||
Here is a table of what each Hex represents in the OverlayPacks:
|
||||
FF - Blank
|
||||
00 - Sandbags (SBAG)
|
||||
01 - Chain-link Fence (CYCL)
|
||||
02 - Concrete Wall (BRIK)
|
||||
03 - C&C Barbed Wire (FENC)
|
||||
04 - Wooden Fence (WOOD)
|
||||
05 - Ore (GOLD01) Most Dense
|
||||
06 - Ore (GOLD02) The Ore/Gem tiles do not SEEM to be
|
||||
07 - Ore (GOLD03) any different, but they must be, both
|
||||
08 - Ore (GOLD04) Least Dense RA and the editor seem to make single
|
||||
09 - Gems (GEM01) Most Dense cell gem/ore tiles look the same, and
|
||||
0A - Gems (GEM02) they only look different in clumps.
|
||||
0B - Gems (GEM03)
|
||||
0C - Gems (GEM04) Least Dense
|
||||
0D - Haystacks (V12)
|
||||
0E - Haystack (V13)
|
||||
0F - Wheat Field (V14)
|
||||
10 - Fallow Field (V15)
|
||||
11 - Corn Field (V16)
|
||||
12 - Celery Field (V17)
|
||||
13 - Potato Field (V18)
|
||||
14 - Circular Thing (FPLS)
|
||||
15 - Wood Crate (WCRATE)
|
||||
16 - Silver Crate (SCRATE)
|
||||
17 - RA Barbed Wire (BARB)
|
||||
18 - RA Sandbags (exactly same as C&C ones) (SBAG)
|
||||
|
||||
19
|
||||
.. - I've only tested 19h and 1Ah, but I assume the rest also crash RA
|
||||
FE
|
||||
|
||||
I've noticed that the Water Crate (WWCRATE) is missing, I've tried 19h and 1Ah
|
||||
onto Water tiles, but it still crashes. Also the concrete floor tile (CONC) is
|
||||
not there either, although if you look at my scenario creation guide, you can
|
||||
put both these on using the [OVERLAY] section.
|
||||
|
||||
Recompressing Overlay Data To An Overlaypack
|
||||
|
||||
|
||||
|
||||
|
||||
How To Get The 'Compressed' Binary To A Map File
|
||||
|
||||
It is nearly exactly the same as the overlaypacks,
|
||||
|
||||
with 6 [....] parts though instead of the two mentioned above.
|
||||
The map should be 48k when expanded (i.e 3 bytes per cell, 128x128x3)
|
||||
Using Format80 again for each [...] section, an 8k destination buffer,
|
||||
and again, splice all these 6 together to make 48k.
|
||||
|
||||
Here is a simple BASIC-like representation of how it is done:
|
||||
|
||||
DIM MAP(0 to 127,0 to 127,1 to 3) as byte
|
||||
|
||||
FOR Y=0 to 127
|
||||
FOR X=0 to 127
|
||||
GET 1 BYTE, PUT IT INTO MAP(X,Y,1)
|
||||
GET 1 BYTE, PUT IT INTO MAP(X,Y,2)
|
||||
NEXT
|
||||
NEXT
|
||||
FOR Y=0 to 127
|
||||
FOR X=0 to 127
|
||||
GET 1 BYTE, PUT IT INTO MAP(X,Y,3)
|
||||
NEXT
|
||||
NEXT
|
||||
|
||||
It is quite strange, but It's Westwood's solution to use more than 256 tiles,
|
||||
which was the limit in C&C.
|
||||
|
||||
Well the hex from the new 48k file is 3 bytes per cell, with them meaning
|
||||
the following :
|
||||
|
||||
[ xx xx ] [ xx ]
|
||||
Tile I.D. Part Of That Tile
|
||||
To Show In That Cell
|
||||
|
||||
Here are some of them, I'll probably get round to doing a full list sometime,
|
||||
but I havent checked the old C&C resources for this *yet*.
|
||||
|
||||
Remember the Tile ID is a word, so I've switched it round (as you should), just
|
||||
in case you think it looks wrong :)
|
||||
|
||||
TILE ID NO. OF PARTS
|
||||
------- ------------
|
||||
FFFF ? - Blank Tile
|
||||
0001 ? - Water
|
||||
|
||||
Recompressing Map Data back into a MapPack
|
||||
|
||||
|
||||
|
||||
|
||||
Appendix A : Format80/40/20 Images (thanks to Vladan Bato)
|
||||
|
||||
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 and RefForm fields are used for. They tell 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 RefOffs field contains the number of the first Format40 image in the
|
||||
chain, and the RefForm field is always 4800h.
|
||||
|
||||
Here's an example :
|
||||
|
||||
0) Off0 8000h 0000h 0000h
|
||||
1) Off1 8000h 0000h 0000h
|
||||
2) Off2 4000h Off1 8000h
|
||||
3) Off3 8000h 0000h 0000h
|
||||
4) Off4 4000h Off1 8000h
|
||||
5) Off5 2000h 0400h 4800h
|
||||
6) Off6 2000h 0400h 4800h
|
||||
7) Off7 4000h Off3 8000h
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
|
||||
Appendix B : 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.
|
||||
|
||||
My Note : The CPS files contain all the full screen Red Alert Images, where
|
||||
PCX files are used for the 640x400 images used in the Windows '95 version.
|
||||
|
||||
|
||||
Thanks to:
|
||||
Westwood for making the great game
|
||||
|
||||
Everyone who contributed to the "Scenario Creation Guide"
|
||||
|
||||
Mike Cocca for help on the MapPacks.
|
||||
|
||||
Moritz Mertinkat for writing RA-MiXer 3.0, I found this very
|
||||
handy when looking for the offsets, as I myself only could find the SHP files reliably :), and for the RMT/PCX info!
|
||||
|
||||
Vladan Bato for the C&C Format80/40/20 images
|
||||
and for the great work on the old C&C MIX files.
|
||||
|
||||
Andrew Griffin for the correction of the RMT files.
|
||||
|
||||
Anyone who thinks they deserve it.
|
||||
|
||||
|
||||
|
||||
Also, email me if you see anything that is wrong, or missing, or should be included, I'll
|
||||
give you credit for that particular part of course :)
|
||||
|
||||
|
||||
You'll find the latest version of this text here:
|
||||
http://www.geocities.com/TimesSquare/Arcade/5553
|
||||
|
||||
|
||||
(c)Gavin Pugh 1997
|
||||
rascenedit@geocities.com
|
||||
Reference in New Issue
Block a user