Coding Rules

The coding principals for Batocera are as follows:

  • Keep it simple
  • Worse is better

In other words, don't try to handle all cases if it makes the software complicated and unmaintainable.

The batocera.linux git is based on a Buildroot tree. Ideally, a file is either owned by Batocera or by Buildroot. Buildroot files must not be modified. The following directories are owned by Batocera:

Located at, these are the defconfig files required to build Batocera for a particular platform. These files must be kept as small as possible. Other necessary alterations can be handled by packages from

The only information that you should find in this file are the physical architecture and the packages it requires (when it is technically not possible to put them in the with KConfig).

Located at, this is the place where you can find files specific to each board. This directory must be kept as small as possible; ideally most files should be handled by packages.

Every platform has several key directories:

  • fsoverlay Files that will be globally copied over to the final Batocera image. This directory is ideally empty. Avoid adding files here, instead, add them in a package. The same folder exists for each board: <architecture>/fsoverlay.
  • patches Package patches. The same folder exists specific to each board: <architecture>/patches.
  • <architecture>/boot Files specific to the boot.
  • <architecture>/linux_patches Linux patches specific to the board.

Located at, these are the packages executable after Batocera has booted. Essentially, the “main part” of Batocera. Try to organize most auxiliary packages required for booting up successfully here.

Packages should be independent (executable outside of dependencies, accessible to any platform) and contained entirely within their own sub-folder. The idea is to keep these modular, so no one package is dependent on the other.

When you've become more familiar with the basics, you can refer to the notable files/folders page as a cheatsheet!

