Download Manifest Format
The Download manifest manages content streaming and prioritization during game installation and updates. It defines which files are essential for gameplay and their download order.
Overview
The Download manifest assigns a priority to each file entry so the client can download essential content first (enabling play before full download) and stream remaining content in the background. Tag bitmaps enable per-platform and per-locale filtering. File sizes in entries support progress estimation.
File Structure
The Download manifest is BLTE-encoded and contains:
[BLTE Container]
[Header]
[File Entries]
[Tag Section]
Binary Format
Header
struct DownloadHeader {
char magic[2]; // "DL" (0x44, 0x4C)
uint8_t version; // Version (1, 2, or 3)
uint8_t ekey_size; // Encoding key size in bytes (16)
uint8_t has_checksum; // Checksum presence flag
uint32_t entry_count; // Number of entries (big-endian)
uint16_t tag_count; // Number of tags (big-endian)
// Version 2+ fields (header grows to 12 bytes)
uint8_t flag_size; // Number of flag bytes per entry (max 4)
// Version 3+ fields (header grows to 16 bytes)
int8_t base_priority; // Base priority offset
uint8_t _reserved[3]; // Reserved (agent does not validate these)
};
Entry Order
The download manifest stores data in this order:
- Header
- All file entries
- All tags (appear after entries)
File Entry
struct DownloadEntry {
uint8_t ekey[16]; // Encoding key (variable size from header)
uint8_t file_size[5]; // 40-bit file size (big-endian)
int8_t priority; // Download priority (adjusted by base_priority)
// Optional fields
uint32_t checksum; // If has_checksum is true (big-endian)
uint8_t flags[N]; // If version >= 2, N = flag_size
};
Tag Entry
Tags appear after all file entries in the manifest:
struct DownloadTag {
char name[]; // Null-terminated tag name
uint16_t type; // Tag type (big-endian)
uint8_t bitmap[]; // Bit mask ((entry_count + 7) / 8 bytes)
};
Each bit in the bitmap corresponds to a file entry index. If bit N is set, entry N has this tag.
Priority System
Priority Calculation
In version 3+, priorities are adjusted:
final_priority = entry.priority - header.base_priority
Priority Levels
Lower values indicate higher priority:
| Priority | Category | Typical Content |
|---|---|---|
| < 0 | Critical | Must download before game starts |
| 0 | Essential | Required for basic gameplay |
| 1-2 | High | Important for full experience |
| 3-5 | Normal | Standard content |
| > 5 | Low | Optional/deferred content |
Priority-Based Download
#![allow(unused)]
fn main() {
fn get_download_order(entries: &[DownloadFileEntry]) -> Vec<&DownloadFileEntry>
{
let mut sorted = entries.iter().collect::<Vec<_>>();
sorted.sort_by_key(|e| (e.priority, e.file_size));
sorted
}
}
Streaming Strategy
Minimum Playable Set
Calculate minimum download for gameplay:
#![allow(unused)]
fn main() {
fn get_minimum_download(
download_file: &DownloadFile
) -> (Vec<DownloadFileEntry>, u64) {
let essential: Vec<_> = download_file.entries
.iter()
.filter(|e| e.priority <= 1) // Essential + Critical
.cloned()
.collect();
let total_size = essential.iter()
.map(|e| e.file_size as u64)
.sum();
(essential, total_size)
}
}
Progressive Download
Download in priority order while game runs:
#![allow(unused)]
fn main() {
struct DownloadManager {
queue: VecDeque<DownloadItem>,
active: Vec<DownloadTask>,
completed: HashSet<[u8; 16]>,
}
impl DownloadManager {
pub fn start_progressive_download(&mut self) {
// Sort by priority
self.queue.sort_by_key(|item| item.priority);
// Start downloading highest priority
while self.active.len() < MAX_CONCURRENT {
if let Some(item) = self.queue.pop_front() {
self.start_download(item);
}
}
}
}
}
Tag-Based Filtering
Platform-Specific Downloads
Tags are stored separately from entries. Each tag contains a bitmap indicating which entries it applies to. To filter by tag, find the tag by name and check its bitmap:
#![allow(unused)]
fn main() {
fn filter_by_tag<'a>(
manifest: &'a DownloadManifest,
tag_name: &str,
) -> Vec<(usize, &'a DownloadFileEntry)> {
let tag = match manifest.tags.iter().find(|t| t.name == tag_name) {
Some(t) => t,
None => return Vec::new(),
};
manifest.entries.iter().enumerate()
.filter(|(index, _)| tag.has_file(*index))
.collect()
}
}
Language Packs
#![allow(unused)]
fn main() {
fn get_language_pack<'a>(
manifest: &'a DownloadManifest,
locale: &str,
) -> Vec<&'a DownloadFileEntry> {
let tag = match manifest.tags.iter().find(|t| t.name == locale) {
Some(t) => t,
None => return Vec::new(),
};
manifest.entries.iter().enumerate()
.filter(|(index, _)| tag.has_file(*index))
.map(|(_, entry)| entry)
.collect()
}
}
Download Optimization
Bandwidth Management
#![allow(unused)]
fn main() {
struct BandwidthManager {
max_bandwidth: u64, // Bytes per second
current_usage: u64,
priority_limits: Vec<u64>, // Per-priority limits
}
impl BandwidthManager {
pub fn allocate_bandwidth(&mut self, priority: u8) -> u64 {
let priority_limit = self.priority_limits[priority as usize];
let available = self.max_bandwidth - self.current_usage;
std::cmp::min(priority_limit, available)
}
}
}
Chunk-Based Downloads
For large files, download in chunks:
#![allow(unused)]
fn main() {
struct ChunkedDownload {
encoding_key: [u8; 16],
total_size: u64,
chunk_size: u64,
chunks_completed: Vec<bool>,
}
impl ChunkedDownload {
pub fn get_next_chunk(&self) -> Option<(u64, u64)> {
for (idx, &completed) in self.chunks_completed.iter().enumerate() {
if !completed {
let offset = idx as u64 * self.chunk_size;
let size = std::cmp::min(
self.chunk_size,
self.total_size - offset
);
return Some((offset, size));
}
}
None
}
}
}
Progress Tracking
Download Statistics
#![allow(unused)]
fn main() {
struct DownloadProgress {
total_files: u32,
completed_files: u32,
total_bytes: u64,
downloaded_bytes: u64,
current_speed: f64,
eta_seconds: u64,
}
impl DownloadProgress {
pub fn update(&mut self, bytes_downloaded: u64) {
self.downloaded_bytes += bytes_downloaded;
self.current_speed = self.calculate_speed();
self.eta_seconds = self.calculate_eta();
}
pub fn completion_percentage(&self) -> f32 {
(self.downloaded_bytes as f32 / self.total_bytes as f32) * 100.0
}
}
}
Implementation Example
#![allow(unused)]
fn main() {
struct DownloadFile {
header: DownloadHeader,
priorities: Vec<DownloadPriority>,
tags: Vec<DownloadTag>,
entries: Vec<DownloadFileEntry>,
}
impl DownloadFile {
pub fn get_download_plan(
&self,
tags: &[String],
max_priority: u8
) -> DownloadPlan {
let tag_mask = self.build_tag_mask(tags);
let files: Vec<_> = self.entries
.iter()
.filter(|e| e.priority <= max_priority)
.filter(|e| (e.tag_mask & tag_mask) != 0)
.cloned()
.collect();
let total_size = files.iter()
.map(|f| f.file_size as u64)
.sum();
DownloadPlan {
files,
total_size,
estimated_time: self.estimate_time(total_size),
}
}
}
}
On-Demand Streaming
Asset Request Handling
#![allow(unused)]
fn main() {
struct OnDemandManager {
download_file: DownloadFile,
cache: LruCache<[u8; 16], Vec<u8>>,
}
impl OnDemandManager {
pub async fn get_asset(&mut self, encoding_key: &[u8; 16]) -> Result<Vec<u8>> {
// Check cache first
if let Some(data) = self.cache.get(encoding_key) {
return Ok(data.clone());
}
// Find in download manifest
if let Some(entry) = self.find_entry(encoding_key) {
// Download with high priority
let data = self.download_immediate(entry).await?;
self.cache.put(*encoding_key, data.clone());
return Ok(data);
}
Err("Asset not found")
}
}
}
Verification
Checksum Validation
#![allow(unused)]
fn main() {
fn verify_download(
data: &[u8],
entry: &DownloadFileEntry
) -> bool {
if entry.checksum != [0; 16] {
let computed = md5::compute(data);
computed.0 == entry.checksum
} else {
true // No checksum to verify
}
}
}
Common Issues
- Priority conflicts: Multiple systems requesting same file
- Bandwidth throttling: ISP or network limitations
- Incomplete downloads: Handle partial file recovery
- Cache corruption: Verify cached files periodically
- Tag mismatches: Platform detection errors
Special Features
Differential Downloads
Download only changed portions:
#![allow(unused)]
fn main() {
struct DifferentialDownload {
old_version: [u8; 16],
new_version: [u8; 16],
patches: Vec<PatchInfo>,
}
}
Peer-to-Peer Support
Share downloaded content locally:
#![allow(unused)]
fn main() {
struct P2PManager {
local_peers: Vec<PeerInfo>,
shared_files: HashSet<[u8; 16]>,
}
}
Parser Implementation Status
Python Parser (cascette-py)
Status: Complete
Capabilities:
-
Version 1-3 header parsing with DL magic detection
-
40-bit big-endian compressed size parsing
-
Priority system with base priority adjustment (v3)
-
Tag parsing with bitmap support (tags stored after all entries)
-
Platform/architecture tag identification with type classification
-
Sample entry display (first 100 entries)
-
Format evolution tracking across versions
-
BLTE decompression for compressed manifests
-
Correct entry/tag ordering (entries first, then tags)
Verified Against:
-
WoW 11.0.5.57689 (2.4M entries, 28 tags)
-
WoW 9.0.2.37176 (Shadowlands)
-
WoW 7.3.5.25848 (Legion)
-
WoW Classic builds
Known Issues: None
See https://github.com/wowemulation-dev/cascette-py for the Python implementation.
Version History
The Download manifest format has evolved through 3 versions:
Version 1 (Initial)
- Header Size: 11 bytes
- Features: Basic download prioritization with encoding keys, file sizes, optional checksums
- Fields: magic, version, ekey_size, has_checksum, entry_count, tag_count
Version 2 (Flag Support)
- Header Size: 12 bytes
- Added Features: Entry-level flags for additional metadata
- New Fields: flag_size (number of flag bytes per entry, max 4)
- Use Cases: Platform-specific flags, content type markers
Version 3 (Priority System)
- Header Size: 16 bytes
- Added Features: Base priority adjustment for dynamic prioritization
- New Fields: base_priority (signed adjustment), reserved (3 bytes)
- Priority Calculation:
final_priority = entry.priority - header.base_priority
Version Detection
Parsers detect version by reading the version field at offset 2 in the header. All versions use the same “DL” magic bytes and big-endian encoding.
Implementation Status
- cascette-formats: Full support for versions 1-3 with version-aware parsing
- cascette-py: Complete parsing for versions 1-3 with validation
References
-
See Install Manifest for installation management
-
See Encoding Documentation for key resolution
-
See CDN Architecture for download sources
-
See Format Transitions for version evolution timeline