Task 265: .GODOT File Format

Task 265: .GODOT File Format

1. List of all the properties of this file format intrinsic to its file system

The .GODOT file format is the project configuration file for the Godot game engine, stored as a plain text file in INI (Initialization) format. It is human-readable and designed for version control compatibility. Based on official documentation and source analysis, the intrinsic properties of the format (focusing on structural and file system aspects, not content-specific settings) are:

  • File extension: .godot (conventionally named project.godot in the project root).
  • Encoding: UTF-8 (to support international characters in keys and values).
  • File type: Plain text, line-based (no binary data; each logical element is on a single line).
  • Section delimiters: Sections are defined by lines starting with [ and ending with ] (e.g., [section]); sections are optional but organize keys hierarchically.
  • Key-value pairs: Lines in the form key = value (spaces around = are optional and ignored; keys are case-sensitive).
  • Comment syntax: Lines starting with ; (semicolon) are treated as comments and ignored (Godot also supports # in some contexts, but ; is standard).
  • Blank lines: Ignored; used for readability.
  • String quoting: Values containing spaces or special characters (e.g., =, [, ], ;) can be enclosed in double quotes " (e.g., key = "value with space"); quotes are stripped during parsing.
  • Value representation: Values are stored as strings but can represent complex types (e.g., arrays as [1, 2, 3], booleans as true/false) when parsed by Godot; no native multi-line values.
  • Parsing rules: Duplicate keys in the same section overwrite previous values; sections are case-sensitive; the file can be empty (defaults to Godot's built-in settings).

These properties ensure the file is simple, editable in any text editor, and parsed by Godot's ProjectSettings class using a custom INI loader that handles type conversion.

3. Ghost blog embedded HTML JavaScript

Below is a self-contained HTML snippet with JavaScript that can be embedded in a Ghost blog post (e.g., paste into an HTML card in the Ghost editor). It supports drag-and-drop of a .GODOT file, reads it as text, parses it as INI, and dumps the structural properties (sections, keys, values) to the screen in a readable format. No external libraries are used; the parser is custom-written for basic INI support matching Godot's format.

Drag and drop a .GODOT file here to parse its properties.

To embed in Ghost: Add an HTML card, paste the code, and publish. Users can drag a .GODOT file onto the dashed box to see the dumped properties.

4. Python class

Below is a Python class GodotProject that opens a .GODOT file, decodes/reads it using configparser (standard library, handles INI perfectly for this format), prints the structural properties to console (sections and items), and supports writing back to file.

import configparser
import os

class GodotProject:
    def __init__(self, file_path):
        self.file_path = file_path
        self.config = configparser.ConfigParser(allow_no_value=True, delimiters=('=',), comment_prefixes=(';', '#'))
        self.load()

    def load(self):
        """Decode and read the .GODOT file."""
        if os.path.exists(self.file_path):
            with open(self.file_path, 'r', encoding='utf-8') as f:
                self.config.read_file(f)
        else:
            print(f"File {self.file_path} not found.")

    def print_properties(self):
        """Print all structural properties (sections and key-value pairs) to console."""
        print("Parsed .GODOT Properties:")
        for section in self.config.sections():
            print(f"[{section}]")
            for key, value in self.config.items(section):
                print(f"  {key} = {value}")
            print()

    def write(self, output_path=None):
        """Write the parsed properties back to a .GODOT file."""
        path = output_path or self.file_path
        with open(path, 'w', encoding='utf-8') as f:
            self.config.write(f)
        print(f"Wrote properties to {path}.")

# Example usage
if __name__ == "__main__":
    proj = GodotProject("project.godot")
    proj.print_properties()
    proj.write("output.godot")

Run with python godot_parser.py (place project.godot in the same directory). It loads, prints, and writes.

5. Java class

Below is a Java class GodotProject that opens a .GODOT file, decodes/reads it using a simple custom INI parser (since Java's Properties is basic; this matches Godot's format), prints the structural properties to console, and supports writing back to file. Compile with javac GodotProject.java and run with java GodotProject project.godot.

import java.io.*;
import java.util.*;

public class GodotProject {
    private Map<String, Map<String, String>> config = new LinkedHashMap<>();
    private String filePath;

    public GodotProject(String filePath) {
        this.filePath = filePath;
        load();
    }

    private void load() {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            String currentSection = null;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.isEmpty() || line.startsWith(";") || line.startsWith("#")) continue;
                if (line.startsWith("[") && line.endsWith("]")) {
                    currentSection = line.substring(1, line.length() - 1).trim();
                    config.put(currentSection, new LinkedHashMap<>());
                    continue;
                }
                int eqIndex = line.indexOf('=');
                if (eqIndex > 0 && currentSection != null) {
                    String key = line.substring(0, eqIndex).trim();
                    String value = line.substring(eqIndex + 1).trim();
                    if (value.startsWith("\"") && value.endsWith("\"")) {
                        value = value.substring(1, value.length() - 1);
                    }
                    config.get(currentSection).put(key, value);
                }
            }
        } catch (IOException e) {
            System.err.println("Error loading file: " + e.getMessage());
        }
    }

    public void printProperties() {
        System.out.println("Parsed .GODOT Properties:");
        for (Map.Entry<String, Map<String, String>> entry : config.entrySet()) {
            System.out.println("[" + entry.getKey() + "]");
            for (Map.Entry<String, String> item : entry.getValue().entrySet()) {
                System.out.println("  " + item.getKey() + " = " + item.getValue());
            }
            System.out.println();
        }
    }

    public void write(String outputPath) {
        try (PrintWriter writer = new PrintWriter(new FileWriter(outputPath))) {
            for (Map.Entry<String, Map<String, String>> entry : config.entrySet()) {
                writer.println("[" + entry.getKey() + "]");
                for (Map.Entry<String, String> item : entry.getValue().entrySet()) {
                    writer.println(item.getKey() + " = " + item.getValue());
                }
                writer.println();
            }
            System.out.println("Wrote properties to " + outputPath);
        } catch (IOException e) {
            System.err.println("Error writing file: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            System.err.println("Usage: java GodotProject <project.godot>");
            return;
        }
        GodotProject proj = new GodotProject(args[0]);
        proj.printProperties();
        proj.write("output.godot");
    }
}

