Task 375: .MA File Format
Task 375: .MA File Format
.MA File Format Specifications
The .MA file format is the Maya ASCII Scene file format used by Autodesk Maya for storing 3D project scenes in a text-based format. It consists of MEL (Maya Embedded Language) commands that describe the scene's geometry, lighting, animation, rendering, and other properties. The format is structured into several sections, making it readable and editable with a text editor. It is preferred for version control and file referencing because it is human-readable, unlike the binary .MB format.
- List of all the properties of this file format intrinsic to its file system:
From the detailed description of the format, the intrinsic properties and elements include:
- Header (must start with "//Maya" to identify creation details)
- File References (references to other Maya files using absolute paths or environment variables)
- Requirements (software needs, including Maya version and plug-ins)
- Units (interpretation of number units in the file)
- File Information (application name, production details, version, cut identifier with date and time, operating system, version, and additional file-specific commands)
- Nodes (named blocks of data representing geometry, models, and animations, connected by parenting or attribute connections)
- Attributes (properties of nodes, including size, position, translation, scaling, rotation, colors, textures, material types, and shading)
- Script Nodes (contains MEL code for scripting)
- Disconnections (breaks attribute connections among nodes from referenced files)
- Connections (establishes attribute connections among nodes)
- 3D Model Geometry (includes size, position, translation, scaling, and rotation)
- 3D Model Appearance (includes colors, textures, material types, and shading)
- 3D Model Scene (includes lighting and other scene properties)
- 3D Model Animation (includes animation and rendering data)
- Two direct download links for files of format .MA:
- https://raw.githubusercontent.com/iimachines/Maya2glTF/master/maya/scenes/StepScalingTest.ma
- https://docs.fileformat.com/3d/sample.ma
- Ghost blog embedded HTML JavaScript for drag and drop .MA file to dump properties to screen:
This HTML page allows dragging and dropping a .MA file, reads it as text, parses for the properties using simple string matching, and displays them in JSON format on the screen.
- Python class for .MA file:
import re
class MAFileHandler:
def __init__(self, filepath):
self.filepath = filepath
self.content = None
self.properties = {}
def read(self):
with open(self.filepath, 'r', encoding='utf-8') as f:
self.content = f.read()
self.decode()
def decode(self):
if not self.content:
raise ValueError("No content to decode")
lines = self.content.split('\n')
self.properties = {
'Header': '',
'FileReferences': [],
'Requirements': [],
'Units': '',
'FileInformation': [],
'Nodes': [],
'Attributes': [],
'ScriptNodes': [],
'Disconnections': [],
'Connections': [],
'3DModelGeometry': [],
'3DModelAppearance': [],
'3DModelScene': [],
'3DModelAnimation': []
}
for line in lines:
if line.startswith('//Maya'):
self.properties['Header'] = line.strip()
elif line.startswith('file -r'):
self.properties['FileReferences'].append(line.strip())
elif line.startswith('requires'):
self.properties['Requirements'].append(line.strip())
elif line.startswith('currentUnit'):
self.properties['Units'] = line.strip()
elif line.startswith('fileInfo'):
self.properties['FileInformation'].append(line.strip())
elif line.startswith('createNode'):
self.properties['Nodes'].append(line.strip())
elif line.startswith('setAttr'):
self.properties['Attributes'].append(line.strip())
elif line.startswith('scriptNode'):
self.properties['ScriptNodes'].append(line.strip())
elif line.startswith('disconnectAttr'):
self.properties['Disconnections'].append(line.strip())
elif line.startswith('connectAttr'):
self.properties['Connections'].append(line.strip())
elif re.search(r'translate|scale|rotate', line):
self.properties['3DModelGeometry'].append(line.strip())
elif re.search(r'color|texture|material|shading', line):
self.properties['3DModelAppearance'].append(line.strip())
elif re.search(r'light|camera', line):
self.properties['3DModelScene'].append(line.strip())
elif re.search(r'keyframe|animation', line):
self.properties['3DModelAnimation'].append(line.strip())
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}:")
if isinstance(value, list):
for item in value:
print(item)
else:
print(value)
print('-' * 40)
def write(self, new_filepath=None):
if not self.content:
raise ValueValue("No content to write")
filepath = new_filepath or self.filepath
with open(filepath, 'w', encoding='utf-8') as f:
f.write(self.content)
# Example usage:
# handler = MAFileHandler('path/to/file.ma')
# handler.read()
# handler.print_properties()
# handler.write('path/to/newfile.ma')
This class opens the .MA file, reads and parses the content to extract properties using string matching, prints them to console, and can write the content to a new file.
- Java class for .MA file:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MAFileHandler {
private String filepath;
private String content;
private Map<String, Object> properties;
public MAFileHandler(String filepath) {
this.filepath = filepath;
this.content = null;
this.properties = new HashMap<>();
}
public void read() throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
String line;
while (line = br.readLine() != null) {
sb.append(line).append("\n");
}
}
this.content = sb.toString();
decode();
}
private void decode() {
if (content == null) {
throw new IllegalStateException("No content to decode");
}
String[] lines = content.split("\n");
properties.put("Header", "");
properties.put("FileReferences", new ArrayList<String>());
properties.put("Requirements", new ArrayList<String>());
properties.put("Units", "");
properties.put("FileInformation", new ArrayList<String>());
properties.put("Nodes", new ArrayList<String>());
properties.put("Attributes", new ArrayList<String>());
properties.put("ScriptNodes", new ArrayList<String>());
properties.put("Disconnections", new ArrayList<String>());
properties.put("Connections", new ArrayList<String>());
properties.put("3DModelGeometry", new ArrayList<String>());
properties.put("3DModelAppearance", new ArrayList<String>());
properties.put("3DModelScene", new ArrayList<String>());
properties.put("3DModelAnimation", new ArrayList<String>());
for (String line : lines) {
if (line.startsWith("//Maya")) {
properties.put("Header", line.trim());
} else if (line.startsWith("file -r")) {
((List<String>) properties.get("FileReferences")).add(line.trim());
} else if (line.startsWith("requires")) {
((List<String>) properties.get("Requirements")).add(line.trim());
} else if (line.startsWith("currentUnit")) {
properties.put("Units", line.trim());
} else if (line.startsWith("fileInfo")) {
((List<String>) properties.get("FileInformation")).add(line.trim());
} else if (line.startsWith("createNode")) {
((List<String>) properties.get("Nodes")).add(line.trim());
} else if (line.startsWith("setAttr")) {
((List<String>) properties.get("Attributes")).add(line.trim());
} else if (line.startsWith("scriptNode")) {
((List<String>) properties.get("ScriptNodes")).add(line.trim());
} else if (line.startsWith("disconnectAttr")) {
((List<String>) properties.get("Disconnections")).add(line.trim());
} else if (line.startsWith("connectAttr")) {
((List<String>) properties.get("Connections")).add(line.trim());
} else if (line.matches(".*(translate|scale|rotate).*")) {
((List<String>) properties.get("3DModelGeometry")).add(line.trim());
} else if (line.matches(".*(color|texture|material|shading).*")) {
((List<String>) properties.get("3DModelAppearance")).add(line.trim());
} else if (line.matches(".*(light|camera).*")) {
((List<String>) properties.get("3DModelScene")).add(line.trim());
} else if (line.matches(".*(keyframe|animation).*")) {
((List<String>) properties.get("3DModelAnimation")).add(line.trim());
}
}
}
public void printProperties() {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ":");
if (entry.getValue() instanceof List) {
for (String item : (List<String>) entry.getValue()) {
System.out.println(item);
}
} else {
System.out.println(entry.getValue());
}
System.out.println("----------------------------------------");
}
}
public void write(String newFilepath) throws IOException {
if (content == null) {
throw new IllegalStateException("No content to write");
}
String path = (newFilepath == null) ? filepath : newFilepath;
try (FileWriter fw = new FileWriter(path)) {
fw.write(content);
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// MAFileHandler handler = new MAFileHandler("path/to/file.ma");
// handler.read();
// handler.printProperties();
// handler.write("path/to/newfile.ma");
// }
}
This Java class opens the .MA file, reads and parses the content, prints the properties to console, and can write to a new file.
- JavaScript class for .MA file (for Node.js):
const fs = require('fs');
class MAFileHandler {
constructor(filepath) {
this.filepath = filepath;
this.content = null;
this.properties = {};
}
read() {
this.content = fs.readFileSync(this.filepath, 'utf8');
this.decode();
}
decode() {
if (!this.content) {
throw new Error('No content to decode');
}
const lines = this.content.split('\n');
this.properties = {
Header: '',
FileReferences: [],
Requirements: [],
Units: '',
FileInformation: [],
Nodes: [],
Attributes: [],
ScriptNodes: [],
Disconnections: [],
Connections: [],
'3DModelGeometry': [],
'3DModelAppearance': [],
'3DModelScene': [],
'3DModelAnimation': []
};
lines.forEach(line => {
if (line.startsWith('//Maya')) {
this.properties.Header = line.trim();
} else if (line.startsWith('file -r')) {
this.properties.FileReferences.push(line.trim());
} else if (line.startsWith('requires')) {
this.properties.Requirements.push(line.trim());
} else if (line.startsWith('currentUnit')) {
this.properties.Units = line.trim();
} else if (line.startsWith('fileInfo')) {
this.properties.FileInformation.push(line.trim());
} else if (line.startsWith('createNode')) {
this.properties.Nodes.push(line.trim());
} else if (line.startsWith('setAttr')) {
this.properties.Attributes.push(line.trim());
} else if (line.startsWith('scriptNode')) {
this.properties.ScriptNodes.push(line.trim());
} else if (line.startsWith('disconnectAttr')) {
this.properties.Disconnections.push(line.trim());
} else if (line.startsWith('connectAttr')) {
this.properties.Connections.push(line.trim());
} else if (/translate|scale|rotate/.test(line)) {
this.properties['3DModelGeometry'].push(line.trim());
} else if (/color|texture|material|shading/.test(line)) {
this.properties['3DModelAppearance'].push(line.trim());
} else if (/light|camera/.test(line)) {
this.properties['3DModelScene'].push(line.trim());
} else if (/keyframe|animation/.test(line)) {
this.properties['3DModelAnimation'].push(line.trim());
}
});
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}:`);
if (Array.isArray(value)) {
value.forEach(item => console.log(item));
} else {
console.log(value);
}
console.log('----------------------------------------');
}
}
write(newFilepath = this.filepath) {
if (!this.content) {
throw new Error('No content to write');
}
fs.writeFileSync(newFilepath, this.content, 'utf8');
}
}
// Example usage:
// const handler = new MAFileHandler('path/to/file.ma');
// handler.read();
// handler.printProperties();
// handler.write('path/to/newfile.ma');
This JavaScript class (for Node.js) opens the .MA file, reads and parses the content, prints the properties to console, and can write to a new file.
- C class for .MA file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
typedef struct {
char *header;
char **file_references;
int file_references_count;
char **requirements;
int requirements_count;
char *units;
char **file_information;
int file_information_count;
char **nodes;
int nodes_count;
char **attributes;
int attributes_count;
char **script_nodes;
int script_nodes_count;
char **disconnections;
int disconnections_count;
char **connections;
int connections_count;
char **geometry;
int geometry_count;
char **appearance;
int appearance_count;
char **scene;
int scene_count;
char **animation;
int animation_count;
} MAProperties;
typedef struct {
char *filepath;
char *content;
MAProperties properties;
} MAFileHandler;
void init_properties(MAProperties *props) {
props->header = NULL;
props->file_references = NULL;
props->file_references_count = 0;
props->requirements = NULL;
props->requirements_count = 0;
props->units = NULL;
props->file_information = NULL;
props->file_information_count = 0;
props->nodes = NULL;
props->nodes_count = 0;
props->attributes = NULL;
props->attributes_count = 0;
props->script_nodes = NULL;
props->script_nodes_count = 0;
props->disconnections = NULL;
props->disconnections_count = 0;
props->connections = NULL;
props->connections_count = 0;
props->geometry = NULL;
props->geometry_count = 0;
props->appearance = NULL;
props->appearance_count = 0;
props->scene = NULL;
props->scene_count = 0;
props->animation = NULL;
props->animation_count = 0;
}
void add_string_to_list(char ***list, int *count, const char *str) {
*list = realloc(*list, sizeof(char*) * (*count + 1));
(*list)[*count] = strdup(str);
(*count)++;
}
void read_ma(MAFileHandler *handler) {
FILE *f = fopen(handler->filepath, "r");
if (f == NULL) {
perror("Failed to open file");
return;
}
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
handler->content = malloc(length + 1);
fread(handler->content, 1, length, f);
handler->content[length] = '\0';
fclose(f);
// Decode
init_properties(&handler->properties);
char *line = strtok(handler->content, "\n");
while (line != NULL) {
char *trimmed = line;
while (*trimmed == ' ') trimmed++;
if (strstr(trimmed, "//Maya") == trimmed) {
handler->properties.header = strdup(trimmed);
} else if (strstr(trimmed, "file -r") == trimmed) {
add_string_to_list(&handler->properties.file_references, &handler->properties.file_references_count, trimmed);
} else if (strstr(trimmed, "requires") == trimmed) {
add_string_to_list(&handler->properties.requirements, &handler->properties.requirements_count, trimmed);
} else if (strstr(trimmed, "currentUnit") == trimmed) {
handler->properties.units = strdup(trimmed);
} else if (strstr(trimmed, "fileInfo") == trimmed) {
add_string_to_list(&handler->properties.file_information, &handler->properties.file_information_count, trimmed);
} else if (strstr(trimmed, "createNode") == trimmed) {
add_string_to_list(&handler->properties.nodes, &handler->properties.nodes_count, trimmed);
} else if (strstr(trimmed, "setAttr") == trimmed) {
add_string_to_list(&handler->properties.attributes, &handler->properties.attributes_count, trimmed);
} else if (strstr(trimmed, "scriptNode") == trimmed) {
add_string_to_list(&handler->properties.script_nodes, &handler->properties.script_nodes_count, trimmed);
} else if (strstr(trimmed, "disconnectAttr") == trimmed) {
add_string_to_list(&handler->properties.disconnections, &handler->properties.disconnections_count, trimmed);
} else if (strstr(trimmed, "connectAttr") == trimmed) {
add_string_to_list(&handler->properties.connections, &handler->properties.connections_count, trimmed);
} else if (strstr(trimmed, "translate") || strstr(trimmed, "scale") || strstr(trimmed, "rotate")) {
add_string_to_list(&handler->properties.geometry, &handler->properties.geometry_count, trimmed);
} else if (strstr(trimmed, "color") || strstr(trimmed, "texture") || strstr(trimmed, "material") || strstr(trimmed, "shading")) {
add_string_to_list(&handler->properties.appearance, &handler->properties.appearance_count, trimmed);
} else if (strstr(trimmed, "light") || strstr(trimmed, "camera")) {
add_string_to_list(&handler->properties.scene, &handler->properties.scene_count, trimmed);
} else if (strstr(trimmed, "keyframe") || strstr(trimmed, "animation")) {
add_string_to_list(&handler->properties.animation, &handler->properties.animation_count, trimmed);
}
line = strtok(NULL, "\n");
}
}
void print_properties(MAProperties *props) {
printf("Header:\n%s\n----------------------------------------\n", props->header ? props->header : "");
printf("FileReferences:\n");
for (int i = 0; i < props->file_references_count; i++) {
printf("%s\n", props->file_references[i]);
}
printf("----------------------------------------\n");
// Similarly for other properties...
// (Omitted for brevity, add similar blocks for each property)
}
void write_ma(MAFileHandler *handler, const char *new_filepath) {
const char *path = new_filepath ? new_filepath : handler->filepath;
FILE *f = fopen(path, "w");
if (f == NULL) {
perror("Failed to open file for writing");
return;
}
fprintf(f, "%s", handler->content);
fclose(f);
}
void free_properties(MAProperties *props) {
free(props->header);
for (int i = 0; i < props->file_references_count; i++) free(props->file_references[i]);
free(props->file_references);
// Similarly for other lists...
// (Omitted for brevity)
}
int main() {
MAFileHandler handler = {.filepath = "path/to/file.ma"};
read_ma(&handler);
print_properties(&handler.properties);
write_ma(&handler, "path/to/newfile.ma");
free(handler.content);
free_properties(&handler.properties);
return 0;
}
This C structure and functions open the .MA file, read and parse the content, print the properties to console, and can write to a new file. Note: The print and free functions are abbreviated; expand them to cover all properties. Regex is included for potential advanced matching, but simple strstr is used.