// METHODS // -decompile() // Attempts to convert the Nightfire BSP file back into a .MAP file. // // This is another one of the most complex things I've ever had to code. I've // never nested for loops four deep before. // Iterators: // i: Current entity in the list // j: Current leaf, referenced in a list by the model referenced by the current entity // k: Current brush, referenced in a list by the current leaf. // l: Current side of the current brush. // m: When attempting vertex decompilation, the current vertex. 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.Brushes.Count + 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. int firstLeaf = BSPObject.Models[currentModel].FirstLeaf; int numLeaves = BSPObject.Models[currentModel].NumLeaves; 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; for (int j = 0; j < numLeaves; j++) { // For each leaf in the bunch Leaf currentLeaf = BSPObject.Leaves[j + firstLeaf]; int firstBrushIndex = 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 if (!brushesUsed[(int)BSPObject.MarkBrushes[firstBrushIndex + k]]) { // If the current brush has NOT been used in this entity //Console.Write("Brush " + numBrshs); brushesUsed[(int)BSPObject.MarkBrushes[firstBrushIndex + k]] = true; decompileBrush(BSPObject.Brushes[(int)BSPObject.MarkBrushes[firstBrushIndex + k]], i); // Decompile the brush numBrshs++; numTotalItems++; if (numTotalItems % onePercent == 0) { parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count)); } } } } } } numTotalItems++; if (numTotalItems % onePercent == 0) { parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count)); } } 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 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; worldspawn = mapFile[mapFile.findAllWithAttribute("classname", "worldspawn")[0]]; int onePercent = (int)((BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.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"]); numBrshs = 0; // Reset the brush count for each entity // 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. int firstBrush = BSPObject.Models[currentModel].FirstBrush; int numBrushes = BSPObject.Models[currentModel].NumBrushes; numBrshs = 0; for (int j = 0; j < numBrushes; j++) { // For each brush //Console.Write("Brush " + j); decompileBrush(BSPObject.Brushes[j + firstBrush], i); // Decompile the brush numBrshs++; numTotalItems++; if (numTotalItems % onePercent == 0) { parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count)); } } } numTotalItems++; if (numTotalItems % onePercent == 0) { parent.OnProgress(this, numTotalItems / (double)(BSPObject.Brushes.Count + BSPObject.Entities.Count + BSPObject.Faces.Count)); } } 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); } foreach (Face face in BSPObject.Faces) { if (face.Facetype == Face.faceType.PATCH) { MAPPatch mapPatch = new MAPPatch(face.PatchSize[0], face.PatchSize[1], BSPObject.Textures[face.Texture].Name); for (int i = 0; i < face.NumVertices; i++) { mapPatch.Add(BSPObject.Vertices[face.FirstVertex + i]); } MAPBrush mapBrush = new MAPBrush(mapPatch); worldspawn.Brushes.Add(mapBrush); } } parent.OnProgress(this, 1.0); return(mapFile); }
// 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 // Attempt to turn the Quake 2 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; } bool containsAreaPortals = false; for (int i = 0; i < BSPObject.Entities.Count; i++) { // For each entity //DecompilerThread.OnMessage(this, "Entity " + i + ": " + mapFile[i]["classname"]); // Deal with area portals. if (mapFile[i]["classname"].Equals("func_areaportal", StringComparison.CurrentCultureIgnoreCase)) { mapFile[i].Attributes.Remove("style"); containsAreaPortals = true; } // 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 firstBrushIndex = 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 if (!brushesUsed[(int)BSPObject.MarkBrushes[firstBrushIndex + k]]) { // If the current brush has NOT been used in this entity //Console.Write("Brush " + numBrshs); brushesUsed[(int)BSPObject.MarkBrushes[firstBrushIndex + k]] = true; Brush brush = BSPObject.Brushes[(int)BSPObject.MarkBrushes[firstBrushIndex + k]]; if ((brush.Contents[1] & ((sbyte)1 << 7)) == 0) { decompileBrush(brush, i); // Decompile the brush } else { containsAreaPortals = true; } 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)); } } if (containsAreaPortals) { // If this map was found to have area portals int j = 0; for (int i = 0; i < BSPObject.Brushes.Count; i++) { // For each brush in this map if ((BSPObject.Brushes[i].Contents[1] & ((sbyte)1 << 7)) != 0) { // If the brush is an area portal brush for (j++; j < BSPObject.Entities.Count; j++) { // Find an areaportal entity if (BSPObject.Entities[j]["classname"].Equals("func_areaportal", StringComparison.CurrentCultureIgnoreCase)) { decompileBrush(BSPObject.Brushes[i], j); // Add the brush to that entity break; // And break out of the inner loop, but remember your place. } } if (j == BSPObject.Entities.Count) { // If we're out of entities, stop this whole thing. break; } } } } 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); }