6. JavaScript class

Below is a Node.js class GodotProject (run with node godot_parser.js project.godot). It opens a .GODOT file using fs, decodes/reads it with a custom INI parser, prints the structural properties to console, and supports writing back to file.

const fs = require('fs');

class GodotProject {
  constructor(filePath) {
    this.filePath = filePath;
    this.config = new Map();
    this.load();
  }

  load() {
    try {
      const text = fs.readFileSync(this.filePath, 'utf8');
      const lines = text.split('\n');
      let currentSection = null;
      lines.forEach(line => {
        line = line.trim();
        if (!line || line.startsWith(';') || line.startsWith('#')) return;
        if (line.startsWith('[') && line.endsWith(']')) {
          currentSection = line.slice(1, -1).trim();
          this.config.set(currentSection, new Map());
          return;
        }
        const eqIndex = line.indexOf('=');
        if (eqIndex > 0 && currentSection) {
          const key = line.slice(0, eqIndex).trim();
          let value = line.slice(eqIndex + 1).trim();
          if (value.startsWith('"') && value.endsWith('"')) {
            value = value.slice(1, -1);
          }
          this.config.get(currentSection).set(key, value);
        }
      });
    } catch (err) {
      console.error('Error loading file:', err.message);
    }
  }

  printProperties() {
    console.log('Parsed .GODOT Properties:');
    for (const [section, items] of this.config) {
      console.log(`[${section}]`);
      for (const [key, value] of items) {
        console.log(`  ${key} = ${value}`);
      }
      console.log('');
    }
  }

  write(outputPath) {
    let content = '';
    for (const [section, items] of this.config) {
      content += `[${section}]\n`;
      for (const [key, value] of items) {
        content += `${key} = ${value}\n`;
      }
      content += '\n';
    }
    fs.writeFileSync(outputPath, content, 'utf8');
    console.log(`Wrote properties to ${outputPath}.`);
  }
}

// Example usage
if (require.main === module) {
  if (process.argv.length < 3) {
    console.error('Usage: node godot_parser.js <project.godot>');
    process.exit(1);
  }
  const proj = new GodotProject(process.argv[2]);
  proj.printProperties();
  proj.write('output.godot');
}

