public void Load(string adtFileName, string wdtFileName) { WdtFile = new WDT.WDT(); WdtFile.Load(wdtFileName); using (var reader = new BinaryReader(File.OpenRead(adtFileName))) { while (reader.BaseStream.Position < reader.BaseStream.Length) { var chunkName = new string(reader.ReadChars(4).Reverse().ToArray()); var chunkSize = reader.ReadInt32(); var chunkType = Type.GetType(chunkName); if (chunkType != null) { // If chunkType is an array, it can only be MCNK if (chunkType.IsArray) { MCNK[MCNK.Count(c => c != null)] = (MCNK)Activator.CreateInstance(typeof(MCNK), reader.ReadBytes(chunkSize), WdtFile); } else { GetType().GetProperty(chunkName)?.SetValue(this, Activator.CreateInstance(chunkType, reader.ReadBytes(chunkSize))); } } } } }
public MCLQ(byte[] chunkBytes, MCNK parentChunk) : base(chunkBytes) { MinHeight = ReadSingle(); MaxHeight = ReadSingle(); for (int i = 0; i < 81; i++) { if (parentChunk.Flags.HasFlag(MCNKFlags.LiquidMagma)) { Vertices[i] = new Magma(); } else if (parentChunk.Flags.HasFlag(MCNKFlags.LiquidOcean)) { Vertices[i] = new Ocean(); } else { Vertices[i] = new Water(); } Vertices[i].Read(this); } for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { Tiles[i, j] = ReadByte(); } } NumberFlowvs = ReadUInt32(); for (int i = 0; i < 3; i++) { Flowvs[i].Read(this); } Close(); }
private MCNK Read335MCNKChunk(uint size, BinaryReader bin) { var mapchunk = new MCNK() { header = bin.Read <MCNKheader>() }; using (var stream = new MemoryStream(bin.ReadBytes((int)size - 128))) using (var subbin = new BinaryReader(stream)) { long subpos = 0; while (subpos < stream.Length) { subbin.BaseStream.Position = subpos; var subChunkName = new string(subbin.ReadChars(4).Reverse().ToArray()); var subChunkSize = subbin.ReadUInt32(); subpos = stream.Position + subChunkSize; switch (subChunkName) { case "MCVT": mapchunk.vertices = ReadMCVTSubChunk(subbin); break; case "MCNR": mapchunk.normals = ReadMCNRSubChunk(subbin); subpos = subpos + 13; //The weird data that the wiki speaks about [Thanks Marl!] break; case "MCLY": adtfile.texChunks[currentChunk].layers = ReadMCLYSubChunk(subChunkSize, subbin); //very ghetto, much wow break; case "MCRF": break; case "MCSH": break; case "MCAL": adtfile.texChunks[currentChunk].alphaLayer = ReadMCALSubChunk(subChunkSize, subbin, adtfile.texChunks[currentChunk]); //very ghetto, much wow break; case "MCLQ": //FIND PROPER PLACE IN STACK (not that it matters) case "MCSE": mapchunk.soundEmitters = ReadMCSESubChunk(subChunkSize, subbin); break; default: throw new Exception(string.Format("Found unknown header at offset {1} \"{0}\" while we should've already read them all! (Total size: {2})", subChunkName, subpos.ToString(), size)); } } } return(mapchunk); }
public MCSE(byte[] chunkBytes, MCNK parentChunk) : base(chunkBytes) { for (int i = 0; i < parentChunk.NumberSoundEmitters; i++) { SoundEmitters[i] = new SoundEmitter(this); } Close(); }
// Read in an MCNK chunk at supplied offset. private MCNK parseMapChunk(FileStream ms, uint offset, uint size) { BinaryReader bin = new BinaryReader(ms); ms.Position = offset; BlizChunkHeader tempHeader = new BlizChunkHeader(bin.ReadChars(4), bin.ReadUInt32()); tempHeader.Flip(); if (!tempHeader.Is("MCNK")) { throw new Exception("This was supposed to be an MCNK chunk. Wtf?"); } long lastpos = ms.Position + tempHeader.Size; MCNK mapChunk = new MCNK(); mapChunk.flags = bin.ReadUInt32(); mapChunk.ix = bin.ReadUInt32(); mapChunk.iy = bin.ReadUInt32(); mapChunk.nLayers = bin.ReadUInt32(); mapChunk.nDoodadRefs = bin.ReadUInt32(); mapChunk.ofsHeight = bin.ReadUInt32(); mapChunk.ofsNormal = bin.ReadUInt32(); mapChunk.ofsLayer = bin.ReadUInt32(); mapChunk.ofsRefs = bin.ReadUInt32(); mapChunk.ofsAlpha = bin.ReadUInt32(); mapChunk.sizeAlpha = bin.ReadUInt32(); mapChunk.ofsShadow = bin.ReadUInt32(); mapChunk.sizeShadow = bin.ReadUInt32(); mapChunk.areaid = bin.ReadUInt32(); mapChunk.nMapObjRefs = bin.ReadUInt32(); mapChunk.holes = bin.ReadUInt32(); mapChunk.s1 = bin.ReadUInt16(); mapChunk.s2 = bin.ReadUInt16(); mapChunk.d1 = bin.ReadUInt32(); mapChunk.d2 = bin.ReadUInt32(); mapChunk.d3 = bin.ReadUInt32(); mapChunk.predTex = bin.ReadUInt32(); mapChunk.nEffectDoodad = bin.ReadUInt32(); mapChunk.ofsSndEmitters = bin.ReadUInt32(); mapChunk.nSndEmitters = bin.ReadUInt32(); mapChunk.ofsLiquid = bin.ReadUInt32(); mapChunk.sizeLiquid = bin.ReadUInt32(); mapChunk.zpos = bin.ReadSingle(); mapChunk.xpos = bin.ReadSingle(); mapChunk.ypos = bin.ReadSingle(); mapChunk.textureId = bin.ReadUInt32(); mapChunk.props = bin.ReadUInt32(); mapChunk.effectId = bin.ReadUInt32(); // Parse this MapChunk's SubChunks! parseMapChunkSubChunks(ref mapChunk, ms, lastpos); return(mapChunk); }
public MCAL(byte[] chunkBytes, MCNK parentChunk, WDT.WDT wdt) : base(chunkBytes) { AlphaMaps = new MCALAlphaMap[parentChunk.MCLY.Layers.Length]; for (int i = 0; i < parentChunk.MCLY.Layers.Length; i++) { AlphaMaps[i] = new MCALAlphaMap(this, parentChunk, wdt, i); } Close(); }
public MCNK ReadMCNKChunk(BlizzHeader chunk, BinaryReader bin) { //256 of these chunks per file MCNK mapchunk = new MCNK(); mapchunk.header = bin.Read <MCNKheader>(); MemoryStream stream = new MemoryStream(bin.ReadBytes((int)chunk.Size - 128)); var subbin = new BinaryReader(stream); BlizzHeader subchunk; long subpos = 0; while (subpos < stream.Length) { subbin.BaseStream.Position = subpos; subchunk = new BlizzHeader(subbin.ReadChars(4), subbin.ReadUInt32()); subchunk.Flip(); subpos = stream.Position + subchunk.Size; switch (subchunk.ToString()) { case "MCVT": mapchunk.vertices = ReadMCVTSubChunk(subchunk, subbin); break; case "MCCV": mapchunk.vertexShading = ReadMCCVSubChunk(subchunk, subbin); break; case "MCNR": mapchunk.normals = ReadMCNRSubChunk(subchunk, subbin); break; case "MCSE": mapchunk.soundEmitters = ReadMCSESubChunk(subchunk, subbin); break; case "MCBB": mapchunk.blendBatches = ReadMCBBSubChunk(subchunk, subbin); break; case "MCLQ": case "MCLV": continue; default: throw new Exception(String.Format("Found unknown header at offset {1} \"{0}\" while we should've already read them all!", subchunk.ToString(), subpos.ToString())); } } return(mapchunk); }
private MCNK ReadMCNKChunk(uint size, BinaryReader bin) { MCNK mapchunk = new MCNK() { header = bin.Read <MCNKheader>() }; using (var stream = new MemoryStream(bin.ReadBytes((int)size - 128))) using (var subbin = new BinaryReader(stream)) { long subpos = 0; while (subpos < stream.Length) { subbin.BaseStream.Position = subpos; var subChunkName = new string(subbin.ReadChars(4).Reverse().ToArray()); var subChunkSize = subbin.ReadUInt32(); subpos = stream.Position + subChunkSize; switch (subChunkName) { case "MCVT": mapchunk.vertices = ReadMCVTSubChunk(subbin); break; case "MCCV": mapchunk.vertexShading = ReadMCCVSubChunk(subbin); break; case "MCNR": mapchunk.normals = ReadMCNRSubChunk(subbin); break; case "MCSE": mapchunk.soundEmitters = ReadMCSESubChunk(subChunkSize, subbin); break; case "MCBB": mapchunk.blendBatches = ReadMCBBSubChunk(subChunkSize, subbin); break; case "MCLQ": case "MCLV": continue; default: throw new Exception(String.Format("Found unknown header at offset {1} \"{0}\" while we should've already read them all!", subChunkName, subpos.ToString())); } } } return(mapchunk); }
public MapChunk(ADT adt, Chunk chunk, bool isObj0 = false) { ADT = adt; Chunk = chunk; MCNK = new MCNK(chunk); Holes = MCNK.Flags.HasFlag(MCNK.MCNKFlags.HighResolutionHoles) ? HighResHoles : TransformToHighRes(MCNK.Holes); var stream = chunk.GetStream(); stream.Seek(chunk.Offset + MCNK.ChunkHeaderSize, SeekOrigin.Begin); Read(new ChunkData(stream, chunk.Size - MCNK.ChunkHeaderSize)); }
public byte[] GetChunkBytes(MCNK parentChunk, WDT.WDT wdt) { using (var stream = new MemoryStream()) { using (var writer = new BinaryWriter(stream)) { for (int i = 0; i < AlphaMaps.Length; i++) { AlphaMaps[i].Write(writer, parentChunk, wdt, i); } } return(stream.ToArray()); } }
/// <summary> /// Parse all MCNK element from file stream /// </summary> public override MCNK[,] Parse() { var MCNK = new MCNK[16, 16]; int count = 0; for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { MCNK[x, y] = processMCNK(_mcins[count].Offset); count++; } } return(MCNK); // processMH2Os(); }
public MCRF(byte[] chunkBytes, MCNK parentChunk) : base(chunkBytes) { DoodadReferences = new uint[parentChunk.NumberDoodadRefs]; ObjectReferences = new uint[parentChunk.NumberMapObjectRefs]; for (int i = 0; i < DoodadReferences.Length; i++) { DoodadReferences[i] = ReadUInt32(); } for (int i = 0; i < ObjectReferences.Length; i++) { ObjectReferences[i] = ReadUInt32(); } Close(); }
/// <summary> /// Processes an individual MCNK Chunk /// </summary> /// <param name="offset">Offset to the MCNK Chunk</param> /// <returns>MCNK Filled with information</returns> private MCNK processMCNK(UInt32 offset) { Reader.BaseStream.Position = offset + 12; // Get off the header //br.ReadBytes(4); // Data that's not needed var index_x = (int)Reader.ReadUInt32(); var index_y = (int)Reader.ReadUInt32(); Reader.BaseStream.Position = offset + 0x08 + 0x03C; // Get off the header uint holes = Reader.ReadUInt32() & 0x00FF; if (holes > 0) { Console.WriteLine(Convert.ToString(holes, 2)); } Reader.BaseStream.Position = offset + 0x08 + 0x068; //br.BaseStream.Position += 28; // Get past the data we don't want //UInt32 offsLiquid = br.ReadUInt32(); // Offset to the liquid information //UInt32 sizeLiquid = br.ReadUInt32(); // If there's no liquid information it will be 8 float z = Reader.ReadSingle(); float x = Reader.ReadSingle(); float y = Reader.ReadSingle(); var currentMCNK = new MCNK(x, y, z, (UInt16)holes); Reader.ReadBytes(20); // Read the reaming 12 bytes of data and 8 for the next header which is a MCVT chunk for (int i = 0; i < 145; i++) { currentMCNK._MCVT.heights[i] = Reader.ReadSingle(); } Reader.ReadBytes(8); // We're going to read past the next header which is for the normals sbyte normalZ = 0; sbyte normalX = 0; sbyte normalY = 0; for (int i = 0; i < 145; i++) { normalZ = Reader.ReadSByte(); normalX = Reader.ReadSByte(); normalY = Reader.ReadSByte(); currentMCNK._MCNR.normals[i] = new Vector3(-(float)normalX / 127.0f, normalY / 127.0f, -(float)normalZ / 127.0f); } return(currentMCNK); }
public static bool ConvertADT(ChunkedFile adt, string mapName, string outputPath, int gx, int gy, uint build, bool ignoreDeepWater) { // Prepare map header MapFileHeader map; map.mapMagic = SharedConst.MAP_MAGIC; map.versionMagic = SharedConst.MAP_VERSION_MAGIC; map.buildMagic = build; // Get area flags data for (var x = 0; x < SharedConst.ADT_CELLS_PER_GRID; ++x) { for (var y = 0; y < SharedConst.ADT_CELLS_PER_GRID; ++y) { AreaIDs[x][y] = 0; LiquidEntries[x][y] = 0; LiquidFlags[x][y] = 0; } } for (var x = 0; x < SharedConst.ADT_GRID_SIZE; ++x) { for (var y = 0; y < SharedConst.ADT_GRID_SIZE; ++y) { V8[x][y] = 0; LiquidSnow[x][y] = false; } } for (var x = 0; x < SharedConst.ADT_GRID_SIZE + 1; ++x) { for (var y = 0; y < SharedConst.ADT_GRID_SIZE + 1; ++y) { V9[x][y] = 0; } } for (var x = 0; x < SharedConst.ADT_CELLS_PER_GRID; ++x) { for (var y = 0; y < SharedConst.ADT_CELLS_PER_GRID; ++y) { for (var z = 0; z < 8; ++z) { Holes[x][y][z] = 0; } } } bool hasHoles = false; bool hasFlightBox = false; foreach (var fileChunk in adt.chunks.LookupByKey("MCNK")) { MCNK mcnk = fileChunk.As <MCNK>(); // Area data AreaIDs[mcnk.IndexY][mcnk.IndexX] = (ushort)mcnk.AreaID; // Height // Height values for triangles stored in order: // 1 2 3 4 5 6 7 8 9 // 10 11 12 13 14 15 16 17 // 18 19 20 21 22 23 24 25 26 // 27 28 29 30 31 32 33 34 // . . . . . . . . // For better get height values merge it to V9 and V8 map // V9 height map: // 1 2 3 4 5 6 7 8 9 // 18 19 20 21 22 23 24 25 26 // . . . . . . . . // V8 height map: // 10 11 12 13 14 15 16 17 // 27 28 29 30 31 32 33 34 // . . . . . . . . // Set map height as grid height for (int y = 0; y <= SharedConst.ADT_CELL_SIZE; y++) { int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y; for (int x = 0; x <= SharedConst.ADT_CELL_SIZE; x++) { int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x; V9[cy][cx] = mcnk.ypos; } } for (int y = 0; y < SharedConst.ADT_CELL_SIZE; y++) { int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y; for (int x = 0; x < SharedConst.ADT_CELL_SIZE; x++) { int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x; V8[cy][cx] = mcnk.ypos; } } // Get custom height FileChunk chunk = fileChunk.GetSubChunk("MCVT"); if (chunk != null) { MCVT mcvt = chunk.As <MCVT>(); // get V9 height map for (int y = 0; y <= SharedConst.ADT_CELL_SIZE; y++) { int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y; for (int x = 0; x <= SharedConst.ADT_CELL_SIZE; x++) { int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x; V9[cy][cx] += mcvt.HeightMap[y * (SharedConst.ADT_CELL_SIZE * 2 + 1) + x]; } } // get V8 height map for (int y = 0; y < SharedConst.ADT_CELL_SIZE; y++) { int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y; for (int x = 0; x < SharedConst.ADT_CELL_SIZE; x++) { int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x; V8[cy][cx] += mcvt.HeightMap[y * (SharedConst.ADT_CELL_SIZE * 2 + 1) + SharedConst.ADT_CELL_SIZE + 1 + x]; } } } // Liquid data if (mcnk.MCLQCount > 8) { FileChunk lquidChunk = fileChunk.GetSubChunk("MCLQ"); if (lquidChunk != null) { MCLQ liquid = lquidChunk.As <MCLQ>(); int count = 0; for (int y = 0; y < SharedConst.ADT_CELL_SIZE; ++y) { int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y; for (int x = 0; x < SharedConst.ADT_CELL_SIZE; ++x) { int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x; if (liquid.Flags[y][x] != 0x0F) { LiquidSnow[cy][cx] = true; if (!ignoreDeepWater && Convert.ToBoolean(liquid.Flags[y][x] & (1 << 7))) { LiquidFlags[mcnk.IndexY][mcnk.IndexX] |= LiquidHeaderTypeFlags.DarkWater; } ++count; } } } if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidRiver)) { LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 1; LiquidFlags[mcnk.IndexY][mcnk.IndexX] |= LiquidHeaderTypeFlags.Water; // water } if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidOcean)) { LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 2; LiquidFlags[mcnk.IndexY][mcnk.IndexX] |= LiquidHeaderTypeFlags.Ocean; // ocean } if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidMagma)) { LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 3; LiquidFlags[mcnk.IndexY][mcnk.IndexX] |= LiquidHeaderTypeFlags.Magma; // magma } if (mcnk.Flags.HasAnyFlag(MCNKFlags.LiquidSlime)) { LiquidEntries[mcnk.IndexY][mcnk.IndexX] = 4; LiquidFlags[mcnk.IndexY][mcnk.IndexX] |= LiquidHeaderTypeFlags.Slime; // slime } if (count == 0 && LiquidFlags[mcnk.IndexY][mcnk.IndexX] != 0) { Console.WriteLine("Wrong liquid detect in MCLQ chunk"); } for (int y = 0; y <= SharedConst.ADT_CELL_SIZE; ++y) { int cy = (int)mcnk.IndexY * SharedConst.ADT_CELL_SIZE + y; for (int x = 0; x <= SharedConst.ADT_CELL_SIZE; ++x) { int cx = (int)mcnk.IndexX * SharedConst.ADT_CELL_SIZE + x; LiquidHeight[cy][cx] = liquid.Liquid[y][x].Height; } } } } // Hole data if (!mcnk.Flags.HasAnyFlag(MCNKFlags.HighResHoles)) { uint hole = mcnk.HolesLowRes; if (hole != 0) { if (TransformToHighRes((ushort)hole, Holes[mcnk.IndexY][mcnk.IndexX])) { hasHoles = true; } } } else { Buffer.BlockCopy(mcnk.HighResHoles, 0, Holes[mcnk.IndexY][mcnk.IndexX], 0, 8); if (BitConverter.ToUInt64(Holes[mcnk.IndexY][mcnk.IndexX], 0) != 0) { hasHoles = true; } } } // Get liquid map for grid (in WOTLK used MH2O chunk) FileChunk chunkMH2O = adt.GetChunk("MH2O"); if (chunkMH2O != null) { MH2O h2o = chunkMH2O.As <MH2O>(); for (int i = 0; i < SharedConst.ADT_CELLS_PER_GRID; i++) { for (int j = 0; j < SharedConst.ADT_CELLS_PER_GRID; j++) { MH2OInstance?h = h2o.GetLiquidInstance(i, j); if (!h.HasValue) { continue; } MH2OInstance adtLiquidHeader = h.Value; MH2OChunkAttribute?attrs = h2o.GetLiquidAttributes(i, j); int count = 0; ulong existsMask = h2o.GetLiquidExistsBitmap(adtLiquidHeader); for (int y = 0; y < adtLiquidHeader.GetHeight(); y++) { int cy = i * SharedConst.ADT_CELL_SIZE + y + adtLiquidHeader.GetOffsetY(); for (int x = 0; x < adtLiquidHeader.GetWidth(); x++) { int cx = j * SharedConst.ADT_CELL_SIZE + x + adtLiquidHeader.GetOffsetX(); if (Convert.ToBoolean(existsMask & 1)) { LiquidSnow[cy][cx] = true; ++count; } existsMask >>= 1; } } LiquidEntries[i][j] = h2o.GetLiquidType(adtLiquidHeader); if (LiquidEntries[i][j] != 0) { var liquidTypeRecord = LiquidTypeStorage[LiquidEntries[i][j]]; switch ((LiquidType)liquidTypeRecord.SoundBank) { case LiquidType.Water: LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Water; break; case LiquidType.Ocean: LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Ocean; if (!ignoreDeepWater && attrs.Value.Deep != 0) { LiquidFlags[i][j] |= LiquidHeaderTypeFlags.DarkWater; } break; case LiquidType.Magma: LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Magma; break; case LiquidType.Slime: LiquidFlags[i][j] |= LiquidHeaderTypeFlags.Slime; break; default: Console.WriteLine($"\nCan't find Liquid type {adtLiquidHeader.LiquidType} for map {mapName}\nchunk {i},{j}\n"); break; } if (count == 0 && LiquidFlags[i][j] != 0) { Console.WriteLine("Wrong liquid detect in MH2O chunk"); } } else { Console.WriteLine($"LiquidEntries is 0 for [{i}][{j}] ({i * j})"); } int pos = 0; for (int y = 0; y <= adtLiquidHeader.GetHeight(); y++) { int cy = i * SharedConst.ADT_CELL_SIZE + y + adtLiquidHeader.GetOffsetY(); for (int x = 0; x <= adtLiquidHeader.GetWidth(); x++) { int cx = j * SharedConst.ADT_CELL_SIZE + x + adtLiquidHeader.GetOffsetX(); LiquidHeight[cy][cx] = h2o.GetLiquidHeight(adtLiquidHeader, i, j, pos); pos++; } } } } } FileChunk chunkMFBO = adt.GetChunk("MFBO"); if (chunkMFBO != null) { MFBO mfbo = chunkMFBO.As <MFBO>(); for (var i = 0; i < 3; ++i) { FlightBoxMax[i][0] = mfbo.Max.coords[0 + i * 3]; FlightBoxMax[i][1] = mfbo.Max.coords[1 + i * 3]; FlightBoxMax[i][2] = mfbo.Max.coords[2 + i * 3]; FlightBoxMin[i][0] = mfbo.Min.coords[0 + i * 3]; FlightBoxMin[i][1] = mfbo.Min.coords[1 + i * 3]; FlightBoxMin[i][2] = mfbo.Min.coords[2 + i * 3]; } hasFlightBox = true; } //============================================ // Try pack area data //============================================ bool fullAreaData = false; uint areaId = AreaIDs[0][0]; for (int y = 0; y < SharedConst.ADT_CELLS_PER_GRID; ++y) { for (int x = 0; x < SharedConst.ADT_CELLS_PER_GRID; ++x) { if (AreaIDs[y][x] != areaId) { fullAreaData = true; break; } } } map.areaMapOffset = 44; map.areaMapSize = 8; MapAreaHeader areaHeader; areaHeader.fourcc = SharedConst.MAP_AREA_MAGIC; areaHeader.flags = 0; if (fullAreaData) { areaHeader.gridArea = 0; map.areaMapSize += 512; } else { areaHeader.flags |= AreaHeaderFlags.NoArea; areaHeader.gridArea = (ushort)areaId; } //============================================ // Try pack height data //============================================ float maxHeight = -20000; float minHeight = 20000; for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++) { float h = V8[y][x]; if (maxHeight < h) { maxHeight = h; } if (minHeight > h) { minHeight = h; } } } for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++) { float h = V9[y][x]; if (maxHeight < h) { maxHeight = h; } if (minHeight > h) { minHeight = h; } } } // Check for allow limit minimum height (not store height in deep ochean - allow save some memory) if (minHeight < -2000.0f) { for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++) { if (V8[y][x] < -2000.0f) { V8[y][x] = -2000.0f; } } } for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++) { if (V9[y][x] < -2000.0f) { V9[y][x] = -2000.0f; } } } if (minHeight < -2000.0f) { minHeight = -2000.0f; } if (maxHeight < -2000.0f) { maxHeight = -2000.0f; } } map.heightMapOffset = map.areaMapOffset + map.areaMapSize; map.heightMapSize = (uint)Marshal.SizeOf <MapHeightHeader>(); MapHeightHeader heightHeader; heightHeader.fourcc = SharedConst.MAP_HEIGHT_MAGIC; heightHeader.flags = 0; heightHeader.gridHeight = minHeight; heightHeader.gridMaxHeight = maxHeight; if (maxHeight == minHeight) { heightHeader.flags |= HeightHeaderFlags.NoHeight; } // Not need store if flat surface if ((maxHeight - minHeight) < 0.005f) { heightHeader.flags |= HeightHeaderFlags.NoHeight; } if (hasFlightBox) { heightHeader.flags |= HeightHeaderFlags.HasFlightBounds; map.heightMapSize += 18 + 18; } // Try store as packed in uint16 or uint8 values if (!heightHeader.flags.HasFlag(HeightHeaderFlags.NoHeight)) { float step = 0; // Try Store as uint values if (true)//CONF_allow_float_to_int { float diff = maxHeight - minHeight; if (diff < 2.0f) // As uint8 (max accuracy = CONF_float_to_int8_limit/256) { heightHeader.flags |= HeightHeaderFlags.AsInt8; step = 255 / diff; } else if (diff < 2048.0f) // As uint16 (max accuracy = CONF_float_to_int16_limit/65536) { heightHeader.flags |= HeightHeaderFlags.AsInt16; step = 65535 / diff; } } // Pack it to int values if need if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt8)) { for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++) { UInt8_V8[y][x] = (byte)((V8[y][x] - minHeight) * step + 0.5f); } } for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++) { UInt8_V9[y][x] = (byte)((V9[y][x] - minHeight) * step + 0.5f); } } map.heightMapSize += 16641 + 16384; } else if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt16)) { for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++) { UInt16_V8[y][x] = (ushort)((V8[y][x] - minHeight) * step + 0.5f); } } for (int y = 0; y <= SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x <= SharedConst.ADT_GRID_SIZE; x++) { UInt16_V9[y][x] = (ushort)((V9[y][x] - minHeight) * step + 0.5f); } } map.heightMapSize += 33282 + 32768; } else { map.heightMapSize += 66564 + 65536; } } //============================================ // Pack liquid data //============================================ ushort firstLiquidType = LiquidEntries[0][0]; LiquidHeaderTypeFlags firstLiquidFlag = LiquidFlags[0][0]; bool fullType = false; for (int y = 0; y < SharedConst.ADT_CELLS_PER_GRID; y++) { for (int x = 0; x < SharedConst.ADT_CELLS_PER_GRID; x++) { if (LiquidEntries[y][x] != firstLiquidType || LiquidFlags[y][x] != firstLiquidFlag) { fullType = true; y = SharedConst.ADT_CELLS_PER_GRID; break; } } } MapLiquidHeader mapLiquidHeader = new(); // no water data (if all grid have 0 liquid type) if (firstLiquidFlag == 0 && !fullType) { // No liquid data map.liquidMapOffset = 0; map.liquidMapSize = 0; } else { int minX = 255, minY = 255; int maxX = 0, maxY = 0; maxHeight = -20000; minHeight = 20000; for (int y = 0; y < SharedConst.ADT_GRID_SIZE; y++) { for (int x = 0; x < SharedConst.ADT_GRID_SIZE; x++) { if (LiquidSnow[y][x]) { if (minX > x) { minX = x; } if (maxX < x) { maxX = x; } if (minY > y) { minY = y; } if (maxY < y) { maxY = y; } float h = LiquidHeight[y][x]; if (maxHeight < h) { maxHeight = h; } if (minHeight > h) { minHeight = h; } } else { LiquidHeight[y][x] = -2000.0f; } } } map.liquidMapOffset = map.heightMapOffset + map.heightMapSize; map.liquidMapSize = (uint)Marshal.SizeOf <MapLiquidHeader>(); mapLiquidHeader.fourcc = SharedConst.MAP_LIQUID_MAGIC; mapLiquidHeader.flags = LiquidHeaderFlags.None; mapLiquidHeader.liquidType = 0; mapLiquidHeader.offsetX = (byte)minX; mapLiquidHeader.offsetY = (byte)minY; mapLiquidHeader.width = (byte)(maxX - minX + 1 + 1); mapLiquidHeader.height = (byte)(maxY - minY + 1 + 1); mapLiquidHeader.liquidLevel = minHeight; if (maxHeight == minHeight) { mapLiquidHeader.flags |= LiquidHeaderFlags.NoHeight; } // Not need store if flat surface if ((maxHeight - minHeight) < 0.001f) { mapLiquidHeader.flags |= LiquidHeaderFlags.NoHeight; } if (!fullType) { mapLiquidHeader.flags |= LiquidHeaderFlags.NoType; } if (mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoType)) { mapLiquidHeader.liquidFlags = firstLiquidFlag; mapLiquidHeader.liquidType = firstLiquidType; } else { map.liquidMapSize += 512 + 256; } if (!mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoHeight)) { map.liquidMapSize += (uint)(sizeof(float) * mapLiquidHeader.width * mapLiquidHeader.height); } } if (hasHoles) { if (map.liquidMapOffset != 0) { map.holesOffset = map.liquidMapOffset + map.liquidMapSize; } else { map.holesOffset = map.heightMapOffset + map.heightMapSize; } map.holesSize = 2048; } else { map.holesOffset = 0; map.holesSize = 0; } // Ok all data prepared - store it using (BinaryWriter binaryWriter = new(File.Open(outputPath, FileMode.Create, FileAccess.Write))) { binaryWriter.WriteStruct(map); // Store area data binaryWriter.WriteStruct(areaHeader); if (!areaHeader.flags.HasFlag(AreaHeaderFlags.NoArea)) { for (var x = 0; x < AreaIDs.Length; ++x) { for (var y = 0; y < AreaIDs[x].Length; ++y) { binaryWriter.Write(AreaIDs[x][y]); } } } // Store height data binaryWriter.WriteStruct(heightHeader); if (!heightHeader.flags.HasFlag(HeightHeaderFlags.NoHeight)) { if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt16)) { for (var x = 0; x < UInt16_V9.Length; ++x) { for (var y = 0; y < UInt16_V9[x].Length; ++y) { binaryWriter.Write(UInt16_V9[x][y]); } } for (var x = 0; x < UInt16_V8.Length; ++x) { for (var y = 0; y < UInt16_V8[x].Length; ++y) { binaryWriter.Write(UInt16_V8[x][y]); } } } else if (heightHeader.flags.HasFlag(HeightHeaderFlags.AsInt8)) { for (var x = 0; x < UInt8_V9.Length; ++x) { for (var y = 0; y < UInt8_V9[x].Length; ++y) { binaryWriter.Write(UInt8_V9[x][y]); } } for (var x = 0; x < UInt8_V8.Length; ++x) { for (var y = 0; y < UInt8_V8[x].Length; ++y) { binaryWriter.Write(UInt8_V8[x][y]); } } } else { for (var x = 0; x < V9.Length; ++x) { for (var y = 0; y < V9[x].Length; ++y) { binaryWriter.Write(V9[x][y]); } } for (var x = 0; x < V8.Length; ++x) { for (var y = 0; y < V8[x].Length; ++y) { binaryWriter.Write(V8[x][y]); } } } } if (heightHeader.flags.HasFlag(HeightHeaderFlags.HasFlightBounds)) { for (var x = 0; x < 3; ++x) { for (var y = 0; y < 3; ++y) { binaryWriter.Write(FlightBoxMax[x][y]); } } for (var x = 0; x < 3; ++x) { for (var y = 0; y < 3; ++y) { binaryWriter.Write(FlightBoxMin[x][y]); } } } // Store liquid data if need if (map.liquidMapOffset != 0) { binaryWriter.WriteStruct(mapLiquidHeader); if (!mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoType)) { for (var x = 0; x < LiquidEntries.Length; ++x) { for (var y = 0; y < LiquidEntries[x].Length; ++y) { binaryWriter.Write(LiquidEntries[x][y]); } } for (var x = 0; x < LiquidFlags.Length; ++x) { for (var y = 0; y < LiquidFlags[x].Length; ++y) { binaryWriter.Write((byte)LiquidFlags[x][y]); } } } if (!mapLiquidHeader.flags.HasFlag(LiquidHeaderFlags.NoHeight)) { for (int y = 0; y < mapLiquidHeader.height; y++) { for (int x = 0; x < mapLiquidHeader.width; x++) { binaryWriter.Write(LiquidHeight[y + mapLiquidHeader.offsetY][x + mapLiquidHeader.offsetX]); } } } } // store hole data if (hasHoles) { for (var x = 0; x < Holes.Length; ++x) { for (var y = 0; y < Holes[x].Length; ++y) { for (var z = 0; z < Holes[x][y].Length; ++z) { binaryWriter.Write(Holes[x][y][z]); } } } } } return(true); }
private void parseMapChunkSubChunks(ref MCNK mapChunk, FileStream ms, long lastpos) { BinaryReader bin = new BinaryReader(ms); BlizChunkHeader tempHeader; long pos = ms.Position; // Read bytes from the stream until we run out while (pos < lastpos) { // Advance to the next Chunk ms.Position = pos; // Read in Chunk Header Name tempHeader = new BlizChunkHeader(bin.ReadChars(4), bin.ReadUInt32()); tempHeader.Flip(); // Set pos to the location of the next Chunk pos = ms.Position + tempHeader.Size; if (tempHeader.Is("MCVT")) // These are the actual height values for the 9x9+8x8 vertices. { mapChunk.VerticesOuter = new float[9][]; mapChunk.VerticesInner = new float[8][]; for (int i = 0; i < 9; i++) { mapChunk.VerticesOuter[i] = new float[9]; for (int j = 0; j < 9; j++) { mapChunk.VerticesOuter[i][j] = bin.ReadSingle(); } if (i == 8) { continue; } mapChunk.VerticesInner[i] = new float[8]; for (int j = 0; j < 8; j++) { mapChunk.VerticesInner[i][j] = bin.ReadSingle(); } } continue; } if (tempHeader.Is("MCNR")) // Normal vectors for each vertex. { pos = ms.Position + 0x1C0; // sizefix? // Not needed. continue; } if (tempHeader.Is("MCLY")) // Texture layer definitions for this map chunk. { // Not needed. continue; } if (tempHeader.Is("MCRF")) // Unknown. List of integers. { // Not needed. continue; } if (tempHeader.Is("MCSH")) // Shadow map for static shadows on the terrain. { // Not needed. continue; } if (tempHeader.Is("MCAL")) // Alpha maps for additional texture layers. { // Not needed. continue; } if (tempHeader.Is("MCLQ")) // Water levels for this map chunk. { mapChunk.Liquid = new MCLQ(); // I dunno. MCLQ header size lies. If MCSE is immidiately following, there's no water. tempHeader = new BlizChunkHeader(bin.ReadChars(4), 0); tempHeader.Flip(); if (tempHeader.Is("MCSE")) { mapChunk.Liquid.waterLevel = float.NaN; } else { // After reading water, we stop. I do NOT like this solution, but // 1) We don't know much about MCLQ chunks. // 2) The size field lies, saying its always 0. (no idea why) // 3) I can't kludge this with a size fix, BECAUSE THE LENGTH VARIES?! Its in the area of 0x320, or 0x31F or 0x31E. // 4) So stopping after this chunk is the best we can do; this is what wowmapview does in any case. pos = lastpos; ms.Seek(-4, SeekOrigin.Current); // Go back! Re-read the last 4 bytes as its actually a float not char[4]. mapChunk.Liquid.waterLevel = bin.ReadSingle(); } continue; } if (tempHeader.Is("MCSE")) // Sound emitters. { // Not needed. continue; } // If we're still down here, we got a problem throw new Exception(String.Format("ADTFile: Woah. Got a header of Sub-{0}. Don't know how to deal with this, bailing out.", tempHeader.ToString())); } }
private void parseFile() { FileStream ms = adtStream; if (ms == null) { throw new Exception("Stream null!"); } BinaryReader bin = new BinaryReader(ms); BlizChunkHeader tempHeader; long pos = 0; // Read bytes from the stream until we run out while (pos < ms.Length) { // Advance to the next Chunk ms.Position = pos; // Read in Chunk Header Name tempHeader = new BlizChunkHeader(bin.ReadChars(4), bin.ReadUInt32()); tempHeader.Flip(); // Set pos to the location of the next Chunk pos = ms.Position + tempHeader.Size; if (tempHeader.Is("MVER")) // ADT File Version { mver = new MVER(); mver.version = bin.ReadUInt32(); continue; } if (tempHeader.Is("MHDR")) // ADT File Header { mhdr = new MHDR(); mhdr.pad = bin.ReadUInt32(); mhdr.offsInfo = bin.ReadUInt32(); mhdr.offsTex = bin.ReadUInt32(); mhdr.offsModels = bin.ReadUInt32(); mhdr.offsModelsIds = bin.ReadUInt32(); mhdr.offsMapObejcts = bin.ReadUInt32(); mhdr.offsMapObejctsIds = bin.ReadUInt32(); mhdr.offsDoodsDef = bin.ReadUInt32(); mhdr.offsObjectsDef = bin.ReadUInt32(); mhdr.pad1 = bin.ReadUInt32(); mhdr.pad2 = bin.ReadUInt32(); mhdr.pad3 = bin.ReadUInt32(); mhdr.pad4 = bin.ReadUInt32(); mhdr.pad5 = bin.ReadUInt32(); mhdr.pad6 = bin.ReadUInt32(); mhdr.pad7 = bin.ReadUInt32(); continue; } if (tempHeader.Is("MCIN")) // Index for MCNK chunks. { if (tempHeader.Size != 256 * 16) { throw new Exception("MCIN Chunk is short??"); } mcin_array = new MCIN[256]; // Read in the 256 records for (int i = 0; i < 256; i++) { mcin_array[i].MCNK_offset = bin.ReadUInt32(); mcin_array[i].MCNK_size = bin.ReadUInt32(); mcin_array[i].flags = bin.ReadUInt32(); mcin_array[i].asyncID = bin.ReadUInt32(); } continue; } if (tempHeader.Is("MTEX")) // List of texture filenames used by the terrain in this map tile. { // Not needed. continue; } if (tempHeader.Is("MMDX")) // List of filenames for M2 models that appear in this map tile. { // Not needed. continue; } if (tempHeader.Is("MMID")) // Lists the relative offsets of string beginnings in the above MMDX chunk. { // Not needed. continue; } if (tempHeader.Is("MWMO")) // List of filenames for WMOs (world map objects) that appear in this map tile. { byte[] wmoFilesChunk = bin.ReadBytes((int)tempHeader.Size); wmoFiles = new List <String>(); StringBuilder str = new StringBuilder(); // Convert szString's to a List<String>. for (int i = 0; i < wmoFilesChunk.Length; i++) { if (wmoFilesChunk[i] == '\0') { if (str.Length > 1) { wmoFiles.Add(str.ToString()); } str = new StringBuilder(); } else { str.Append((char)wmoFilesChunk[i]); } } continue; } if (tempHeader.Is("MWID")) // Lists the relative offsets of string beginnings in the above MWWO chunk. { // Not needed. continue; } if (tempHeader.Is("MDDF")) // Placement information for doodads (M2 models). { uint num = tempHeader.Size / 32; doodadLocations = new MDDF[num]; for (int i = 0; i < num; i++) { doodadLocations[i].nameId = bin.ReadUInt32(); doodadLocations[i].uniqueId = bin.ReadUInt32(); doodadLocations[i].coord = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); } continue; } if (tempHeader.Is("MODF")) // Placement information for WMOs. { uint num = tempHeader.Size / 64; wmoLocations = new MODF[num]; for (int i = 0; i < num; i++) { wmoLocations[i].nameId = bin.ReadUInt32(); wmoLocations[i].uniqueId = bin.ReadUInt32(); wmoLocations[i].coord = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].orientation = new Vect3D(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].coord2 = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].coord3 = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].flags = bin.ReadUInt32(); wmoLocations[i].doodadSet = bin.ReadUInt16(); wmoLocations[i].nameSet = bin.ReadUInt16(); } continue; } if (tempHeader.Is("MCNK")) // || tempHeader.Is("MCVT") || tempHeader.Is("MCNR") || tempHeader.Is("MCLY") || tempHeader.Is("MCRF") || tempHeader.Is("MCSH") || tempHeader.Is("MCAL") || tempHeader.Is("MCLQ") || tempHeader.Is("MCSE")) { // Skip these. They are read in afterwards. continue; } // If we're still down here, we got a problem throw new Exception(String.Format("ADTFile: Woah. Got a header of {0}. Don't know how to deal with this, bailing out.", tempHeader.ToString())); } // Read in Map Chunks mapChunkTable = new MCNK[16][]; for (int i = 0; i < 16; i++) { mapChunkTable[i] = new MCNK[16]; for (int j = 0; j < 16; j++) { int index = i * 16 + j; Log.WriteLine(LogType.Terrain, "Parsing MCNK Chunk #{0} [{1}, {2}]", index, i, j); mapChunkTable[i][j] = parseMapChunk(ms, mcin_array[index].MCNK_offset, mcin_array[index].MCNK_size); } } }
public void Write(BinaryWriter writer, MCNK parentChunk, WDT.WDT wdt, int currentLayer) { if (parentChunk.MCLY.Layers[currentLayer].Flags.HasFlag(MCLYFlags.CompressedAlphaMap) && (wdt.MPHD.Flags.HasFlag(MPHDFlags.HasBigAlpha) || wdt.MPHD.Flags.HasFlag(MPHDFlags.MCALSize4096))) { // Compressed var positionInAlphaMap = 0; var tempAlphaMap = new byte[4096]; while (positionInAlphaMap < 4096) { var info = AlphaMap[positionInAlphaMap % 64, positionInAlphaMap / 64]; tempAlphaMap[positionInAlphaMap] = info; positionInAlphaMap += 1; var mode = (info & 0x80) >> 7; var count = info & 0x7F; // Copy mode if (mode == 0) { for (int j = 0; j < count - 1; j++) { tempAlphaMap[positionInAlphaMap + j] = AlphaMap[positionInAlphaMap % 64 + j, positionInAlphaMap / 64 + j]; } } else // Fill mode { var data = AlphaMap[positionInAlphaMap % 64, positionInAlphaMap / 64]; for (int j = 0; j < count; j++) { tempAlphaMap[positionInAlphaMap + j] = data; } } positionInAlphaMap += count; } writer.Write(tempAlphaMap); } else if (wdt.MPHD.Flags.HasFlag(MPHDFlags.HasBigAlpha) || wdt.MPHD.Flags.HasFlag(MPHDFlags.MCALSize4096)) { // Uncompressed (4096) for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { writer.Write(AlphaMap[x, y]); } } } else { // Uncompressed (2048) - TODO build in FLAG_DO_NOT_FIX_ALPHA_MAP if (parentChunk.Flags.HasFlag(MCNKFlags.DoNotFixAlphaMap)) { for (int y = 0; y < 63; y++) { for (int x = 0; x < 63; x += 2) { var byte1 = AlphaMap[x, y]; var byte2 = AlphaMap[x + 1, y]; byte fullByte = 0; fullByte = (byte)(fullByte | byte1 << 4); fullByte = (byte)(fullByte | byte2); writer.Write(fullByte); } if (y == 62) { var byte1 = AlphaMap[] } } } else { for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x += 2) { var byte1 = AlphaMap[x, y]; var byte2 = AlphaMap[x + 1, y]; byte fullByte = 0; fullByte = (byte)(fullByte | byte1 << 4); fullByte = (byte)(fullByte | byte2); writer.Write(fullByte); } } } } }
private TriangleList GenerateVertexAndIndices() { var vertices = new List <VertexPositionNormalColored>(); var indices = new List <int>(); for (int My = 0; My < 16; My++) { for (int Mx = 0; Mx < 16; Mx++) { MCNK lMCNK = MCNKArray[Mx, My]; var HolesMap = new bool[4, 4]; if (lMCNK.holes > 0) { HolesMap = lMCNK.GetHolesMap(); } #region indexing for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { if (!HolesMap[row / 2, col / 2]) { /* The order metter*/ /*This 3 index add the up triangle * * 0--1--2 *| /| / *|/ |/ * 9 10 11 */ indices.Add(vertices.Count + ((row + 1) * (8 + 1) + col)); //9 ... 10 indices.Add(vertices.Count + (row * (8 + 1) + col)); //0 ... 1 indices.Add(vertices.Count + (row * (8 + 1) + col + 1)); //1 ... 2 /*This 3 index add the low triangle * * 0 1 2 * /| /| * / | / | * 9--10--11 */ indices.Add(vertices.Count + ((row + 1) * (8 + 1) + col + 1)); indices.Add(vertices.Count + ((row + 1) * (8 + 1) + col)); indices.Add(vertices.Count + (row * (8 + 1) + col + 1)); } #endregion } } //this.Indices.AddRange(triangleListIndices); const float offset_x = (533.33333f / 16) / 8; const float offset_z = (533.33333f / 16) / 8; var LowResMap = lMCNK._MCVT.GetLowResMapMatrix(); var LowResNormal = lMCNK._MCNR.GetLowResNormalMatrix(); for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { float x_pos = lMCNK.x - (c * offset_x); float z_pos = lMCNK.z - (r * offset_z); float y_pos = LowResMap[r, c] + lMCNK.y; var normal = LowResNormal[r, c]; float cosAngle = Vector3.Dot(Vector3.Up, normal); float angle = MathHelper.ToDegrees((float)Math.Acos(cosAngle)); var position = new Vector3(x_pos, y_pos, z_pos); vertices.Add(new VertexPositionNormalColored(position, angle > 50.0 ? Color.Brown : Color.Green, normal)); } } } } return(new TriangleList(indices, vertices)); }
public MCALAlphaMap(BinaryReader reader, MCNK parentChunk, WDT.WDT wdt, int currentLayer) { if (parentChunk.MCLY.Layers[currentLayer].Flags.HasFlag(MCLYFlags.CompressedAlphaMap) && (wdt.MPHD.Flags.HasFlag(MPHDFlags.HasBigAlpha) || wdt.MPHD.Flags.HasFlag(MPHDFlags.MCALSize4096))) { // Compressed var positionInAlphaMap = 0; var tempAlphaMap = new byte[4096]; while (positionInAlphaMap < 4096) { var info = reader.ReadByte(); var mode = (info & 0x80) >> 7; var count = info & 0x7F; // Copy mode if (mode == 0) { for (int j = 0; j < count; j++) { tempAlphaMap[positionInAlphaMap + j] = reader.ReadByte(); } } else // Fill mode { var data = reader.ReadByte(); for (int j = 0; j < count; j++) { tempAlphaMap[positionInAlphaMap + j] = data; } } positionInAlphaMap += count; } for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { AlphaMap[x, y] = tempAlphaMap[x * 64 + y]; } } } else if (wdt.MPHD.Flags.HasFlag(MPHDFlags.HasBigAlpha) || wdt.MPHD.Flags.HasFlag(MPHDFlags.MCALSize4096)) { // Uncompressed (4096) for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { AlphaMap[x, y] = reader.ReadByte(); } } } else { // Uncompressed (2048) - TODO: build in FLAG_DO_NOT_FIX_ALPHA_MAP for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x += 2) { var fullByte = reader.ReadByte(); byte lowBits = (byte)(fullByte & 0x0F); byte highBits = (byte)(fullByte >> 4); AlphaMap[x, y] = highBits; AlphaMap[x + 1, y] = lowBits; } } } }
static MCNK ProcessChunk(BinaryReader fileReader, uint mcnkOffset) { var mcnk = new MCNK(); fileReader.BaseStream.Position = mcnkOffset; var sig = fileReader.ReadUInt32(); if (sig != Signatures.MCNK) { Console.WriteLine("Invalid Chunk offset found."); } var mcnkSize = fileReader.ReadUInt32(); mcnk.Flags = fileReader.ReadInt32(); mcnk.IndexX = fileReader.ReadInt32(); mcnk.IndexY = fileReader.ReadInt32(); mcnk.nLayers = fileReader.ReadUInt32(); //0xC mcnk.nDoodadRefs = fileReader.ReadUInt32(); //0x10 mcnk.ofsHeight = fileReader.ReadUInt32(); //0x14 mcnk.ofsNormal = fileReader.ReadUInt32(); //0x18 mcnk.ofsLayer = fileReader.ReadUInt32(); //0x1C mcnk.ofsRefs = fileReader.ReadUInt32(); //0x20 mcnk.ofsAlpha = fileReader.ReadUInt32(); //0x24 mcnk.sizeAlpha = fileReader.ReadUInt32(); //0x28 mcnk.ofsShadow = fileReader.ReadUInt32(); //0x2C mcnk.sizeShadow = fileReader.ReadUInt32(); //0x30 mcnk.AreaId = fileReader.ReadUInt32(); //0x34 mcnk.nMapObjRefs = fileReader.ReadUInt32(); //0x38 mcnk.Holes = fileReader.ReadUInt16(); fileReader.ReadUInt16(); // pad mcnk.predTex = new ushort[8]; for (var i = 0; i < 8; i++) { mcnk.predTex[i] = fileReader.ReadUInt16(); } mcnk.nEffectDoodad = new byte[8]; for (var i = 0; i < 8; i++) { mcnk.nEffectDoodad[i] = fileReader.ReadByte(); } mcnk.ofsSndEmitters = fileReader.ReadUInt32(); //0x58 mcnk.nSndEmitters = fileReader.ReadUInt32(); //0x5C mcnk.ofsLiquid = fileReader.ReadUInt32(); //0x60 mcnk.sizeLiquid = fileReader.ReadUInt32(); //0x64 mcnk.Z = fileReader.ReadSingle(); mcnk.X = fileReader.ReadSingle(); mcnk.Y = fileReader.ReadSingle(); mcnk.offsColorValues = fileReader.ReadInt32(); mcnk.props = fileReader.ReadInt32(); mcnk.effectId = fileReader.ReadInt32(); return mcnk; }
// Read in an MCNK chunk at supplied offset. private MCNK parseMapChunk(FileStream ms, uint offset, uint size) { BinaryReader bin = new BinaryReader(ms); ms.Position = offset; BlizChunkHeader tempHeader = new BlizChunkHeader(bin.ReadChars(4), bin.ReadUInt32()); tempHeader.Flip(); if (!tempHeader.Is("MCNK")) throw new Exception("This was supposed to be an MCNK chunk. Wtf?"); long lastpos = ms.Position + tempHeader.Size; MCNK mapChunk = new MCNK(); mapChunk.flags = bin.ReadUInt32(); mapChunk.ix = bin.ReadUInt32(); mapChunk.iy = bin.ReadUInt32(); mapChunk.nLayers = bin.ReadUInt32(); mapChunk.nDoodadRefs = bin.ReadUInt32(); mapChunk.ofsHeight = bin.ReadUInt32(); mapChunk.ofsNormal = bin.ReadUInt32(); mapChunk.ofsLayer = bin.ReadUInt32(); mapChunk.ofsRefs = bin.ReadUInt32(); mapChunk.ofsAlpha = bin.ReadUInt32(); mapChunk.sizeAlpha = bin.ReadUInt32(); mapChunk.ofsShadow = bin.ReadUInt32(); mapChunk.sizeShadow = bin.ReadUInt32(); mapChunk.areaid = bin.ReadUInt32(); mapChunk.nMapObjRefs = bin.ReadUInt32(); mapChunk.holes = bin.ReadUInt32(); mapChunk.s1 = bin.ReadUInt16(); mapChunk.s2 = bin.ReadUInt16(); mapChunk.d1 = bin.ReadUInt32(); mapChunk.d2 = bin.ReadUInt32(); mapChunk.d3 = bin.ReadUInt32(); mapChunk.predTex = bin.ReadUInt32(); mapChunk.nEffectDoodad = bin.ReadUInt32(); mapChunk.ofsSndEmitters = bin.ReadUInt32(); mapChunk.nSndEmitters = bin.ReadUInt32(); mapChunk.ofsLiquid = bin.ReadUInt32(); mapChunk.sizeLiquid = bin.ReadUInt32(); mapChunk.zpos = bin.ReadSingle(); mapChunk.xpos = bin.ReadSingle(); mapChunk.ypos = bin.ReadSingle(); mapChunk.textureId = bin.ReadUInt32(); mapChunk.props = bin.ReadUInt32(); mapChunk.effectId = bin.ReadUInt32(); // Parse this MapChunk's SubChunks! parseMapChunkSubChunks(ref mapChunk, ms, lastpos); return mapChunk; }
private void parseMapChunkSubChunks(ref MCNK mapChunk, FileStream ms, long lastpos) { BinaryReader bin = new BinaryReader(ms); BlizChunkHeader tempHeader; long pos = ms.Position; // Read bytes from the stream until we run out while (pos < lastpos) { // Advance to the next Chunk ms.Position = pos; // Read in Chunk Header Name tempHeader = new BlizChunkHeader(bin.ReadChars(4), bin.ReadUInt32()); tempHeader.Flip(); // Set pos to the location of the next Chunk pos = ms.Position + tempHeader.Size; if (tempHeader.Is("MCVT")) // These are the actual height values for the 9x9+8x8 vertices. { mapChunk.VerticesOuter = new float[9][]; mapChunk.VerticesInner = new float[8][]; for (int i = 0; i < 9; i++) { mapChunk.VerticesOuter[i] = new float[9]; for (int j = 0; j < 9; j++) { mapChunk.VerticesOuter[i][j] = bin.ReadSingle(); } if (i == 8) continue; mapChunk.VerticesInner[i] = new float[8]; for (int j = 0; j < 8; j++) { mapChunk.VerticesInner[i][j] = bin.ReadSingle(); } } continue; } if (tempHeader.Is("MCNR")) // Normal vectors for each vertex. { pos = ms.Position + 0x1C0; // sizefix? // Not needed. continue; } if (tempHeader.Is("MCLY")) // Texture layer definitions for this map chunk. { // Not needed. continue; } if (tempHeader.Is("MCRF")) // Unknown. List of integers. { // Not needed. continue; } if (tempHeader.Is("MCSH")) // Shadow map for static shadows on the terrain. { // Not needed. continue; } if (tempHeader.Is("MCAL")) // Alpha maps for additional texture layers. { // Not needed. continue; } if (tempHeader.Is("MCLQ")) // Water levels for this map chunk. { mapChunk.Liquid = new MCLQ(); // I dunno. MCLQ header size lies. If MCSE is immidiately following, there's no water. tempHeader = new BlizChunkHeader(bin.ReadChars(4), 0); tempHeader.Flip(); if (tempHeader.Is("MCSE")) { mapChunk.Liquid.waterLevel = float.NaN; } else { // After reading water, we stop. I do NOT like this solution, but // 1) We don't know much about MCLQ chunks. // 2) The size field lies, saying its always 0. (no idea why) // 3) I can't kludge this with a size fix, BECAUSE THE LENGTH VARIES?! Its in the area of 0x320, or 0x31F or 0x31E. // 4) So stopping after this chunk is the best we can do; this is what wowmapview does in any case. pos = lastpos; ms.Seek(-4, SeekOrigin.Current); // Go back! Re-read the last 4 bytes as its actually a float not char[4]. mapChunk.Liquid.waterLevel = bin.ReadSingle(); } continue; } if (tempHeader.Is("MCSE")) // Sound emitters. { // Not needed. continue; } // If we're still down here, we got a problem throw new Exception(String.Format("ADTFile: Woah. Got a header of Sub-{0}. Don't know how to deal with this, bailing out.", tempHeader.ToString())); } }
private void parseFile() { FileStream ms = adtStream; if (ms == null) { throw new Exception("Stream null!"); } BinaryReader bin = new BinaryReader(ms); BlizChunkHeader tempHeader; long pos = 0; // Read bytes from the stream until we run out while (pos < ms.Length) { // Advance to the next Chunk ms.Position = pos; // Read in Chunk Header Name tempHeader = new BlizChunkHeader(bin.ReadChars(4), bin.ReadUInt32()); tempHeader.Flip(); // Set pos to the location of the next Chunk pos = ms.Position + tempHeader.Size; if (tempHeader.Is("MVER")) // ADT File Version { mver = new MVER(); mver.version = bin.ReadUInt32(); continue; } if (tempHeader.Is("MHDR")) // ADT File Header { mhdr = new MHDR(); mhdr.pad = bin.ReadUInt32(); mhdr.offsInfo = bin.ReadUInt32(); mhdr.offsTex = bin.ReadUInt32(); mhdr.offsModels = bin.ReadUInt32(); mhdr.offsModelsIds = bin.ReadUInt32(); mhdr.offsMapObejcts = bin.ReadUInt32(); mhdr.offsMapObejctsIds = bin.ReadUInt32(); mhdr.offsDoodsDef = bin.ReadUInt32(); mhdr.offsObjectsDef = bin.ReadUInt32(); mhdr.pad1 = bin.ReadUInt32(); mhdr.pad2 = bin.ReadUInt32(); mhdr.pad3 = bin.ReadUInt32(); mhdr.pad4 = bin.ReadUInt32(); mhdr.pad5 = bin.ReadUInt32(); mhdr.pad6 = bin.ReadUInt32(); mhdr.pad7 = bin.ReadUInt32(); continue; } if (tempHeader.Is("MCIN")) // Index for MCNK chunks. { if (tempHeader.Size != 256 * 16) throw new Exception("MCIN Chunk is short??"); mcin_array = new MCIN[256]; // Read in the 256 records for (int i = 0; i < 256; i++) { mcin_array[i].MCNK_offset = bin.ReadUInt32(); mcin_array[i].MCNK_size = bin.ReadUInt32(); mcin_array[i].flags = bin.ReadUInt32(); mcin_array[i].asyncID = bin.ReadUInt32(); } continue; } if (tempHeader.Is("MTEX")) // List of texture filenames used by the terrain in this map tile. { // Not needed. continue; } if (tempHeader.Is("MMDX")) // List of filenames for M2 models that appear in this map tile. { // Not needed. continue; } if (tempHeader.Is("MMID")) // Lists the relative offsets of string beginnings in the above MMDX chunk. { // Not needed. continue; } if (tempHeader.Is("MWMO")) // List of filenames for WMOs (world map objects) that appear in this map tile. { byte[] wmoFilesChunk = bin.ReadBytes((int)tempHeader.Size); wmoFiles = new List<String>(); StringBuilder str = new StringBuilder(); // Convert szString's to a List<String>. for (int i = 0; i < wmoFilesChunk.Length; i++) { if (wmoFilesChunk[i] == '\0') { if (str.Length > 1) wmoFiles.Add(str.ToString()); str = new StringBuilder(); } else str.Append((char)wmoFilesChunk[i]); } continue; } if (tempHeader.Is("MWID")) // Lists the relative offsets of string beginnings in the above MWWO chunk. { // Not needed. continue; } if (tempHeader.Is("MDDF")) // Placement information for doodads (M2 models). { uint num = tempHeader.Size / 32; doodadLocations = new MDDF[num]; for (int i = 0; i < num; i++) { doodadLocations[i].nameId = bin.ReadUInt32(); doodadLocations[i].uniqueId = bin.ReadUInt32(); doodadLocations[i].coord = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); } continue; } if (tempHeader.Is("MODF")) // Placement information for WMOs. { uint num = tempHeader.Size / 64; wmoLocations = new MODF[num]; for (int i = 0; i < num; i++) { wmoLocations[i].nameId = bin.ReadUInt32(); wmoLocations[i].uniqueId = bin.ReadUInt32(); wmoLocations[i].coord = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].orientation = new Vect3D(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].coord2 = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].coord3 = new Coordinate(bin.ReadSingle(), bin.ReadSingle(), bin.ReadSingle()); wmoLocations[i].flags = bin.ReadUInt32(); wmoLocations[i].doodadSet = bin.ReadUInt16(); wmoLocations[i].nameSet = bin.ReadUInt16(); } continue; } if (tempHeader.Is("MCNK")) // || tempHeader.Is("MCVT") || tempHeader.Is("MCNR") || tempHeader.Is("MCLY") || tempHeader.Is("MCRF") || tempHeader.Is("MCSH") || tempHeader.Is("MCAL") || tempHeader.Is("MCLQ") || tempHeader.Is("MCSE")) { // Skip these. They are read in afterwards. continue; } // If we're still down here, we got a problem throw new Exception(String.Format("ADTFile: Woah. Got a header of {0}. Don't know how to deal with this, bailing out.", tempHeader.ToString())); } // Read in Map Chunks mapChunkTable = new MCNK[16][]; for (int i = 0; i < 16; i++) { mapChunkTable[i] = new MCNK[16]; for (int j = 0; j < 16; j++) { int index = i * 16 + j; Log.WriteLine(LogType.Terrain, "Parsing MCNK Chunk #{0} [{1}, {2}]", index, i, j); mapChunkTable[i][j] = parseMapChunk(ms, mcin_array[index].MCNK_offset, mcin_array[index].MCNK_size); } } }