Code:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
namespace WoWWorldReader
{
public class ChunkStream : Stream
{
private readonly long _start;
private readonly long _length;
private long _position;
private readonly Stream _parent;
public Stream Parent
{
get { return _parent; }
}
public string Identifier { get; private set; }
public ChunkStream(Stream parent)
{
var reader = new BinaryReader(parent);
lock (parent)
{
_parent = parent;
// read in header
var id = reader.ReadBytes(4);
Identifier = "" + (char)id[3] + (char)id[2] + (char)id[1] + (char)id[0];
_length = reader.ReadUInt32();
_start = _parent.Position;
}
}
public ChunkStream Ascend()
{
return _parent as ChunkStream;
}
public ChunkStream Descend()
{
lock (_parent)
{
var pos = _start;
_parent.Position = pos;
return pos + 8 > _parent.Length ? null : new ChunkStream(_parent);
}
}
public ChunkStream Next()
{
lock (_parent)
{
var pos = _start + _length;
_parent.Position = pos;
return pos + 8 > _parent.Length ? null : new ChunkStream(_parent);
}
}
#region unsupported
public override void Flush()
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
#endregion
public override int Read(byte[] buffer, int offset, int count)
{
lock (_parent)
{
//var oldPos = _parent.Position;
try
{
_parent.Position = _position + _start;
return _parent.Read(buffer, offset, count);
}
finally
{
_position = _parent.Position - _start;
//_parent.Position = oldPos + (_parent.Position - _position);
}
}
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return _length; }
}
public override long Position
{
get { return _position; }
set
{
if (value > _length)
throw new ArgumentOutOfRangeException();
_position = value;
lock (_parent)
_parent.Position = _start + _position;
}
}
}
public static class Extensions
{
public static T ReadStruct<T>(this Stream s) where T : struct
{
var sz = Marshal.SizeOf(typeof(T));
var buf = new byte[sz];
s.Read(buf, 0, sz);
var handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
var t = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return t;
}
}
[StructLayout(LayoutKind.Sequential, Size=4)]
public struct MHDR
{
public uint Flags;
public uint mcin;
public uint mtex;
public uint mmdx;
public uint mmid;
public uint mwmo;
public uint mwid;
public uint mddf;
public uint modf;
public uint mfbo; // this is only set if flags & mhdr_MFBO.
public uint mh2o;
public uint mtfx;
}
[StructLayout(LayoutKind.Sequential, Size = 4)]
public struct Vec3F
{
public float X, Y, Z;
}
[StructLayout(LayoutKind.Sequential, Size = 4)]
public struct MCNK
{
public uint flags;
public uint IndexX;
public uint IndexY;
public uint nLayers; // maximum 4
public uint nDoodadRefs;
public uint ofsHeight;
public uint ofsNormal;
public uint ofsLayer;
public uint ofsRefs;
public uint ofsAlpha;
public uint sizeAlpha;
public uint ofsShadow; // only with flags&0x1
public uint sizeShadow;
public uint areaid;
public uint nMapObjRefs;
public uint holes;
public uint reallyLowQualityTextureingMap0;
public uint reallyLowQualityTextureingMap1;
public uint reallyLowQualityTextureingMap2;
public uint reallyLowQualityTextureingMap3;
public uint predTex; // 03-29-2005 By ObscuR; TODO: Investigate
public uint noEffectDoodad; // 03-29-2005 By ObscuR; TODO: Investigate
public uint ofsSndEmitters;
public uint nSndEmitters; //will be set to 0 in the client if ofsSndEmitters doesn't point to MCSE!
public uint ofsLiquid;
public uint sizeLiquid; // 8 when not used; only read if >8.
public Vec3F position;
public uint ofsMCCV; // only with flags&0x40, had UINT32 textureId; in ObscuR's structure.
public uint ofsMCLV; // introduced in Cataclysm
}
public class MapParser
{
public const int Size = 16 * 17;
public float[,] Data = new float[Size, Size];
public float MaxV { get; private set; }
public float MinV { get; private set; }
public MapParser(Stream adt)
{
MaxV = Single.MinValue;
MinV = Single.MaxValue;
var chunk = new ChunkStream(adt);
if (chunk.Identifier != "MVER")
throw new Exception();
chunk = chunk.Next(); // skip MVER
if (chunk.Identifier != "MHDR")
throw new Exception();
var hdr = chunk.ReadStruct<MHDR>();
while (chunk != null)
{
if (chunk.Identifier == "MCNK")
ParseMapPiece(chunk);
else
Console.WriteLine(chunk.Identifier);
chunk = chunk.Next();
}
}
private void ParseMapPiece(ChunkStream s)
{
var hdr = s.ReadStruct<MCNK>();
var ofs = hdr.ofsHeight - 8;
var z = hdr.position.Z;
s.Position = ofs;
var mcvt = new ChunkStream(s);
var r = new BinaryReader(mcvt);
var xoff = hdr.IndexX * 17;
var yoff = hdr.IndexY * 17;
// read the data points
var y = 0;
while (true)
{
for (var x = 0; x < 9; x++)
{
var v = r.ReadSingle() + z;
if (v > MaxV)
MaxV = v;
if (v < MinV)
MinV = v;
Data[yoff + y, xoff + x * 2] = v;
}
y++;
if (y == 17)
break;
for (var x = 0; x < 8; x++)
{
var v = r.ReadSingle() + z;
if (v > MaxV)
MaxV = v;
if (v < MinV)
MinV = v;
Data[yoff + y, xoff + x * 2 + 1] = v;
}
y++;
}
// interpolate mid values
// (might use a different scheme here..)
for (y = 0; y < 17; y += 2)
{
for (var x = 0; x < 8; x++)
{
var pl = Data[yoff + y, xoff + x * 2];
var pr = Data[yoff + y, xoff + x * 2 + 2];
Data[yoff + y, xoff + x * 2 + 1] = (pl + pr) * 0.5f;
pl = Data[yoff + x * 2, xoff + y];
pr = Data[yoff + x * 2 + 2, xoff + y];
Data[yoff + x * 2 + 1, xoff + y] = (pl + pr) * 0.5f;
}
}
}
public void Dump(string filename)
{
var d = MaxV - MinV;
var scale = 255.0f / d;
var bmp = new Bitmap(Size, Size);
for (var y = 0; y < Size; y++)
{
for (var x = 0; x < Size; x++)
{
var v = (int)((Data[y, x] - MinV) * scale);
if (v < 0)
v = 0;
if (v > 255)
v = 255;
bmp.SetPixel(x, y, Color.FromArgb(v, v, v));
}
}
bmp.Save(filename);
}
}
class Program
{
public static void Dump(float [,] data, int size, float min, float max, int xoff, int yoff, string filename)
{
var d = max - min;
var scale = 255.0f / d;
var bmp = new Bitmap(size, size);
for (var y = 0; y < size; y++)
{
for (var x = 0; x < size; x++)
{
var v = (int)((data[y + yoff, x + xoff] - min) * scale);
if (v < 0)
v = 0;
if (v > 255)
v = 255;
bmp.SetPixel(x, y, Color.FromArgb(v, v, v));
}
}
bmp.Save(filename);
bmp.Dispose();
}
static void Main(string[] args)
{
const int megaSize = 64 * MapParser.Size;
var megamap = new float[megaSize, megaSize];
const string path = @"G:\World of Warcraft\Work\world\maps\Azeroth";
const string prefix = "Azeroth_";
var maxV = Single.MinValue;
var minV = Single.MaxValue;
for (var y = 0; y < 64; y++)
{
for (var x = 0; x < 64; x++)
{
var filename = path + "\\" + prefix + y + "_" + x + ".adt";
if (!File.Exists(filename))
continue;
Console.WriteLine(filename);
var f = new FileStream(filename, FileMode.Open, FileAccess.Read);
var map = new MapParser(f);
// copy to megamap
var yoff = MapParser.Size * x;
var xoff = MapParser.Size * y;
for (var yy = 0; yy < MapParser.Size; yy++)
for (var xx = 0; xx < MapParser.Size; xx++)
megamap[yoff + yy, xoff + xx] = map.Data[yy, xx];
if (map.MaxV > maxV)
maxV = map.MaxV;
if (map.MinV < minV)
minV = map.MinV;
}
}
Console.WriteLine("Dumping...");
var fs = new FileStream("map.dat", FileMode.Create, FileAccess.Write);
var w = new BinaryWriter(fs);
w.Write(megaSize);
w.Write(megaSize);
w.Write(minV);
w.Write(maxV);
for (var y = 0; y < megaSize; y++)
for (var x = 0; x < megaSize; x++)
w.Write(megamap[y, x]);
w.Close();
/*
for (var y = 0; y < 4; y++)
for (var x = 0; x < 4; x++)
Dump(megamap, 16 * MapParser.Size, minV, maxV,
16 * MapParser.Size * x, 16 * MapParser.Size * y,
"Azeroth_" + y + "_" + x + ".bmp");
*/
Console.WriteLine("Press any key");
Console.ReadKey();
}
}
}