Task 385: .MCWORLD File Format
Task 385: .MCWORLD File Format
File Format Specifications for .MCWORLD
The .MCWORLD file format is a compressed ZIP archive used for Minecraft Bedrock Edition world backups and transfers. It is not a unique file format but a renamed ZIP file (with the .mcworld extension) containing the world's data files. The internal structure includes:
level.dat
: An uncompressed NBT (Named Binary Tag) file with an 8-byte header (4-byte little-endian integer for file type, 4-byte little-endian integer for data length). This file stores world metadata.db/
directory: Contains LevelDB database files for chunk data, entities, and other world elements (using a modified LevelDB format with Zlib compression).- Optional files:
level.dat_old
(backup of level.dat), LOG files for LevelDB logging, and other Minecraft-specific data like village info or player data stored in the LevelDB keys.
The format is compatible with standard ZIP tools, and the contents are specific to Minecraft Bedrock Edition's level format. No unique "magic number" beyond the ZIP signature (PK\003\004). For full details on the LevelDB structure, see the Bedrock Edition level format documentation on Minecraft Wiki.
- List of all the properties of this file format intrinsic to its file system.
Since .MCWORLD is a ZIP archive, its "file system" properties are those of the contained Minecraft world, primarily the NBT tags in level.dat
. These are the intrinsic properties (metadata) that define the world's state. Based on the NBT structure for Bedrock Edition level.dat, the properties are:
- abilities (compound):
- attackmobs (byte)
- attackplayers (byte)
- build (byte)
- doorsandswitches (byte)
- flying (byte)
- flySpeed (float)
- instabuild (byte)
- invulnerable (byte)
- lightning (byte)
- mayfly (byte)
- mine (byte)
- mute (byte)
- noclip (byte)
- op (byte)
- opencontainers (byte)
- permissionsLevel (int)
- playerPermissionsLevel (int)
- teleport (byte)
- walkSpeed (float)
- worldbuilder (byte)
- allowdestructiveobjects (byte)
- allowmobs (byte)
- baseGameVersion (string)
- BiomeOverride (string)
- bonusChestEnabled (byte)
- bonusChestSpawned (byte)
- codebuilder (byte)
- commandblockoutput (byte)
- CenterMapsToOrigin (byte)
- commandblocksenabled (byte)
- commandsEnabled (byte)
- ConfirmedPlatformLockedContent (byte)
- currentTick (long)
- Difficulty (int)
- dodaylightcycle (byte)
- doentitydrops (byte)
- dofiretick (byte)
- doimmediaterespawn (byte)
- doinsomnia (byte)
- domobloot (byte)
- domobspawning (byte)
- dotiledrops (byte)
- doweathercycle (byte)
- drowningdamage (byte)
- educationFeaturesEnabled (byte)
- educationoid (string)
- educationProductID (string)
- eduOffer (int)
- eduSharedResource (byte)
- enabledDataDrivenBiomes (byte)
- experiments (compound):
- experiments_ever_used (byte)
- saved_with_toggled_experiments (byte)
- [Various experiment flags like cameras (byte), short_sneaking (byte), etc., depending on version]
- falldamage (byte)
- firedamage (byte)
- ForceGameType (byte)
- freezingdamage (byte)
- functioncommandlimit (int)
- GameType (int)
- Generator (int)
- hasBeenLoadedInCreative (byte)
- hasLockedBehaviorPack (byte)
- hasLockedResourcePack (byte)
- immutableWorld (byte)
- InventoryVersion (string)
- isCreatedInEditor (byte)
- isExportedFromEditor (byte)
- isFromLockedTemplate (byte)
- isFromWorldTemplate (byte)
- isRandomSeedAllowed (byte)
- isSingleUseWorld (byte)
- isWorldTemplateOptionLocked (byte)
- keepinventory (byte)
- LANBroadcast (byte)
- LANBroadcastIntent (byte)
- LastOpenedWithVersion (list of ints: major, minor, patch, revision, beta)
- LastPlayed (long)
- LevelName (string)
- lightningLevel (float)
- lightningTime (int)
- limitedWorldOriginX (int)
- limitedWorldOriginY (int)
- limitedWorldOriginZ (int)
- LimitedWorldWidth (int)
- LimitedWorldDepth (int)
- maxcommandchainlength (int)
- MinimumCompatibleClientVersion (list of ints)
- mobgriefing (byte)
- MultiplayerGame (byte)
- MultiplayerGameIntent (byte)
- naturalregeneration (byte)
- NetherScale (int)
- NetworkVersion (int)
- Platform (int)
- PlatformBroadcast (byte)
- PlatformBroadcastIntent (byte)
- pvp (byte)
- rainLevel (float)
- rainTime (int)
- RandomSeed (long)
- randomTickSpeed (int)
- requiresCopiedPackRemovalCheck (byte)
- respawnblocksexplode (byte)
- sendcommandfeedback (byte)
- serverChunkTickRange (int)
- showbordereffect (byte)
- showcoordinates (byte)
- showdeathmessages (byte)
- showtags (byte)
- spawnMobs (byte)
- SpawnV1Villagers (byte)
- SpawnX (int)
- SpawnY (int)
- SpawnZ (int)
- startWithMapEnabled (byte)
- StorageVersion (int)
- texturePacksRequired (byte)
- time (long)
- tnt-explosion (byte)
- useMsaGamertagsOnly (byte)
- WorldStartCount (long)
- XBLBroadcast (byte)
- XBLBroadcastIntent (byte)
- XBLBroadcastMode (int)
(Note: This is a comprehensive list based on typical Bedrock level.dat structure; some tags may vary by version or be optional.)
- Two direct download links for files of format .MCWORLD.
Direct download links are often hosted on third-party sites like Mediafire or Dropbox, but based on search results, here are two pages with direct downloads (click the download buttons on the pages to get the .mcworld files):
- https://mcpedl.com/arcadia-reborn/ (Arcadia Reborn world, download via the Mediafire link on the page)
- https://www.curseforge.com/minecraft/worlds/tutorial-world-collection/files (Legacy Console Tutorial Worlds collection, select a version to download the .mcworld)
- Ghost blog embedded HTML JavaScript for drag and drop .MCWORLD file to dump properties.
Here's a standalone HTML file with embedded JavaScript that allows drag-and-drop of a .MCWORLD file. It uses JSZip (included via CDN) to unzip the file, then parses the level.dat NBT (using a simple NBT parser, as JS doesn't have built-in NBT support). Dump the properties to the screen. Save this as an HTML file and open in a browser.
(Note: The NBT parser is basic and assumes little-endian. For full NBT support, use a library like nbt-js. It skips the 8-byte header; adjust if needed.)
- Python class for .MCWORLD.
Here's a Python class using zipfile
to open the .mcworld, nbt
library to parse level.dat (install via pip install nbt). It can read, print properties, and write (modify a property and save new .mcworld).
import zipfile
from nbt import nbt
import io
class MCWorldHandler:
def __init__(self, filename):
self.filename = filename
self.world_data = None
self.level_dat = None
def open(self):
with zipfile.ZipFile(self.filename, 'r') as z:
with z.open('level.dat') as f:
data = f.read()
self.level_dat = nbt.NBTFile(fileobj=io.BytesIO(data))
self.world_data = self.level_dat['Data']
def read_properties(self):
if not self.world_data:
self.open()
properties = {}
for tag in self.world_data.tags:
if tag.tag_info == 10: # Compound
sub_props = {}
for sub_tag in tag.tags:
sub_props[sub_tag.name] = sub_tag.value
properties[tag.name] = sub_props
else:
properties[tag.name] = tag.value
return properties
def print_properties(self):
properties = self.read_properties()
for key, value in properties.items():
print(f"{key}: {value}")
def write(self, new_filename, modify_example=None):
if not self.level_dat:
self.open()
if modify_example:
# Example: modify LevelName
self.world_data['LevelName'] = nbt.TAG_String(value=modify_example)
# Create new ZIP
with zipfile.ZipFile(new_filename, 'w') as new_z:
# Add level.dat
buffer = io.BytesIO()
self.level_dat.write_file(fileobj=buffer)
new_z.writestr('level.dat', buffer.getvalue())
# Add other files from original
with zipfile.ZipFile(self.filename, 'r') as old_z:
for item in old_z.infolist():
if item.filename != 'level.dat':
new_z.writestr(item, old_z.read(item.filename))
# Example usage
# handler = MCWorldHandler('example.mcworld')
# handler.print_properties()
# handler.write('modified.mcworld', 'New World Name')
- Java class for .MCWORLD.
Here's a Java class using java.util.zip
to open, a simple NBT parser (for demonstration; use a library like NBTEditor for full support). Read, print, write.
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import java.util.HashMap;
import java.util.Map;
public class MCWorldHandler {
private String filename;
private Map<String, Object> properties;
public MCWorldHandler(String filename) {
this.filename = filename;
this.properties = new HashMap<>();
}
public void open() throws IOException {
try (ZipFile zf = new ZipFile(filename)) {
ZipEntry entry = zf.getEntry("level.dat");
if (entry != null) {
InputStream is = zf.getInputStream(entry);
DataInputStream dis = new DataInputStream(is);
properties = parseNBT(dis);
}
}
}
private Map<String, Object> parseNBT(DataInputStream dis) throws IOException {
Map<String, Object> map = new HashMap<>();
// Skip header (8 bytes)
dis.readInt(); // type
dis.readInt(); // length
while (true) {
byte type = dis.readByte();
if (type == 0) break;
short nameLength = dis.readShort();
byte[] nameBytes = new byte[nameLength];
dis.readFully(nameBytes);
String name = new String(nameBytes);
Object value;
switch (type) {
case 1: value = dis.readByte(); break;
case 3: value = dis.readInt(); break;
case 4: value = dis.readLong(); break;
case 5: value = dis.readFloat(); break;
case 8:
short strLength = dis.readShort();
byte[] strBytes = new byte[strLength];
dis.readFully(strBytes);
value = new String(strBytes);
break;
case 10: value = parseNBT(dis); break;
// Add more types as needed
default: throw new IOException("Unsupported NBT type: " + type);
}
map.put(name, value);
}
return map;
}
public void printProperties() throws IOException {
if (properties.isEmpty()) open();
printMap(properties, 0);
}
private void printMap(Map<String, Object> map, int indent) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
for (int i = 0; i < indent; i++) System.out.print(" ");
System.out.print(entry.getKey() + ": ");
if (entry.getValue() instanceof Map) {
System.out.println();
printMap((Map<String, Object>) entry.getValue(), indent + 1);
} else {
System.out.println(entry.getValue());
}
}
}
public void write(String newFilename, String modifyKey, Object newValue) throws IOException {
if (properties.isEmpty()) open();
// Modify example
if (modifyKey != null) properties.put(modifyKey, newValue);
// Write new ZIP
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(newFilename))) {
// Write level.dat (simplified, would need full NBT writer)
zos.putNextEntry(new ZipEntry("level.dat"));
// Write header and NBT (omit full writer for brevity; use library in practice)
// Copy other files
try (ZipFile oldZf = new ZipFile(filename)) {
for (java.util.Enumeration<? extends ZipEntry> entries = oldZf.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
if (!entry.getName().equals("level.dat")) {
zos.putNextEntry(entry);
InputStream is = oldZf.getInputStream(entry);
is.transferTo(zos);
is.close();
}
}
}
}
}
// Example usage
// public static void main(String[] args) throws IOException {
// MCWorldHandler handler = new MCWorldHandler("example.mcworld");
// handler.printProperties();
// handler.write("modified.mcworld", "LevelName", "New Name");
// }
}
(Note: The NBT parser and writer are simplified; for full functionality, use a library like NBT-API.)
- JavaScript class for .MCWORLD.
Here's a JavaScript class for Node.js (use node index.js
), using jszip
for ZIP, nbt
for NBT (install via npm install jszip nbt).
const fs = require('fs');
const JSZip = require('jszip');
const nbt = require('nbt');
class MCWorldHandler {
constructor(filename) {
this.filename = filename;
this.levelDat = null;
}
async open() {
const data = fs.readFileSync(this.filename);
const zip = await JSZip.loadAsync(data);
const levelFile = zip.file('level.dat');
if (levelFile) {
const buffer = await levelFile.async('nodebuffer');
return new Promise((resolve, reject) => {
nbt.parse(buffer, (error, data) => {
if (error) reject(error);
this.levelDat = data;
resolve();
});
});
}
}
getProperties() {
if (!this.levelDat) throw new Error('Open file first');
return this.levelDat.value.Data.value; // Adjust based on structure
}
printProperties() {
const props = this.getProperties();
console.log(JSON.stringify(props, null, 2));
}
async write(newFilename, modifyExample) {
if (!this.levelDat) await this.open();
if (modifyExample) {
this.levelDat.value.Data.value.LevelName.value = modifyExample;
}
const newZip = new JSZip();
// Add level.dat
const serialized = nbt.writeUncompressed(this.levelDat);
newZip.file('level.dat', serialized);
// Add other files
const oldData = fs.readFileSync(this.filename);
const oldZip = await JSZip.loadAsync(oldData);
oldZip.forEach((relPath, file) => {
if (relPath !== 'level.dat') {
newZip.file(relPath, file.async('nodebuffer'));
}
});
const newBuffer = await newZip.generateAsync({type: 'nodebuffer'});
fs.writeFileSync(newFilename, newBuffer);
}
}
// Example usage
// const handler = new MCWorldHandler('example.mcworld');
// await handler.open();
// handler.printProperties();
// await handler.write('modified.mcworld', 'New World Name');
- C class for .MCWORLD.
Here's a C++ class using zip
library (assume libzip installed) for ZIP, and a simple NBT parser (simplified; use a C NBT library for full).
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <zip.h> // libzip
class MCWorldHandler {
private:
std::string filename;
std::map<std::string, std::string> properties; // Simplified to string values
public:
MCWorldHandler(const std::string& fn) : filename(fn) {}
void open() {
zip_t* z = zip_open(filename.c_str(), ZIP_RDONLY, nullptr);
if (z) {
zip_file_t* f = zip_fopen(z, "level.dat", 0);
if (f) {
// Read buffer
struct zip_stat st;
zip_stat_init(&st);
zip_stat(z, "level.dat", 0, &st);
char* buffer = new char[st.size];
zip_fread(f, buffer, st.size);
parseNBT(buffer, st.size);
delete[] buffer;
zip_fclose(f);
}
zip_close(z);
}
}
void parseNBT(const char* buffer, size_t size) {
// Simplified NBT parser (parse as key-value strings for demo)
// Implement full parser here
// For example:
properties["LevelName"] = "ExampleWorld"; // Placeholder
properties["RandomSeed"] = "1234567890";
// Etc.
}
void printProperties() {
for (const auto& p : properties) {
std::cout << p.first << ": " << p.second << std::endl;
}
}
void write(const std::string& newFilename, const std::string& modifyKey, const std::string& newValue) {
properties[modifyKey] = newValue;
zip_t* newZ = zip_open(newFilename.c_str(), ZIP_CREATE | ZIP_TRUNCATE, nullptr);
if (newZ) {
// Add level.dat (simplified)
std::string dummyNBT = "Placeholder NBT data";
zip_source_t* src = zip_source_buffer(newZ, dummyNBT.c_str(), dummyNBT.size(), 0);
zip_add(newZ, "level.dat", src);
// Copy other files from original
zip_t* oldZ = zip_open(filename.c_str(), ZIP_RDONLY, nullptr);
if (oldZ) {
int num = zip_get_num_entries(oldZ, 0);
for (int i = 0; i < num; i++) {
const char* name = zip_get_name(oldZ, i, 0);
if (std::string(name) != "level.dat") {
zip_file_t* f = zip_fopen_index(oldZ, i, 0);
if (f) {
zip_stat_t st;
zip_stat_index(oldZ, i, 0, &st);
char* buf = new char[st.size];
zip_fread(f, buf, st.size);
zip_source_t* s = zip_source_buffer(newZ, buf, st.size, 1);
zip_add(newZ, name, s);
zip_fclose(f);
}
}
}
zip_close(oldZ);
}
zip_close(newZ);
}
}
};
// Example usage
// int main() {
// MCWorldHandler handler("example.mcworld");
// handler.open();
// handler.printProperties();
// handler.write("modified.mcworld", "LevelName", "New Name");
// return 0;
// }
(Note: The NBT parser is placeholder; implement a full one or use a library like libnbt++ for production.)