Task 381: .MCPACK File Format
Task 381: .MCPACK File Format
1. List of Properties for the .MCPACK File Format
The .MCPACK file format is essentially a ZIP archive (with the .mcpack extension) used for Minecraft Bedrock Edition add-ons, such as resource packs, behavior packs, skin packs, or world templates. It does not have unique file system-level properties beyond those of a standard ZIP file (e.g., no custom magic number or header beyond ZIP's PK signature). The "properties" intrinsic to the format are derived from the required manifest.json
file inside the ZIP archive, which defines the pack's metadata. These properties are parsed from the JSON structure.
Based on the official documentation, here is a comprehensive list of all properties (including subproperties) from manifest.json
. Properties are hierarchical, with types, requirements, and descriptions. Some are specific to pack types (e.g., resource vs. behavior packs). I've included all possible fields, noting optionality and pack-type differences:
- format_version (Number or String, Required): The syntax version of the manifest (e.g., 2 for most packs, 1 for skin packs, or a semver string like "3.0.0-preview" for version 3 in preview).
- header (Object, Required): Public-facing pack information.
- name (String, Required): The display name of the pack.
- description (String, Required): A short description of the pack (1-2 lines recommended).
- uuid (String, Required): Unique identifier in UUID format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
- version (Array of Numbers [major, minor, patch] or SemVer String, Required): The pack's version for comparison during imports.
- min_engine_version (Array of Numbers or SemVer String, Required for resource and behavior packs): Minimum Minecraft engine version required for compatibility.
- pack_scope (String, Optional, Resource packs only): Scope of application ("global", "world", or "any"; defaults to "any").
- base_game_version (Array of Numbers or SemVer String, Optional, World templates only): Base Minecraft version for the world template.
- lock_template_options (Boolean, Required for world templates): Locks world options from modification.
- allow_random_seed (Boolean, Optional, World templates only): Allows random seed generation and changes before world creation.
- modules (Array of Objects, Required): Defines the pack's content modules (at least one required).
- Each module object:
- type (String, Required): Module type ("data" for behavior packs, "resources" for resource packs, "skin_pack" for skin packs, "world_template" for worlds, "script" for scripts).
- uuid (String, Required): Unique module identifier (UUID format, different from header UUID).
- version (Array of Numbers or SemVer String, Required): Module version.
- description (String, Optional): Developer-facing description (not shown in-game).
- language (String, Optional, For "script" type only): Scripting language (currently only "javascript").
- dependencies (Array of Objects, Optional): Lists required dependent packs.
- Each dependency object:
- uuid (String, Required): UUID of the dependent pack.
- version (Array of Numbers or SemVer String, Required): Required version of the dependent pack.
- module_name (String, Optional, For built-in script modules): Name of the module (e.g., "@minecraft/server").
- capabilities (Array of Strings, Optional): Enables experimental or optional features (e.g., ["chemistry", "editorExtension", "experimental_custom_ui", "raytracing", "pbr"]). Each string represents an enabled capability.
- metadata (Object, Optional): Additional pack info (not always present, but common).
- authors (Array of Strings, Optional): List of pack authors.
- license (String, Optional): License information.
- url (String, Optional): URL for more pack info or source.
These properties are extracted and decoded from the JSON. The ZIP may contain other files (e.g., textures, behaviors), but they are not "properties" of the format itself—only manifest.json defines the core metadata.
2. Two Direct Download Links for .MCPACK Files
- https://files2.cdn9mc.com/index.php?act=dl&id=1655776413 (Bare Bones Texture Pack for Minecraft Bedrock)
- https://www.curseforge.com/api/v1/mods/507188/files/6738862/download (Faithful Texture Pack for Minecraft Bedrock)
These links point directly to .mcpack files (may involve server-side redirects but initiate downloads without intermediate pages).
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .MCPACK Parser
Here's a standalone HTML file with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML renderer). It allows drag-and-drop of a .MCPACK file, parses it as a ZIP using JSZip (loaded from CDN), extracts manifest.json
, and dumps all properties to the screen in a readable format. Include this as raw HTML in your blog post.
This code reads the file, parses the ZIP, extracts and parses manifest.json
, and recursively dumps all properties (keys and values) to the screen.
4. Python Class for .MCPACK Handling
import zipfile
import json
import os
import shutil
class McPack:
def __init__(self, filepath):
self.filepath = filepath
self.data = None
self.other_files = {} # To store other files for writing
self._read()
def _read(self):
with zipfile.ZipFile(self.filepath, 'r') as z:
for name in z.namelist():
if name == 'manifest.json':
with z.open(name) as f:
self.data = json.load(f)
else:
self.other_files[name] = z.read(name)
def print_properties(self):
if self.data:
self._dump_properties(self.data)
else:
print("No manifest.json found.")
def _dump_properties(self, data, prefix=''):
for key, value in data.items():
if isinstance(value, dict):
print(f"{prefix}{key}:")
self._dump_properties(value, prefix + ' ')
elif isinstance(value, list):
print(f"{prefix}{key}:")
for i, item in enumerate(value):
print(f"{prefix} [{i}]:")
if isinstance(item, dict):
self._dump_properties(item, prefix + ' ')
else:
print(f"{prefix} {item}")
else:
print(f"{prefix}{key}: {value}")
def write(self, new_filepath):
with zipfile.ZipFile(new_filepath, 'w', zipfile.ZIP_DEFLATED) as z:
# Write other files
for name, content in self.other_files.items():
z.writestr(name, content)
# Write updated manifest
z.writestr('manifest.json', json.dumps(self.data, indent=4))
# Example usage:
# pack = McPack('example.mcpack')
# pack.print_properties()
# pack.data['header']['name'] = 'Updated Name' # Modify
# pack.write('updated.mcpack')
This class opens the .MCPACK (ZIP), decodes manifest.json
, stores properties in self.data
, prints them recursively, and allows writing a modified version by saving a new ZIP with updated manifest and original files.
5. Java Class for .MCPACK Handling
import java.io.*;
import java.util.*;
import java.util.zip.*;
import org.json.*;
public class McPack {
private String filepath;
private JSONObject data;
private Map<String, byte[]> otherFiles = new HashMap<>();
public McPack(String filepath) {
this.filepath = filepath;
read();
}
private void read() {
try (ZipFile zf = new ZipFile(filepath)) {
Enumeration<? extends ZipEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.equals("manifest.json")) {
try (InputStream is = zf.getInputStream(entry)) {
data = new JSONObject(new JSONTokener(is));
}
} else {
try (InputStream is = zf.getInputStream(entry);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
otherFiles.put(name, baos.toByteArray());
}
}
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
public void printProperties() {
if (data != null) {
dumpProperties(data, "");
} else {
System.out.println("No manifest.json found.");
}
}
private void dumpProperties(JSONObject obj, String prefix) {
for (String key : obj.keySet()) {
Object value = obj.get(key);
if (value instanceof JSONObject) {
System.out.println(prefix + key + ":");
dumpProperties((JSONObject) value, prefix + " ");
} else if (value instanceof JSONArray) {
System.out.println(prefix + key + ":");
JSONArray arr = (JSONArray) value;
for (int i = 0; i < arr.length(); i++) {
System.out.println(prefix + " [" + i + "]:");
Object item = arr.get(i);
if (item instanceof JSONObject) {
dumpProperties((JSONObject) item, prefix + " ");
} else {
System.out.println(prefix + " " + item);
}
}
} else {
System.out.println(prefix + key + ": " + value);
}
}
}
public void write(String newFilepath) {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(newFilepath))) {
// Write other files
for (Map.Entry<String, byte[]> entry : otherFiles.entrySet()) {
ZipEntry ze = new ZipEntry(entry.getKey());
zos.putNextEntry(ze);
zos.write(entry.getValue());
zos.closeEntry();
}
// Write updated manifest
ZipEntry ze = new ZipEntry("manifest.json");
zos.putNextEntry(ze);
zos.write(data.toString(4).getBytes());
zos.closeEntry();
} catch (IOException e) {
e.printStackTrace();
}
}
// Example usage:
// public static void main(String[] args) {
// McPack pack = new McPack("example.mcpack");
// pack.printProperties();
// pack.data.getJSONObject("header").put("name", "Updated Name");
// pack.write("updated.mcpack");
// }
}
This Java class uses java.util.zip and org.json (assume included or add dependency) to read/decode the ZIP, parse JSON, print properties recursively, and write a modified ZIP.
6. JavaScript Class for .MCPACK Handling
const fs = require('fs'); // For Node.js
const JSZip = require('jszip'); // Assume npm install jszip
class McPack {
constructor(filepath) {
this.filepath = filepath;
this.data = null;
this.otherFiles = {};
this.read();
}
read() {
const buffer = fs.readFileSync(this.filepath);
JSZip.loadAsync(buffer).then((zip) => {
const promises = [];
zip.forEach((relPath, file) => {
if (relPath === 'manifest.json') {
promises.push(file.async('string').then((content) => {
this.data = JSON.parse(content);
}));
} else {
promises.push(file.async('nodebuffer').then((content) => {
this.otherFiles[relPath] = content;
}));
}
});
return Promise.all(promises);
}).catch((err) => {
console.error('Error reading MCPACK:', err);
});
}
printProperties() {
if (this.data) {
this.dumpProperties(this.data);
} else {
console.log('No manifest.json found.');
}
}
dumpProperties(data, prefix = '') {
for (const key in data) {
const value = data[key];
if (typeof value === 'object' && value !== null) {
if (Array.isArray(value)) {
console.log(`${prefix}${key}:`);
value.forEach((item, i) => {
console.log(`${prefix} [${i}]:`);
this.dumpProperties(item, prefix + ' ');
});
} else {
console.log(`${prefix}${key}:`);
this.dumpProperties(value, prefix + ' ');
}
} else {
console.log(`${prefix}${key}: ${value}`);
}
}
}
write(newFilepath) {
const zip = new JSZip();
for (const name in this.otherFiles) {
zip.file(name, this.otherFiles[name]);
}
zip.file('manifest.json', JSON.stringify(this.data, null, 4));
zip.generateNodeStream({type: 'nodebuffer', streamFiles: true})
.pipe(fs.createWriteStream(newFilepath))
.on('finish', () => console.log('Write complete.'));
}
}
// Example usage:
// const pack = new McPack('example.mcpack');
// pack.printProperties();
// pack.data.header.name = 'Updated Name';
// pack.write('updated.mcpack');
This JavaScript class (for Node.js) uses jszip to handle ZIP, parses JSON, prints properties recursively, and writes modified ZIP. (Assume npm install jszip
.)
7. C "Class" for .MCPACK Handling
C does not have classes, so I've implemented it as a struct with functions (using libzip for ZIP handling; assume installed via system package manager, e.g., apt install libzip-dev
). Include cJSON for JSON parsing (assume sourced from https://github.com/DaveGamble/cJSON). Compile with -lzip -lm
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zip.h>
#include "cJSON.h" // Assume cJSON.h and cJSON.c compiled in
typedef struct {
char* filepath;
cJSON* data;
// For other files: use a simple map (array of key-value for simplicity)
struct OtherFile {
char* name;
void* content;
size_t size;
} *other_files;
int other_count;
} McPack;
void mcpack_init(McPack* self, const char* filepath) {
self->filepath = strdup(filepath);
self->data = NULL;
self->other_files = NULL;
self->other_count = 0;
mcpack_read(self);
}
void mcpack_read(McPack* self) {
zip_t* za = zip_open(self->filepath, ZIP_RDONLY, NULL);
if (!za) {
fprintf(stderr, "Error opening ZIP\n");
return;
}
int num_entries = zip_get_num_entries(za, 0);
self->other_files = malloc(num_entries * sizeof(struct OtherFile));
for (int i = 0; i < num_entries; i++) {
const char* name = zip_get_name(za, i, 0);
if (strcmp(name, "manifest.json") == 0) {
zip_file_t* zf = zip_fopen_index(za, i, 0);
zip_stat_t sb;
zip_stat_index(za, i, 0, &sb);
char* buffer = malloc(sb.size + 1);
zip_fread(zf, buffer, sb.size);
buffer[sb.size] = '\0';
self->data = cJSON_Parse(buffer);
free(buffer);
zip_fclose(zf);
} else {
zip_file_t* zf = zip_fopen_index(za, i, 0);
zip_stat_t sb;
zip_stat_index(za, i, 0, &sb);
void* buffer = malloc(sb.size);
zip_fread(zf, buffer, sb.size);
self->other_files[self->other_count].name = strdup(name);
self->other_files[self->other_count].content = buffer;
self->other_files[self->other_count].size = sb.size;
self->other_count++;
zip_fclose(zf);
}
}
zip_close(za);
}
void mcpack_print_properties(McPack* self) {
if (self->data) {
dump_properties(self->data, "");
} else {
printf("No manifest.json found.\n");
}
}
void dump_properties(cJSON* item, const char* prefix) {
if (cJSON_IsObject(item)) {
cJSON* child = item->child;
while (child) {
printf("%s%s: ", prefix, child->string);
if (cJSON_IsObject(child) || cJSON_IsArray(child)) {
printf("\n");
char new_prefix[1024];
snprintf(new_prefix, sizeof(new_prefix), "%s ", prefix);
dump_properties(child, new_prefix);
} else if (cJSON_IsString(child)) {
printf("%s\n", child->valuestring);
} else if (cJSON_IsNumber(child)) {
printf("%f\n", child->valuedouble);
} else if (cJSON_IsBool(child)) {
printf("%s\n", child->valueint ? "true" : "false");
} else if (cJSON_IsNull(child)) {
printf("null\n");
}
child = child->next;
}
} else if (cJSON_IsArray(item)) {
int i = 0;
cJSON* child = item->child;
while (child) {
printf("%s[%d]: \n", prefix, i++);
char new_prefix[1024];
snprintf(new_prefix, sizeof(new_prefix), "%s ", prefix);
dump_properties(child, new_prefix);
child = child->next;
}
}
}
void mcpack_write(McPack* self, const char* new_filepath) {
zip_t* za = zip_open(new_filepath, ZIP_CREATE | ZIP_TRUNCATE, NULL);
if (!za) {
fprintf(stderr, "Error creating ZIP\n");
return;
}
// Write other files
for (int i = 0; i < self->other_count; i++) {
zip_source_t* src = zip_source_buffer(za, self->other_files[i].content, self->other_files[i].size, 0);
zip_file_add(za, self->other_files[i].name, src, ZIP_FL_OVERWRITE);
}
// Write manifest
char* json_str = cJSON_Print(self->data);
zip_source_t* src = zip_source_buffer(za, json_str, strlen(json_str), 0);
zip_file_add(za, "manifest.json", src, ZIP_FL_OVERWRITE);
zip_close(za);
free(json_str);
}
void mcpack_destroy(McPack* self) {
free(self->filepath);
cJSON_Delete(self->data);
for (int i = 0; i < self->other_count; i++) {
free(self->other_files[i].name);
free(self->other_files[i].content);
}
free(self->other_files);
}
// Example usage:
// int main() {
// McPack pack;
// mcpack_init(&pack, "example.mcpack");
// mcpack_print_properties(&pack);
// // Modify: cJSON_ReplaceItemInObject(pack.data->child, "name", cJSON_CreateString("Updated Name"));
// mcpack_write(&pack, "updated.mcpack");
// mcpack_destroy(&pack);
// return 0;
// }
This C implementation uses a struct to mimic a class, reads/decodes the ZIP with libzip, parses JSON with cJSON, prints properties recursively, and writes a modified ZIP. Note: Modifications require using cJSON functions to update data
.