// METHODS

    // Attempt to turn the BSP into a .MAP file
    public virtual Entities decompile()
    {
        DecompilerThread.OnMessage(this, "Decompiling...");
        // In the decompiler, it is not necessary to copy all entities to a new object, since
        // no writing is ever done back to the BSP file.
        mapFile = BSPObject.Entities;
        //int numAreaPortals=0;
        int numTotalItems = 0;
        int onePercent    = (int)((BSPObject.Brushes.Count + BSPObject.Entities.Count) / 100);

        if (onePercent < 1)
        {
            onePercent = 1;
        }
        int originalNumEntities = BSPObject.Entities.Count;         // Need to keep track of this in this algorithm, since I create more entities on the fly

        for (int i = 0; i < originalNumEntities; i++)
        {
            // For each entity
            //DecompilerThread.OnMessage(this, "Entity " + i + ": " + mapFile[i]["classname"]);
            // getModelNumber() returns 0 for worldspawn, the *# for brush based entities, and -1 for everything else
            int currentModel = mapFile[i].ModelNumber;
            if (currentModel > -1)                // If this is still -1 then it's strictly a point-based entity. Move on to the next one.
            {
                Leaf[] leaves      = BSPObject.getLeavesInModel(currentModel);
                int    numLeaves   = leaves.Length;
                bool[] brushesUsed = new bool[BSPObject.Brushes.Count]; // Keep a list of brushes already in the model, since sometimes the leaves lump references one brush several times
                numBrshs = 0;                                           // Reset the brush count for each entity
                for (int j = 0; j < numLeaves; j++)
                {
                    // For each leaf in the bunch
                    Leaf currentLeaf         = leaves[j];
                    int  firstMarkBrushIndex = currentLeaf.FirstMarkBrush;
                    int  numBrushIndices     = currentLeaf.NumMarkBrushes;
                    if (numBrushIndices > 0)
                    {
                        // A lot of leaves reference no brushes. If this is one, this iteration of the j loop is finished
                        for (int k = 0; k < numBrushIndices; k++)
                        {
                            // For each brush referenced
                            long currentBrushIndex = BSPObject.MarkBrushes[firstMarkBrushIndex + k];
                            if (!brushesUsed[(int)currentBrushIndex])
                            {
                                // If the current brush has NOT been used in this entity
                                //Console.Write("Brush " + numBrshs);
                                brushesUsed[(int)currentBrushIndex] = true;
                                Brush brush = BSPObject.Brushes[(int)currentBrushIndex];
                                decompileBrush(brush, i);                                 // Decompile the brush
                                numBrshs++;
                                numTotalItems++;
                                if (numTotalItems % onePercent == 0)
                                {
                                    parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count));
                                }
                            }
                        }
                    }
                }
            }
            numTotalItems++;             // This entity
            if (numTotalItems % onePercent == 0)
            {
                parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count));
            }
        }
        // Find displacement faces and generate brushes for them
        for (int i = 0; i < BSPObject.Faces.Count; i++)
        {
            Face face = BSPObject.Faces[i];
            if (face.Displacement > -1)
            {
                SourceDispInfo disp = BSPObject.DispInfos[face.Displacement];
                TexInfo        currentTexInfo;
                if (face.Texture > -1)
                {
                    currentTexInfo = BSPObject.TexInfo[face.Texture];
                }
                else
                {
                    Vector3D[] axes = TexInfo.textureAxisFromPlane(BSPObject.Planes[face.Plane]);
                    currentTexInfo = new TexInfo(axes[0], 0, axes[1], 0, 0, BSPObject.findTexDataWithTexture("tools/toolsclip"));
                }
                SourceTexData currentTexData = BSPObject.TexDatas[currentTexInfo.Texture];
                string        texture        = BSPObject.Textures.getTextureAtOffset((uint)BSPObject.TexTable[currentTexData.StringTableIndex]);
                double[]      textureU       = new double[3];
                double[]      textureV       = new double[3];
                // Get the lengths of the axis vectors
                double SAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexInfo.SAxis.X, 2) + System.Math.Pow((double)currentTexInfo.SAxis.Y, 2) + System.Math.Pow((double)currentTexInfo.SAxis.Z, 2));
                double TAxisLength = System.Math.Sqrt(System.Math.Pow((double)currentTexInfo.TAxis.X, 2) + System.Math.Pow((double)currentTexInfo.TAxis.Y, 2) + System.Math.Pow((double)currentTexInfo.TAxis.Z, 2));
                // In compiled maps, shorter vectors=longer textures and vice versa. This will convert their lengths back to 1. We'll use the actual scale values for length.
                double texScaleU = (1 / SAxisLength);                 // Let's use these values using the lengths of the U and V axes we found above.
                double texScaleV = (1 / TAxisLength);
                textureU[0] = ((double)currentTexInfo.SAxis.X / SAxisLength);
                textureU[1] = ((double)currentTexInfo.SAxis.Y / SAxisLength);
                textureU[2] = ((double)currentTexInfo.SAxis.Z / SAxisLength);
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextSettingsIndex'&keyword='jlca1042'"
                double textureShiftU = (double)currentTexInfo.SShift;
                textureV[0] = ((double)currentTexInfo.TAxis.X / TAxisLength);
                textureV[1] = ((double)currentTexInfo.TAxis.Y / TAxisLength);
                textureV[2] = ((double)currentTexInfo.TAxis.Z / TAxisLength);
                //UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextSettingsIndex'&keyword='jlca1042'"
                double textureShiftV = (double)currentTexInfo.TShift;

                if (face.NumEdges != 4)
                {
                    DecompilerThread.OnMessage(this, "Displacement face with " + face.NumEdges + " edges!");
                }

                // Turn vertices and edges into arrays of vectors
                Vector3D[] froms = new Vector3D[face.NumEdges];
                Vector3D[] tos   = new Vector3D[face.NumEdges];
                for (int j = 0; j < face.NumEdges; j++)
                {
                    if (BSPObject.SurfEdges[face.FirstEdge + j] > 0)
                    {
                        froms[j] = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + j]].FirstVertex].Vector;
                        tos[j]   = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + j]].SecondVertex].Vector;
                    }
                    else
                    {
                        tos[j]   = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + j] * (-1)].FirstVertex].Vector;
                        froms[j] = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + j] * (-1)].SecondVertex].Vector;
                    }
                }

                MAPBrush displacementBrush = MAPBrush.createBrushFromWind(froms, tos, texture, "TOOLS/TOOLSNODRAW", currentTexInfo);

                MAPDisplacement mapdisp = new MAPDisplacement(disp, BSPObject.DispVerts.getVertsInDisp(disp.DispVertStart, disp.Power));
                displacementBrush[0].Displacement = mapdisp;
                mapFile[0].Brushes.Add(displacementBrush);
            }
        }
        for (int i = 0; i < BSPObject.StaticProps.Count; i++)
        {
            Entity           newStaticProp = new Entity("prop_static");
            SourceStaticProp currentProp   = BSPObject.StaticProps[i];
            newStaticProp["model"]       = BSPObject.StaticProps.Dictionary[currentProp.DictionaryEntry];
            newStaticProp["skin"]        = currentProp.Skin + "";
            newStaticProp["origin"]      = currentProp.Origin.X + " " + currentProp.Origin.Y + " " + currentProp.Origin.Z;
            newStaticProp["angles"]      = currentProp.Angles.X + " " + currentProp.Angles.Y + " " + currentProp.Angles.Z;
            newStaticProp["solid"]       = currentProp.Solidity + "";
            newStaticProp["fademindist"] = currentProp.MinFadeDist + "";
            newStaticProp["fademaxdist"] = currentProp.MaxFadeDist + "";
            newStaticProp["fadescale"]   = currentProp.ForcedFadeScale + "";
            if (currentProp.Targetname != null)
            {
                newStaticProp["targetname"] = currentProp.Targetname;
            }
            mapFile.Add(newStaticProp);
        }
        for (int i = 0; i < BSPObject.Cubemaps.Count; i++)
        {
            Entity        newCubemap  = new Entity("env_cubemap");
            SourceCubemap currentCube = BSPObject.Cubemaps[i];
            newCubemap["origin"]      = currentCube.Origin.X + " " + currentCube.Origin.Y + " " + currentCube.Origin.Z;
            newCubemap["cubemapsize"] = currentCube.Size + "";
            mapFile.Add(newCubemap);
        }
        if (!Settings.skipPlaneFlip)
        {
            DecompilerThread.OnMessage(this, "Num simple corrected brushes: " + numSimpleCorrects);
            DecompilerThread.OnMessage(this, "Num advanced corrected brushes: " + numAdvancedCorrects);
            DecompilerThread.OnMessage(this, "Num good brushes: " + numGoodBrushes);
        }
        parent.OnProgress(this, 1.0);
        return(mapFile);
    }
    // METHODS

    // -decompile()
    // Attempts to convert the Quake/Half-life BSP file back into a .MAP file.
    public virtual Entities decompile()
    {
        DecompilerThread.OnMessage(this, "Decompiling...");
        // In the decompiler, it is not necessary to copy all entities to a new object, since
        // no writing is ever done back to the BSP file.
        mapFile = BSPObject.Entities;
        int numTotalItems = 0;
        int onePercent    = (int)((BSPObject.Entities.Count) / 100);

        if (onePercent < 1)
        {
            onePercent = 1;
        }
        // I need to go through each entity and see if it's brush-based.
        // Worldspawn is brush-based as well as any entity with model *#.
        for (int i = 0; i < BSPObject.Entities.Count; i++)
        {
            // For each entity
            DecompilerThread.OnMessage(this, "Entity " + i + ": " + mapFile[i]["classname"]);
            // getModelNumber() returns 0 for worldspawn, the *# for brush based entities, and -1 for everything else
            int currentModel = mapFile[i].ModelNumber;

            if (currentModel > -1)
            {
                // If this is still -1 then it's strictly a point-based entity. Move on to the next one.
                Vector3D origin             = mapFile[i].Origin;
                Model    currentModelObject = BSPObject.Models[currentModel];
                int      firstFace          = currentModelObject.FirstFace;
                int      numFaces           = currentModelObject.NumFaces;
                for (int j = 0; j < numFaces; j++)
                {
                    Face face = BSPObject.Faces[firstFace + j];
                    // Turn vertices and edges into arrays of vectors
                    Vector3D[] froms = new Vector3D[face.NumEdges];
                    Vector3D[] tos   = new Vector3D[face.NumEdges];
                    for (int k = 0; k < face.NumEdges; k++)
                    {
                        if (BSPObject.SurfEdges[face.FirstEdge + k] > 0)
                        {
                            froms[k] = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + k]].FirstVertex].Vector;
                            tos[k]   = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + k]].SecondVertex].Vector;
                        }
                        else
                        {
                            tos[k]   = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + k] * (-1)].FirstVertex].Vector;
                            froms[k] = BSPObject.Vertices[BSPObject.Edges[(int)BSPObject.SurfEdges[face.FirstEdge + k] * (-1)].SecondVertex].Vector;
                        }
                    }

                    TexInfo currentTexInfo = BSPObject.TexInfo[face.Texture];
                    Texture currentTexture = BSPObject.Textures[currentTexInfo.Texture];
                    string  texture        = currentTexture.Name;

                    MAPBrush faceBrush = MAPBrush.createBrushFromWind(froms, tos, texture, "special/nodraw", currentTexInfo);
                    mapFile[i].Brushes.Add(faceBrush);
                }
            }
            numTotalItems++;
            if (numTotalItems % onePercent == 0)
            {
                parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count));
            }
        }

        /*if(!Settings.skipPlaneFlip) {
         * DecompilerThread.OnMessage(this, "Num simple corrected brushes: "+numSimpleCorrects,Settings.VERBOSITY_MAPSTATS);
         * DecompilerThread.OnMessage(this, "Num advanced corrected brushes: "+numAdvancedCorrects,Settings.VERBOSITY_MAPSTATS);
         * DecompilerThread.OnMessage(this, "Num good brushes: "+numGoodBrushes,Settings.VERBOSITY_MAPSTATS);
         * }*/
        parent.OnProgress(this, 1.0);
        return(mapFile);
    }