/// <summary> /// Reads BSP Data /// </summary> public static void ExportBSPData(ProcessReader reader, long assetPoolsAddress, long assetSizesAddress, string gameType, Action <object> printCallback = null) { // Found her printCallback?.Invoke("Found supported game: Call of Duty: Black Ops"); // Get XModel Name var firstXModelName = reader.ReadNullTerminatedString(reader.ReadInt32(reader.ReadInt32(assetPoolsAddress + 0x14) + 4)); // Validate by XModel Name if (firstXModelName == "void" || firstXModelName == "defaultactor" || firstXModelName == "defaultweapon") { // Load BSP Pools (they only have a size of 1 so we have no free header) var gfxMapAsset = reader.ReadStruct <GfxMap>(reader.ReadInt32(assetPoolsAddress + 0x44)); // Name string gfxMapName = reader.ReadNullTerminatedString(gfxMapAsset.NamePointer); string mapName = reader.ReadNullTerminatedString(gfxMapAsset.MapNamePointer); // Verify a BSP is actually loaded (if in base menu, etc, no map is loaded) if (String.IsNullOrWhiteSpace(gfxMapName)) { printCallback?.Invoke("No BSP loaded. Enter Main Menu or a Map to load in the required assets."); } else { // New IW Map var mapFile = new IWMap(); // Print Info printCallback?.Invoke(String.Format("\n--------------------------\n")); printCallback?.Invoke(String.Format("Loaded Gfx Map - {0}", gfxMapName)); printCallback?.Invoke(String.Format("Loaded Map - {0}", mapName)); printCallback?.Invoke(String.Format("Vertex Count - {0}", gfxMapAsset.GfxVertexCount)); printCallback?.Invoke(String.Format("Indices Count - {0}", gfxMapAsset.GfxIndicesCount)); printCallback?.Invoke(String.Format("Surface Count - {0}", gfxMapAsset.SurfaceCount)); printCallback?.Invoke(String.Format("Model Count - {0}", gfxMapAsset.GfxStaticModelsCount)); // Build output Folder string outputName = Path.Combine("exported_maps", "black_ops", gameType, mapName, mapName); Directory.CreateDirectory(Path.GetDirectoryName(outputName)); // Stop watch var stopWatch = Stopwatch.StartNew(); // Read Vertices printCallback?.Invoke("Parsing vertex data...."); var vertices = ReadGfxVertices(reader, gfxMapAsset.GfxVerticesPointer, gfxMapAsset.GfxVertexCount); printCallback?.Invoke(String.Format("Parsed vertex data in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surface indices...."); var indices = ReadGfxIndices(reader, gfxMapAsset.GfxIndicesPointer, gfxMapAsset.GfxIndicesCount); printCallback?.Invoke(String.Format("Parsed indices in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surfaces...."); var surfaces = ReadGfxSufaces(reader, gfxMapAsset.GfxSurfacesPointer, gfxMapAsset.SurfaceCount); printCallback?.Invoke(String.Format("Parsed surfaces in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Write OBJ printCallback?.Invoke("Generating map files..."); // Create new OBJ var obj = new WavefrontOBJ(); // Append Vertex Data foreach (var vertex in vertices) { obj.Vertices.Add(vertex.Position); obj.Normals.Add(vertex.Normal); obj.UVs.Add(vertex.UV); } // Image Names (for Search String) HashSet <string> imageNames = new HashSet <string>(); // Append Faces foreach (var surface in surfaces) { // Create new Material var material = ReadMaterial(reader, surface.MaterialPointer); // Add to images imageNames.Add(material.DiffuseMap); // Add it obj.AddMaterial(material); // Add points for (ushort i = 0; i < surface.FaceCount; i++) { // Face Indices var faceIndex1 = indices[i * 3 + surface.FaceIndex] + surface.VertexIndex; var faceIndex2 = indices[i * 3 + surface.FaceIndex + 1] + surface.VertexIndex; var faceIndex3 = indices[i * 3 + surface.FaceIndex + 2] + surface.VertexIndex; // Validate unique points, and write to OBJ if (faceIndex1 != faceIndex2 && faceIndex1 != faceIndex3 && faceIndex2 != faceIndex3) { // new Obj Face var objFace = new WavefrontOBJ.Face(material.Name); // Add points objFace.Vertices[0] = new WavefrontOBJ.Face.Vertex(faceIndex1, faceIndex1, faceIndex1); objFace.Vertices[2] = new WavefrontOBJ.Face.Vertex(faceIndex2, faceIndex2, faceIndex2); objFace.Vertices[1] = new WavefrontOBJ.Face.Vertex(faceIndex3, faceIndex3, faceIndex3); // Add to OBJ obj.Faces.Add(objFace); } } } // Save it obj.Save(outputName + ".obj"); // Build search strinmg string searchString = ""; // Loop through images, and append each to the search string (for Wraith/Greyhound) foreach (string imageName in imageNames) { searchString += String.Format("{0},", Path.GetFileNameWithoutExtension(imageName)); } // Create .JSON with XModel Data List <IDictionary> ModelData = CreateXModelDictionary(reader, gfxMapAsset.GfxStaticModelsPointer, (int)gfxMapAsset.GfxStaticModelsCount); string xmodeljson = JToken.FromObject(ModelData).ToString(Formatting.Indented); File.WriteAllText(outputName + "_xmodels.json", xmodeljson); // Loop through xmodels, and append each to the search string (for Wraith/Greyhound) List <string> xmodelList = CreateXModelList(ModelData); // Dump it File.WriteAllText(outputName + "_search_string.txt", searchString); File.WriteAllText(outputName + "_xmodelList.txt", String.Join(",", xmodelList.ToArray())); // Read entities and dump to map mapFile.Entities.AddRange(ReadStaticModels(reader, gfxMapAsset.GfxStaticModelsPointer, (int)gfxMapAsset.GfxStaticModelsCount)); mapFile.DumpToMap(outputName + ".map"); // Done printCallback?.Invoke(String.Format("Generated files in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); } } else { printCallback?.Invoke("Call of Duty: Black Ops is supported, but this EXE is not."); } }
/// <summary> /// Reads BSP Data /// </summary> public static void ExportBSPData(uint assetPoolsAddress, uint assetSizesAddress, string gameType, Action <object> printCallback = null) { console.Connect(); if (!console.activeConnection) { printCallback?.Invoke("Couldn't connect"); return; } // Found her printCallback?.Invoke("Found supported game: Call of Duty: Modern Warfare 2"); // Validate by XModel Name printCallback?.Invoke($"AssetPools Address: {assetPoolsAddress:X}"); //_xrpc var _void = console.ReadUInt32(console.ReadUInt32(assetPoolsAddress + 0x10) + 4); var txt = console.ReadString(_void); printCallback?.Invoke($"Address Found: {txt}"); if (txt == "void") { var gfxMapAsset = (GfxMap)console.ReadUInt32(assetPoolsAddress + 0x4C); var gfxMapName = console.ReadString((uint)gfxMapAsset.NamePointer); var mapName = console.ReadString((uint)gfxMapAsset.MapNamePointer); if (string.IsNullOrWhiteSpace(gfxMapName)) { printCallback?.Invoke("No BSP loaded. Enter a Map to load in the required assets."); } else { var mapFile = new IWMap(); printCallback?.Invoke($"Loaded Gfx Map - {gfxMapName}"); printCallback?.Invoke($"Loaded Map - {mapName}"); printCallback?.Invoke($"Vertex Count - {gfxMapAsset.GfxVertexCount}"); printCallback?.Invoke($"Indices Count - {gfxMapAsset.GfxIndicesCount}"); printCallback?.Invoke($"Surface Count - {gfxMapAsset.SurfaceCount}"); printCallback?.Invoke($"Model Count - {gfxMapAsset.GfxStaticModelsCount}"); string outputName = Path.Combine("exported_maps", "modern_warfare_2", gameType, mapName, mapName); Directory.CreateDirectory(Path.GetDirectoryName(outputName)); var stopWatch = Stopwatch.StartNew(); //Read vertex printCallback?.Invoke("Parsing vertex data...."); var vertices = ReadGfxVertices(gfxMapAsset.GfxVerticesPointer, gfxMapAsset.GfxVertexCount); printCallback?.Invoke($"Parsed vertex data in {stopWatch.ElapsedMilliseconds / 1000.0:0.00} seconds."); // Reset timer stopWatch.Restart(); //Read Indices printCallback?.Invoke("Parsing surface indices...."); var indices = ReadGfxIndices(gfxMapAsset.GfxIndicesPointer, gfxMapAsset.GfxIndicesCount); printCallback?.Invoke($"Parsed indices in {stopWatch.ElapsedMilliseconds / 1000.0:0.00} seconds."); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surfaces...."); var surfaces = ReadGfxSufaces(gfxMapAsset.GfxSurfacesPointer, gfxMapAsset.SurfaceCount); printCallback?.Invoke($"Parsed surfaces in {stopWatch.ElapsedMilliseconds / 1000.0:0.00} seconds."); // Reset timer stopWatch.Restart(); // Write OBJ printCallback?.Invoke("Converting to OBJ...."); // Create new OBJ var obj = new WavefrontOBJ(); // Append Vertex Data foreach (var vertex in vertices) { obj.Vertices.Add(vertex.Position); obj.Normals.Add(vertex.Normal); obj.UVs.Add(vertex.UV); } //Image Names (for Search String) var imageNames = new HashSet <string>(); // Append Faces foreach (var surface in surfaces) { // Create new Material var material = ReadMaterial(surface.MaterialPointer); // Add to images imageNames.Add(material.DiffuseMap); // Add it obj.AddMaterial(material); //Add points for (ushort i = 0; i < surface.FaceCount; i++) { //Face Indices var faceIndex1 = indices[i * 3 + surface.FaceIndex] + surface.VertexIndex; var faceIndex2 = indices[i * 3 + surface.FaceIndex + 1] + surface.VertexIndex; var faceIndex3 = indices[i * 3 + surface.FaceIndex + 2] + surface.VertexIndex; //Validate unique points, and write to OBJ if (faceIndex1 != faceIndex2 && faceIndex1 != faceIndex3 && faceIndex2 != faceIndex3) { //new Obj Face var objFace = new WavefrontOBJ.Face(material.Name); //Add points objFace.Vertices[0] = new WavefrontOBJ.Face.Vertex(faceIndex1, faceIndex1, faceIndex1); objFace.Vertices[2] = new WavefrontOBJ.Face.Vertex(faceIndex2, faceIndex2, faceIndex2); objFace.Vertices[1] = new WavefrontOBJ.Face.Vertex(faceIndex3, faceIndex3, faceIndex3); //Add to OBJ obj.Faces.Add(objFace); } } } // Save it obj.Save(outputName + ".obj"); //// Build search strinmg //var searchString = imageNames.Aggregate("", (current, imageName) => current + $"{Path.GetFileNameWithoutExtension(imageName)},"); // Loop through images, and append each to the search string (for Wraith/Greyhound) // Dump it //File.WriteAllText(outputName + "_search_string.txt", searchString); // Read entities and dump to map mapFile.Entities.AddRange(ReadStaticModels((uint)gfxMapAsset.GfxStaticModelsPointer, gfxMapAsset.GfxStaticModelsCount)); mapFile.DumpToMap(outputName + ".map"); // Done printCallback?.Invoke($"Converted to OBJ in {stopWatch.ElapsedMilliseconds / 1000.0:0.00} seconds."); } } else { printCallback?.Invoke("Call of Duty: Modern Warfare 2 is supported, but this EXE is not."); } }
/// <summary> /// Reads BSP Data /// </summary> public static void ExportBSPData(ProcessReader reader, long assetPoolsAddress, long assetSizesAddress, string gameType, Action <object> printCallback = null) { // Found her printCallback?.Invoke("Found supported game: Call of Duty: Modern Warfare Remastered"); // Validate by XModel Name var baseAddr = reader.GetBaseAddress(); var si = reader.ReadInt64(reader.ReadInt64(baseAddr + assetPoolsAddress + 0x38) + 8); var po = reader.ReadNullTerminatedString(si) == "fx"; if (po) { // Load BSP Pools (they only have a size of 1 so we don't care about reading more than 1) var gfxMapAsset = reader.ReadStruct <GfxMap>(reader.ReadInt64(reader.GetBaseAddress() + assetPoolsAddress + 0xF8)); // Name string gfxMapName = reader.ReadNullTerminatedString(gfxMapAsset.NamePointer); string mapName = reader.ReadNullTerminatedString(gfxMapAsset.MapNamePointer); // Verify a BSP is actually loaded (if in base menu, etc, no map is loaded) if (String.IsNullOrWhiteSpace(gfxMapName)) { printCallback?.Invoke("No BSP loaded. Enter Main Menu or a Map to load in the required assets."); } else { // New IW Map var mapFile = new IWMap(); // Print Info printCallback?.Invoke(String.Format("Loaded Gfx Map - {0}", gfxMapName)); printCallback?.Invoke(String.Format("Loaded Map - {0}", mapName)); printCallback?.Invoke(String.Format("Vertex Count - {0}", gfxMapAsset.GfxVertexCount)); printCallback?.Invoke(String.Format("Indices Count - {0}", gfxMapAsset.GfxIndicesCount)); printCallback?.Invoke(String.Format("Surface Count - {0}", gfxMapAsset.SurfaceCount)); printCallback?.Invoke(String.Format("Model Count - {0}", gfxMapAsset.GfxStaticModelsCount)); // Build output Folder string outputName = Path.Combine("exported_maps", "modern_warfare_rm", gameType, mapName, mapName); Directory.CreateDirectory(Path.GetDirectoryName(outputName)); // Stop watch var stopWatch = Stopwatch.StartNew(); // Read Vertices printCallback?.Invoke("Parsing vertex data...."); var vertices = ReadGfxVertices(reader, gfxMapAsset.GfxVerticesPointer, (int)gfxMapAsset.GfxVertexCount); printCallback?.Invoke(String.Format("Parsed vertex data in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surface indices...."); var indices = ReadGfxIndices(reader, gfxMapAsset.GfxIndicesPointer, (int)gfxMapAsset.GfxIndicesCount); printCallback?.Invoke(String.Format("Parsed indices in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surfaces...."); var surfaces = ReadGfxSufaces(reader, gfxMapAsset.GfxSurfacesPointer, gfxMapAsset.SurfaceCount); printCallback?.Invoke(String.Format("Parsed surfaces in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Write OBJ printCallback?.Invoke("Converting to OBJ...."); // Create new OBJ var obj = new WavefrontOBJ(); // Append Vertex Data foreach (var vertex in vertices) { obj.Vertices.Add(vertex.Position); obj.Normals.Add(vertex.Normal); obj.UVs.Add(vertex.UV); } // Image Names (for Search String) HashSet <string> imageNames = new HashSet <string>(); // Append Faces foreach (var surface in surfaces) { // Create new Material var material = ReadMaterial(reader, surface.MaterialPointer); // Add to images imageNames.Add(material.DiffuseMap); // Add it obj.AddMaterial(material); // Add points for (ushort i = 0; i < surface.FaceCount; i++) { // Face Indices var faceIndex1 = indices[i * 3 + surface.FaceIndex] + surface.VertexIndex; var faceIndex2 = indices[i * 3 + surface.FaceIndex + 1] + surface.VertexIndex; var faceIndex3 = indices[i * 3 + surface.FaceIndex + 2] + surface.VertexIndex; // Validate unique points, and write to OBJ if (faceIndex1 != faceIndex2 && faceIndex1 != faceIndex3 && faceIndex2 != faceIndex3) { // new Obj Face var objFace = new WavefrontOBJ.Face(material.Name); // Add points objFace.Vertices[0] = new WavefrontOBJ.Face.Vertex(faceIndex1, faceIndex1, faceIndex1); objFace.Vertices[2] = new WavefrontOBJ.Face.Vertex(faceIndex2, faceIndex2, faceIndex2); objFace.Vertices[1] = new WavefrontOBJ.Face.Vertex(faceIndex3, faceIndex3, faceIndex3); // Add to OBJ obj.Faces.Add(objFace); } } } // Save it obj.Save(outputName + ".obj"); // Build search strinmg string searchString = ""; // Loop through images, and append each to the search string (for Wraith/Greyhound) foreach (string imageName in imageNames) { searchString += String.Format("{0},", Path.GetFileNameWithoutExtension(imageName)); } // Dump it File.WriteAllText(outputName + "_search_string.txt", searchString); // Read entities and dump to map mapFile.Entities.AddRange(ReadStaticModels(reader, gfxMapAsset.GfxStaticModelsPointer, (int)gfxMapAsset.GfxStaticModelsCount)); mapFile.DumpToMap(outputName + ".map"); // Done printCallback?.Invoke(String.Format("Converted to OBJ in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); } } else { printCallback?.Invoke("Call of Duty: Modern Warfare Remastered is supported, but this EXE is not."); } }
/// <summary> /// Reads BSP Data /// </summary> public static void ExportBSPData(ProcessReader reader, long assetPoolsAddress, long assetSizesAddress, string gameType, Action <object> printCallback = null) { // Found her printCallback?.Invoke("Found supported game: Call of Duty: Black Ops 2"); // Validate by XModel Name if (reader.ReadNullTerminatedString(reader.ReadInt32(reader.ReadInt32(assetPoolsAddress + 0x14) + 4)) == "defaultvehicle") { // Load BSP Pools (they only have a size of 1 so we have no free header) var gfxMapAsset = reader.ReadStruct <GfxMap>(reader.ReadInt32(assetPoolsAddress + 0x44)); // Name string gfxMapName = reader.ReadNullTerminatedString(gfxMapAsset.NamePointer); string mapName = reader.ReadNullTerminatedString(gfxMapAsset.MapNamePointer); // Verify a BSP is actually loaded (if in base menu, etc, no map is loaded) if (String.IsNullOrWhiteSpace(gfxMapName)) { printCallback?.Invoke("No BSP loaded. Enter Main Menu or a Map to load in the required assets."); } else { // New IW Map var mapFile = new IWMap(); // Print Info printCallback?.Invoke(String.Format("Loaded Gfx Map - {0}", gfxMapName)); printCallback?.Invoke(String.Format("Loaded Map - {0}", mapName)); printCallback?.Invoke(String.Format("Vertex Count - {0}", gfxMapAsset.GfxVertexCount)); printCallback?.Invoke(String.Format("Indices Count - {0}", gfxMapAsset.GfxIndicesCount)); printCallback?.Invoke(String.Format("Surface Count - {0}", gfxMapAsset.SurfaceCount)); printCallback?.Invoke(String.Format("Model Count - {0}", gfxMapAsset.GfxStaticModelsCount)); // Build output Folder string outputName = Path.Combine("exported_maps", "black_ops_2", gameType, mapName, mapName); Directory.CreateDirectory(Path.GetDirectoryName(outputName)); // Stop watch var stopWatch = Stopwatch.StartNew(); // Read Vertices printCallback?.Invoke("Parsing vertex data...."); var vertexBuffer = reader.ReadBytes(gfxMapAsset.GfxVerticesPointer, gfxMapAsset.GfxVertexBufferSize); printCallback?.Invoke(String.Format("Parsed vertex data in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surface indices...."); var indices = ReadGfxIndices(reader, gfxMapAsset.GfxIndicesPointer, gfxMapAsset.GfxIndicesCount); printCallback?.Invoke(String.Format("Parsed indices in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Read Indices printCallback?.Invoke("Parsing surfaces...."); var surfaces = ReadGfxSufaces(reader, gfxMapAsset.GfxSurfacesPointer, gfxMapAsset.SurfaceCount); printCallback?.Invoke(String.Format("Parsed surfaces in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); // Reset timer stopWatch.Restart(); // Write OBJ printCallback?.Invoke("Converting to OBJ...."); // Create new OBJ var obj = new WavefrontOBJ(); // Image Names (for Search String) HashSet <string> imageNames = new HashSet <string>(); // Vertex Index Tracker int vertexIndex = 0; // Append Faces foreach (var surface in surfaces) { // Create new Material var material = ReadMaterial(reader, surface.MaterialPointer); // Add to images imageNames.Add(material.DiffuseMap); // Add it obj.AddMaterial(material); // Add points for (ushort i = 0; i < surface.FaceCount; i++) { // Face Indices var faceIndex1 = indices[i * 3 + surface.FaceIndex]; var faceIndex2 = indices[i * 3 + surface.FaceIndex + 1]; var faceIndex3 = indices[i * 3 + surface.FaceIndex + 2]; // Validate unique points, and write to OBJ if (faceIndex1 != faceIndex2 && faceIndex1 != faceIndex3 && faceIndex2 != faceIndex3) { // new Obj Face var objFace = new WavefrontOBJ.Face(material.Name); // Unpack vertices var vertex1 = UnpackVertex(ByteUtil.BytesToStruct <GfxVertex>(vertexBuffer, surface.VertexBufferOffset + (faceIndex1 * 36))); var vertex2 = UnpackVertex(ByteUtil.BytesToStruct <GfxVertex>(vertexBuffer, surface.VertexBufferOffset + (faceIndex2 * 36))); var vertex3 = UnpackVertex(ByteUtil.BytesToStruct <GfxVertex>(vertexBuffer, surface.VertexBufferOffset + (faceIndex3 * 36))); // Add Offsets obj.Vertices.Add(vertex1.Position); obj.Vertices.Add(vertex2.Position); obj.Vertices.Add(vertex3.Position); // Add Normals obj.Normals.Add(vertex1.Normal); obj.Normals.Add(vertex2.Normal); obj.Normals.Add(vertex3.Normal); // Add UVs obj.UVs.Add(vertex1.UV); obj.UVs.Add(vertex2.UV); obj.UVs.Add(vertex3.UV); // Add points objFace.Vertices[0] = new WavefrontOBJ.Face.Vertex(vertexIndex, vertexIndex, vertexIndex); objFace.Vertices[2] = new WavefrontOBJ.Face.Vertex(vertexIndex + 1, vertexIndex + 1, vertexIndex + 1); objFace.Vertices[1] = new WavefrontOBJ.Face.Vertex(vertexIndex + 2, vertexIndex + 2, vertexIndex + 2); // Add to OBJ obj.Faces.Add(objFace); vertexIndex += 3; } } } // Save it obj.Save(outputName + ".obj"); // Build search strinmg string searchString = ""; // Loop through images, and append each to the search string (for Wraith/Greyhound) foreach (string imageName in imageNames) { searchString += String.Format("{0},", Path.GetFileNameWithoutExtension(imageName)); } // Dump it File.WriteAllText(outputName + "_search_string.txt", searchString); // Read entities and dump to map mapFile.Entities.AddRange(ReadStaticModels(reader, gfxMapAsset.GfxStaticModelsPointer, gfxMapAsset.GfxStaticModelsCount)); mapFile.DumpToMap(outputName + ".map"); // Done printCallback?.Invoke(String.Format("Converted to OBJ in {0:0.00} seconds.", stopWatch.ElapsedMilliseconds / 1000.0)); } } else { printCallback?.Invoke("Call of Duty: Black Ops 2 is supported, but this EXE is not."); } }