// 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); }