Task 260: .GM9 File Format
Task 260: .GM9 File Format
1. List of All Properties of the .GM9 File Format Intrinsic to Its File System
The .GM9 file format is a plain-text script format used by GodMode9 (a file browser for the Nintendo 3DS console). It supports interaction with the 3DS file system (SD card, SysNAND, EmuNAND partitions) through commands and variables. The intrinsic properties related to the file system are runtime variables automatically set by GodMode9 before or during script execution. These describe key file system aspects like paths, sizes, and sectors. They cannot be directly modified by scripts in most cases and are referenced using $[VAR]
syntax.
Here is the complete list:
- CURRDIR: Path to the directory containing the script file (no trailing forward slash). Example:
0:/gm9/scripts
. - GM9OUT: GodMode9's output directory path for dumps and logs. Example:
0:/gm9/out
. - EMUBASE: Base sector number of the current EmuNAND partition (integer value, or empty if no EmuNAND).
- SDSIZE: Total size of the SD card, formatted with units (bytes if <1 KiB, KiB/MiB/GiB otherwise, rounded down to nearest tenth). Example:
1273.0 GB
. - SDFREE: Free space on the SD card, formatted like
SDSIZE
(never exceedsSDSIZE
). Example:500.2 GB
. - NANDSIZE: Total size of SysNAND (including all partitions and bonus drive), formatted like
SDSIZE
. Known values:943.0 MB
or954.0 MB
(O3DS),1.2 GB
or1.8 GB
(N3DS/O2DS).
These properties are file-system-specific and enable scripts to perform operations like copying to output paths or checking storage availability.
2. Two Direct Download Links for .GM9 Files
- https://raw.githubusercontent.com/annson24/GM9Megascript/master/GM9Megascript.gm9 (All-in-one megascript combining multiple GM9 scripts)
- https://raw.githubusercontent.com/d0k3/GodMode9/master/resources/gm9/scripts/GM9Megascript.gm9 (Official example megascript bundled with GodMode9)
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .GM9 Property Dump
Drag and drop a .GM9 file here to dump its file system properties.
This HTML/JS snippet can be embedded in a Ghost blog post. It creates a drag-and-drop zone that parses the .GM9 text file for set <prop> <value>
lines matching the properties list and dumps them to the screen (with descriptions if not explicitly set in the script).
4. Python Class for .GM9 Handling
import os
class GM9Decoder:
def __init__(self, filename):
self.filename = filename
self.properties = {
'CURRDIR': 'Path to the directory containing the script file (no trailing forward slash).',
'GM9OUT': "GodMode9's output directory path for dumps and logs.",
'EMUBASE': 'Base sector number of the current EmuNAND partition (integer value, or empty if no EmuNAND).',
'SDSIZE': 'Total size of the SD card, formatted with units (bytes if <1 KiB, KiB/MiB/GiB otherwise, rounded down to nearest tenth).',
'SDFREE': 'Free space on the SD card, formatted like SDSIZE (never exceeds SDSIZE).',
'NANDSIZE': 'Total size of SysNAND (including all partitions and bonus drive), formatted like SDSIZE.'
}
self.found_values = {}
def read(self):
if not os.path.exists(self.filename):
print(f"Error: {self.filename} not found.")
return
with open(self.filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
trimmed = line.strip()
if trimmed.startswith('set '):
parts = trimmed.split(maxsplit=2)
if len(parts) >= 3 and parts[1] in self.properties:
self.found_values[parts[1]] = parts[2]
self.print_properties()
def print_properties(self):
print("File System Properties:")
for name, desc in self.properties.items():
value = self.found_values.get(name, 'Not set in script (runtime value)')
print(f"{name}: {value}")
print(f"Description: {desc}\n")
def write(self, output_filename=None):
if output_filename is None:
output_filename = self.filename
with open(output_filename, 'r', encoding='utf-8') as f:
content = f.read()
lines = content.split('\n')
new_lines = []
modified = False
for line in lines:
trimmed = line.strip()
if trimmed.startswith('set '):
parts = trimmed.split(maxsplit=2)
if len(parts) >= 3 and parts[1] in self.properties:
# Example: Set a default if not present; in real use, pass values to set
if parts[1] not in self.found_values:
self.found_values[parts[1]] = 'default_value' # Placeholder; customize
new_line = f"set {parts[1]} {self.found_values[parts[1]]}"
new_lines.append(new_line)
modified = True
continue
new_lines.append(line)
if not modified:
# Append example sets if none found
for name in self.properties:
new_lines.append(f"set {name} default_value # Example")
with open(output_filename, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines) + '\n')
print(f"Written to {output_filename}")
# Usage
if __name__ == "__main__":
decoder = GM9Decoder("example.gm9")
decoder.read()
decoder.write()
This class reads the .GM9 file, parses for set
commands matching properties, prints them (with descriptions), and writes modifications (e.g., adds/updates sets).
5. Java Class for .GM9 Handling
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class GM9Decoder {
private String filename;
private Map<String, String> properties = new HashMap<>();
private Map<String, String> foundValues = new HashMap<>();
public GM9Decoder(String filename) {
this.filename = filename;
properties.put("CURRDIR", "Path to the directory containing the script file (no trailing forward slash).");
properties.put("GM9OUT", "GodMode9's output directory path for dumps and logs.");
properties.put("EMUBASE", "Base sector number of the current EmuNAND partition (integer value, or empty if no EmuNAND).");
properties.put("SDSIZE", "Total size of the SD card, formatted with units (bytes if <1 KiB, KiB/MiB/GiB otherwise, rounded down to nearest tenth).");
properties.put("SDFREE", "Free space on the SD card, formatted like SDSIZE (never exceeds SDSIZE).");
properties.put("NANDSIZE", "Total size of SysNAND (including all partitions and bonus drive), formatted like SDSIZE.");
}
public void read() {
try {
List<String> lines = Files.readAllLines(Paths.get(filename));
for (String line : lines) {
String trimmed = line.trim();
if (trimmed.startsWith("set ")) {
String[] parts = trimmed.split("\\s+", 3);
if (parts.length >= 3 && properties.containsKey(parts[1])) {
foundValues.put(parts[1], parts[2]);
}
}
}
printProperties();
} catch (IOException e) {
System.out.println("Error: " + filename + " not found.");
}
}
private void printProperties() {
System.out.println("File System Properties:");
for (Map.Entry<String, String> entry : properties.entrySet()) {
String name = entry.getKey();
String value = foundValues.getOrDefault(name, "Not set in script (runtime value)");
System.out.println(name + ": " + value);
System.out.println("Description: " + entry.getValue() + "\n");
}
}
public void write(String outputFilename) {
if (outputFilename == null) outputFilename = filename;
try {
List<String> lines = Files.readAllLines(Paths.get(filename));
List<String> newLines = new ArrayList<>();
boolean modified = false;
for (String line : lines) {
String trimmed = line.trim();
if (trimmed.startsWith("set ")) {
String[] parts = trimmed.split("\\s+", 3);
if (parts.length >= 3 && properties.containsKey(parts[1])) {
// Placeholder update
if (!foundValues.containsKey(parts[1])) {
foundValues.put(parts[1], "default_value");
}
newLines.add("set " + parts[1] + " " + foundValues.get(parts[1]));
modified = true;
continue;
}
}
newLines.add(line);
}
if (!modified) {
for (String name : properties.keySet()) {
newLines.add("set " + name + " default_value # Example");
}
}
Files.write(Paths.get(outputFilename), newLines);
System.out.println("Written to " + outputFilename);
} catch (IOException e) {
System.out.println("Write error: " + e.getMessage());
}
}
// Usage example in main
public static void main(String[] args) {
GM9Decoder decoder = new GM9Decoder("example.gm9");
decoder.read();
decoder.write(null);
}
}
This Java class reads the .GM9 file, parses set
commands for properties, prints them, and writes updates (compiles with Java 8+; uses java.util.*).
6. JavaScript Class for .GM9 Handling
class GM9Decoder {
constructor(filename) {
this.filename = filename;
this.properties = {
CURRDIR: 'Path to the directory containing the script file (no trailing forward slash).',
GM9OUT: "GodMode9's output directory path for dumps and logs.",
EMUBASE: 'Base sector number of the current EmuNAND partition (integer value, or empty if no EmuNAND).',
SDSIZE: 'Total size of the SD card, formatted with units (bytes if <1 KiB, KiB/MiB/GiB otherwise, rounded down to nearest tenth).',
SDFREE: 'Free space on the SD card, formatted like SDSIZE (never exceeds SDSIZE).',
NANDSIZE: 'Total size of SysNAND (including all partitions and bonus drive), formatted like SDSIZE.'
};
this.foundValues = {};
}
async read() {
try {
const response = await fetch(this.filename);
const content = await response.text();
const lines = content.split('\n');
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.startsWith('set ')) {
const parts = trimmed.split(/\s+/);
if (parts.length >= 3 && this.properties.hasOwnProperty(parts[1])) {
this.foundValues[parts[1]] = parts.slice(2).join(' ');
}
}
});
this.printProperties();
} catch (e) {
console.error(`Error: ${this.filename} not found.`);
}
}
printProperties() {
console.log('File System Properties:');
Object.entries(this.properties).forEach(([name, desc]) => {
const value = this.foundValues[name] || 'Not set in script (runtime value)';
console.log(`${name}: ${value}`);
console.log(`Description: ${desc}\n`);
});
}
write(outputFilename = this.filename) {
// For Node.js; browser would use File System Access API
const fs = require('fs');
fs.readFile(this.filename, 'utf8', (err, content) => {
if (err) {
console.error('Read error:', err);
return;
}
const lines = content.split('\n');
const newLines = [];
let modified = false;
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.startsWith('set ')) {
const parts = trimmed.split(/\s+/);
if (parts.length >= 3 && this.properties.hasOwnProperty(parts[1])) {
if (!this.foundValues[parts[1]]) {
this.foundValues[parts[1]] = 'default_value';
}
newLines.push(`set ${parts[1]} ${this.foundValues[parts[1]]}`);
modified = true;
return;
}
}
newLines.push(line);
});
if (!modified) {
Object.keys(this.properties).forEach(name => {
newLines.push(`set ${name} default_value # Example`);
});
}
fs.writeFile(outputFilename, newLines.join('\n') + '\n', (err) => {
if (err) console.error('Write error:', err);
else console.log(`Written to ${outputFilename}`);
});
});
}
}
// Usage (Node.js)
const decoder = new GM9Decoder('example.gm9');
decoder.read();
decoder.write();
This Node.js-compatible class reads/parses the .GM9, prints properties, and writes updates. For browser, adapt read()
to use FileReader.
7. C Class (Struct) for .GM9 Handling
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *filename;
char *properties[6][2]; // [name][desc]
char *found_values[6];
} GM9Decoder;
void init_decoder(GM9Decoder *decoder, const char *filename) {
decoder->filename = strdup(filename);
// Initialize properties
strcpy(decoder->properties[0][0], "CURRDIR");
strcpy(decoder->properties[0][1], "Path to the directory containing the script file (no trailing forward slash).");
strcpy(decoder->properties[1][0], "GM9OUT");
strcpy(decoder->properties[1][1], "GodMode9's output directory path for dumps and logs.");
strcpy(decoder->properties[2][0], "EMUBASE");
strcpy(decoder->properties[2][1], "Base sector number of the current EmuNAND partition (integer value, or empty if no EmuNAND).");
strcpy(decoder->properties[3][0], "SDSIZE");
strcpy(decoder->properties[3][1], "Total size of the SD card, formatted with units (bytes if <1 KiB, KiB/MiB/GiB otherwise, rounded down to nearest tenth).");
strcpy(decoder->properties[4][0], "SDFREE");
strcpy(decoder->properties[4][1], "Free space on the SD card, formatted like SDSIZE (never exceeds SDSIZE).");
strcpy(decoder->properties[5][0], "NANDSIZE");
strcpy(decoder->properties[5][1], "Total size of SysNAND (including all partitions and bonus drive), formatted like SDSIZE.");
for (int i = 0; i < 6; i++) {
decoder->found_values[i] = NULL;
}
}
void read_gm9(GM9Decoder *decoder) {
FILE *file = fopen(decoder->filename, "r");
if (!file) {
printf("Error: %s not found.\n", decoder->filename);
return;
}
char line[1024];
while (fgets(line, sizeof(line), file)) {
char *trimmed = line;
while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
if (strncmp(trimmed, "set ", 4) == 0) {
char *second = strtok(trimmed + 4, " \t");
if (second) {
char *value = strtok(NULL, "\n");
if (value) {
value += strspn(value, " \t"); // Skip spaces
for (int i = 0; i < 6; i++) {
if (strcmp(second, decoder->properties[i][0]) == 0) {
decoder->found_values[i] = strdup(value);
break;
}
}
}
}
}
}
fclose(file);
print_properties(decoder);
}
void print_properties(GM9Decoder *decoder) {
printf("File System Properties:\n");
for (int i = 0; i < 6; i++) {
char *value = decoder->found_values[i] ? decoder->found_values[i] : "Not set in script (runtime value)";
printf("%s: %s\n", decoder->properties[i][0], value);
printf("Description: %s\n\n", decoder->properties[i][1]);
}
}
void write_gm9(GM9Decoder *decoder, const char *output_filename) {
char *out_name = strdup(output_filename ? output_filename : decoder->filename);
FILE *in_file = fopen(decoder->filename, "r");
FILE *out_file = fopen(out_name, "w");
if (!in_file || !out_file) {
printf("Write error.\n");
free(out_name);
if (in_file) fclose(in_file);
return;
}
char line[1024];
int modified = 0;
while (fgets(line, sizeof(line), in_file)) {
char *trimmed = line;
while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
if (strncmp(trimmed, "set ", 4) == 0) {
char *second = strtok(trimmed + 4, " \t\n");
if (second) {
for (int i = 0; i < 6; i++) {
if (strcmp(second, decoder->properties[i][0]) == 0) {
char new_line[1024];
snprintf(new_line, sizeof(new_line), "set %s %s\n", second, decoder->found_values[i] ? decoder->found_values[i] : "default_value");
fputs(new_line, out_file);
modified = 1;
break;
}
}
continue;
}
}
fputs(line, out_file);
}
if (!modified) {
for (int i = 0; i < 6; i++) {
fprintf(out_file, "set %s default_value # Example\n", decoder->properties[i][0]);
}
}
fclose(in_file);
fclose(out_file);
printf("Written to %s\n", out_name);
free(out_name);
}
void free_decoder(GM9Decoder *decoder) {
free(decoder->filename);
for (int i = 0; i < 6; i++) {
if (decoder->found_values[i]) free(decoder->found_values[i]);
}
}
// Usage
int main() {
GM9Decoder decoder;
init_decoder(&decoder, "example.gm9");
read_gm9(&decoder);
write_gm9(&decoder, NULL);
free_decoder(&decoder);
return 0;
}
This C implementation uses a struct for the decoder, reads/parses the .GM9 file for properties, prints them, and writes updates. Compile with gcc -o gm9dec gm9dec.c
. Note: Descriptions are hardcoded as fixed strings for simplicity; dynamic allocation used for values.