What was the goal? Extract a level static geometry and its materials. My choice was the first level of the game but which file I should be interested in? After some brute force investigation (e.g. load the whole file as a binary point cloud) it turned out there are two candidates: LEVEL7O.SNI and LEVEL7O.MTO. The first one stores the corridors and the other one stores the arenas. AFAIK, the game uses a clever method to overcome the memory limitations
(The game's original system requirements were a 60 MHz Pentium, 16 MB of RAM... - from wiki) only one arena is loaded at once (which can fit into the memory) and not the whole level. When the player enters a corridor the old arena is unloaded and the next arena will be loaded. I guess this may be one of the reasons why the corridors are in a separated file (those are relatively small so can be present in the memory all the time). LEVEL7O.SNI: Its structure seems very straight-forward. First comes a header then chunks' information and at last chunks' data. The way of reading this file in pseudocode and the tokens' meanings: in.Read(XXX) - XXX may represent a sized type or a byte count in.Seek(XXX, YYY) - XXX means byte count, YYY is equivalent to the token from C's stdio in.Padding - skips arbitrary number of bytes to continue reading at an aligned offset push_back - this indicates that the value will be stored in a dynamic array for_each - iterates through a dynamic array [i] - points to an element in a dynamic array stream - a low-endian byte stream stream in; filesize = in.Read(uint32_t) filename = in.Read(12) filesize2 = in.Read(uint32_t) num_chunks = in.Read(uint32_t) for_each(num_chunks) { chunk_name = in.Read(12) // store the chunk's type chunk_types.push_back(in.Read(uint32_t)) // store the offset where the chunk's data is at chunk_data_offsets.push_back(in.Read(uint32_t)) in.Seek(4, SEEK_CUR) } for_each(num_chunks) { // it seems chunks with type 0 means a mesh if(chunk_types[i] == 0) { in.Seek(chunk_data_offsets[i], SEEK_SET) CreateGeometry(in) } } You can see the CreateGeometry function here which is the heart of our project. Let's see what it does: { // unknown 4 bytes in.Seek(4, SEEK_CUR); { num_material_references = in.Read(uint32_t); for_eacb(num_material_references) { // 10 byte length character literal - name of the material material_references.push_back(in.Read(10)); } } // it seems need a 4 byte alignment in.Padding(4) { num_planes = in.Read(uint32_t); for_eacb(num_planes) { // this may be the four coefficients for the plane equation in.Read(float); in.Read(float); in.Read(float); in.Read(float); // unknown 28 bytes in.Read(28); } } { num_triangles = in.Read(uint32_t); for_eacb(num_triangles) { // these indices forms a triangle vertex_indices.push_back(in.Read(uint16_t)) vertex_indices.push_back(in.Read(uint16_t)) vertex_indices.push_back(in.Read(uint16_t)) // which material reference is assigned to this triangle material_indices.push_back(in.Read(uint16_t)) // non-normalized texture coordinates (U, V) for the three vertices uvs.push_back(in.Read(float)) uvs.push_back(in.Read(float)) uvs.push_back(in.Read(float)) uvs.push_back(in.Read(float)) uvs.push_back(in.Read(float)) uvs.push_back(in.Read(float)) } } { num_vertices = in.Read(uint32_t); for_eacb(num_triangles) { // these are the vertex positions in an RH, Z-up coord. system vertices.push_back(in.Read(float)) vertices.push_back(in.Read(float)) vertices.push_back(in.Read(float)) } } // there is some information beyond this which seemed irrelevant for this project }
LEVEL7O.MTO:
This function is also suitable for the LEVEL7O.MTO, however,
the file structure is not fully explored.
This means I manually discovered the file offsets to the chunks' data in this file. These are: 0x93524 - 4 0x1b50ec - 4 0x2a0e4c - 4 0x366d10 - 4 0x439cf8 - 4 0x53fcfc - 4 0x5e78d0 - 4 0x693508 - 4 0x79f728 - 4
There's one thing missing: the textures.
In this case I cheated and used a PowerVR emulator for capture those.
And the result: