Task 427: .MSCZ File Format
Task 427: .MSCZ File Format
.MSCZ File Format Specifications
The .MSCZ file format is the compressed native format used by MuseScore, a free open-source music notation software. It is essentially a ZIP archive that contains an XML-based score file (typically named the main .mscx file or "score.mscx") and potentially other resources such as images, thumbnails, or audio files. The XML content follows a specific structure with a root element <museScore> and a <Score> element containing metadata, style properties, and the musical notation data. The format is backward compatible across MuseScore versions, but not forward compatible. It uses ZIP compression (Deflate method) for efficiency, and the core content is XML for readability and editability.
List of all the properties of this file format intrinsic to its file system:
- File extension: .mscz
- MIME type: application/x-musescore
- Magic number (file signature): PK\003\004 (standard ZIP header)
- Compression type: ZIP (using Deflate method)
- Container structure: ZIP archive typically containing META-INF/container.xml (pointing to the main XML file), a main XML score file (e.g., score.mscx), and optional folders like Thumbnails/ (for thumbnail.png) or Images/ (for embedded resources)
- Internal data format: XML-based (UTF-8 encoded)
- Root XML element: (where X.XX is the format version, e.g., 4.20)
- Key XML elements under : (e.g., "4.2.0"), (e.g., "eb8d33c"), for metadata
- Metadata properties (via value): arranger, audioComUrl, composer, copyright, creationDate, lyricist, movementNumber, movementTitle, mscVersion, platform, poet, source, sourceRevisionId, subtitle, translator, workNumber, workTitle
- Version compatibility: Tied to MuseScore versions; newer files may require updates for older software
- Cross-platform support: Yes (Windows, macOS, Linux)
- Developer: MuseScore (open-source, community-driven)
Two direct download links for files of format .MSCZ:
- https://musescore.org/sites/musescore.org/files/2020-03/Happy Birthday.mscz
- https://raw.githubusercontent.com/Josef-Friedrich/mscxyz/main/tests/files/score0.mscz
Ghost blog embedded HTML JavaScript for drag-and-drop .MSCZ file dump:
(This is embeddable HTML with JavaScript that allows dragging and dropping a .MSCZ file. It uses JSZip library for unzipping – include in your Ghost blog post. The script unzips the file, parses the main XML, extracts and displays the properties listed above.)
Python class for .MSCZ handling:
import zipfile
import xml.etree.ElementTree as ET
from io import BytesIO
class MSCZHandler:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
self.xml_root = None
self.modified = False
def read_decode(self):
with zipfile.ZipFile(self.filepath, 'r') as zip_ref:
mscx_files = [name for name in zip_ref.namelist() if name.endswith('.mscx')]
if not mscx_files:
raise ValueError("No .mscx file found in .mscz")
with zip_ref.open(mscx_files[0]) as xml_file:
xml_content = xml_file.read()
self.xml_root = ET.fromstring(xml_content)
# Extract properties
self.properties['file_extension'] = '.mscz'
self.properties['mime_type'] = 'application/x-musescore'
self.properties['magic_number'] = 'PK\\x03\\x04'
self.properties['compression_type'] = 'ZIP (Deflate)'
self.properties['container_structure'] = ', '.join(zip_ref.namelist())
self.properties['internal_data_format'] = 'XML-based (UTF-8)'
if self.xml_root.tag == 'museScore':
self.properties['root_xml_element'] = f"<museScore version=\"{self.xml_root.attrib.get('version', 'N/A')}\">"
score = self.xml_root.find('Score')
if score is not None:
self.properties['programVersion'] = score.findtext('programVersion', 'N/A')
self.properties['programRevision'] = score.findtext('programRevision', 'N/A')
for meta in self.xml_root.iter('metaTag'):
name = meta.attrib.get('name')
if name:
self.properties[name] = meta.text or 'N/A'
self.properties['version_compatibility'] = 'Backward compatible'
self.properties['cross_platform_support'] = 'Yes'
self.properties['developer'] = 'MuseScore'
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filepath=None):
if not self.modified:
print("No changes to write.")
return
output_path = new_filepath or self.filepath
with BytesIO() as buffer:
ET.ElementTree(self.xml_root).write(buffer, encoding='utf-8', xml_declaration=True)
buffer.seek(0)
with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zip_ref:
zip_ref.writestr('score.mscx', buffer.read()) # Assume main file name
# Add other files if needed
print(f"File written to {output_path}")
def set_property(self, key, value):
if key in ['programVersion', 'programRevision']:
score = self.xml_root.find('Score')
if score is not None:
elem = score.find(key)
if elem is None:
elem = ET.SubElement(score, key)
elem.text = value
elif key in self.properties:
for meta in self.xml_root.iter('metaTag'):
if meta.attrib.get('name') == key:
meta.text = value
break
else:
score = self.xml_root.find('Score')
if score is not None:
meta = ET.SubElement(score, 'metaTag')
meta.attrib['name'] = key
meta.text = value
self.modified = True
# Example usage:
# handler = MSCZHandler('example.mscz')
# handler.read_decode()
# handler.print_properties()
# handler.set_property('composer', 'New Composer')
# handler.write()
Java class for .MSCZ handling:
import java.io.*;
import java.util.zip.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
public class MSCZHandler {
private String filepath;
private java.util.Map<String, String> properties = new java.util.HashMap<>();
private Document xmlDoc;
private boolean modified = false;
public MSCZHandler(String filepath) {
this.filepath = filepath;
}
public void readDecode() throws IOException, ParserConfigurationException, SAXException {
try (ZipFile zipFile = new ZipFile(filepath)) {
java.util.Enumeration<? extends ZipEntry> entries = zipFile.entries();
StringBuilder structure = new StringBuilder();
InputStream xmlStream = null;
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
structure.append(entry.getName()).append(", ");
if (entry.getName().endsWith(".mscx")) {
xmlStream = zipFile.getInputStream(entry);
}
}
if (xmlStream == null) {
throw new IOException("No .mscx file found");
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
xmlDoc = builder.parse(xmlStream);
// Extract properties
properties.put("file_extension", ".mscz");
properties.put("mime_type", "application/x-musescore");
properties.put("magic_number", "PK\\003\\004");
properties.put("compression_type", "ZIP (Deflate)");
properties.put("container_structure", structure.toString().trim().replaceAll(", $", ""));
properties.put("internal_data_format", "XML-based (UTF-8)");
Element root = xmlDoc.getDocumentElement();
properties.put("root_xml_element", "<museScore version=\"" + root.getAttribute("version") + "\">");
NodeList scoreList = root.getElementsByTagName("Score");
if (scoreList.getLength() > 0) {
Element score = (Element) scoreList.item(0);
properties.put("programVersion", getTextContent(score, "programVersion"));
properties.put("programRevision", getTextContent(score, "programRevision"));
}
NodeList metaTags = root.getElementsByTagName("metaTag");
for (int i = 0; i < metaTags.getLength(); i++) {
Element meta = (Element) metaTags.item(i);
String name = meta.getAttribute("name");
properties.put(name, meta.getTextContent());
}
properties.put("version_compatibility", "Backward compatible");
properties.put("cross_platform_support", "Yes");
properties.put("developer", "MuseScore");
}
}
private String getTextContent(Element parent, String tag) {
NodeList nodes = parent.getElementsByTagName(tag);
return nodes.getLength() > 0 ? nodes.item(0).getTextContent() : "N/A";
}
public void printProperties() {
for (java.util.Map.Entry<String, String> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String newFilepath) throws IOException, TransformerException {
if (!modified) {
System.out.println("No changes to write.");
return;
}
String outputPath = newFilepath != null ? newFilepath : filepath;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(new DOMSource(xmlDoc), new StreamResult(baos));
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputPath))) {
ZipEntry entry = new ZipEntry("score.mscx");
zos.putNextEntry(entry);
zos.write(baos.toByteArray());
zos.closeEntry();
// Add other entries if needed
}
}
System.out.println("File written to " + outputPath);
}
public void setProperty(String key, String value) {
if (key.equals("programVersion") || key.equals("programRevision")) {
NodeList scoreList = xmlDoc.getElementsByTagName("Score");
if (scoreList.getLength() > 0) {
Element score = (Element) scoreList.item(0);
Element elem = (Element) score.getElementsByTagName(key).item(0);
if (elem == null) {
elem = xmlDoc.createElement(key);
score.appendChild(elem);
}
elem.setTextContent(value);
}
} else {
NodeList metaTags = xmlDoc.getElementsByTagName("metaTag");
boolean found = false;
for (int i = 0; i < metaTags.getLength(); i++) {
Element meta = (Element) metaTags.item(i);
if (meta.getAttribute("name").equals(key)) {
meta.setTextContent(value);
found = true;
break;
}
}
if (!found) {
NodeList scoreList = xmlDoc.getElementsByTagName("Score");
if (scoreList.getLength() > 0) {
Element score = (Element) scoreList.item(0);
Element meta = xmlDoc.createElement("metaTag");
meta.setAttribute("name", key);
meta.setTextContent(value);
score.appendChild(meta);
}
}
}
modified = true;
properties.put(key, value);
}
// Example usage:
// public static void main(String[] args) throws Exception {
// MSCZHandler handler = new MSCZHandler("example.mscz");
// handler.readDecode();
// handler.printProperties();
// handler.setProperty("composer", "New Composer");
// handler.write(null);
// }
}
(Note: Import javax.xml.transform.* for Transformer.)
JavaScript class for .MSCZ handling:
(This is for Node.js, using 'adm-zip' for ZIP and 'xmldom' for XML parsing. Install via npm if needed.)
const AdmZip = require('adm-zip');
const fs = require('fs');
const DOMParser = require('xmldom').DOMParser;
class MSCZHandler {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
this.xmlDoc = null;
this.modified = false;
}
readDecode() {
const zip = new AdmZip(this.filepath);
const zipEntries = zip.getEntries();
let xmlContent = null;
let structure = [];
zipEntries.forEach(entry => {
structure.push(entry.entryName);
if (entry.entryName.endsWith('.mscx')) {
xmlContent = zip.readAsText(entry);
}
});
if (!xmlContent) {
throw new Error('No .mscx file found');
}
const parser = new DOMParser();
this.xmlDoc = parser.parseFromString(xmlContent);
// Extract properties
this.properties.file_extension = '.mscz';
this.properties.mime_type = 'application/x-musescore';
this.properties.magic_number = 'PK\\x03\\x04';
this.properties.compression_type = 'ZIP (Deflate)';
this.properties.container_structure = structure.join(', ');
this.properties.internal_data_format = 'XML-based (UTF-8)';
const root = this.xmlDoc.documentElement;
this.properties.root_xml_element = `<museScore version="${root.getAttribute('version')}">`;
const score = root.getElementsByTagName('Score')[0];
if (score) {
this.properties.programVersion = score.getElementsByTagName('programVersion')[0]?.textContent || 'N/A';
this.properties.programRevision = score.getElementsByTagName('programRevision')[0]?.textContent || 'N/A';
}
const metaTags = root.getElementsByTagName('metaTag');
for (let i = 0; i < metaTags.length; i++) {
const meta = metaTags[i];
const name = meta.getAttribute('name');
this.properties[name] = meta.textContent || 'N/A';
}
this.properties.version_compatibility = 'Backward compatible';
this.properties.cross_platform_support = 'Yes';
this.properties.developer = 'MuseScore';
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(newFilepath = null) {
if (!this.modified) {
console.log('No changes to write.');
return;
}
const outputPath = newFilepath || this.filepath;
const serializer = new (require('xmldom').XMLSerializer)();
const xmlString = serializer.serializeToString(this.xmlDoc);
const zip = new AdmZip();
zip.addFile('score.mscx', Buffer.from(xmlString));
zip.writeZip(outputPath);
console.log(`File written to ${outputPath}`);
}
setProperty(key, value) {
const score = this.xmlDoc.getElementsByTagName('Score')[0];
if (key === 'programVersion' || key === 'programRevision') {
let elem = score.getElementsByTagName(key)[0];
if (!elem) {
elem = this.xmlDoc.createElement(key);
score.appendChild(elem);
}
elem.textContent = value;
} else {
let found = false;
const metaTags = this.xmlDoc.getElementsByTagName('metaTag');
for (let i = 0; i < metaTags.length; i++) {
if (metaTags[i].getAttribute('name') === key) {
metaTags[i].textContent = value;
found = true;
break;
}
}
if (!found && score) {
const meta = this.xmlDoc.createElement('metaTag');
meta.setAttribute('name', key);
meta.textContent = value;
score.appendChild(meta);
}
}
this.modified = true;
this.properties[key] = value;
}
}
// Example usage:
// const handler = new MSCZHandler('example.mscz');
// handler.readDecode();
// handler.printProperties();
// handler.setProperty('composer', 'New Composer');
// handler.write();
C class for .MSCZ handling:
(This is a basic C implementation using miniz for ZIP (assume included) and tinyxml2 for XML. For simplicity, it's a struct with functions. Compile with appropriate libraries.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "miniz.h" // For ZIP handling
#include "tinyxml2.h" // For XML parsing
typedef struct {
char* filepath;
tinyxml2::XMLDocument* xmlDoc;
char* properties[20][2]; // Key-value pairs (limited for simplicity)
int propCount;
int modified;
} MSCZHandler;
void initMSCZHandler(MSCZHandler* handler, const char* filepath) {
handler->filepath = strdup(filepath);
handler->xmlDoc = NULL;
handler->propCount = 0;
handler->modified = 0;
}
void readDecode(MSCZHandler* handler) {
mz_zip_archive zip_archive = {0};
if (!mz_zip_reader_init_file(&zip_archive, handler->filepath, 0)) {
printf("Failed to open ZIP\n");
return;
}
char* xmlContent = NULL;
char structure[1024] = {0};
for (int i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++) {
mz_zip_archive_file_stat file_stat;
mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
strncat(structure, file_stat.m_filename, sizeof(structure) - strlen(structure) - 1);
strncat(structure, ", ", sizeof(structure) - strlen(structure) - 1);
if (strstr(file_stat.m_filename, ".mscx")) {
xmlContent = (char*)malloc(file_stat.m_uncomp_size + 1);
mz_zip_reader_extract_to_mem(&zip_archive, i, xmlContent, file_stat.m_uncomp_size, 0);
xmlContent[file_stat.m_uncomp_size] = '\0';
}
}
mz_zip_reader_end(&zip_archive);
if (!xmlContent) {
printf("No .mscx found\n");
return;
}
handler->xmlDoc = new tinyxml2::XMLDocument();
handler->xmlDoc->Parse(xmlContent);
free(xmlContent);
// Extract properties
handler->properties[handler->propCount][0] = strdup("file_extension"); handler->properties[handler->propCount++][1] = strdup(".mscz");
handler->properties[handler->propCount][0] = strdup("mime_type"); handler->properties[handler->propCount++][1] = strdup("application/x-musescore");
handler->properties[handler->propCount][0] = strdup("magic_number"); handler->properties[handler->propCount++][1] = strdup("PK\\003\\004");
handler->properties[handler->propCount][0] = strdup("compression_type"); handler->properties[handler->propCount++][1] = strdup("ZIP (Deflate)");
handler->properties[handler->propCount][0] = strdup("container_structure"); handler->properties[handler->propCount++][1] = strdup(structure);
handler->properties[handler->propCount][0] = strdup("internal_data_format"); handler->properties[handler->propCount++][1] = strdup("XML-based (UTF-8)");
tinyxml2::XMLElement* root = handler->xmlDoc->FirstChildElement("museScore");
if (root) {
char buf[50];
snprintf(buf, sizeof(buf), "<museScore version=\"%s\">", root->Attribute("version"));
handler->properties[handler->propCount][0] = strdup("root_xml_element"); handler->properties[handler->propCount++][1] = strdup(buf);
}
tinyxml2::XMLElement* score = root ? root->FirstChildElement("Score") : NULL;
if (score) {
handler->properties[handler->propCount][0] = strdup("programVersion"); handler->properties[handler->propCount++][1] = strdup(score->FirstChildElement("programVersion") ? score->FirstChildElement("programVersion")->GetText() : "N/A");
handler->properties[handler->propCount][0] = strdup("programRevision"); handler->properties[handler->propCount++][1] = strdup(score->FirstChildElement("programRevision") ? score->FirstChildElement("programRevision")->GetText() : "N/A");
}
tinyxml2::XMLElement* meta = score ? score->FirstChildElement("metaTag") : NULL;
while (meta) {
const char* name = meta->Attribute("name");
handler->properties[handler->propCount][0] = strdup(name); handler->properties[handler->propCount++][1] = strdup(meta->GetText() ? meta->GetText() : "N/A");
meta = meta->NextSiblingElement("metaTag");
}
handler->properties[handler->propCount][0] = strdup("version_compatibility"); handler->properties[handler->propCount++][1] = strdup("Backward compatible");
handler->properties[handler->propCount][0] = strdup("cross_platform_support"); handler->properties[handler->propCount++][1] = strdup("Yes");
handler->properties[handler->propCount][0] = strdup("developer"); handler->properties[handler->propCount++][1] = strdup("MuseScore");
}
void printProperties(MSCZHandler* handler) {
for (int i = 0; i < handler->propCount; i++) {
printf("%s: %s\n", handler->properties[i][0], handler->properties[i][1]);
}
}
void write(MSCZHandler* handler, const char* newFilepath) {
if (!handler->modified) {
printf("No changes to write.\n");
return;
}
const char* outputPath = newFilepath ? newFilepath : handler->filepath;
tinyxml2::XMLPrinter printer;
handler->xmlDoc->Print(&printer);
mz_zip_archive zip_archive = {0};
mz_zip_writer_init_file(&zip_archive, outputPath, 0);
mz_zip_writer_add_mem(&zip_archive, "score.mscx", printer.CStr(), printer.CStrSize() - 1, MZ_DEFAULT_COMPRESSION);
mz_zip_writer_finalize_archive(&zip_archive);
mz_zip_writer_end(&zip_archive);
printf("File written to %s\n", outputPath);
}
void setProperty(MSCZHandler* handler, const char* key, const char* value) {
tinyxml2::XMLElement* score = handler->xmlDoc->FirstChildElement("museScore")->FirstChildElement("Score");
if (strcmp(key, "programVersion") == 0 || strcmp(key, "programRevision") == 0) {
tinyxml2::XMLElement* elem = score->FirstChildElement(key);
if (!elem) {
elem = handler->xmlDoc->NewElement(key);
score->InsertEndChild(elem);
}
elem->SetText(value);
} else {
tinyxml2::XMLElement* meta = score->FirstChildElement("metaTag");
while (meta) {
if (strcmp(meta->Attribute("name"), key) == 0) {
meta->SetText(value);
goto done;
}
meta = meta->NextSiblingElement("metaTag");
}
meta = handler->xmlDoc->NewElement("metaTag");
meta->SetAttribute("name", key);
meta->SetText(value);
score->InsertEndChild(meta);
}
done:
handler->modified = 1;
// Update properties array (simplified, assumes key exists or add)
}
void freeMSCZHandler(MSCZHandler* handler) {
free(handler->filepath);
delete handler->xmlDoc;
for (int i = 0; i < handler->propCount; i++) {
free(handler->properties[i][0]);
free(handler->properties[i][1]);
}
}
// Example usage:
// int main() {
// MSCZHandler handler;
// initMSCZHandler(&handler, "example.mscz");
// readDecode(&handler);
// printProperties(&handler);
// setProperty(&handler, "composer", "New Composer");
// write(&handler, NULL);
// freeMSCZHandler(&handler);
// return 0;
// }