module.exports = GodotProject;

7. C class

Below is a C "class" (struct with functions) GodotProject that opens a .GODOT file using fopen/fgets, decodes/reads it with a custom line parser, prints the structural properties to stdout, and supports writing back to file. Compile with gcc godot_parser.c -o godot_parser and run with ./godot_parser project.godot.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 1024
#define MAX_SECTIONS 100
#define MAX_KEYS 200

typedef struct {
    char sections[MAX_SECTIONS][MAX_LINE];
    char keys[MAX_SECTIONS][MAX_KEYS][MAX_LINE];
    char values[MAX_SECTIONS][MAX_KEYS][MAX_LINE];
    int num_sections;
    int num_keys[MAX_SECTIONS];
} GodotProject;

void init_godot_project(GodotProject *proj) {
    proj->num_sections = 0;
    memset(proj->num_keys, 0, sizeof(proj->num_keys));
}

void load_godot_project(GodotProject *proj, const char *file_path) {
    FILE *file = fopen(file_path, "r");
    if (!file) {
        fprintf(stderr, "Error opening file: %s\n", file_path);
        return;
    }

    char line[MAX_LINE];
    char *current_section = NULL;
    int section_idx = -1;

    while (fgets(line, sizeof(line), file)) {
        // Trim trailing newline
        line[strcspn(line, "\n")] = 0;
        // Trim spaces
        char *start = line;
        while (isspace(*start)) start++;
        char *end = start + strlen(start) - 1;
        while (end > start && isspace(*end)) end--;
        *(end + 1) = 0;
        if (!*start || start[0] == ';' || start[0] == '#') continue;

        if (start[0] == '[' && end[0] == ']') {
            // New section
            current_section = start + 1;
            current_section[end - start - 1] = 0;
            section_idx = proj->num_sections++;
            strcpy(proj->sections[section_idx], current_section);
            proj->num_keys[section_idx] = 0;
            continue;
        }

        char *eq = strchr(start, '=');
        if (eq && section_idx >= 0) {
            *eq = 0;
            char *key_start = start;
            while (isspace(*key_start)) key_start++;
            char *key_end = eq - 1;
            while (isspace(*key_end)) key_end--;
            *(key_end + 1) = 0;

            char *val_start = eq + 1;
            while (isspace(*val_start)) val_start++;
            char *val = val_start;
            if (*val_start == '"' && val_start[strlen(val_start) - 1] == '"') {
                val_start++;
                val_start[strlen(val_start) - 1] = 0;
                val = val_start;
            }

            int key_idx = proj->num_keys[section_idx]++;
            strcpy(proj->keys[section_idx][key_idx], key_start);
            strcpy(proj->values[section_idx][key_idx], val);
        }
    }
    fclose(file);
}

void print_properties(const GodotProject *proj) {
    printf("Parsed .GODOT Properties:\n");
    for (int s = 0; s < proj->num_sections; s++) {
        printf("[%s]\n", proj->sections[s]);
        for (int k = 0; k < proj->num_keys[s]; k++) {
            printf("  %s = %s\n", proj->keys[s][k], proj->values[s][k]);
        }
        printf("\n");
    }
}

void write_properties(const GodotProject *proj, const char *output_path) {
    FILE *file = fopen(output_path, "w");
    if (!file) {
        fprintf(stderr, "Error writing file: %s\n", output_path);
        return;
    }

    for (int s = 0; s < proj->num_sections; s++) {
        fprintf(file, "[%s]\n", proj->sections[s]);
        for (int k = 0; k < proj->num_keys[s]; k++) {
            fprintf(file, "%s = %s\n", proj->keys[s][k], proj->values[s][k]);
        }
        fprintf(file, "\n");
    }
    fclose(file);
    printf("Wrote properties to %s.\n", output_path);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <project.godot>\n", argv[0]);
        return 1;
    }

    GodotProject proj;
    init_godot_project(&proj);
    load_godot_project(&proj, argv[1]);
    print_properties(&proj);
    write_properties(&proj, "output.godot");

    return 0;
}