Iff
From The Sims Technical Wiki
Contents |
[edit] IFF (Interchangeable File Format)
These files are the workhorses of 'The Sims'. They contain different chunks:
- FWAV - textstring specifying what soundfile to use for an object. How the filename is associated with a particular object is still unclear.
- BMP_ - BMP files that are used in textbubbles and the interface (buy mode).
- BHAV - Behaviour scripts for the internal VM (Virtual Machine) that have been encoded in a special way.
- SPR2 - Sprite resources that are encoded BMP files. Used to display objects in-game.
- CTSS - Catalog text strings in one or more languages.
- OBJD - Data about an object (cost, category, etc).
- STRn - Text strings (animation names, alerts. etc.) in one or more languages.
- TTAs - Pie menutext strings in one or more languages.
- TTAB - Binary version of TTAs.
- CARR - Carreer data (sim job info).
Each chunk is formated as listed below:
- Type - A string or char array of 4 bytes (4 chars) that specifies the type of the chunk (see the list above).
- Size - A small-endian hex-encoded (??) number of 4 bytes that specifies the size of the chunk. See below for information on how to decode this number.
- TypeNum - A small-endian hex-encoded (??) number of 2 bytes that is unique for each chunk.
- ID - A small-endian hex-encoded (??) number of 2 bytes that corresponds to the ID of the Resource Map (see below).
- Label - A string or char array of 64 bytes (64 chars) that may or may not contain text describing a particular chunk (commentary). The label may contain fill-data to pad it out, but it should still be read!
- Data - The binary data of the chunk. The size of the data will always be Size - 76 bytes (which is the size of the chunk header).
The key to decode the numbers in each chunk header (above) is the function listed below. It's written in C# but should be fairly easy to convert into other languages. Please note that strings are just read as normal chars without any decoding.
private static long HexToInt(byte[] Bytes)
{
long Result = 0;
byte B = 0;
for (int i = 0; i < Bytes.Length; i++)
{
B = Bytes[Bytes.Length - 1 - i];
Result = Result + (long)(B * Math.Pow(256, i));
}
return Result;
}
Thanks to [CheEtah] for providing the original C++ code for this!
[edit] Header
Every *.iff file starts with a header that is exactly 60 bytes long, consisting of the following characters:
IFF FILE 2.5:TYPE FOLLOWED BY SIZE JAMIE DOORNBOS & MAXIS 1
Note the linebreaks here. The safest bet is to read everything as characters (char) and it should display nicely. It is, however, safe to assume that skipping the header won't do any harm, as version 2.5 by and large seems to be the most common version. The header is directly followed by a big-endian (?) encoded number that either contains the offset to the RSMP (ReSource Map) chunk (from the beginning of the file) or contains the following 0-terminated string: '996' (terminated by 0x00). This string has not yet been encountered, however. As you can see, this number is 4 bytes long, and can be decoded by the following code (assuming data has been read as an array of chars):
private int ConvertFromCharArray(char[] CharArray)
{
int result = 0;
for (int i = 0; i < CharArray.Length; ++i)
result += (CharArray[(CharArray.Length - 1) - i] << (i * 8));
return result;
}
[edit] RSMP Chunk
What follows after the header and the RSMP offset is (usually) the RSMP itself, but if you want to read the RSMP you should seek to the RSMP offset. Please note that every number in the RSMP chunk (including the header) should be decoded using the above function (ConvertFromCharArray)! The RSMP's header is structured like any other chunk, but the binary data consists of information about each chunk's position in the file. Not much is known for certain about the contents of the RSMP, but the following fields are known and confirmed:
- Unknown1 - Unknown. 8 bytes.
- InvType - The charactersequence 'psmr' ('rsmp' inverted). 4 bytes.
- Unknown2 - Unknown. 4 bytes.
- NumTypes - The number of unique types in the file (BMP_, FWAV, SPR2 etc). 4 bytes. Presumably used to iterate through a ragged array of chunks for each type.
It is speculated that the data following the NumTypes field can be iterated through as follows (pseudo-code):
for(int i = 0; i < NumTypes; i++)
{
int NumChunks = ConvertFromCharArray(Reader.ReadChars(4));
for(int j = 0; j < NumChunks; j++)
{
int ChunkOffset = ConvertFromCharArray(Reader.ReadChars(4));
int ChunkSize = ConvertFromCharArray(Reader.ReadChars(4));
}
}
[edit] Reading all the chunks
The easiest, but not neccessarily most efficient way of reading an *.iff file is to seek to offset 64 in the datastream (Header + RSMP offset) and continiue reading chunks until the end of the file. Example code for doing this in C# is provided below;
/// <summary>
/// Gets all of the chunks of various types inside an
/// iff file and returns them in a List.
/// </summary>
/// <param name="Reader">A BinaryReader that has been associated with an Iff filestream.</param>
/// <returns>A List of IffType instances.</returns>
public static List<IffChunk> GetIffChunks(BinaryReader Reader)
{
List<IffChunk> Types = new List<IffChunk>();
Reader.BaseStream.Seek(64, SeekOrigin.Begin);
while (Reader.BaseStream.Position < Reader.BaseStream.Length)
{
IffChunk T = new IffChunk();
T.m_Offset = (int)Reader.BaseStream.Position;
T.m_Type = new string(Reader.ReadChars(4));
T.m_Size = IffChunk.HexToInt(Reader.ReadBytes(4));
T.m_TypeNum = IffChunk.HexToInt(Reader.ReadBytes(2));
T.m_ID = IffChunk.HexToInt(Reader.ReadBytes(2));
T.m_Label = new string(Reader.ReadChars(64));
T.m_DataOffset = T.m_Offset + 76;
T.m_DataSize = ((int)T.m_Size - 76);
T.m_Data = Reader.ReadBytes(T.m_DataSize);
Types.Add(T);
Console.WriteLine("Type: " + T.Type.ToString());
Console.WriteLine("Size: " + T.m_Size.ToString());
Console.WriteLine("ID: " + T.m_ID.ToString());
Console.WriteLine("TypeNum: " + T.m_TypeNum.ToString());
Console.WriteLine("Label: " + T.m_Label + "\n");
}
Reader.Close();
return Types;
}
As you can see, the RSMP chunk's numbers can also be decoded using aforementioned HexToInt().