Windows Vista and above support symlinks, but this is not built-in by default. Here's an unofficial extension to explorer.exe that adds such functionality:

  1. Install (its initial configuration script in its package at<new emulator>/ is called by and compile (make file for the new emulator at<new emulator>/<new emulator>.mk and its build configuration at<new emulator>/ your emulator with Batocera. Ensure that there are no valid build errors with it.
  2. Add your system/emulator to the EmulationStation system configuration at (if you're adding an emulator to an already existing system and don't need it to be the default, you can leave this alone).
  3. Add your system/emulator to the features list ( and any advanced system settings if necessary.
    1. Define the generator (syntax: from generators.<shortname>.<shortname>Generator import <CamelCased shortname>Generator, shortname is the system name) with its system shortname here: (you can test this locally with /usr/lib/python3.9/site-packages/configgen/ The commandArray is ultimately the line that is used to run the emulator from bash.
    2. Include it in the packages list (syntax: configgen.generators.<shortname>) here:
    3. Create the “main” configuration generator Python script here: followed by <shortname>/<shortname>
    4. If appropriate, split the file into multiple Python scripts called by <shortname> in the generators/<shortname>/ folder.
  4. Configure the default settings for particular architectures (such as if your emulator requires certain settings to function on a particular architecture) at
  5. Limit your emulator to particular platforms (typically, complex standalone emulators are x86/x86_64 only) with

If you'd like an example of a recent pull request that adds a whole new emulator:

Define global features and advanced system options in the es_features.yml file (available on the Github here). This is a human-readable file containing all the advanced settings available in ES.

Syntax (replace everything including less-than and greater-than symbols (<>)):

<emulator_1_shortname>: # We refer to the internal name of the emulator, not the system name.
  features: [<common feature 1>, <common feature 2>, <...>] # All the global options applicable to this emulator.
  cfeatures: # Custom features, found in the advanced settings.
          <batocera.conf key name 1>: # This is what is written as the key name to batocera.conf
            archs_include: [<arch>] # Platforms to limit this option to, if applicable.
            prompt:      <ALL CAPS OPTION NAME> # All caps because consistency with other labels in ES.
            description: <Explanation of the sentence that doesn't exceed 1000px across.>
                "<Option 1 name>":    <batocera.conf value 1> # This is the value written after the "=" in batocera.conf.
                "<Option 2 name>":    <batocera.conf value 2>
                "<...>":              <...> # etc. for additional values.
           <batocera.conf key name 2>: # Add as many unique keys as needed.
# This section is optional. If there are settings in your emulator that only apply to a specific
# system, you can add them in here.
    <system 1 shortname>:
          <batocera.conf key name 3>: # Options that only apply to system 1
            <...> # As above.
<emulator 2 shortname>: # Another emulator's settings follow this, so on and so forth...

Here's an example:

  features: [ratio]
            archs_include: [x86, x86_64, rpi4, odroidgoa, gameforce]
            prompt:      GRAPHICS BACKEND
            description: Choose your graphics rendering
                "OpenGL": OGL
                "Vulkan": Vulkan
            prompt:      PERFORMANCE HACKS
            description: Increase emulator performance, at the cost of accuracy/stability
                "Off": 0
                "On":  1
                prompt:      EMULATE WIIMOTE
                description: Use your gamepad like a vertical Wiimote in game
                    "Off":                       0
                    "On":                        1

Spacing is everything here. Triple-check that you have used the correct amount of indentation. If your build fails to compile because of errors related to es_features.yml after you have made modifications to it then this is likely the culprit.

General practice isn't to be a one-for-one representation of the settings available to the emulator, that would defy the point of Batocera. Important settings that may need to be changed frequently/per game should be represented as is, but generally the more advanced settings should be simplified (with the help of the config generator setting all the correct values for that emulator's configuration files as necessary) and clearly explained in the advanced system settings. For example, the above PERFORMANCE HACKS setting actually changes a bunch of settings within Dolphin all at once. Here's a snippet of its config generator:

        # Various performance hacks - Default Off
        if system.isOptSet('perf_hacks') and system.getOptBoolean('perf_hacks'):
            dolphinGFXSettings.set("Hacks", "BBoxEnable", '"False"')
            dolphinGFXSettings.set("Hacks", "DeferEFBCopies", '"True"')
            dolphinGFXSettings.set("Hacks", "EFBEmulateFormatChanges", '"False"')
            dolphinGFXSettings.set("Hacks", "EFBScaledCopy", '"True"')
            dolphinGFXSettings.set("Hacks", "EFBToTextureEnable", '"True"')
            dolphinGFXSettings.set("Hacks", "SkipDuplicateXFBs", '"True"')
            dolphinGFXSettings.set("Hacks", "XFBToTextureEnable", '"True"')
            dolphinGFXSettings.set("Enhancements", "ForceFiltering", '"True"')
            dolphinGFXSettings.set("Enhancements", "ArbitraryMipmapDetection", '"True"')
            dolphinGFXSettings.set("Enhancements", "DisableCopyFilter", '"True"')
            dolphinGFXSettings.set("Enhancements", "ForceTrueColor", '"True"')            
            if dolphinGFXSettings.has_section("Hacks"):
                dolphinGFXSettings.remove_option("Hacks", "BBoxEnable")
                dolphinGFXSettings.remove_option("Hacks", "DeferEFBCopies")
                dolphinGFXSettings.remove_option("Hacks", "EFBEmulateFormatChanges")
                dolphinGFXSettings.remove_option("Hacks", "EFBScaledCopy")
                dolphinGFXSettings.remove_option("Hacks", "EFBToTextureEnable")
                dolphinGFXSettings.remove_option("Hacks", "SkipDuplicateXFBs")
                dolphinGFXSettings.remove_option("Hacks", "XFBToTextureEnable")
            if dolphinGFXSettings.has_section("Enhancements"):
                dolphinGFXSettings.remove_option("Enhancements", "ForceFiltering")
                dolphinGFXSettings.remove_option("Enhancements", "ArbitraryMipmapDetection")
                dolphinGFXSettings.remove_option("Enhancements", "DisableCopyFilter")
                dolphinGFXSettings.remove_option("Enhancements", "ForceTrueColor")  

For each emulator, a config generator plugin must be written to update the emulator's config files before the emulator launches. There are two kinds of configuration: The one handled by Batocera and static, user-editable files. To let the user modify static configuration files:

  • Don't base the configuration on templates (a.k.a. “magic” values, hard-coded values, etc.)
  • Only update values controlled by Batocera

Configuration handled by Batocera comes from three places:

  • the configgen plugin. hardcoded or dependant on non user values (the number of plugged pads for example)
  • configgen-defaults.yml the default configuration
  • configgen-defaults-arch.yml the configuration that overrides configgen-defaults.yml defaults for a given architecture
  • batocera.conf The settings file saved to by EmulationStation or manually edited by the user. Contains keys such as global.<setting>=<value> or <system>.<emulator>.<setting>=<value>

Ideally, batocera.conf must be installed empty and keep only user-changed settings. Default system configurations must be only in YML files. EmulationStation is capable of editing the batocera.conf file.

Buildroot utilizes a great feature: patching programs.

Patches are separate files that will modify a program at compilation time. There are three locations where you can put patches:

  • In the package/batocera/<package_name> directory. This is the preferred place for Batocera packages when the patch is required for all architectures. It is very visible for the package maintainer.
  • In the board/batocera/patches/<package_name> directory. This is the preferred place when patching a Buildroot package that affects all architectures.
  • In the board/batocera/<architecture>/patches/<package_name> directory. This is the preferred place when patch a package having an issue with a specific board. If the patch is not related to the board or has no impact for other boards (like adding a choice entry in a Makefile for a board), the preferred place is not this one. It is preferred to reduce the number of entries in this place to reduce the differences between boards.

Avoid modifying Buildroot's files. This makes upgrading Buildroot more difficult. The following script: may help you to find altered Buildroot files. The number of files found using this must be reduced to the minimum amount required.

In case a modification to a Buildroot file is absolutely necessary, prefix each paragraph by '# batocera' to help during the merges.

When to merge buildroot

The good way to update buildroot packages is to merge the last official buildroot tree (stable if possible). During last month before a release, we avoid buildroot merges to avoid adding unpredictable bugs. There are some cases we can merge buildroot packages directly, because it worth to have up to date packages. It includes :

  • mesa packages (gpu drivers)
  • pipewire packages (because it is in a very active phase and solving regularly issues)
  • linux-firmwares (because it is incrementing files, not real risk)
  • rpi kernels (to be up to date)
  • rust (because ruffle uses the last versions)
  • linux LTS kernels
  • coding_rules.txt
  • Last modified: 3 weeks ago
  • by nadenislamarre