public void readData()
        {
            BinaryReader file = binaryFile.openReader();
            int reference;

            //Get the name of the mmh file
            file.BaseStream.Seek(binaryFile.dataOffset + binaryFile.structs[0].fields[MMH_NAME_INDEX].index, SeekOrigin.Begin);
            mmhName = IOUtilities.readECString(file, binaryFile.dataOffset + file.ReadInt32()).ToLower();

            //Get the name of the msh file
            file.BaseStream.Seek(binaryFile.dataOffset + binaryFile.structs[0].fields[MSH_NAME_INDEX].index, SeekOrigin.Begin);
            mshName = IOUtilities.readECString(file, binaryFile.dataOffset + file.ReadInt32()).ToLower();

            //Get the total number of bones in the mmh
            file.BaseStream.Seek(binaryFile.dataOffset + binaryFile.structs[0].fields[TOTAL_BONES_INDEX].index, SeekOrigin.Begin);
            numBones = file.ReadInt32();

            //Apparently fx models have an extra field...
            isFXModel = binaryFile.structs[0].fields.Length == 8;
            if (isFXModel)
            {
                file.Close();
                return;
            }

            //Get the children list (should only contain GOB)
            file.BaseStream.Seek(binaryFile.dataOffset + binaryFile.structs[0].fields[TOP_LEVEL_CHILDREN_INDEX].index, SeekOrigin.Begin);
            reference = file.ReadInt32();
            file.BaseStream.Seek(binaryFile.dataOffset + reference, SeekOrigin.Begin);
            GenericList childrenList = new GenericList(file);

            //Get the children of the GOB object
            //Sometimes its a node struct, sometimes its a mshh struct . . .

            if ((int)(childrenList.type[0].id) == nodeStructIndex)
            {
                file.BaseStream.Seek(binaryFile.dataOffset + childrenList[0] + nodeStruct.fields[GOB_CHILDREN_INDEX].index, SeekOrigin.Begin);
            }
            else if ((int)(childrenList.type[0].id) == meshChunkInfoIndex)
            {
                file.BaseStream.Seek(binaryFile.dataOffset + childrenList[0] + meshChunkInfoStruct.fields[MSH_CHUNK_CHILDREN_INDEX].index, SeekOrigin.Begin);
            }

            reference = file.ReadInt32();
            file.BaseStream.Seek(binaryFile.dataOffset + reference, SeekOrigin.Begin);
            childrenList = new GenericList(file);


            //Find the mesh
            GFF temp = ResourceManager.findFile<GFF>(mshName);
            //If its not there throw an exception cause we need it
            if (temp == null)
            {
                Console.WriteLine("Could not find mesh file \"{0}\".", mshName);
                file.Close();
                return;
                //throw new Exception("COULD NOT FIND MESH FILE, LOOK AT CONSOLE!!!!!!");
            }

            loadedMesh = true;

            //Make the mesh
            mesh = new ModelMesh(temp);

            //For each thing in the child list
            for (int i = 0; i < childrenList.length; i++)
            {
                //If the child is a mesh chunk info struct
                if ((int)childrenList.type[i].id == meshChunkInfoIndex)
                {
                    //Fill in the missing mesh chunk info
                    updateChunk(file, binaryFile.dataOffset + childrenList[i], new Vector3(), new Quaternion());
                }
            }
            file.Close();
        }
        private void updateChunk(BinaryReader file, long startPosition, Vector3 offset, Quaternion rotation)
        {
            file.BaseStream.Seek(startPosition + meshChunkInfoStruct.fields[MSH_CHUNK_GROUP_NAME_INDEX].index,SeekOrigin.Begin);

            //Get the name of the meshchunk this info is for
            String currentMeshChunkName = IOUtilities.readECString(file, binaryFile.dataOffset + file.ReadInt32());

            //Console.WriteLine("Doing Chunk " + currentMeshChunkName);

            //Find the meshChunk we need
            MeshChunk currentMeshChunk = null;
            foreach (MeshChunk m in mesh.chunks)
            {
                if (m.name == currentMeshChunkName)
                {
                    currentMeshChunk = m;
                    break;
                }
            }
            //If no mesh chunk could be found there was a problem
            if (currentMeshChunk == null)
            {
                Console.WriteLine("Could not find mesh chunk \"{0}\" in msh file \"{1}\".", currentMeshChunkName, mshName);
                throw new Exception("COULD NOT FIND MESHCHUNK, LOOK AT CONSOLE!!!!!!");
            }

            //Get the material name
            file.BaseStream.Seek(startPosition + meshChunkInfoStruct.fields[MSH_CHUNK_MATERIAL_INDEX].index, SeekOrigin.Begin);
            currentMeshChunk.materialObjectName = IOUtilities.readECString(file, binaryFile.dataOffset + file.ReadInt32()) + ".mao";

            //Get the chunk ID
            file.BaseStream.Seek(startPosition + meshChunkInfoStruct.fields[MSH_CHUNK_ID_INDEX].index, SeekOrigin.Begin);
            currentMeshChunk.id = IOUtilities.readECString(file, binaryFile.dataOffset + file.ReadInt32());


            //Get whether it casts shadows
            file.BaseStream.Seek(startPosition + meshChunkInfoStruct.fields[MSH_CHUNK_CASTS_BAKED_INDEX].index, SeekOrigin.Begin);
            currentMeshChunk.casts = file.ReadByte() == 1;

            //Get whether it receives shadows
            file.BaseStream.Seek(startPosition + meshChunkInfoStruct.fields[MSH_CHUNK_RECEIVES_BAKED_INDEX].index, SeekOrigin.Begin);
            currentMeshChunk.receives = currentMeshChunk.usesTwoTexCoords ? file.ReadByte() == 1 : false;

            //Get translation offset and rotation
            file.BaseStream.Seek(startPosition + meshChunkInfoStruct.fields[MSH_CHUNK_CHILDREN_INDEX].index, SeekOrigin.Begin);
            int reference = file.ReadInt32();
            file.BaseStream.Seek(binaryFile.dataOffset + reference, SeekOrigin.Begin);
            GenericList attributes = new GenericList(file);

            for (int j = 0; j < attributes.length; j++)
            {
                if ((int)(attributes.type[j].id) == rotationStructIndex)
                {
                    file.BaseStream.Seek(binaryFile.dataOffset + attributes[j], SeekOrigin.Begin);
                    currentMeshChunk.chunkRotation = new Quaternion(file.ReadSingle(), file.ReadSingle(), file.ReadSingle(), file.ReadSingle()) * rotation;
                }
                else if ((int)(attributes.type[j].id) == translationStructIndex)
                {
                    file.BaseStream.Seek(binaryFile.dataOffset + attributes[j], SeekOrigin.Begin);
                    currentMeshChunk.chunkOffset = offset + new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());
                }
                else if ((int)(attributes.type[j].id) == meshChunkInfoIndex)
                {
                    updateChunk(file, binaryFile.dataOffset + attributes[j], currentMeshChunk.chunkOffset, currentMeshChunk.chunkRotation);
                }
            }
        }
        private List<Light> readLights(GenericList objectList, Vector3 roomOffset, Quaternion roomOrientation, int roomID)
        {
            List<Light> lights = new List<Light>();
            BinaryReader file = headerFile.openReader();
            long currentPosition;   //position of begining of light struct (for offsets within struct)

            int type;       //point = 0, ambient = 1, spot = 2                      -The type of the light
            int effect;     //baked = 0, static = 2,  animated = 3, negative = 4;   -How light effects environment
            //LightType lightEffect;                                                  //enum for above (TODO: used to decide whether it affects shadow map or lightmap)
            float radius;                                                           //radius of point lights
            float intensity = 1;                                                    //intensity of light (colour multiplier)
            Vector3 position;                                                       //position of the light
            Quaternion rotation;                                                    //rotation of the light (only used in spot lights)
            Vector3 colour;                                                         //colour of the light
            float inAngle, outAngle, distance;                                      //spot light values


            //for all the objects in the list
            for (int i = 0; i < objectList.length; i++)
            {
                //if its a light object process it
                if (headerFile.structs[(int)objectList.type[i].id].type == GFFSTRUCTTYPE.LIGHT)
                {
                    //seek to the light struct data
                    file.BaseStream.Seek(headerFile.dataOffset + objectList[i], SeekOrigin.Begin);
                    currentPosition = file.BaseStream.Position;
                    
                    //get the position
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_POSITION_INDEX].index,SeekOrigin.Begin);
                    position = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());
                    position += roomOffset;

                    //get the rotation
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_ROTATION_INDEX].index, SeekOrigin.Begin);
                    rotation = new Quaternion(file.ReadSingle(), file.ReadSingle(), file.ReadSingle(), file.ReadSingle());
                    rotation *= roomOrientation;

                    //get the colour
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_COLOUR_INDEX].index, SeekOrigin.Begin);
                    colour = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                    //get the type
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_TYPE_INDEX].index, SeekOrigin.Begin);
                    type = file.ReadInt32();

                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_RADIUS_INDEX].index, SeekOrigin.Begin);
                    radius = file.ReadSingle();

                    //get the multiplier
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_COLOUR_MULTIPLIER_INDEX].index, SeekOrigin.Begin);
                    intensity = file.ReadSingle();

                    Console.WriteLine("Intensity is {0}.", intensity);

                    //get the effect
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_EFFECT_INDEX].index, SeekOrigin.Begin);
                    effect = file.ReadByte();

                    /*
                    //set the lightEffect variable for easy constructing
                    if (effect == LIGHT_BAKED)
                        lightEffect = LightType.Baked;
                    else if (effect == LIGHT_STATIC)
                        lightEffect = LightType.Static;
                    else
                        continue;
                    */

                    //get the in angle
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_INANGLE_INDEX].index, SeekOrigin.Begin);
                    inAngle = file.ReadSingle();

                    //get the out angle
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_OUTANGLE_INDEX].index, SeekOrigin.Begin);
                    outAngle = file.ReadSingle();

                    //get the distance
                    file.BaseStream.Seek(currentPosition + lightStruct.fields[LIGHT_DISTANCE_INDEX].index, SeekOrigin.Begin);
                    distance = file.ReadSingle();

                    //add the light to the lights list
                    /*
                    switch (type)
                    {
                        case LIGHT_AMBIENT:
                            lights.Add(new AmbientLight(position, colour, intensity, lightEffect, false)); break;
                        case LIGHT_SPOT:
                            lights.Add(new SpotLight(position, rotation, colour, intensity, inAngle, outAngle, distance, lightEffect, true)); break;
                        case LIGHT_POINT:
                            lights.Add(new PointLight(position, colour, intensity, radius, lightEffect, true)); break;
                    }
                     */
                }
                else if (headerFile.structs[(int)objectList.type[i].id].type == GFFSTRUCTTYPE.LVL_GROUP)
                {
                    //seek to the group struct data
                    file.BaseStream.Seek(headerFile.dataOffset + objectList[i], SeekOrigin.Begin);
                    currentPosition = file.BaseStream.Position;

                    file.BaseStream.Seek(currentPosition + levelGroupStruct.fields[GROUP_POSITION_INDEX].index, SeekOrigin.Begin);
                    Vector3 groupPosition = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                    file.BaseStream.Seek(currentPosition + levelGroupStruct.fields[GROUP_ROTATION_INDEX].index, SeekOrigin.Begin);
                    Quaternion groupRotation = new Quaternion(file.ReadSingle(), file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                    //seek to the list
                    file.BaseStream.Seek(currentPosition + levelGroupStruct.fields[GROUP_LIST_INDEX].index, SeekOrigin.Begin);
                    int reference = file.ReadInt32();
                    file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);

                    lights.AddRange(readLights(new GenericList(file), roomOffset + Vector3.Transform(groupPosition, groupRotation * roomOrientation), groupRotation * roomOrientation, roomID));
                }
            }
            return lights;
        }
        private List<ModelInstance> readPropModels(GenericList objectList, Vector3 roomOffset, Quaternion roomOrientation, int roomID)
        {
            List<ModelInstance> propModels = new List<ModelInstance>();
            BinaryReader file = headerFile.openReader();
            long currentPosition;   //position of beginning of model struct (for offsets within struct

            GenericList propertyList;   //to hold the list of properties of the model
            int reference;              //for referencing structs

            Vector3 position;
            Quaternion rotation;
            String modelFileName;
            int lightmapValue;
            uint modelID;

            //for all the objects in the list
            for (int i = 0; i < objectList.length; i++)
            {
                //if its a model object process it
                if (headerFile.structs[(int)objectList.type[i].id].type == GFFSTRUCTTYPE.MODEL)
                {
                    //seek to the model struct data
                    file.BaseStream.Seek(headerFile.dataOffset + objectList[i], SeekOrigin.Begin);
                    currentPosition = file.BaseStream.Position;

                    //get position
                    file.BaseStream.Seek(currentPosition + modelStruct.fields[MODEL_POSITION_INDEX].index, SeekOrigin.Begin);
                    position = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());
                    position += roomOffset;

                    //get the rotation
                    file.BaseStream.Seek(currentPosition + modelStruct.fields[MODEL_ROTATION_INDEX].index, SeekOrigin.Begin);
                    rotation = new Quaternion(file.ReadSingle(), file.ReadSingle(), file.ReadSingle(), file.ReadSingle());
                    rotation = rotation * roomOrientation;

                    //get the property List reference
                    file.BaseStream.Seek(currentPosition + modelStruct.fields[MODEL_PROPERTY_INDEX].index + propertyStruct.fields[PROPERTY_CHILDREN_INDEX].index, SeekOrigin.Begin);
                    reference = file.ReadInt32();
                    //seek to the children field  and make the children list
                    file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);
                    propertyList = new GenericList(file);
                    
                    // get the reference to the model file name string
                    file.BaseStream.Seek(headerFile.dataOffset + propertyList[MODEL_FILENAME_INDEX] + propertyStruct.fields[PROPERTY_VALUE_INDEX].index, SeekOrigin.Begin);
                    modelFileName = IOUtilities.readECString(file, headerFile.dataOffset + file.ReadInt32()) + ".mmh";

                    //get the lightmap value
                    file.BaseStream.Seek(headerFile.dataOffset + propertyList[MODEL_LIGHTMAPVALUE_INDEX] + propertyStruct.fields[PROPERTY_VALUE_INDEX].index, SeekOrigin.Begin);

                    lightmapValue = Int32.Parse(IOUtilities.readECString(file, headerFile.dataOffset + file.ReadInt32()));

                    //get the ID value
                    file.BaseStream.Seek(currentPosition + modelStruct.fields[MODEL_ID_INDEX].index,SeekOrigin.Begin);
                    modelID = file.ReadUInt32();

                    //If the model isnt in the dictionary already
                    if (!baseModels.ContainsKey(modelFileName))
                    {
                        //Find the mmh file
                        GFF tempGFF = ResourceManager.findFile<GFF>(modelFileName);
                        //If the file was not found
                        if (tempGFF != null)
                        {
                            ModelHierarchy h = new ModelHierarchy(tempGFF);
                            //Only add it if its not a effects model
                            if (!h.isFXModel)
                            {
                                baseModels.Add(modelFileName, h.mesh.toModel());
                            }
                        }
                        else
                        {
                            //Print an error  
                            //Settings.stream.AppendFormatLine("Could not find model file \"{0}\".",modelFileName);
                        }
                    }

                    //If its not in the dictionary this time then we just ignore it
                    if(baseModels.ContainsKey(modelFileName))
                    {
                        if (baseModels[modelFileName].isLightmapped || baseModels[modelFileName].castsShadows)
                        {
                            propModels.Add(new ModelInstance(modelFileName, baseModels[modelFileName], position, rotation, modelID, roomID, layoutName));
                        }
                    }
                }
                else if (headerFile.structs[(int)objectList.type[i].id].type == GFFSTRUCTTYPE.LVL_GROUP)
                {
                    //seek to the group struct data
                    file.BaseStream.Seek(headerFile.dataOffset + objectList[i], SeekOrigin.Begin);
                    currentPosition = file.BaseStream.Position;

                    file.BaseStream.Seek(currentPosition + levelGroupStruct.fields[GROUP_POSITION_INDEX].index, SeekOrigin.Begin);
                    Vector3 groupPosition = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                    file.BaseStream.Seek(currentPosition + levelGroupStruct.fields[GROUP_ROTATION_INDEX].index, SeekOrigin.Begin);
                    Quaternion groupRotation = new Quaternion(file.ReadSingle(), file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                    //seek to the list
                    file.BaseStream.Seek(currentPosition + levelGroupStruct.fields[GROUP_LIST_INDEX].index, SeekOrigin.Begin);
                    reference = file.ReadInt32();
                    file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);

                    propModels.AddRange(readPropModels(new GenericList(file), roomOffset + groupPosition, groupRotation * roomOrientation, roomID));
                }
            }
            file.Close();
            return propModels;
        }
        public void readObjects()
        {
            int reference;      //used for storing the reference to structs in files
            BinaryReader file = headerFile.openReader();

            //If the level is outdoors, read the models and the terrain mesh, otherwise just read in the models
            //      the file is layed out differently for outdoors and indoor environments
            models = new List<BiowareModel>();
            lights = new List<Light>(); ;
            GenericList objectList;

            //If this is an outdoor level read in the terrain
            if (environmentStruct.type == GFFSTRUCTTYPE.ENV_WORLD_TERRAIN)
            {
                models.AddRange(readTerrainModels());
            }

            //then make the generic list for lights and models

            //get to the beginning of the data block
            file.BaseStream.Seek(headerFile.dataOffset, SeekOrigin.Begin);

            //seek to the reference to the terrain world reference (first field in the struct) then go there
            file.BaseStream.Seek(headerFile.structs[TOP_LEVEL_STRUCT_INDEX].fields[0].index, SeekOrigin.Current);

            //seek to the list of objects in the environment struct and go there
            file.BaseStream.Seek(environmentStruct.fields[ENVIRONMENT_LIST_INDEX].index, SeekOrigin.Current);
            reference = file.ReadInt32();
            file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);

            //make the list of objects
            objectList = new GenericList(file);

            //If this is an outdoor level this is the list of models and lights
            if (environmentStruct.type == GFFSTRUCTTYPE.ENV_WORLD_TERRAIN)
            {
                lightmapModels.AddRange(readPropModels(objectList, new Vector3(), new Quaternion(), 0));
                lights.AddRange(readLights(objectList, new Vector3(), new Quaternion(), 0));
            }
            //if its an indoor room we have to go farther into the data
            else if (environmentStruct.type == GFFSTRUCTTYPE.ENV_WORLD_ROOM)
            {
                //get to the area struct data
                file.BaseStream.Seek(headerFile.dataOffset + objectList[0], SeekOrigin.Begin);


                //Get the layout name
                file.BaseStream.Seek(areaStruct.fields[AREA_PROPERTY_INDEX].index + propertyStruct.fields[PROPERTY_CHILDREN_INDEX].index, SeekOrigin.Current);
                reference = file.ReadInt32();
                file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);
                GenericList propertyList = new GenericList(file);

                file.BaseStream.Seek(headerFile.dataOffset + propertyList[AREA_LAYOUT_NAME_INDEX] + propertyStruct.fields[PROPERTY_VALUE_INDEX].index, SeekOrigin.Begin);
                layoutName = IOUtilities.readECString(file, headerFile.dataOffset + file.ReadInt32());                

                //now get the reference to the room list and make it
                file.BaseStream.Seek(headerFile.dataOffset + objectList[0], SeekOrigin.Begin);

                file.BaseStream.Seek(areaStruct.fields[AREA_LIST_INDEX].index, SeekOrigin.Current);
                reference = file.ReadInt32();
                file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);
                GenericList roomList = new GenericList(file);


                //for each room in the list
                for (int i = 0; i < roomList.length; i++)
                {
                    if (headerFile.structs[(int)roomList.type[i].id].type == GFFSTRUCTTYPE.EVN_ROOM)
                    {
                        //get the room position
                        file.BaseStream.Seek(headerFile.dataOffset + roomList[i] + roomStruct.fields[ROOM_POSITION_INDEX].index, SeekOrigin.Begin);
                        Vector3 position = new Vector3(file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                        //get the room orientation
                        file.BaseStream.Seek(headerFile.dataOffset + roomList[i] + roomStruct.fields[ROOM_ROTATION_INDEX].index, SeekOrigin.Begin);
                        Quaternion orientation = new Quaternion(file.ReadSingle(), file.ReadSingle(), file.ReadSingle(), file.ReadSingle());

                        //get the room id
                        file.BaseStream.Seek(headerFile.dataOffset + roomList[i] + roomStruct.fields[ROOM_ID_INDEX].index, SeekOrigin.Begin);
                        int id = file.ReadInt32();

                        //seek to the object list
                        file.BaseStream.Seek(headerFile.dataOffset + roomList[i] + roomStruct.fields[ROOM_OBJECT_LIST_INDEX].index, SeekOrigin.Begin);
                        reference = file.ReadInt32();
                        file.BaseStream.Seek(headerFile.dataOffset + reference, SeekOrigin.Begin);
                        //Make the list of objects in the room
                        objectList = new GenericList(file);
                        //Add the models and lights to the lists
                        lightmapModels.AddRange(readPropModels(objectList, position, orientation, id));
                        lights.AddRange(readLights(objectList, position, orientation, id));
                    }
                }
            }
            generatePatches(baseModels);

            file.Close();            
        }