public Chunk_MAIN(WDTFile file) : base(file, "MAIN", Magic) { map = new bool[64, 64]; int tileCount = 0; for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { UInt32 flags = file.readUInt32(); file.skip(4); // Runtime area if (flags == 1) { map[x, y] = true; tileCount++; } else { map[x, y] = false; } } } LogWrite(string.Format("Loaded ADT map structure with {0} tiles (out of possible 4096).", tileCount)); }
public ADTFile(WDTFile wdt, string fileName, uint indexX, uint indexY, bool initial = false) { bool unkFlag = (wdt.Flags & 0x84) != 0; if (unkFlag) throw new Exception(); IndexX = indexX; IndexY = indexY; FileName = fileName; mLoadEvent = new System.Threading.ManualResetEvent(false); if (initial == false) { Game.GameManager.ThreadManager.LaunchThread( () => { try { AsyncLoadProc(); } catch (Exception) { } } ); } else AsyncLoadProc(); ADTManager.AddADT(this); }
public Chunk_MODF(WDTFile file) : base(file, "MODF", Magic) { Stuffer.Stuff(this, file, GetLogPrefix()); // There are 2 bytes (UInt16) of padding here, but we should // be safe to ignore as chunks are parsed by size. }
/// <summary> /// Writes all height maps to the default MapDir /// </summary> public static void ExportHeightMaps(WDTFile wdt) { if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0) { // has no terrain return; } var terrainInfo = ExtractRegionHeightMaps(wdt); var count = 0; var path = Path.Combine(ToolConfig.MapDir, wdt.Entry.Id.ToString()); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++) { for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++) { var adtTerrainInfo = terrainInfo[tileX, tileY]; if (adtTerrainInfo == null) continue; if (adtTerrainInfo.HeightMaps == null) continue; using (var file = File.Create(Path.Combine(path, TerrainConstants.GetHeightMapFile(tileX, tileY)))) { WriteADTTerrainInfo(file, adtTerrainInfo); } count++; } } log.Info("Extracted {0} tiles for {1}.", count, (MapId)wdt.Entry.Id); }
public static ADTTerrainInfo[,] ExtractRegionHeightMaps(WDTFile wdt) { var terrainInfo = new ADTTerrainInfo[TerrainConstants.TilesPerMapSide, TerrainConstants.TilesPerMapSide]; for (var y = 0; y < TerrainConstants.TilesPerMapSide; y++) { for (var x = 0; x < TerrainConstants.TilesPerMapSide; x++) { if (!wdt.TileProfile[x, y]) continue; var info = new ADTTerrainInfo(); var adtName = TerrainConstants.GetADTFile(wdt.Name, x, y); var adt = ADTParser.Process(WDTParser.MpqManager, wdt.Path, adtName); if (adt == null) continue; info.LiquidProfile = ExtractTileLiquidProfile(adt); info.LiquidTypes = ExtractTileLiquidTypes(adt); info.HeightMaps = ExtractTileHeightMaps(adt); info.CheckFlatness(); terrainInfo[x, y] = info; } } return terrainInfo; }
private static RegionModels ExtractRegionM2s(WDTFile wdt) { LoadedM2Ids = new List <uint>(250); RegionModels models; if (wdt == null) { return(null); } if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0) { // No terrain, load the global WMO if (wdt.ObjectDefinitions == null) { return(null); } models = new RegionModels { HasTiles = false, ObjectsByTile = new TileModels[1, 1] }; models.ObjectsByTile[0, 0] = ExtractWDTM2s(wdt.ObjectDefinitions); LoadedM2Ids.Clear(); return(models); } models = new RegionModels { HasTiles = true }; for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++) { for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++) { //if (tileX != 36) continue; //if (tileY != 49) continue; if (!wdt.TileProfile[tileX, tileY]) { continue; } var adtName = string.Format("{0}_{1:00}_{2:00}", wdt.Name, tileX, tileY); var adt = ADTParser.Process(manager, wdt.Path, adtName); if (adt == null) { continue; } models.ObjectsByTile[tileX, tileY] = ExtractTileM2s(tileX, tileY, adt); } } LoadedM2Ids.Clear(); return(models); }
public Chunk_MAIN(WDTFile file) : base(file, "MAIN", Magic) { map = new bool[64,64]; int tileCount = 0; for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { UInt32 flags = file.readUInt32(); file.skip(4); // Runtime area if (flags == 1) { map[x, y] = true; tileCount++; } else { map[x, y] = false; } } } LogWrite(string.Format("Loaded ADT map structure with {0} tiles (out of possible 4096).", tileCount)); }
public ADTFile(WDTFile wdt, string fileName, uint indexX, uint indexY, bool initial = false) { bool unkFlag = (wdt.Flags & 0x84) != 0; if (unkFlag) { throw new Exception(); } IndexX = indexX; IndexY = indexY; FileName = fileName; mLoadEvent = new System.Threading.ManualResetEvent(false); if (initial == false) { Game.GameManager.ThreadManager.LaunchThread( () => { try { AsyncLoadProc(); } catch (Exception) { } } ); } else { AsyncLoadProc(); } ADTManager.AddADT(this); }
public string SetZone(string zone) { int continentID; if (!zoneToMapId.TryGetValue(zone, out continentID)) { int colon = zone.IndexOf(":"); if (colon == -1) { return(null); } zone = zone.Substring(colon); if (!zoneToMapId.TryGetValue(zone, out continentID)) { return(null); } } archive.ExtractFile("DBFilesClient\\Map.dbc", "Map.dbc"); DBC maps = new DBC(); DBCFile mf = new DBCFile("Map.dbc", maps); for (int i = 0; i < maps.recordCount; i++) { int mapID = maps.GetInt(i, 0); // Console.WriteLine(" ID:" + maps.GetInt(i, 0)); // Console.WriteLine(" File: " + maps.GetString(i, 1)); // Console.WriteLine(" Name: " + maps.GetString(i, 4)); // the file!!! if (mapID == continentID) // file == continentFile) { // Console.WriteLine(String.Format("{0,4} {1}", mapID, maps.GetString(i, 1))); string file = maps.GetString(i, 1); continentFile = file; wdt = new WDT(); wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager); if (!wdtf.loaded) { wdt = null; // bad } else { // Console.WriteLine(" global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count); //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f }; } return(continentFile); } } if (wdt == null) { return("Failed to open file files for continent ID" + continentID); } return(null); }
public Chunk_MODF(WDTFile file) : base(file, "MODF", Magic) { Stuffer.Stuff(this, file, GetLogPrefix()); // There are 2 bytes (UInt16) of padding here, but we should // be safe to ignore as chunks are parsed by size. }
public Chunk_MPHD(WDTFile file) : base(file, "MPHD", Magic) { flags = file.readUInt32(); unk1 = file.readUInt32(); // 4 * 6 bytes of unused data here. // No need to skip, seek check higher up will take care of it. LogValue("flags", flags); LogValue("unk1", unk1); }
/// <summary> /// Writes all height maps to the default MapDir /// </summary> public static void ExportMapTiles(WDTFile wdt) { // Map data should only be stored per map ClearObjectData(); var path = Path.Combine(ToolConfig.MapDir, wdt.Entry.Id.ToString()); if (wdt.IsWMOOnly) { // This Map has no Tiles, but the MODF still needs to be written // These maps will be considered to have one tile at {0, 0} with only one MODF written therein var adt = ExtractWMOOnly(wdt); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } using (var file = File.Create(Path.Combine(path, TerrainConstants.GetMapFilename(0, 0)))) { WriteTileInfo(file, adt); } log.Info("Extracted {0}.", (MapId)wdt.Entry.Id); return; } // Read in the ADT data - this includes height and liquid maps, WMO information and M2 information TerrainInfo = ExtractMapTiles(wdt); // Write the processed data to files var count = 0; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++) { for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++) { var adt = TerrainInfo[tileY, tileX]; if (adt == null) continue; using (var file = File.Create(Path.Combine(path, TerrainConstants.GetMapFilename(tileX, tileY)))) { WriteTileInfo(file, adt); file.Close(); } count++; } } log.Info("Extracted {0} tiles for {1}.", count, (MapId)wdt.Entry.Id); }
public override void Close() { archive.Close(); wdt = null; wdtf = null; modelmanager = null; wmomanager = null; zoneToMapId = null; mapIdToFile = null; areaIdToName = null; archive = null; base.Close(); }
public static void GetTileSets(WDTFile wdt) { if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) == 0) { var tileSet = new ZoneTileSet(); Tiles[wdt.Entry.Id] = tileSet; ZoneGrid grid; // Read in the Tiles for (var y = 0; y < 64; y++) { for (var x = 0; x < 64; x++) { if (!wdt.TileProfile[x, y]) continue; ++count; var adtName = TerrainConstants.GetADTFile(wdt.Name, x, y); var adt = ADTParser.Process(WDTParser.MpqManager, wdt.Path, adtName); if (adt == null) continue; tileSet.ZoneGrids[x, y] = grid = new ZoneGrid(new uint[TerrainConstants.ChunksPerTileSide, TerrainConstants.ChunksPerTileSide]); // Read in the TileChunks for (var j = 0; j < 16; j++) { for (var i = 0; i < 16; i++) { var areaId = adt.MapChunks[i, j].AreaId; if (Enum.IsDefined(typeof(ZoneId), areaId)) { grid.ZoneIds[i, j] = areaId; } else { grid.ZoneIds[i, j] = 0; } } } //return tiles; } } } else { log.Info("Could not read Zones from WMO: " + (MapId)wdt.Entry.Id); } }
public void SetContinent(string continent) { continentFile = continent; wdt = new WDT(); wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager, this.logger, dataConfig); if (!wdtf.loaded) { wdt = null; // bad throw new Exception("Failed to set continent to: " + continent); } else { // logger.WriteLine(" global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count); //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f }; } }
public void SetContinent(string continent) { continentFile = continent; wdt = new WDT(); wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager); if (!wdtf.loaded) { wdt = null; // bad } else { // PathGraph.Log(" global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count); //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f }; } }
static void Process(DBCMapEntry entry) { if (Parsed == null) { throw new Exception("WDTParser.Parsed must be set before calling WDTParser.Process"); } var dir = entry.MapDirName; var wdtDir = Path.Combine(baseDir, dir); var wdtName = dir; var wdtFilePath = Path.Combine(wdtDir, wdtName + Extension); if (!MpqManager.FileExists(wdtFilePath)) return; var wdt = new WDTFile { Manager = MpqManager, Entry = entry, Name = wdtName, Path = wdtDir }; using (var fileReader = new BinaryReader(MpqManager.OpenFile(wdtFilePath))) { ReadMVER(fileReader, wdt); ReadMPHD(fileReader, wdt); ReadMAIN(fileReader, wdt); if (wdt.Header.IsWMOMap) { // No terrain, the map is a "global" wmo // MWMO and MODF chunks follow wdt.IsWMOOnly = true; ReadMWMO(fileReader, wdt); ReadMODF(fileReader, wdt); } } Parsed(wdt); }
public void SetContinent(string continent) { continentFile = continent; wdt = new WDT(); wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager); if (!wdtf.loaded) { wdt = null; // bad throw new Exception("Failed to set continent to: " + continent); } else { // Console.WriteLine(" global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count); //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f }; } }
public override void Close() { archive.Close(); wdt = null; wdtf = null; modelmanager = null; wmomanager = null; zoneToMapId = null; mapIdToFile = null; areaIdToName = null; archive = null; base.Close(); }
public string SetZone(string zone) { int continentID; if (!zoneToMapId.TryGetValue(zone, out continentID)) { int colon = zone.IndexOf(":"); if(colon == -1) return null; zone = zone.Substring(colon); if (!zoneToMapId.TryGetValue(zone, out continentID)) { return null; } } archive.ExtractFile("DBFilesClient\\Map.dbc", "Map.dbc"); DBC maps = new DBC(); DBCFile mf = new DBCFile("Map.dbc", maps); for (int i = 0; i < maps.recordCount; i++) { int mapID = maps.GetInt(i, 0); // Console.WriteLine(" ID:" + maps.GetInt(i, 0)); // Console.WriteLine(" File: " + maps.GetString(i, 1)); // Console.WriteLine(" Name: " + maps.GetString(i, 4)); // the file!!! if (mapID == continentID) // file == continentFile) { // Console.WriteLine(String.Format("{0,4} {1}", mapID, maps.GetString(i, 1))); string file = maps.GetString(i, 1); continentFile = file; wdt = new WDT(); wdtf = new WDTFile(archive, continentFile, wdt, wmomanager, modelmanager); if (!wdtf.loaded) wdt = null; // bad else { // Console.WriteLine(" global Objects " + wdt.gwmois.Count + " Models " + wdt.gwmois.Count); //global_triangles.color = new float[3] { 0.8f, 0.8f, 1.0f }; } return continentFile; } } if (wdt == null) { return "Failed to open file files for continent ID" + continentID; } return null; }
static void ReadMAIN(BinaryReader fileReader, WDTFile wdt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); // Rows are along the x-axis for (var x = 0; x < TerrainConstants.MapTileCount; x++) { // Columns are along the y-axis for (var y = 0; y < TerrainConstants.MapTileCount; y++) { //if (x == 48 && y == 30) //{ // wdt.TileProfile[y, x] = true; //} //else //{ // wdt.TileProfile[y, x] = false; //} // Stored as [col, row], that's weird. wdt.TileProfile[y, x] = (fileReader.ReadInt64() != 0); } } }
public Chunk_MWMO(WDTFile file) : base(file, "MWMO", Magic) { fileName = file.readString(); // Zero-terminated, this should be safe. LogValue("Map WMO", fileName); }
public static ADT[,] ExtractMapTiles(WDTFile wdt) { var mapTiles = new ADT[TerrainConstants.TilesPerMapSide, TerrainConstants.TilesPerMapSide]; for (var x = 0; x < TerrainConstants.TilesPerMapSide; x++) { for (var y = 0; y < TerrainConstants.TilesPerMapSide; y++) { if (!wdt.TileProfile[y, x]) continue; var adt = ADTParser.Process(WDTParser.MpqManager, wdt.Entry, x, y); if (adt == null) continue; adt.IsWMOOnly = false; // Load in the WMORoots and their DoodadDefinitions // Load in the ADTs referenced M2Models PrepareChunkInfo(wdt.Manager, adt); mapTiles[y, x] = adt; } } return mapTiles; }
static void ReadMODF(BinaryReader fileReader, WDTFile wdt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); var endPos = fileReader.BaseStream.Position + size; while (fileReader.BaseStream.Position < endPos) { var objectDef = new MapObjectDefinition(); var nameIndex = fileReader.ReadInt32(); // 4 bytes objectDef.FilePath = wdt.WmoFiles[nameIndex]; objectDef.UniqueId = fileReader.ReadUInt32(); // 4 bytes objectDef.Position = fileReader.ReadVector3(); // 12 bytes objectDef.OrientationA = fileReader.ReadSingle(); // 4 Bytes objectDef.OrientationB = fileReader.ReadSingle(); // 4 Bytes objectDef.OrientationC = fileReader.ReadSingle(); // 4 Bytes objectDef.Extents = fileReader.ReadBoundingBox(); // 12*2 bytes objectDef.Flags = fileReader.ReadUInt16(); // 2 bytes objectDef.DoodadSetId = fileReader.ReadUInt16(); // 2 bytes objectDef.NameSet = fileReader.ReadUInt16(); // 2 bytes fileReader.ReadUInt16(); // padding wdt.WmoDefinitions.Add(objectDef); } }
static void ReadMWMO(BinaryReader fileReader, WDTFile wdt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); var endPos = fileReader.BaseStream.Position + size; while (fileReader.BaseStream.Position < endPos) { if (fileReader.PeekByte() == 0) { fileReader.BaseStream.Position++; } else { wdt.WmoFiles.Add(fileReader.ReadCString()); } } }
public Chunk_MVER(WDTFile file) : base(file, "MVER", Magic) { version = file.readUInt32(); LogValue("version", version); }
private static RegionModels ExtractRegionM2s(WDTFile wdt) { LoadedM2Ids = new List<uint>(250); RegionModels models; if (wdt == null) return null; if ((wdt.Header.Header1 & WDTFlags.GlobalWMO) != 0) { // No terrain, load the global WMO if (wdt.ObjectDefinitions == null) return null; models = new RegionModels { HasTiles = false, ObjectsByTile = new TileModels[1, 1] }; models.ObjectsByTile[0, 0] = ExtractWDTM2s(wdt.ObjectDefinitions); LoadedM2Ids.Clear(); return models; } models = new RegionModels { HasTiles = true }; for (var tileY = 0; tileY < TerrainConstants.TilesPerMapSide; tileY++) { for (var tileX = 0; tileX < TerrainConstants.TilesPerMapSide; tileX++) { //if (tileX != 36) continue; //if (tileY != 49) continue; if (!wdt.TileProfile[tileX, tileY]) continue; var adtName = string.Format("{0}_{1:00}_{2:00}", wdt.Name, tileX, tileY); var adt = ADTParser.Process(manager, wdt.Path, adtName); if (adt == null) continue; models.ObjectsByTile[tileX, tileY] = ExtractTileM2s(tileX, tileY, adt); } } LoadedM2Ids.Clear(); return models; }
public Chunk_MWMO(WDTFile file) : base(file, "MWMO", Magic) { fileName = file.readString(); // Zero-terminated, this should be safe. LogValue("Map WMO", fileName); }
static void ReadMVER(BinaryReader fileReader, WDTFile wdt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); wdt.Version = fileReader.ReadInt32(); }
static void ReadMPHD(BinaryReader fileReader, WDTFile wdt) { var type = fileReader.ReadUInt32(); var size = fileReader.ReadUInt32(); wdt.Header.Header1 = (WDTFlags)fileReader.ReadInt32(); wdt.Header.Header2 = fileReader.ReadInt32(); wdt.Header.Header3 = fileReader.ReadInt32(); wdt.Header.Header4 = fileReader.ReadInt32(); wdt.Header.Header5 = fileReader.ReadInt32(); wdt.Header.Header6 = fileReader.ReadInt32(); wdt.Header.Header7 = fileReader.ReadInt32(); wdt.Header.Header8 = fileReader.ReadInt32(); }
public static ADT ExtractWMOOnly(WDTFile wdt) { var adt = new ADT(""); adt.ObjectDefinitions.Capacity = wdt.WmoDefinitions.Count; foreach (var wmoDefinition in wdt.WmoDefinitions) { adt.ObjectDefinitions.Add(wmoDefinition); } adt.ObjectFiles.Capacity = wdt.WmoFiles.Count; foreach (var wmoFile in wdt.WmoFiles) { adt.ObjectFiles.Add(wmoFile); } foreach (var def in adt.ObjectDefinitions) { LoadWMO(wdt.Manager, def); } return adt; }
public Chunk_MVER(WDTFile file) : base(file, "MVER", Magic) { version = file.readUInt32(); LogValue("version", version); }
public static void GetBoundaries(WDTFile wdt) { Vector3 min; Vector3 max; if (wdt.Header.Header1.HasFlag(WDTFlags.GlobalWMO)) { const float maxDim = TerrainConstants.TileSize * (32.0f); // No terrain, get the boundaries from the MODF min = wdt.WmoDefinitions[0].Extents.Min; max = wdt.WmoDefinitions[0].Extents.Max; var diff = Math.Max(max.X - min.X, max.Y - min.Y); if ((min.X + diff) > maxDim) { var newdiff = maxDim - diff; if ((min.X - newdiff) < -maxDim) { throw new Exception("Can't square map: " + wdt.Name); } min.X = min.X - newdiff; } if ((min.Y + diff) > maxDim) { var newdiff = maxDim - diff; if ((min.Y - newdiff) < -maxDim) { throw new Exception("Can't square map: " + wdt.Name); } min.Y = min.Y - newdiff; } max.X = min.X + diff; max.Y = min.Y + diff; } else { var minX = TerrainConstants.TilesPerMapSide; var maxX = 0; var minY = TerrainConstants.TilesPerMapSide; var maxY = 0; for (var y = 0; y < TerrainConstants.TilesPerMapSide; y++) { for (var x = 0; x < TerrainConstants.TilesPerMapSide; x++) { if (!wdt.TileProfile[x, y]) continue; minX = Math.Min(minX, x); maxX = Math.Max(maxX, x); minY = Math.Min(minY, y); maxY = Math.Max(maxY, y); } } var diff = Math.Max(maxX - minX, maxY - minY); if ((minX + diff) > TerrainConstants.TilesPerMapSide) { var newdiff = TerrainConstants.TilesPerMapSide - diff; if ((minX - newdiff) < 0) { throw new Exception("Can't square this map."); } minX = minX - newdiff; } if ((minY + diff) > TerrainConstants.TilesPerMapSide) { var newdiff = TerrainConstants.TilesPerMapSide - diff; if ((minY - newdiff) < 0) { throw new Exception("Can't square this map."); } minY = minY - newdiff; } maxX = minX + diff; maxY = minY + diff; var maxXLoc = TerrainConstants.TileSize * (32 - minX); var maxYLoc = TerrainConstants.TileSize * (32 - minY); var minXLoc = TerrainConstants.TileSize * (31 - maxX); var minYLoc = TerrainConstants.TileSize * (31 - maxY); //var maxXLoc = TerrainConstants.TileSize * (maxX - 32); //var maxYLoc = TerrainConstants.TileSize * (maxY - 32); //var minXLoc = TerrainConstants.TileSize * (minX - 32); //var minYLoc = TerrainConstants.TileSize * (minY - 32); min = new Vector3(minXLoc, minYLoc, -2048.0f); max = new Vector3(maxXLoc, maxYLoc, 2048.0f); // Convert to in-game coordinates var temp = min.X; min.X = min.Y; min.Y = temp; temp = max.X; max.X = max.Y; max.Y = temp; } // TODO: Map bounds that in the file are a little off min.X -= 200.0f; min.Y -= 200.0f; max.X += 200.0f; max.Y += 200.0f; // Some bounding boxes (specifically the ones for the WMO-only tiles) have incorrect Z spans min.Z = -2048.0f; max.Z = 2048.0f; //text.WriteLine(dbcMapEntry.Id + "\t" + wdtName + "\tMin: " + min + "\tMax: " + max); Boundaries[wdt.Entry.Id] = new BoundingBox(min, max); }
public override void Work() { LogWrite("Beginning export of {0}...", mapName); TextureBox texProvider = new TextureBox(); WaveFrontWriter ob = new WaveFrontWriter(fileName, texProvider, true, true, false); try { if (!Program.IsCASCReady) { throw new MapExportException("CASC file engine is not loaded."); } string wdtPath = string.Format(@"World\Maps\{0}\{0}.wdt", mapName); Program.CASCEngine.SaveFileTo(wdtPath, Constants.TEMP_DIRECTORY); WDTFile headerFile = new WDTFile(Path.Combine(Constants.TEMP_DIRECTORY, wdtPath)); headerFile.parse(); if (!headerFile.Chunks.Any(c => c.ChunkID == Formats.WDT.Chunk_MPHD.Magic)) { throw new MapExportException("Invalid map header (WDT)"); } // ToDo: Check if world WMO exists and load it. // Ensure we have a MAIN chunk before trying to process terrain. Formats.WDT.Chunk_MAIN mainChunk = (Formats.WDT.Chunk_MAIN)headerFile.Chunks.Where(c => c.ChunkID == Formats.WDT.Chunk_MAIN.Magic).FirstOrDefault(); Formats.WDT.Chunk_MPHD headerChunk = (Formats.WDT.Chunk_MPHD)headerFile.Chunks.Where(c => c.ChunkID == Formats.WDT.Chunk_MPHD.Magic).FirstOrDefault(); if (mainChunk != null && headerChunk != null) { // Pre-calculate UV mapping for terrain. UV[,,] uvMaps = new UV[8, 8, 5]; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { float uTL = 1 - (0.125f * x); float vTL = 1 - (0.125f * y); float uBR = uTL - 0.125f; float vBR = vTL - 0.125f; int aX = 7 - x; uvMaps[aX, y, 0] = new UV(uBR, vTL); // TL uvMaps[aX, y, 1] = new UV(uBR, vBR); // TR uvMaps[aX, y, 2] = new UV(uTL, vTL); // BL uvMaps[aX, y, 3] = new UV(uTL, vBR); // BR uvMaps[aX, y, 4] = new UV(uTL - 0.0625f, vTL - 0.0625f); } } int meshIndex = 1; int wmoIndex = 1; // Create a directory for map data (alpha maps, etc). string dataDirRaw = string.Format("{0}.data", Path.GetFileNameWithoutExtension(fileName)); string dataDir = Path.Combine(Path.GetDirectoryName(fileName), dataDirRaw); if (!Directory.Exists(dataDir)) { Directory.CreateDirectory(dataDir); } Dictionary <byte[], uint> texCache = new Dictionary <byte[], uint>(new ByteArrayComparer()); for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { if (mainChunk.map[x, y] && ShouldInclude(x, y)) { string pathBase = string.Format(@"World\Maps\{0}\{0}_{1}_{2}", mapName, x, y); string adtPath = pathBase + ".adt"; string texPath = pathBase + "_tex0.adt"; string objPath = pathBase + "_obj0.adt"; Program.CASCEngine.SaveFileTo(adtPath, Constants.TEMP_DIRECTORY); Program.CASCEngine.SaveFileTo(texPath, Constants.TEMP_DIRECTORY); Program.CASCEngine.SaveFileTo(objPath, Constants.TEMP_DIRECTORY); string adtTempPath = Path.Combine(Constants.TEMP_DIRECTORY, adtPath); string texTempPath = Path.Combine(Constants.TEMP_DIRECTORY, texPath); string objTempPath = Path.Combine(Constants.TEMP_DIRECTORY, objPath); try { ADTFile adt = new ADTFile(adtTempPath, ADTFileType.ROOT); adt.parse(); ADTFile tex = new ADTFile(texTempPath, ADTFileType.TEX); tex.parse(); ADTFile obj = new ADTFile(objTempPath, ADTFileType.OBJ); obj.parse(); // Textures Chunk_MTEX texChunk = (Chunk_MTEX)tex.getChunk(Chunk_MTEX.Magic); uint texIndex = 0; Bitmap[] textureData = new Bitmap[texChunk.textures.count()]; foreach (KeyValuePair <int, string> texture in texChunk.textures.raw()) { string texFile = texture.Value; string tempPath = Path.Combine(Constants.TEMP_DIRECTORY, texFile); if (!File.Exists(tempPath)) { Program.CASCEngine.SaveFileTo(texFile, Constants.TEMP_DIRECTORY); } using (BlpFile blp = new BlpFile(File.OpenRead(tempPath))) textureData[texIndex] = blp.GetBitmap(0); texIndex++; } Chunk_MCNK[] soupChunks = adt.getChunksByID(Chunk_MCNK.Magic).Cast <Chunk_MCNK>().ToArray(); Chunk_MCNK[] layerChunks = tex.getChunksByID(Chunk_MCNK.Magic).Cast <Chunk_MCNK>().ToArray(); // Terrain for (int i = 0; i < 256; i++) { Chunk_MCNK soupChunk = soupChunks[i]; Chunk_MCNK layerChunk = layerChunks[i]; // Terrain chunks Chunk_MCVT hmChunk = (Chunk_MCVT)soupChunk.getChunk(Chunk_MCVT.Magic); Chunk_MCNR nChunk = (Chunk_MCNR)soupChunk.getChunk(Chunk_MCNR.Magic); // Texture chunks Chunk_MCLY layers = (Chunk_MCLY)layerChunk.getChunk(Chunk_MCLY.Magic); // Alpha mapping Chunk_MCAL alphaMapChunk = (Chunk_MCAL)layerChunk.getChunk(Chunk_MCAL.Magic, false); string texFileName = string.Format("baked_{0}_{1}_{2}.png", x, y, i); string texFilePath = Path.Combine(dataDir, texFileName); EventManager.Trigger_LoadingPrompt(string.Format("Rendering tile {0} at {1},{2}...", i + 1, x, y)); uint texFaceIndex = 0; if (!File.Exists(texFilePath)) { Bitmap bmpBase = new Bitmap(layers.layers.Length > 0 ? textureData[layers.layers[0].textureID] : blank); using (Graphics baseG = Graphics.FromImage(bmpBase)) using (ImageAttributes att = new ImageAttributes()) { att.SetWrapMode(WrapMode.TileFlipXY); baseG.CompositingQuality = CompositingQuality.HighQuality; baseG.InterpolationMode = InterpolationMode.HighQualityBicubic; baseG.CompositingMode = CompositingMode.SourceOver; for (int mI = 1; mI < layers.layers.Length; mI++) // First layer never has an alpha map { byte[,] alphaMap; MCLYLayer layer = layers.layers[mI]; bool headerFlagSet = ((headerChunk.flags & 0x4) == 0x4) || ((headerChunk.flags & 0x80) == 0x80); bool layerFlagSet = ((layer.flags & 0x200) == 0x200); bool fixAlphaMap = !((soupChunk.flags & 0x200) == 0x200); if (layerFlagSet) { alphaMap = alphaMapChunk.parse(Chunk_MCAL.CompressType.COMPRESSED, layer.ofsMCAL, fixAlphaMap); } else { alphaMap = alphaMapChunk.parse(headerFlagSet ? Chunk_MCAL.CompressType.UNCOMPRESSED_4096 : Chunk_MCAL.CompressType.UNCOMPRESSED_2048, layer.ofsMCAL, fixAlphaMap); } Bitmap bmpRawTex = textureData[layer.textureID]; Bitmap bmpAlphaMap = new Bitmap(64, 64); for (int drawX = 0; drawX < 64; drawX++) { for (int drawY = 0; drawY < 64; drawY++) { bmpAlphaMap.SetPixel(drawX, drawY, Color.FromArgb(alphaMap[drawX, drawY], 0, 0, 0)); } } Bitmap bmpAlphaMapScaled = new Bitmap(bmpRawTex.Width, bmpRawTex.Height); using (Graphics g = Graphics.FromImage(bmpAlphaMapScaled)) { g.CompositingQuality = CompositingQuality.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.CompositingMode = CompositingMode.SourceCopy; g.DrawImage(bmpAlphaMap, new Rectangle(0, 0, bmpAlphaMapScaled.Width, bmpAlphaMapScaled.Height), 0, 0, bmpAlphaMap.Width, bmpAlphaMap.Height, GraphicsUnit.Pixel, att); } bmpAlphaMap.Dispose(); Bitmap bmpTex = new Bitmap(bmpRawTex.Width, bmpRawTex.Height); for (int drawX = 0; drawX < bmpRawTex.Width; drawX++) { for (int drawY = 0; drawY < bmpRawTex.Height; drawY++) { // Hacky fix to flip the texture. // Remove this if we fix the terrain read-order. int sourceX = (bmpRawTex.Width - 1) - drawX; //int sourceY = (bmpRawTex.Height - 1) - drawY; bmpTex.SetPixel(sourceX, drawY, Color.FromArgb( bmpAlphaMapScaled.GetPixel(drawX, drawY).A, bmpRawTex.GetPixel(drawX, drawY).R, bmpRawTex.GetPixel(drawX, drawY).G, bmpRawTex.GetPixel(drawX, drawY).B )); } } bmpAlphaMapScaled.Dispose(); baseG.DrawImage(bmpTex, 0, 0, bmpBase.Width, bmpBase.Height); } using (MemoryStream str = new MemoryStream()) using (MD5 md5 = MD5.Create()) { bmpBase.Save(str, ImageFormat.Png); byte[] raw = md5.ComputeHash(str.ToArray()); uint cacheID; if (texCache.TryGetValue(raw, out cacheID)) { // Cache found, use that instead. texFaceIndex = cacheID; } else { // No cache found, store new. bmpBase.Save(texFilePath); texProvider.addTexture(-1, Path.Combine(dataDirRaw, texFileName)); cacheID = (uint)texProvider.LastIndex; texFaceIndex = cacheID; texCache.Add(raw, cacheID); } } bmpBase.Dispose(); } } Mesh mesh = new Mesh("Terrain Mesh #" + meshIndex); meshIndex++; int v = 0; float pX = soupChunk.position.X; float pY = -soupChunk.position.Y; float pZ = soupChunk.position.Z; int ofs = 10; for (int sX = 8; sX > 0; sX--) { for (int sY = 1; sY < 9; sY++) { int cIndex = ofs - 1; int blIndex = cIndex - 9; int tlIndex = cIndex + 8; float tr = hmChunk.vertices[tlIndex + 1]; float tl = hmChunk.vertices[tlIndex]; float br = hmChunk.vertices[blIndex + 1]; float bl = hmChunk.vertices[blIndex]; float c = hmChunk.vertices[cIndex]; float oX = pX + (sX * ADTFile.TILE_SIZE); float oY = pY + (sY * ADTFile.TILE_SIZE); // Apply UV mapping for (int uv = 0; uv < 5; uv++) { mesh.addUV(uvMaps[sX - 1, sY - 1, uv]); } // Apply verts mesh.addVert(new Position(oX, tl + pZ, oY)); mesh.addVert(new Position(oX, tr + pZ, oY + ADTFile.TILE_SIZE)); mesh.addVert(new Position(oX + ADTFile.TILE_SIZE, bl + pZ, oY)); mesh.addVert(new Position(oX + ADTFile.TILE_SIZE, br + pZ, oY + ADTFile.TILE_SIZE)); mesh.addVert(new Position(oX + (ADTFile.TILE_SIZE / 2), c + pZ, oY + (ADTFile.TILE_SIZE / 2))); // Normals mesh.addNormal(nChunk.normals[tlIndex]); mesh.addNormal(nChunk.normals[tlIndex + 1]); mesh.addNormal(nChunk.normals[blIndex]); mesh.addNormal(nChunk.normals[blIndex + 1]); mesh.addNormal(nChunk.normals[cIndex]); // Faces mesh.addFace(texFaceIndex, v, v + 2, v + 4); mesh.addFace(texFaceIndex, v + 1, v + 3, v + 4); mesh.addFace(texFaceIndex, v, v + 1, v + 4); mesh.addFace(texFaceIndex, v + 2, v + 3, v + 4); v += 5; ofs += 1; } ofs += 9; } ob.addMesh(mesh); } // Parse WMO objects that appear in the world. EventManager.Trigger_LoadingPrompt("Constructing buildings..."); Chunk_MWID wmoIndexChunk = (Chunk_MWID)obj.getChunk(Chunk_MWID.Magic, false); Chunk_MWMO wmoModelChunk = (Chunk_MWMO)obj.getChunk(Chunk_MWMO.Magic, false); Chunk_MODF wmoRefChunk = (Chunk_MODF)obj.getChunk(Chunk_MODF.Magic, false); if (wmoIndexChunk != null && wmoModelChunk != null && wmoRefChunk != null) { foreach (Chunk_MODF.MODFEntry entry in wmoRefChunk.entries) { string wmoModel = wmoModelChunk.objects.get((int)wmoIndexChunk.offsets[entry.entry]); List <CASCFile> groupSearch = CASCSearch.Search(Path.Combine(Path.GetDirectoryName(wmoModel), Path.GetFileNameWithoutExtension(wmoModel)), CASCSearch.SearchType.STARTS_WITH); if (groupSearch.Count > 0) { // Set-up root/group files for the WMO. WMOFile wmo = null; List <WMOFile> groupFiles = new List <WMOFile>(groupSearch.Count - 1); string rootName = Path.GetFileName(wmoModel).ToLower(); foreach (CASCFile file in groupSearch) { Program.CASCEngine.SaveFileTo(file.FullName, Constants.TEMP_DIRECTORY); string tempPath = Path.Combine(Constants.TEMP_DIRECTORY, file.FullName); if (file.FullName.ToLower().EndsWith(rootName)) { wmo = new WMOFile(tempPath, true); } else { groupFiles.Add(new WMOFile(tempPath, false)); } } foreach (WMOFile groupFile in groupFiles) { wmo.addGroupFile(groupFile); } groupFiles.Clear(); wmo.parse(); // Export/register textures needed for this WMO. Formats.WMO.Chunk_MOTX wmoTexChunk = (Formats.WMO.Chunk_MOTX)wmo.getChunk(Formats.WMO.Chunk_MOTX.Magic); Dictionary <int, int> wmoTexMap = new Dictionary <int, int>(); foreach (KeyValuePair <int, string> node in wmoTexChunk.textures.raw()) { string tempPath = Path.Combine(Constants.TEMP_DIRECTORY, node.Value); string dumpPath = Path.Combine(Path.GetDirectoryName(node.Value), Path.GetFileNameWithoutExtension(node.Value) + ".png"); // Extract if (!File.Exists(tempPath)) { Program.CASCEngine.SaveFileTo(node.Value, Constants.TEMP_DIRECTORY); } // Convert using (BlpFile blp = new BlpFile(File.OpenRead(tempPath))) using (Bitmap bmp = blp.GetBitmap(0)) { string dumpLoc = Path.Combine(dataDir, dumpPath); Directory.CreateDirectory(Path.GetDirectoryName(dumpLoc)); bmp.Save(dumpLoc); } // Register texProvider.addTexture(-1, Path.Combine(dataDirRaw, dumpPath)); wmoTexMap.Add(node.Key, texProvider.LastIndex); } Formats.WMO.Chunk_MOGN wmoNameChunk = (Formats.WMO.Chunk_MOGN)wmo.getChunk(Formats.WMO.Chunk_MOGN.Magic); Formats.WMO.Chunk_MOMT wmoMatChunk = (Formats.WMO.Chunk_MOMT)wmo.getChunk(Formats.WMO.Chunk_MOMT.Magic); foreach (Chunk_Base rawChunk in wmo.getChunksByID(Formats.WMO.Chunk_MOGP.Magic)) { Formats.WMO.Chunk_MOGP chunk = (Formats.WMO.Chunk_MOGP)rawChunk; string meshName = wmoNameChunk.data.get((int)chunk.groupNameIndex); // Skip antiportals. if (meshName.ToLower().Equals("antiportal")) { continue; } Mesh mesh = new Mesh(string.Format("WMO{0}_{1}", wmoIndex, meshName)); // Populate mesh with vertices. Formats.WMO.Chunk_MOVT vertChunk = (Formats.WMO.Chunk_MOVT)chunk.getChunk(Formats.WMO.Chunk_MOVT.Magic); foreach (Position vertPos in vertChunk.vertices) { mesh.addVert(new Position(entry.position.X + vertPos.X, entry.position.Y + vertPos.Y, entry.position.Z + vertPos.Z)); } // Populate mesh with UVs. Formats.WMO.Chunk_MOTV uvChunk = (Formats.WMO.Chunk_MOTV)chunk.getChunk(Formats.WMO.Chunk_MOTV.Magic); foreach (UV uv in uvChunk.uvData) { mesh.addUV(uv); } // Populate mesh with normals. Formats.WMO.Chunk_MONR normChunk = (Formats.WMO.Chunk_MONR)chunk.getChunk(Formats.WMO.Chunk_MONR.Magic); foreach (Position norm in normChunk.normals) { mesh.addNormal(norm); } // Populate mesh with triangles (faces). Formats.WMO.Chunk_MOVI faceChunk = (Formats.WMO.Chunk_MOVI)chunk.getChunk(Formats.WMO.Chunk_MOVI.Magic); Formats.WMO.Chunk_MOPY faceMatChunk = (Formats.WMO.Chunk_MOPY)chunk.getChunk(Formats.WMO.Chunk_MOPY.Magic); for (int i = 0; i < faceChunk.positions.Length; i++) { Formats.WMO.FacePosition position = faceChunk.positions[i]; Formats.WMO.FaceInfo info = faceMatChunk.faceInfo[i]; if (info.materialID != 0xFF) // 0xFF (255) identifies a collision face. { Material mat = wmoMatChunk.materials[info.materialID]; uint texID = (uint)wmoTexMap[(int)mat.texture1.offset]; mesh.addFace(texID, mat.texture2.colour, position.point1, position.point2, position.point3); } } Log.Write("CreateWMOMesh (ADT): " + mesh.ToAdvancedString()); ob.addMesh(mesh); } } } } } catch (ADTException ex) { LogWrite("Unable to process tile {0},{1} due to exception: {2}", x, y, ex.Message); LogWrite(ex.StackTrace); } } } } EventManager.Trigger_LoadingPrompt(new Random().Next(100) == 42 ? "Reticulating splines..." : "Writing terrain data to file..."); ob.Write(); ob.Close(); } // Job's done. EventManager.Trigger_MapExportDone(true); } catch (Exception e) { ob.Close(); LogWrite("Unable to complete map extraction due to exception: {0}", e.Message); EventManager.Trigger_MapExportDone(false, e.Message + e.StackTrace); } }