Mix Format
This explains the MIX file format used by Tiberian Dawn and Red Alert for data storage.
Written by Thomas Johansson.
File Layout
The first thing in the archive is the header. It looks like this:
| Data Type | Field Name | Description |
|---|---|---|
| uint16 | file_count | Amount of files in the archive |
| uint32 | data_size | Size of mix archive in bytes, not including header and index |
Following the header is the index, which is file_count file records, which have the following layout:
| Data Type | Field Name | Description |
|---|---|---|
| uint32 | id | File id, see below how to compute this |
| uint32 | offset | Offset in bytes of the file in the archive, this is from the end of the index |
| uint32 | size | Size of the file in bytes |
Following the index is the actual file data. It is simply the raw bytes, there is no compression.
This means that to get the actual offset of a file, you simply do: (file_count * 12) + 6 + offset
Computing the file ID
The original mix id algorithm only supports 8.3 style filenames, so 12 chars max.
To compute the ID you first zero-pad the filename up to 12 chars. You then walk the name in steps of 4 bytes, until you hit a 0. In other words you compute either 4, 8 or 12 bytes. For each step, first rotate the current id 1 bit to the left. Then cast the next 4 bytes to a 32 bit integer, and add it to the id.
# Python function to compute a mix id def compute_id(filename): """ Calculates a mix id from filename. filename: The filename to encode. Must be <= 12 chars. returns: The computed id. """ if len(filename) > 12: raise Error, "'filename' must not exceed 12 characters in length" # Prepare filename s = filename.upper().ljust(12, '\0') # Compute ID id = 0 for i in xrange(0, 12, 4): if s[i] != '\0': # Rotate 1 bit to the left id = ((id<<1) | ((id>>31) & 1)) # Add the new value id += struct.unpack("<L", s[i:i+4])[0] else: break # Truncate to 32 bits id &= 0xFFFFFFFF return id
// // Sample C application to calculate a mix id // #include <stdio.h> #include <ctype.h> #define ROL(n) ((n<<1) | ((n>>31) & 1)) long calc_id(char *fname) { long calc; int i; char buffer[13]; for (i=0; *fname!='\0' && i<12; i++) buffer[i]=toupper(*(fname++)); while(i<13) buffer[i++]=0; calc=0; for(i=0;buffer[i]!=0;i+=4) calc=ROL(calc)+*(long *)(buffer+i); return calc; } void main() { printf("ID = %lXn",calc_id("B1.DES")); } <pre> <pre> 'Sample VB.Net Module To Calculate Mix ID Private Declare Sub CopyMemoryCVL Lib "Kernel32" Alias "RtlMoveMemory" (ByRef hDest As Integer, ByVal hSource As String, ByVal iBytes As Integer) Public Function GetID(ByVal strFile As String) As UInt32 Dim FArray(0 To 11) As UInt16 Dim i As Integer Dim CurrentID As UInt32 Dim PointID As UInt32 If Len(strFile) > 12 Then strFile = Mid(strFile, 1, 12) End If strFile = UCase(strFile) For i = 0 To 11 FArray(i) = 0 Next For i = 1 To Len(strFile) FArray(i - 1) = Asc(Mid(strFile, i, 1)) Next For i = 0 To 11 Step 4 PointID = CVL(Chr(FArray(i)) & Chr(FArray(i + 1)) & Chr(FArray(i + 2)) & Chr(FArray(i + 3))) If i <> 0 And i < Len(strFile) Then CurrentID = CurrentID << 1 End If CurrentID = CurrentID + PointID Next GetID = CurrentID + 1 End Function Public Function CVL(ByRef Argument As String) As Long Dim lTemp As Integer = 0I If Len(Argument) <> 4 Then Return Long.MinValue End If CopyMemoryCVL(lTemp, Argument, 4I) Return CLng(lTemp) End Function

Some MIX archives contains file with name more then 12 characters length.
For example file named “local mix database.dat” contain archive files names.
I’m create Total Commander archive plugin for viewing/unpacking MIX files. Write me if you interested in it to wicked_digger@mail.ru.
Comment by Digger — January 14, 2012 @ 4:33 am