Hello all,

I'm trying to understand the below C# code. I'm a bit limited on C# knowledge, I've done a few crash course tutorials on it so I think I have the most basic of basics.

This program is taking binary strings from a 3d model that was used in an old game and then outputting the basic values of that model vertices, faces, normals and some other bits for that model.

Now the problem is the guy that wrote this has disappeared off the face of the earth and the program doesn't quite work right or I'm not understanding how the models are put together.

I think its more likely that I'm not understanding how the models components should be imported into a modeling program. I'm getting the vertices imported fine, its the faces(tris and quads) I'm not getting. As a result I'm basically spaghettifing the model.

Is there anyone out there that can assist me in understanding how the model was originally put together so that I can get the import script right.

Code:
using System;
using System.Collections.Generic;
using System.IO;

namespace HWBinaryModelParser
{
    class Program
    {
        TextWriter m_log = null;
        int m_offset = 0;

        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Usage: HWBinaryModelParser <filename of .bin_n>");
                return;
            }

            Program p = new Program();
            p.Run(args[0]);
        }

        void Write(string format, params object[] arg)
        {
            Console.WriteLine(format, arg);
            //m_log.WriteLine(format, arg);
        }

        int ReadInt(BinaryReader r)
        {
            m_offset += 4;
            return r.ReadInt32();
        }

        Int16 ReadShort(BinaryReader r)
        {
            m_offset += 2;
            return r.ReadInt16();
        }

        float ReadFloat(BinaryReader r)
        {
            m_offset += 4;
            return r.ReadSingle();
        }

        void Run(string file)
        {
            //m_log = File.CreateText(@"c:\temp\output.txt");

            MemoryStream m = new MemoryStream(File.ReadAllBytes(file));
            BinaryReader r = new BinaryReader(m);

            try
            {
                // ID and FileType
                byte[] ID = r.ReadBytes(4);
                m_offset += 4;

                Write("S0B ID = {0}{1}{2}{3}", (char)ID[0], (char)ID[1], (char)ID[2], (char)ID[3]);
                Write("FileType {0}", ReadInt(r));

                // Name of object
                int namelen = ReadInt(r);
                ReadName(r, namelen, "Name");

                // Verts
                int numVerts = ReadInt(r);
                Write("Num Verts: {0}", numVerts);
                for (int i = 0; i < numVerts; i++)
                {
                    ReadVertex3D(r, "");
                }

                // Normals
                int numNormals = ReadInt(r);
                Write("Num Normals: {0}", numNormals);

                int baseNormal_Unused = ReadInt(r);
                int currVert_unused = ReadInt(r);

                for (int i = 0; i < numNormals; i++)
                {
                    ReadVertex3D(r, "");
                    int vertexIndex = ReadInt(r);
                    Write("Vertex Index: {0}", vertexIndex);
                }

                // Model is split up into blocks with different materials
                int numBlocks = ReadInt(r);
                Write("Num Blocks: {0}", numBlocks);
                int numAnimFrames = ReadInt(r);
                Write("Num Anim Frames: {0}", numAnimFrames);

                // Per instance data - used at runtime, probably!
                for (int i = 0; i < 64; i++)
                {
                    int perInstanceIntData = ReadInt(r);
                    Write("PerInstanceInt[{0}] = {1}", i, perInstanceIntData);
                }

                for (int i = 0; i < 64; i++)
                {
                    float perInstanceFloatData = ReadFloat(r);
                    Write("PerInstanceFloat[{0}] = {1}", i, perInstanceFloatData);
                }

                float objectSize = ReadFloat(r);
                Write("ObjectSize {0}", objectSize);
                ReadVertex3D(r, "Min Vertex");
                ReadVertex3D(r, "Max Vertex");
                ReadVertex3D(r, "Transform Matrix Offset");
                ReadVertex3D(r, "Bounding Centre Offset");
                ReadVertex3D(r, "Bounding Box extents");
                ReadVertex3D(r, "Small bounding box scale");
                ReadVertex3D(r, "Reciprical Bounding Box Extents");
                float boundingSphereRadius = ReadFloat(r);
                Write("Bounding Sphere Radius {0}", boundingSphereRadius);

                int bumpMapValid = ReadInt(r);
                Write("bumpMapValid {0}", bumpMapValid);    // Bump map is never valid, was never implemented
                int textureMapValid = ReadInt(r);
                Write("textureMapValid {0}", textureMapValid);
                int environmentMapValid = ReadInt(r);
                Write("environmentMapValid {0}", environmentMapValid);

                if (textureMapValid != 0)
                {
                    ReadTextureFaceList(r, "Main Model");
                }

                if (environmentMapValid != 0)
                {
                    ReadEnvironmentMapFaceList(r, "Main Model");
                }

                // End of parsing for an 'Object'
                // Now assuming we're parsing the derived class 'MatrixObject', an object that can have animation. This is the type of 
                // object that all of the player vehicles are derived from

                for (int i = 0; i < numBlocks; i++)
                {
                    int blockNameLen = ReadInt(r);
                    ReadName(r, blockNameLen, "BlockName");

                    int num = ReadInt(r);
                    Write("Num: {0}", num);

                    ReadMatrix(r, "");

                    float size = ReadFloat(r);
                    Write("Size: {0}", size);

                    int FirstVert = ReadInt(r);
                    Write("FirstVert: {0}", FirstVert);
                    int NumVerts = ReadInt(r);
                    Write("NumVerts: {0}", NumVerts);
                    int FirstLVert = ReadInt(r);
                    Write("First Normal: {0}", FirstLVert);
                    int NumLVerts = ReadInt(r);
                    Write("Num Normals: {0}", NumLVerts);

                    int materialNameLen = ReadInt(r);
                    ReadName(r, materialNameLen, "MaterialName");

                    int hasAnims = ReadInt(r);
                    if (hasAnims != 0)
                    {
                        for (int j = 0; j < numAnimFrames; j++)
                        {
                            ReadMatrix(r, "Anim" + j);
                        }
                    }

                    int numKeys = ReadInt(r);

                    if (numKeys > 0)
                    {
                        for (int j = 0; j < numKeys; j++)
                        {
                            ReadMatrix(r, "Keyframe" + j);
                            int frameNumber = ReadInt(r);
                            Write("FrameNumber {0}", frameNumber);
                        }
                    }
                }

                int numLODs = ReadInt(r);
                Write("Num LODs {0}", numLODs);

                for (int i = 0; i < numLODs; i++)
                {
                    float ZDepth = ReadFloat(r);
                    Write("LOD{0}: ZDepth {1}", i, ZDepth);

                    for (int j = 0; j < numBlocks; j++)
                    {
                        int lodFirstVert = ReadInt(r);
                        Write("LOD{0} Block{1} FirstVert = {2}", i, j, lodFirstVert);
                        int lodNumVerts = ReadInt(r);
                        Write("LOD{0} Block{1} lodNumVerts = {2}", i, j, lodNumVerts);
                        int lodFirstNormal = ReadInt(r);
                        Write("LOD{0} Block{1} lodFirstNormal = {2}", i, j, lodFirstNormal);
                        int lodNumNormals = ReadInt(r);
                        Write("LOD{0} Block{1} lodNumNormals = {2}", i, j, lodNumNormals);
                    }

                    int lodBumpMapValid = ReadInt(r);
                    Write("LODBumpMapValid {0}", lodBumpMapValid);  // Never valid
                    int lodTextureMapValid = ReadInt(r);
                    Write("LODTextureMapValid {0}", lodTextureMapValid);
                    int lodEnvironmentMapValid = ReadInt(r);
                    Write("LODEnvironmentMapValid {0}", lodEnvironmentMapValid);

                    if (lodTextureMapValid != 0)
                    {
                        ReadTextureFaceList(r, "LOD" + i);
                    }

                    if (lodEnvironmentMapValid != 0)
                    {
                        ReadEnvironmentMapFaceList(r, "LOD" + i);
                    }
                }
            }
            catch(System.Exception ex)
            {
                m_log.Write("Exception: " + ex.Message);
            }
            finally
            {
                r.Close();
                if (m_log != null)
                {
                    m_log.Close();
                }
            }
        }

        void ReadEnvironmentMapFaceList(BinaryReader r, string prefix)
        {
            int numTextureBlocks = ReadInt(r);
            Write("{0}: numTextureBlocks {1}", prefix, numTextureBlocks);
            int currQuad_Unused = ReadInt(r);
            int numQuads = ReadInt(r);
            Write("{0}: numQuads {1}", prefix, numQuads);
            int currTri_Unused = ReadInt(r);
            int numTris = ReadInt(r);
            Write("{0}: numTris {1}", prefix, numTris);

            for (int i = 0; i < numTextureBlocks; i++)
            {
                int blockFirstQuad = ReadInt(r);
                Write("Block[{0}] blockFirstQuad {1}", i, blockFirstQuad);
                int blockNumQuads = ReadInt(r);
                Write("Block[{0}] blockNumQuads {1}", i, blockNumQuads);
                int blockFirstTri = ReadInt(r);
                Write("Block[{0}] blockFirstTri {1}", i, blockFirstTri);
                int blockNumTris = ReadInt(r);
                Write("Block[{0}] blockNumTris {1}", i, blockNumTris);
            }

            for (int i = 0; i < numTris; i++)
            {
                int shine = ReadInt(r);
                Write("Shine {0}", shine);
                ReadVertex2D(r, "UV0");
                ReadVertex2D(r, "UV1");
                ReadVertex2D(r, "UV2");
            }

            for (int i = 0; i < numQuads; i++)
            {
                int shine = ReadInt(r);
                Write("Shine {0}", shine);
                ReadVertex2D(r, "UV0");
                ReadVertex2D(r, "UV1");
                ReadVertex2D(r, "UV2");
                ReadVertex2D(r, "UV3");
            }

            int environmentMapNameLen = ReadInt(r);
            ReadName(r, environmentMapNameLen, "Environment Map Name");
        }

        void ReadTextureFaceList(BinaryReader r, string prefix)
        {
            int numTextureBlocks = ReadInt(r);
            Write("TFACE: {0}: numTextureBlocks {1}", prefix, numTextureBlocks);
            int currQuad_Unused = ReadInt(r);
            int numQuads = ReadInt(r);
            Write("TFACE: {0}: numQuads {1}", prefix, numQuads);
            int currTri_Unused = ReadInt(r);
            int numTris = ReadInt(r);
            Write("TFACE: {0}: numTris {1}", prefix, numTris);

            for (int i = 0; i < numTextureBlocks; i++)
            {
                int blockFirstQuad = ReadInt(r);
                Write("Block[{0}] blockFirstQuad {1}", i, blockFirstQuad);
                int blockNumQuads = ReadInt(r);
                Write("Block[{0}] blockNumQuads {1}", i, blockNumQuads);
                int blockFirstTri = ReadInt(r);
                Write("Block[{0}] blockFirstTri {1}", i, blockFirstTri);
                int blockNumTris = ReadInt(r);
                Write("Block[{0}] blockNumTris {1}", i, blockNumTris);
            }


            for (int i = 0; i < numTris; i++)
            {
                Int16 vindex0 = ReadShort(r);
                Int16 vindex1 = ReadShort(r);
                Int16 vindex2 = ReadShort(r);
                Int16 pad_unused = ReadShort(r);

                Write("Vertex Indices {0}, {1}, {2}", vindex0, vindex1, vindex2);
                
                Write("Offset {0:x}", m_offset);
                ReadVertex2D(r, "UV0");
                ReadVertex2D(r, "UV1");
                ReadVertex2D(r, "UV2");
                int glow = ReadInt(r);
                Write("Glow {0}", glow);
                float opacity = ReadFloat(r);
                Write("Opacity {0}", opacity);
                int transparencyType = ReadInt(r);  // INVALID=0, NONE, ALPHA, ADD, SUB, SHADOW, MULTIPLY, MULTIPLY2X, DEST_COLOUR, SOURCE_COLOUR
                Write("Transparency Type {0}", transparencyType);
            }

            for (int i = 0; i < numQuads; i++)
            {
                Int16 vindex0 = ReadShort(r);
                Int16 vindex1 = ReadShort(r);
                Int16 vindex2 = ReadShort(r);
                Int16 vindex3 = ReadShort(r);

                Write("Vertex Indices {0}, {1}, {2}, {3}", vindex0, vindex1, vindex2, vindex3);

                ReadVertex2D(r, "UV0");
                ReadVertex2D(r, "UV1");
                ReadVertex2D(r, "UV2");
                ReadVertex2D(r, "UV3");
                int glow = ReadInt(r);
                Write("Glow {0}", glow);
                float opacity = ReadFloat(r);
                Write("Opacity {0}", opacity);
                int transparencyType = ReadInt(r);  // INVALID=0, NONE, ALPHA, ADD, SUB, SHADOW, MULTIPLY, MULTIPLY2X, DEST_COLOUR, SOURCE_COLOUR
                Write("Transparency Type {0}", transparencyType);
            }

            for (int i = 0; i < numTextureBlocks; i++)
            {
                int materialNameLen = ReadInt(r);
                ReadName(r, materialNameLen, "MaterialName");

                int numFacelists = ReadInt(r);

                for (int j = 0; j < numFacelists; j++)
                {
                    int SelfIllumination_unused = ReadInt(r);
                    //Write("SelfIllumination {0}", SelfIllumination);
                    int TransType = ReadInt(r);
                    Write("TransType {0}", TransType);
                    int ZWrite_unused = ReadInt(r);
                    //Write("ZWrite {0}", ZWrite);
                    int FirstQuad = ReadInt(r);
                    Write("FirstQuad {0}", FirstQuad);
                    int NumQuads = ReadInt(r);
                    Write("NumQuads {0}", NumQuads);
                    int FirstTri = ReadInt(r);
                    Write("FirstTri {0}", FirstTri);
                    int NumTris = ReadInt(r);
                    Write("NumTris {0}", NumTris);
                }
            } 
        }

        void ReadName(BinaryReader r, int len, string prefix)
        {
            if (len != 0)
            {
                byte[] name = r.ReadBytes(len);
                r.ReadByte();   // Skip 0 terminator as we don't need it in C#
                Write("{0}: {1}", prefix, System.Text.Encoding.ASCII.GetString(name));

                m_offset += (len + 1);
            }
        }

        void ReadMatrix(BinaryReader r, string prefix)
        {
            float[] matrix = new float[16];
            for (int i = 0; i < 16; i++)
            {
                matrix[i] = ReadFloat(r);
            }

            int isNormal = ReadInt(r);

            Write("{0}:0 {1} {2} {3} {4}", prefix, matrix[0], matrix[1], matrix[2], matrix[3]);
            Write("{0}:1 {1} {2} {3} {4}", prefix, matrix[4], matrix[5], matrix[6], matrix[7]);
            Write("{0}:2 {1} {2} {3} {4}", prefix, matrix[8], matrix[9], matrix[10], matrix[11]);
            Write("{0}:T {1} {2} {3} {4}", prefix, matrix[12], matrix[13], matrix[14], matrix[15]);
        }

        void ReadVertex2D(BinaryReader r, string prefix)
        {
            float u = ReadFloat(r);
            float v = ReadFloat(r);
            Write("{0}: {1} {2}", prefix, u, v);
        }

        void ReadVertex3D(BinaryReader r, string prefix)
        {
            float x = ReadFloat(r);
            float y = ReadFloat(r);
            float z = ReadFloat(r);
            float w = ReadFloat(r);   // Not used
            Write("{0}: {1} {2} {3}", prefix, x, y, z);
        }
    }
}
Additional information, the model file types that the game used were binary versions of a text biased model type (which were written by the developers and called it a .SO1). The binary version of that was the .bin_n which was the file type shipped with the game. I suspect that there were also additional sub models within that bin_n that contained simploid models for the physics, animations, lighting ect.

What I do have is a copy of a few SO1 files given to me by an anonymous benefactor (who also gave us a steer on the above program) that were not compiled to the bin_n file type.

Any help at all would be appreciated!

modeler.zip