The binary mode has a block of data that doesn't seem to be used. A big block in the middle of the file which I don't use at all, it's just weird.
Since I can read and display the meshes without it, I cannot work out what it is for.
However if I write a binary file without it, the game doesn't load it.
So pretty stuffed.
I've been working on my own 3dsMax import/export tool to read and write both text and binary formats, and have figured out what that block is by reverse-engineering il2_corep4.dll. I realize I'm very late and I don't know if this tool is still being worked on, but I thought I'd share my findings in case you're interested in implementing support for the binary format in your tool (or anybody else with their own).
That block is some sort of hash table, with a size of 514 + 2 * SectionCount bytes, presumably used for verifying file integrity or other type of validation. Of the initial 514 bytes, the first 257 bytes are zero-padding, and the other half defines the number of hits for a particular hash code. The last bytes are an array of section indexes: The first half of that array is also zero-padding, with the actual section indexes being in the latter half.
The purpose of the padding is because when the msh file is loaded in memory by the game, the bytes in the hash table are unrolled into a sequence of word-size items, which will take up the whole space as it effectively doubles in size. I don't know why they convert it to 16-bit; it seems a bit wasteful but maybe they wanted to make sure to avoid overflow, although I don't see how it could realistically happen.
For example, a msh file's hash table will be converted as such (for brevity and the sake of comparison, the padding is excluded in the former, and the latter is written as bytes instead of words):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 01 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00
00 00 02 06 01 09 03 05 08 0B 07 0A 04
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01
01 01 01 01 01 01 01 01 01 01 01 01 01 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 03 03
04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 04 05 05 05 05 05 05 05
05 05 05 05 05 05 05 05 05 06 06 06 06 06 06 06 06 06 06 06 07 07 07 07 07 07 07 07 07 07 08 08
08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 09 09 09 09 09 09 09 09 09 09
09 09 09 09 09 09 09 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A
0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0A 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B
0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0C 0C 0C 0C 0C
0C 00 02 06 01 09 03 05 08 0B 07 0A 04Considering that this msh file has 12 sections, the last 12 values in blue are the section indexes that the hash codes correspond to. The hash is based on the section names: For example, [Materials] will yield 44, thus the byte at offset 44 (first red value in the former block) was incremented by one when the binary msh file was compiled. Hash collisions further increment the value, and its section index is inserted at the corresponding position in the array (pushing the subsequent values to the right). For example, if section index
06 first mapped to hash code 44 in the array and section index
02 then also mapped to it, the latter took its place and pushed the rest to the right. The order technically doesn't matter, just that the sequence of indexes from the colliding hashes is at the correct position in the array.
After the msh is loaded in-game and the table is converted, the game looks for the [Materials] section by reading the value of the hash code at offset 44, which is
01 in this example. Offset 1 in the array of indexes is
02, which corresponds to the third section in the msh file (which should indeed be the [Materials] section).
The next value in the hash table (
02) is subtracted by the first one (
01) to define the number of sections that map to this specific hash code. In this example, the count will be 1, so the game will only look for the [Materials] section once. If the count turns out to be more than 1 (for example if the byte is
02 in the file, resulting in
01 03 after conversion) and the string from the current index doesn't match with the one that the game is looking for, the next index in the array is checked (which would be
06 here). If the game doesn't find a matching section within that run, it doesn't bother looking for it in the whole msh file and loading for the entire aircraft is aborted.
The square brackets that surround the section names are included for the hash function. Section names are hashed thusly:
unsigned __int8 sectionHash(int strLength, char* str) { //il2_corep4.dll+0xEB6D0 (4.13.4)
unsigned __int8 al = 0;
unsigned __int8 dl;
if (strLength) {
do {
dl = *str ^ ((al + al) | (al >> 7));
al = dl;
++str;
} while (--strLength);
}
return al;
}
(Code blocks are considered "external links" for some reason, and prevent me from posting.)