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.

  1. 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.)

  1. 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):

  1. 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.

MCWORLD Property Dumper
Drag and drop .MCWORLD file here

  

(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.)

  1. 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')
  1. 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.)

  1. 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');
  1. 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.)