ADT Format ποΈ
ADT (Area Data Terrain) files contain the terrain and object information for a single map tile in World of Warcraft. The world is divided into a grid of 64x64 maps, with each map consisting of 16x16 chunks. Each ADT file represents one map tile, which is 533.33333 yards (1600 feet) on each side.
Overview
- Extension:
.adt - Purpose: Terrain geometry, textures, water, and object placement
- Grid Size: 16x16 chunks per ADT, 64x64 ADTs per continent
- Chunk Size: 33.33333 yards (100 feet) per chunk
- Format: Chunk-based binary format
File Types
Since Cataclysm (4.x), ADT data is split across multiple files. Confirmed in Cataclysm Preservation Project TrinityCore 4.3.4:
| File Pattern | Description | Content | Chunks Present |
|---|---|---|---|
MapName_XX_YY.adt | Root file | Terrain structure and header | MVER, MHDR, MCNK, MFBO |
MapName_XX_YY_tex0.adt | Texture file | Texture data and amplitude | MVER, MTEX, MAMP, MCNK, MTXP (MoP+) |
MapName_XX_YY_tex1.adt | Texture file | Additional texture layers | MVER, MTEX, MAMP, MCNK, MTXP (MoP+) |
MapName_XX_YY_obj0.adt | Object file | M2 and WMO placement data | MVER, MMDX, MMID, MWMO, MWID, MDDF, MODF, MCNK |
MapName_XX_YY_obj1.adt | Object file | Additional object data | MVER, MMDX, MMID, MWMO, MWID, MDDF, MODF, MCNK |
MapName_XX_YY_lod.adt | LOD file | Level of detail information | (Not analyzed) |
Where XX and YY are the tile coordinates (0-63).
Coordinate System
World of Warcraft uses a right-handed coordinate system:
- X-axis: North (positive) to South (negative)
- Y-axis: West (positive) to East (negative)
- Z-axis: Up (positive) to Down (negative)
Map coordinates:
- One ADT tile = 533.33333 yards = 1600 feet
- One chunk = 33.33333 yards = 100 feet
- One unit = 0.33333 yards = 1 foot
The world origin (0,0) is at the center of map (32,32).
Chunk Structure
All chunks follow this format:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct ChunkHeader {
/// Four-character chunk identifier
magic: [u8; 4],
/// Size of chunk data (excluding this header)
size: u32,
}
}
Main ADT File Structure
MVER - Version
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MVERChunk {
header: ChunkHeader, // Magic: "MVER"
version: u32, // ADT version number (consistently 18)
}
}
Version history (based on analysis of original MPQ files):
- 18 = All versions analyzed (1.12.1 through 5.4.8) use version 18
- Note: Earlier documentation suggesting version 17 for TBC appears to be incorrect
MHDR - Header
Contains offsets to all other chunks in the file:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MHDRChunk {
header: ChunkHeader, // Magic: "MHDR"
/// All offsets are relative to start of file (0 if chunk not present)
flags: u32, // Always 0 in ADT files
mcin_offset: u32, // Offset to MCIN chunk
mtex_offset: u32, // Offset to MTEX chunk
mmdx_offset: u32, // Offset to MMDX chunk
mmid_offset: u32, // Offset to MMID chunk
mwmo_offset: u32, // Offset to MWMO chunk
mwid_offset: u32, // Offset to MWID chunk
mddf_offset: u32, // Offset to MDDF chunk
modf_offset: u32, // Offset to MODF chunk
mfbo_offset: u32, // Offset to MFBO chunk (TBC+)
mh2o_offset: u32, // Offset to MH2O chunk (TBC+)
mtxf_offset: u32, // Offset to MTXF chunk (TBC+)
reserved: [u32; 4], // Padding
}
}
MCIN - Chunk Information
Contains offsets and sizes for all 256 (16x16) map chunks:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCINEntry {
/// Absolute offset to MCNK chunk
mcnk_offset: u32,
/// Size of MCNK chunk including header
size: u32,
/// Flags (usually 0)
flags: u32,
/// Async object id (0 if none)
async_id: u32,
}
#[repr(C, packed)]
struct MCINChunk {
header: ChunkHeader, // Magic: "MCIN"
entries: [MCINEntry; 256], // 16x16 grid
}
}
MTEX - Texture List
Contains null-terminated texture filenames:
#![allow(unused)]
fn main() {
struct MTEXChunk {
header: ChunkHeader, // Magic: "MTEX"
/// Concatenated null-terminated strings
/// Example: "Tileset\\Elwynn\\ElwynnGrass01.blp\0"
texture_names: Vec<u8>,
}
}
MMDX/MMID - Model List
- MMDX: Contains null-terminated M2 model filenames
- MMID: Maps model instances to their filename offsets in MMDX
MWMO/MWID - WMO List
- MWMO: Contains null-terminated WMO filenames
- MWID: Maps WMO instances to their filename offsets in MWMO
MDDF - Model (Doodad) Placement
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MDDFEntry {
/// Index into MMID
mmid_entry: u32,
/// Unique instance ID
unique_id: u32,
/// Position in world coordinates
position: [f32; 3], // x, y, z
/// Rotation in degrees
rotation: [f32; 3], // x, y, z
/// Scale factor (1024 = 1.0)
scale: u16,
/// Flags
flags: u16,
}
// MDDF flags
const MDDF_BIODOME: u16 = 0x0001; // Use for biodome in WMO
const MDDF_SHRUBBERY: u16 = 0x0002; // Shrubbery scale factor
}
MODF - WMO Placement
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MODFEntry {
/// Index into MWID
mwid_entry: u32,
/// Unique instance ID
unique_id: u32,
/// Position in world coordinates
position: [f32; 3],
/// Rotation in degrees
rotation: [f32; 3],
/// Bounding box
extent_lower: [f32; 3],
extent_upper: [f32; 3],
/// Flags (same as MDDF)
flags: u16,
/// Doodad set index
doodad_set: u16,
/// Name set index
name_set: u16,
/// Scale (Legion+)
scale: u16,
}
}
MH2O - Water Information (WotLK+)
First appeared in Wrath of the Lich King (3.3.5a). Contains water levels and types, replacing the legacy MCLQ system.
Structural Analysis: Complex variable-size chunks with 16Γ16 grid structure. Validated across multiple TrinityCore versions:
- TrinityCore 3.3.5a (detailed implementation)
- SkyFire 5.4.8 (simplified MoP implementation)
Both versions confirm the 16Γ16 grid structure with variable liquid instances and attributes.
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MH2OHeader {
/// Offset to MH2OInformation for this chunk
offset_information: u32,
/// Number of water layers
layer_count: u32,
/// Offset to render mask
offset_render_mask: u32,
}
#[repr(C, packed)]
struct MH2OInformation {
/// Water type (ocean, lake, etc)
liquid_type: u16,
/// Flags
flags: u16,
/// Height levels
height_level1: f32,
height_level2: f32,
/// Position within chunk
x_offset: u8,
y_offset: u8,
width: u8,
height: u8,
/// Offset to vertex data
offset_vertex_data: u32,
}
// MH2O Flags
const MH2O_OCEAN: u16 = 0x0002;
const MH2O_DEEP: u16 = 0x0004;
const MH2O_FISHABLE: u16 = 0x0008;
}
MFBO - Flight Bounds (TBC+)
Contains flight ceiling information. First appeared in The Burning Crusade (2.4.3):
Structural Analysis: All analyzed MFBO chunks are consistently 36 bytes. Validated against TrinityCore implementation which defines the structure as two planes with 9 int16 coordinates each.
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MFBOPlane {
/// 9 coordinate values defining a plane
coords: [i16; 9], // 18 bytes
}
#[repr(C, packed)]
struct MFBOChunk {
header: ChunkHeader, // Magic: "MFBO"
/// Maximum flight bounds plane
max: MFBOPlane, // 18 bytes
/// Minimum flight bounds plane
min: MFBOPlane, // 18 bytes
}
}
Total data size: 36 bytes (18 + 18), confirmed by empirical analysis and validated across multiple TrinityCore versions:
- TrinityCore 3.3.5a (WotLK)
- Cataclysm Preservation Project TrinityCore 4.3.4
Usage: Both TrinityCore versions extract these as int16 flight_box_max[3][3] and int16 flight_box_min[3][3] arrays for flight ceiling calculations.
Validation: Structure confirmed across multiple production WoW server emulator implementations spanning TBC through Cataclysm.
Note: SkyFire 5.4.8 (MoP) does not implement MFBO chunks in their map extractor, suggesting flight bounds may be handled differently in later expansions or not required for server-side processing.
MAMP - Amplitude Map (Cataclysm+)
First appeared in Cataclysm (4.3.4). Contains amplitude or deformation data for terrain:
Structural Analysis: Always 4 bytes, appears to be flags or simple values. Structure based on empirical analysis only.
Server Implementation Status:
- Not implemented in Cataclysm Preservation Project TrinityCore 4.3.4
- Not implemented in SkyFire 5.4.8 (MoP)
- Suggests this chunk is texture-file specific or client-rendering optimization only
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MAMPChunk {
header: ChunkHeader, // Magic: "MAMP"
/// Amplitude value or flags
/// Common values: 0x00000000, 0x00000001
value: u32,
}
}
MTXP - Texture Parameters (MoP+)
First appeared in Mists of Pandaria (5.4.8). Contains texture parameters for advanced material properties:
Structural Analysis: Variable size, contains arrays of 16-byte entries. Average size 154 bytes. Structure based on empirical analysis only.
Server Implementation Status:
- Not implemented in SkyFire 5.4.8 (MoP)
- Suggests this chunk is client-rendering specific for advanced texture material properties
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MTXPEntry {
/// Texture parameter data (16 bytes per entry)
/// Structure appears to be 4 u32 values
params: [u32; 4],
}
#[repr(C, packed)]
struct MTXPChunk {
header: ChunkHeader, // Magic: "MTXP"
entries: Vec<MTXPEntry>, // Variable count
}
}
Note: The exact meaning of the parameter values requires further analysis.
MTXF - Texture Flags (Legacy)
Rarely found in analyzed files. May be deprecated in favor of MTXP:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MTXFChunk {
header: ChunkHeader, // Magic: "MTXF"
flags: Vec<u32>, // One per texture
}
// Texture flags
const MTXF_DISABLE_ALPHA: u32 = 0x0001;
const MTXF_USE_CUBE_MAP: u32 = 0x0002;
}
Map Chunk (MCNK) Structure
Each MCNK chunk represents a 33.33x33.33 yard square of terrain:
MCNK Header
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCNKHeader {
/// Flags for this chunk
flags: u32,
/// Index of this chunk
index_x: u32,
index_y: u32,
/// Number of texture layers (max 4)
n_layers: u32,
/// Number of doodad references
n_doodad_refs: u32,
/// High-res holes (8x8 grid)
holes_high_res: u64,
/// Offsets to sub-chunks (relative to MCNK data start)
offset_mcly: u32, // Texture layers
offset_mcrf: u32, // References
offset_mcal: u32, // Alpha maps
size_mcal: u32, // Alpha map size
offset_mcsh: u32, // Shadow map
size_mcsh: u32, // Shadow map size
/// Area ID
area_id: u32,
/// Number of WMO references
n_map_obj_refs: u32,
/// Low-res holes (4x4 grid)
holes_low_res: u16,
/// Unknown
unknown_0x3C: u16,
/// Low-res texture map (8x8 grid)
low_res_texture_map: [u16; 8],
/// No effect doodad
no_effect_doodad: u32,
/// Sound emitters
offset_mcse: u32,
n_sound_emitters: u32,
/// Liquid
offset_mclq: u32,
size_mclq: u32,
/// Position of chunk
position: [f32; 3],
/// Vertex colors
offset_mccv: u32,
/// Vertex lighting (unused)
offset_mclv: u32,
/// Unused
unused: u32,
}
// MCNK flags
const MCNK_HAS_MCSH: u32 = 0x0001; // Has shadow map
const MCNK_IMPASS: u32 = 0x0002; // Impassable
const MCNK_LQ_RIVER: u32 = 0x0004; // River
const MCNK_LQ_OCEAN: u32 = 0x0008; // Ocean
const MCNK_LQ_MAGMA: u32 = 0x0010; // Magma
const MCNK_LQ_SLIME: u32 = 0x0020; // Slime
const MCNK_HAS_MCCV: u32 = 0x0040; // Has vertex colors
const MCNK_DO_NOT_FIX_ALPHA_MAP: u32 = 0x8000; // Don't fix alpha map
const MCNK_HIGH_RES_HOLES: u32 = 0x10000; // Use high-res holes
}
MCVT - Vertex Heights
Contains 145 height values (9x9 outer + 8x8 inner vertices):
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCVTChunk {
header: ChunkHeader, // Magic: "MCVT"
/// Height values relative to MCNK position
/// Order: 9x9 outer vertices, then 8x8 inner vertices
heights: [f32; 145], // 9*9 + 8*8 = 145
}
}
Vertex layout:
Outer vertices (9x9): Grid corners
Inner vertices (8x8): Center of each quad
MCCV - Vertex Colors
Optional vertex colors for terrain shading:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCCVColor {
r: u8,
g: u8,
b: u8,
a: u8, // Usually 127 or 255
}
#[repr(C, packed)]
struct MCCVChunk {
header: ChunkHeader, // Magic: "MCCV"
colors: [MCCVColor; 145], // Same layout as MCVT
}
}
MCNR - Normals
Normal vectors for lighting:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCNREntry {
normal: [i8; 3], // X, Y, Z components (-127 to 127)
}
#[repr(C, packed)]
struct MCNRChunk {
header: ChunkHeader, // Magic: "MCNR"
normals: [MCNREntry; 145], // Same layout as MCVT
padding: [u8; 13], // Unknown padding
}
}
MCLY - Texture Layers
Defines up to 4 texture layers:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCLYEntry {
/// Texture index in MTEX
texture_id: u32,
/// Flags for this layer
flags: u32,
/// Offset to alpha map in MCAL
offset_in_mcal: u32,
/// Effect ID (usually 0)
effect_id: u32,
}
// MCLY flags
const MCLY_ANIMATION_ROTATION: u32 = 0x040; // 45Β° rotation
const MCLY_ANIMATION_SPEED_FAST: u32 = 0x080; // Faster animation
const MCLY_ANIMATION_SPEED_FASTER: u32 = 0x100; // Even faster
const MCLY_ANIMATION_SPEED_FASTEST: u32 = 0x200; // Fastest
const MCLY_ANIMATE: u32 = 0x400; // Enable animation
const MCLY_USE_ALPHA_MAP: u32 = 0x800; // Use alpha map
const MCLY_USE_CUBE_MAP_REFLECTION: u32 = 0x1000; // Skybox reflection
}
MCRF - References
Lists doodad and object references for this chunk:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCRFChunk {
header: ChunkHeader, // Magic: "MCRF"
/// Indices into MDDF and MODF
doodad_refs: Vec<u32>,
object_refs: Vec<u32>,
}
}
MCSH - Shadow Map
64x64 bit shadow map:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCSHChunk {
header: ChunkHeader, // Magic: "MCSH"
/// 1 bit per terrain quad
shadow_map: [u8; 512], // 64 * 64 / 8
}
}
MCAL - Alpha Maps
Alpha maps control texture blending. The first texture layer has no alpha map (itβs the base):
#![allow(unused)]
fn main() {
enum AlphaMapFormat {
/// Uncompressed 64x64 (4096 bytes)
Uncompressed4096,
/// Uncompressed 32x32 (2048 bytes)
Uncompressed2048,
/// Compressed (variable size)
Compressed,
}
}
Compression algorithm:
#![allow(unused)]
fn main() {
fn decompress_alpha_map(compressed: &[u8], output: &mut [u8]) {
let mut input_pos = 0;
let mut output_pos = 0;
while output_pos < output.len() && input_pos < compressed.len() {
let count = compressed[input_pos] & 0x7F;
let fill = (compressed[input_pos] & 0x80) != 0;
input_pos += 1;
for _ in 0..count {
if fill {
// Repeat single value
output[output_pos] = compressed[input_pos];
output_pos += 1;
} else {
// Copy values
output[output_pos] = compressed[input_pos];
input_pos += 1;
output_pos += 1;
}
}
if fill {
input_pos += 1;
}
}
}
}
MCLQ - Liquid (Legacy, removed in WotLK)
Deprecated: Removed in Wrath of the Lich King (3.3.5a+), replaced by MH2O chunk.
Pre-WotLK liquid data:
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCLQHeader {
min_height: f32,
max_height: f32,
}
#[repr(C, packed)]
struct MCLQVertex {
/// Water height
height: f32,
/// Flow data
flow: [u8; 4],
}
#[repr(C, packed)]
struct MCLQChunk {
header: ChunkHeader, // Magic: "MCLQ"
liquid_header: MCLQHeader,
/// 9x9 vertex heights
vertices: [MCLQVertex; 81],
/// 8x8 flags
flags: [u8; 64],
}
// MCLQ flags
const MCLQ_HIDDEN: u8 = 0x01;
const MCLQ_FISHABLE: u8 = 0x02;
const MCLQ_SHARED: u8 = 0x04;
}
MCSE - Sound Emitters
#![allow(unused)]
fn main() {
#[repr(C, packed)]
struct MCSEEntry {
/// Sound ID
sound_id: u32,
/// Position relative to chunk
position: [f32; 3],
/// Size/radius
size: [f32; 3],
}
}
Height Calculation Algorithm
To calculate terrain height at any position:
#![allow(unused)]
fn main() {
/// Get interpolated height at position within chunk
pub fn get_height_at_position(
mcvt: &MCVTChunk,
x: f32, // 0.0 to 33.33333
y: f32, // 0.0 to 33.33333
) -> f32 {
// Convert to cell coordinates
let cell_x = (x / (33.33333 / 8.0)).min(7.999);
let cell_y = (y / (33.33333 / 8.0)).min(7.999);
let ix = cell_x as usize;
let iy = cell_y as usize;
let fx = cell_x - ix as f32;
let fy = cell_y - iy as f32;
// Get the four corner heights
let h00 = mcvt.get_outer(ix, iy);
let h01 = mcvt.get_outer(ix, iy + 1);
let h10 = mcvt.get_outer(ix + 1, iy);
let h11 = mcvt.get_outer(ix + 1, iy + 1);
// Get center height
let hc = mcvt.get_inner(ix, iy);
// Determine which triangle and interpolate
if fx + fy < 1.0 {
// Lower triangle
if fx < fy {
// Left triangle
h00 * (1.0 - fx - fy) + h01 * fy + hc * fx
} else {
// Bottom triangle
h00 * (1.0 - fx - fy) + h10 * fx + hc * fy
}
} else {
// Upper triangle
if fx > fy {
// Right triangle
h11 * (fx + fy - 1.0) + h10 * (1.0 - fy) + hc * (1.0 - fx)
} else {
// Top triangle
h11 * (fx + fy - 1.0) + h01 * (1.0 - fx) + hc * (1.0 - fy)
}
}
}
}
Coordinate Transformations
#![allow(unused)]
fn main() {
/// World position to ADT tile coordinates
pub fn world_to_adt(world_x: f32, world_y: f32) -> (i32, i32) {
// World origin is at center of map (32, 32)
let adt_x = 32 - (world_x / 533.33333);
let adt_y = 32 - (world_y / 533.33333);
(adt_x as i32, adt_y as i32)
}
/// ADT tile to world coordinates (tile corner)
pub fn adt_to_world(adt_x: i32, adt_y: i32) -> (f32, f32) {
let world_x = (32 - adt_x) as f32 * 533.33333;
let world_y = (32 - adt_y) as f32 * 533.33333;
(world_x, world_y)
}
/// Position within ADT to chunk index
pub fn position_to_chunk(x: f32, y: f32) -> (usize, usize) {
let chunk_x = (x / 33.33333) as usize;
let chunk_y = (y / 33.33333) as usize;
(chunk_x.min(15), chunk_y.min(15))
}
}
Usage Examples
Loading and Parsing ADT (Current Implementation)
Implementation Status: β οΈ Basic Parsing Only - Advanced terrain processing not implemented
#![allow(unused)]
fn main() {
use wow_adt::{Adt, AdtVersion};
// β
Load ADT file with correct API
let adt = Adt::from_path("World/Maps/Azeroth/Azeroth_32_48.adt")?;
// β
Check detected version
println!("Detected version: {}", adt.version());
// β
Access terrain chunk data
println!("MCNK chunks: {}", adt.mcnk_chunks.len());
if adt.mfbo.is_some() {
println!("Has flight boundaries (TBC+)");
}
if adt.mh2o.is_some() {
println!("Has water data (WotLK+)");
}
// β Height interpolation algorithms not implemented
// β export_heightmap() method does not exist
// β High-level texture/doodad access APIs not implemented
// β Advanced terrain processing not available
}
Texture Blending (Format Specification)
Implementation Status: β οΈ Format Specification Only - Mathematical blending approach documented
ADT files support up to 4 texture layers per chunk with alpha map blending. The official format specification describes a complex blending algorithm involving:
- Layer Weight Calculation - Based on alpha map values
- Height-based Scaling - Terrain height influences blending
- Vertex Color Integration - MCCV chunk data affects final output
// From Legion terrain shader (format specification):
// layer_pct = layer_pct / vec4(sum(layer_pct));
//
// Current implementation provides access to raw texture and alpha data
// through parsed MCNK chunks, but blending logic must be implemented separately
Liquid Rendering
#![allow(unused)]
fn main() {
// MH2O water (WotLK+)
if let Some(water_chunk) = &chunk.water {
for layer in &water_chunk.layers {
let water_type = layer.liquid_type;
let height = layer.height_level1;
// Render water surface
render_water_layer(layer, water_type);
}
}
// Legacy MCLQ water
if let Some(liquid) = &chunk.liquid {
for x in 0..9 {
for y in 0..9 {
let vertex = &liquid.vertices[y * 9 + x];
let depth = vertex.height;
let flow = vertex.flow;
// Render water vertex
}
}
}
}
Implementation Notes
Reading ADT Files
#![allow(unused)]
fn main() {
pub struct ADTReader {
data: Vec<u8>,
position: usize,
}
impl ADTReader {
pub fn read_chunk(&mut self) -> Result<(ChunkHeader, &[u8]), ADTError> {
if self.position + 8 > self.data.len() {
return Err(ADTError::UnexpectedEof);
}
// Read header
let header = unsafe {
*(self.data.as_ptr().add(self.position) as *const ChunkHeader)
};
self.position += 8;
// Get chunk data
let chunk_size = header.size as usize;
if self.position + chunk_size > self.data.len() {
return Err(ADTError::InvalidChunkSize);
}
let chunk_data = &self.data[self.position..self.position + chunk_size];
self.position += chunk_size;
Ok((header, chunk_data))
}
}
}
Performance Optimizations
- Memory mapping: Use memory-mapped files for large ADT files
- Lazy loading: Only parse chunks when needed
- Caching: Cache frequently accessed data like height maps
- LOD: Use WDL files for distant terrain
- Frustum culling: Only render visible chunks
- Texture atlasing: Combine texture layers to reduce draw calls
Common Pitfalls
- Byte order: All values are little-endian
- Chunk alignment: Chunks are not always aligned to 4-byte boundaries
- String parsing: Strings in MTEX/MMDX/MWMO are null-terminated
- Coordinate systems: Y-axis is north/south (not up/down)
- Height interpolation: Must use the center vertices for proper interpolation
- Alpha map compression: Check MCLY flags to determine format
- Scale values: MDDF scale is 1024 = 1.0, not a float
Chunk Version Evolution
Based on analysis of original ADT files across World of Warcraft versions:
Core Chunks (1.12.1+)
These chunks are present in all analyzed versions:
- MVER - Version information
- MHDR - File header with offsets
- MCIN - Chunk index table (pre-Cataclysm) / distributed in split files (Cataclysm+)
- MTEX - Texture filename list
- MMDX - M2 model filename list
- MMID - M2 model indices
- MWMO - WMO filename list
- MWID - WMO indices
- MDDF - M2 model placement data
- MODF - WMO placement data
- MCNK - Map chunk data (terrain)
Legacy Chunks (Removed)
- MCLQ - Legacy liquid data (1.12.1 - 2.4.3, replaced by MH2O in WotLK)
The Burning Crusade Additions (2.4.3+)
- MFBO - Flight bounds data (appears in ~34% of TBC ADT files)
Wrath of the Lich King Additions (3.3.5a+)
- MH2O - New water system (replaces legacy MCLQ)
Cataclysm Additions (4.3.4+)
- MAMP - Amplitude map data for terrain deformation (4 bytes, flag/value)
- MTXF - Enhanced texture flags (evolution from earlier texture systems)
Mists of Pandaria Additions (5.4.8+)
- MTXP - Texture parameters for advanced material properties (16-byte entries, variable count)
Structural Changes Across Versions
Analysis of chunk sizes and structures reveals several evolution patterns:
MHDR Structure Evolution
- Pre-Cataclysm: All files have MCIN offsets (centralized chunk index)
- Cataclysm+: MCIN offsets removed from many files (distributed across split files)
- MFBO Support: 90% of TBC files, 75% of WotLK files, 67% of Cataclysm+ files
- MH2O Support: 0% in TBC, 63% in WotLK, variable in later versions
Chunk Size Patterns
- MVER: Consistently 4 bytes across all versions
- MHDR: Consistently 64 bytes across all versions
- MCIN: Consistently 4096 bytes (256 entries Γ 16 bytes) when present
- MCNK: Highly variable sizes, trend toward larger chunks in later versions
- 1.12.1: avg 4905 bytes
- 2.4.3: avg 2892 bytes
- 3.3.5a: avg 5428 bytes
- 4.3.4+: avg 383-940 bytes (split file architecture)
Split File Architecture Impact (Cataclysm+)
- Root ADT files become much smaller (mainly MHDR + MCNK structure)
- Texture files contain MAMP, MTEX, MTXP chunks
- Object files contain model/WMO placement data
- Average MCNK size drops significantly due to data redistribution
Analysis Results
- β WoW 1.12.1: 11 chunk types, monolithic files (avg 4905 byte MCNK)
- β WoW 2.4.3: 12 chunk types (+MFBO 36 bytes), MFBO in 90% of files
- β WoW 3.3.5a: 13 chunk types (+MH2O variable size), MH2O in 63% of files
- β WoW 4.3.4: 14 chunk types (+MAMP 4 bytes), split file architecture introduced
- β WoW 5.4.8: 14 chunk types (+MTXP 16-byte entries), MTXP in texture files
Server Implementation Validation
Our analysis has been cross-validated against production WoW server emulator implementations:
Validation Sources
- TrinityCore 3.3.5a (WotLK) - Reference implementation
- Cataclysm Preservation Project TrinityCore 4.3.4 - Cataclysm support
- SkyFire 5.4.8 - Mists of Pandaria support
Implementation Confidence Levels
| Chunk | Confidence | Server Validation | Usage |
|---|---|---|---|
| MFBO | Very High | TrinityCore 3.3.5a + Cataclysm 4.3.4 | Flight bounds for gameplay |
| MH2O | Very High | TrinityCore 3.3.5a + SkyFire 5.4.8 | Water collision/rendering |
| Split Files | High | Cataclysm 4.3.4 + SkyFire 5.4.8 | Data organization |
| MAMP | Medium | None (empirical only) | Client rendering optimization |
| MTXP | Medium | None (empirical only) | Client texture enhancement |
Key Insight: Server-validated chunks are essential for gameplay mechanics, while empirical-only chunks appear to be client-side rendering optimizations.
Version History
Based on analysis of original MPQ archives:
| MVER Version | Game Versions | Major Changes |
|---|---|---|
| 18 | All analyzed versions (1.12.1 - 5.4.8) | Consistent version across all expansions |
Format Evolution by Game Version
| Game Version | MVER | Changes |
|---|---|---|
| 1.12.1 (Vanilla) | 18 | Core ADT format established |
| 2.4.3 (The Burning Crusade) | 18 | Added MFBO chunk |
| 3.3.5a (Wrath of the Lich King) | 18 | Added MH2O water, replaced MCLQ |
| 4.3.4 (Cataclysm) | 18 | Split files (_tex0,_obj0, _obj1), added MAMP |
| 5.4.8 (Mists of Pandaria) | 18 | Added MTXP chunk |
References
- WoWDev Wiki - ADT Format
- Map Coordinates System
- Trinity Core Map Extractor
- libwarcraft ADT Implementation
See Also
- Rendering ADT Terrain Guide
- WDT Format - World tables that reference ADTs
- WDL Format - Low-detail world data
- Coordinate System - Detailed coordinate information