> For the complete documentation index, see [llms.txt](https://docs.kouch.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kouch.dev/for-creators/creators/emulator-profiles.md).

# Write an emulator profile

Write a .kod emulator profile: where the emulator comes from, how games start, players and motion, hotkeys, data folders, options, mods and netplay.

An emulator profile tells Kouch everything it needs to run one emulator. It's a JSON document; saved with a `.kod` extension, it's ready to share. Anyone can write one, and the person importing it always sees what it does first. See [Add an emulator](/emulators/emulators/add-an-emulator.md).

This page walks through a profile section by section: one file can serve Windows and Linux (the Steam Deck), and point to its own online home for updates. Start from the complete example at the end and change what you need.

## Rules every profile follows

1. **Never name, ship or point at games, BIOS, firmware or keys.** Kouch refuses a profile at import if any part of it refers to such files, in `args`, `env`, `config_edits` or anywhere else. People copy their own system files into the emulator's `system` folder themselves; a profile only says the folder exists.
2. **No console brand names** in ids, names or text. Use the emulator's own name and neutral system ids of your choosing, like `"my-system"`.
3. **Downloads are HTTPS only**, from the emulator's **official** releases. A plain URL needs a SHA-256 checksum.
4. **The user decides, not the profile.** Installing updates automatically starts off, and Steam Cloud sync starts on; the user can change either. A profile can only suggest updates, and it can't add anything to what syncs beyond its own `cloud` folders.

## The basics

```json
{
  "version": 3,
  "id": "my-emulator",
  "name": "My Emulator",
```

* `version`: `3` for everything on this page. A `1` or `2` profile still loads.
* `id`: a stable lowercase name. Kouch uses it for folders, so don't change it later.
* `name`: what people see.

## One file for every system

A profile can carry **a section per operating system**, so the same `.kod` works on Windows and on Linux (a Steam Deck is Linux). Put what differs under `platforms`, and keep everything else at the top:

```json
  "version": 3,
  "platforms": {
    "windows": {
      "launch": { "kind": "exe", "path": "Emu.exe" },
      "source": { "kind": "github_release", "repo": "someone/my-emulator", "asset": "*-windows-x64.zip", "executable": "Emu.exe" }
    },
    "linux": {
      "launch": { "kind": "flatpak", "app_id": "org.example.Emu" },
      "source": { "kind": "flatpak", "app_id": "org.example.Emu" },
      "user_data": { "config_edits": [] }
    }
  },
```

* **The keys** are `windows`, `linux` and `macos`. Kouch for macOS is a future release: a `macos` section is checked and kept today, so profiles you write now are ready for it.
* **A section** needs a `launch`. It may also have its own `source`, `args`, `env`, `working_dir` and `user_data` (config files usually live in different places per system). **Anything a section leaves out comes from the top level.** A section's `user_data` replaces the top-level one as a whole.
* **With `platforms`, there's no top-level `launch`.** Each section has its own.
* **Flatpak is Linux only.** A `flatpak` launch or source in another section is refused.
* **A file with nothing for the user's system** installs nothing there. Kouch says so before anything downloads (*This file has no Windows version of …*) and offers the user a copy they already have, the same project's build for their system, or bringing the data in now and installing later. Add a section for every system your emulator supports.
* **Kouch keeps every section** when it saves, backs up, transfers or exports the profile, so a profile moved to another system still works there.

**Older profiles keep working.** A profile without `platforms` is for one system: an `.exe` launch (or a Windows-looking release file) means Windows, and a Flatpak or AppImage means Linux. Anything else runs on any system, as before.

## Where the program is

In each section's `launch`, either the user already has the emulator:

```json
      "launch": { "kind": "exe", "path": "C:/Emulators/MyEmulator/emu.exe" }
```

or Kouch installs it from a `source`, and `launch.path` is then the program's path inside the install, which Kouch fills in after unpacking.

On Linux the emulator can also be a **Flatpak**:

```json
      "launch": { "kind": "flatpak", "app_id": "org.example.Emu", "extra_args": [] }
```

Kouch runs `flatpak run <extra_args> <app_id> <args>`. Use `extra_args` for options to `flatpak run` itself, like `--filesystem={rom_dir}` when games live somewhere the Flatpak can't see. An **AppImage** isn't a Flatpak: use `"kind": "exe"` with its path.

## Sources: where the emulator comes from

A `source` is always the emulator's **official** releases, over HTTPS. Kouch only ever downloads the emulator itself.

| `kind`           | Where from                                                                                                                                 | Updates and versions                                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `github_release` | A GitHub project's releases: `repo` (`owner/repo`), `asset` (a pattern for the file name), `executable` (the program inside the download). | Yes: the list of releases.                                                                                                                              |
| `gitlab_release` | A GitLab project's releases: `repo` (the project path), `asset`, `executable`; `host` for a self-hosted GitLab (default `gitlab.com`).     | Yes: the list of releases.                                                                                                                              |
| `json_feed`      | The project's own "latest release" feed at `url`, read with `feed` (below).                                                                | The latest version; a list if the feed has one.                                                                                                         |
| `flatpak`        | **Linux only.** Installs `app_id` from `remote` (default Flathub) for the user.                                                            | Yes.                                                                                                                                                    |
| `direct_url`     | One file at `url`, with its `sha256`.                                                                                                      | **None.** Fine for a profile you keep yourself; a shared `.kod` should use one of the above, and the import sheet tells the user this one won't update. |

{% tabs %}
{% tab title="GitHub or GitLab release" %}

```json
      "source": {
        "kind": "github_release",
        "repo": "someone/my-emulator",
        "asset": "*-win64.zip",
        "executable": "my-emulator-*/emu.exe"
      }
```

The checksum comes from the release's own checksum file when there is one. For GitLab, write `"kind": "gitlab_release"`, the project path in `repo`, and `"host"` if it isn't `gitlab.com`.
{% endtab %}

{% tab title="The project's feed" %}

```json
      "source": {
        "kind": "json_feed",
        "url": "https://example.org/api/latest.json",
        "feed": {
          "version": "version",
          "assets": "downloads",
          "match_field": "platform",
          "match_value": "windows-x64",
          "url_field": "url",
          "sha256_field": "sha256"
        },
        "executable": "emu.exe"
      }
```

The `feed` fields are paths into the feed's JSON: where the version is, which list holds the downloads, how to pick the right one, and where its URL (and checksum, if any) are.
{% endtab %}

{% tab title="Flatpak (Linux)" %}

```json
      "source": { "kind": "flatpak", "app_id": "org.example.Emu", "remote": "flathub" }
```

Pair it with a Flatpak `launch` for the same `app_id`.
{% endtab %}

{% tab title="A direct link" %}

```json
      "source": {
        "kind": "direct_url",
        "url": "https://example.org/downloads/emu-1.2.zip",
        "sha256": "…64 hex characters…",
        "executable": "emu.exe"
      }
```

{% endtab %}
{% endtabs %}

Downloads can be `.zip`, `.7z`, `.tar.gz` or `.tar` archives, or, on Linux, an AppImage, which Kouch installs as the program itself and marks runnable.

## The profile's online home

If you publish your `.kod` on GitHub or GitLab, say where, so Kouch can offer users newer copies of the profile and show its history:

```json
  "origin": { "kind": "github", "repo": "someone/kouch-profiles", "path": "my-emulator.kod" },
  "revision": "1.4",
```

* **`kind`** is `github` or `gitlab`; add `"host"` for a self-hosted GitLab. `repo` is `owner/repo` on GitHub, the project path on GitLab.
* **`path`** is the file's path in the repo. Kouch looks for it among the repo's releases first, then on a branch (`branch`, default the main one), where its history is the commits that changed it.
* **`revision`** is your own version number for the profile file. Raise it whenever you change the file.
* **Profile updates are never applied by themselves.** The user sees there's a newer profile, can read its history, and applies it. Their own settings (switches, option values, the installed emulator) stay.

## How a game starts

```json
  "args": ["--fullscreen", "{rom}"],
  "working_dir": null,
  "env": {},
  "window_mode": "borderless",
```

`window_mode` is `borderless` (recommended: the Quick Menu shows over it), `windowed` or `fullscreen` (exclusive fullscreen may hide the Quick Menu).

### Placeholders

Use these in `args`, `env`, `working_dir` and config edits. Kouch fills them in at launch:

| Placeholder                                        | Becomes                                    |
| -------------------------------------------------- | ------------------------------------------ |
| `{rom}`                                            | the game file's full path                  |
| `{rom_dir}`, `{rom_stem}`                          | its folder; its name without the extension |
| `{emulator_dir}`                                   | the emulator program's folder              |
| `{profile_dir}`                                    | the profile's folder                       |
| `{saves}`, `{states}`, `{screenshots}`, `{config}` | the emulator's data folders                |
| `{system}` / `{bios}`                              | the folder for the user's own system files |
| `{user_data}`                                      | the emulator's data folder as a whole      |
| `{roms}`                                           | the main `roms` folder                     |
| `{storage}`                                        | big data that isn't synced                 |
| `{media}`                                          | shared art                                 |
| `{dsu_host}`, `{dsu_port}`                         | where Kouch sends players 1–4              |
| `{dsu_port_2}`                                     | players 5–8                                |

Always use `{dsu_host}` and `{dsu_port}` rather than typing an address: Kouch moves to free ports when another program holds the usual ones. Any folder placeholder written with `:/`, like `{saves:/}`, gives the path with `/` separators, for config files that treat `\` as an escape character.

## Systems and files

```json
  "systems": [
    { "id": "my-system", "name": "My System", "folder": "mysystem", "art": { "thumbnails": "Index Folder Name" } }
  ],
  "extensions": ["iso", "chd"],
  "data_folder": "myemu",
```

* `id` and `name`: neutral names you choose.
* `folder`: the system's folder in `Emulation\roms\`. Use the name other tools already use for that system, so an existing folder is picked up as it is.
* `art.thumbnails`: how the public thumbnail index names this system, for [automatic art](/library/library/art.md).
* `extensions`: file extensions (no dots). They let an **Any** folder send files to this emulator.
* `data_folder`: the name of this emulator's `bios`, `saves` and `storage` folders. Defaults to `id`.

**Games that are folders.** An extension like `"code/*.rpx"` means "a file with that extension inside a `code` folder": each match is one game, named after the folder above `code`.

## Players and controllers

```json
  "max_players": 4,
  "players": [
    { "dsu": { "server": 0, "slot": 0 }, "motion": true },
    { "dsu": { "server": 0, "slot": 1 }, "motion": true },
    { "dsu": { "server": 0, "slot": 2 }, "motion": false },
    { "dsu": { "server": 0, "slot": 3 }, "motion": false }
  ],
  "native_gamepads": "disable_in_emulator_config",
```

Kouch passes controllers to emulators over **DSU**, a network protocol many emulators read for controllers and motion. It runs two DSU servers (ports 26760 and 26761 unless something else holds them) with four slots each, so Players 1–4 are the first server's slots and Players 5–8 the second's. List one entry per player the emulator supports (up to 8), and set `motion: true` on the ones that should get motion. A profile that still names `{dsu_port_3}` or `{dsu_port_4}` gets a warning: those servers are gone.

`native_gamepads` says who reads the controllers:

* `disable_in_emulator_config` (default): the emulator gets its controllers **only from Kouch**, over DSU. Your config edits should bind only DSU devices, so players aren't read twice.
* `leave_alone`: the emulator also reads controllers itself, for example buttons directly and only motion over DSU.

### Rumble

A game's rumble can't come back to Kouch over DSU, so the emulator rumbles a controller it can see, bound as an **output only**: bind its motor and nothing else from it, so buttons, sticks and motion stay on DSU. Describe the binding, and Kouch writes it for every player at each launch:

```json
  "rumble": {
    "file": "{user_data}/config/controllers.ini",
    "format": "ini",
    "key": "Pad{player}.Rumble",
    "steam": "<the emulator's name for Steam's virtual pad number {pad}, and its motors>"
  },
```

* `file` and `format` follow the config-edit rules. `key` is one player's motor output, with `{player}` (1, 2, …); Kouch writes it for every player up to `max_players`.
* `steam` is the value when Steam's virtual pads carry rumble, the default on Windows. `{pad}` becomes the number of Steam's virtual pad for the controller in that seat. Steam offers four, so players 1 to 4 get rumble, and a swap during a game needs a restart (see [Rumble in games](/controllers/players-and-seats.md#rumble-in-games)).
* A seat with no pad gets an empty value: no rumble, no error, nothing left from an earlier session.
* Use the emulator's own names for the device and its motors; its controller settings or its log list the devices it sees.

### Pointing and shaking

For a system whose controller points at the screen, bind the emulator's pointer to the DSU device's motion for **every** player with `motion: true`, turn pointing from motion on in your config edits (even when it's the emulator's default), and bind a recenter button the games don't use. Steam reports acceleration only up to about 2 g, so a game that waits for a hard shake may miss some: where the emulator can, add a shake triggered near that limit, and a spare button that shakes while held. The developer docs' profile guide has the details.

## Pause, quit and hotkeys

```json
  "pause": { "method": "suspend" },
  "quit": { "method": "close", "force_kill_after_ms": 5000 },
  "hotkeys": {
    "save_state": { "keys": ["F1"] },
    "load_state": { "keys": ["F3"] },
    "reset": null,
    "screenshot": { "keys": ["F12"] },
    "pause": null,
    "quit": null
  },
  "on_disconnect": "hold_neutral",
```

* `quit.method`: `close` asks the window to close, then ends the program after `force_kill_after_ms`. Or `hotkey` (needs `hotkeys.quit`), or `signal`.
* `pause.method`: `suspend`, or `{ "method": "hotkey", "chord": { "keys": ["P"] } }` when the emulator has a real pause key.
* **Hotkeys** are what the Quick Menu's **Save state**, **Load state**, **Reset** and **Screenshot** press. Use the emulator's own shortcuts. A combination is a list: `["Ctrl", "S"]`. Leave one `null` if the emulator has none. `fullscreen` is the emulator's fullscreen key, if it has one.
* **More buttons for the Quick Menu (`extra`).** Any other shortcut the emulator has can become a button on the Quick Menu's **Game** page, for example swapping the two screens of a dual-screen handheld system mid-game:

```json
  "hotkeys": {
    "extra": [
      { "id": "swap-screens", "label": "Swap screens", "chord": { "keys": ["F9"] } },
      { "id": "next-layout", "label": "Next layout", "chord": { "keys": ["F10"] } }
    ]
  },
```

`id` uses lowercase letters, digits, `_` and `-`, is unique, and can't be one of the built-in ones (`pause`, `save_state`, `load_state`, `reset`, `screenshot`, `quit`, `fullscreen`). `label` is what the button says. Up to 12.

## Where the emulator keeps its data

```json
  "user_data": {
    "folders": { "saves": "saves", "states": "states", "screenshots": "screenshots", "config": "config", "system": "system" },
    "cloud": ["saves", "states", "screenshots", "config"],
    "system_required": false,
    "config_edits": [
      {
        "file": "{emulator_dir}/settings.ini",
        "format": "ini",
        "set": {
          "Paths.SaveDir": "{saves}",
          "Paths.StateDir": "{states}",
          "Paths.SysDir": "{system}",
          "Input.Backend": "dsu",
          "Input.Server": "{dsu_host}:{dsu_port}"
        }
      }
    ]
  }
```

* `folders`: each one becomes a folder under `Emulation\saves\<data_folder>`, except `system`, which is always `Emulation\bios\<data_folder>`.
* `cloud`: which of those sync to Steam Cloud (sync is on by default; the user can turn it off per emulator). `system` is never allowed.
* `system_required`: `true` when the emulator can't start a game without the user's own files. Kouch then refuses to launch while the folder is empty, and tells the user which folder to copy their files into.
* `config_edits`: changes Kouch writes into the emulator's own config file before every launch, so it uses these folders and reads controllers from Kouch.
  * `format`: `ini` (`"Section.key"`), `json` or `toml` (a dotted path), `text` (`key=value` lines) or `xml` (a `/` path from the root element; `entry[3]` picks the third repeated element; a final `@name` sets an attribute).
  * `file` must be under `{emulator_dir}`, `{user_data}`, `{profile_dir}` or `{storage}`.
  * The original file is backed up as `<file>.kouch-backup` before the first edit. **Restore emulator config** puts it back.

**Folders an emulator can't move.** If an emulator reads something from a fixed place in its own folder, add a link, and Kouch makes that place point at the user's folder before each launch:

```json
    "links": [{ "link": "{config}/keys", "target": "{system}" }]
```

**System files kept among saves.** Some emulators keep installed system software or key files inside their saves. List those paths in `private`, and Kouch treats them exactly like the `system` folder: never read, synced, sent, packed or moved.

```json
    "private": ["saves/nand/sys/**", "saves/nand/ticket/**"]
```

**The user's own system files (`system_files`).** List the keys, BIOS or firmware the emulator needs, so that when someone drops them onto Kouch (or picks them with **Add system files…**), Kouch copies each into this emulator's `system` folder after one confirm. Kouch never downloads, syncs, sends or packs these files, and never says where to get them.

```json
    "system_files": [
      { "names": ["keys.txt"], "what": "Keys", "required": true },
      { "patterns": ["*.bin"], "sizes": [2097152], "what": "BIOS",
        "sub_path_choices": [{ "label": "USA", "sub_path": "BIOS/USA" }, { "label": "Europe", "sub_path": "BIOS/EUR" }] }
    ]
```

* `names` match ignoring case; a `pattern` only matches together with one of the `sizes`.
* `kind`: `file` (default), `zip` (kept as a zip), `folder` (copied whole) or `installer_package` (the emulator installs it).
* `sub_path` is where inside `system` it goes; `sub_path_choices` lets the person pick one, for example a region. `what` and `required` are shown in the sheet.
* **`read_at`**: for an emulator that reads a system file from another folder (beside its settings, say), set `read_at` to that folder's key. The file stays in `system`; Kouch copies it there for each session and removes the copy afterwards, and that place is never exported or synced.

**One game's files (`game_files`).** For [Export one game](/emulators/emulators/backup.md#export-one-game), tell Kouch which files belong to one game. Each rule names one of your `folders` (never `system`) and a pattern using the game's keys: `{title_id}`, `{rom_stem}` (the file name without extension), `{game_hash}` or `{game_version}`.

```json
    "game_files": [
      { "part": "saves", "glob": "**/{title_id}/**" },
      { "part": "states", "glob": "{rom_stem}*" }
    ]
```

A key can be reshaped: `|lower`, `|upper`, `|slice:0:8` and `|hex`. Without `game_files`, the game menu offers the whole emulator's export instead.

## Game ids and names

`game_id` tells Kouch how to read a game's **id**, **version** and **own title** from the game file. The id keys mods, per-game defaults and one game's exports; the title gives [clean names](/library/library.md#game-names). Give one reader, or a list tried in order:

```json
  "game_id": [
    { "from": "header", "offset": 0, "len": 6, "name": { "offset": 32, "len": 64 }, "extensions": ["iso"] },
    { "from": "meta_file", "path": "meta/meta.xml", "field": "title_id", "name_field": "longname_en" },
    { "from": "filename", "pattern": "\\[(?P<id>[0-9A-F]{16})\\]" }
  ],
```

* `header`: bytes at `offset` in the file, read as `ascii`, `hex`, `hex_le`, `be_uint` or `le_uint`.
* `meta_file`: a field in a metadata file inside a folder game.
* `filename`: a pattern over the file name with a group named `id` (and optionally `version` and `name`).
* `smdh`: the English title from a handheld image's icon data, only when the image isn't encrypted.

Only what the game file itself carries is read: never anything encrypted, and never a system, BIOS or key file.

## Options people can change

Options appear on each game's **Options** tab and in **Settings › Emulators › Options**, grouped into tabs: `graphics`, `controls`, `audio`, `multiplayer` and `advanced`.

```json
  "options": [
    { "id": "scale", "label": "Resolution", "tab": "graphics", "type": "choice",
      "values": [{ "value": 1, "label": "Native" }, { "value": 2, "label": "2×" }],
      "default": 1,
      "apply": { "kind": "arg", "args": ["--scale={value}"] } },
    { "id": "fullscreen", "label": "Fullscreen", "tab": "graphics", "type": "toggle", "default": true,
      "apply": { "kind": "arg", "args": ["--fullscreen"], "args_off": ["--windowed"] } },
    { "id": "volume", "label": "Volume", "tab": "audio", "type": "slider", "min": 0, "max": 100, "step": 5, "default": 80,
      "apply": { "kind": "config_edit", "file": "{emulator_dir}/settings.ini", "format": "ini", "key": "Audio.Volume" } }
  ],
```

* `type`: `toggle`, `choice` (needs `values`), `slider` (needs `min` < `max`) or `text`.
* `apply`: extra launch arguments (`{value}` is replaced; a toggle uses `args` when on and `args_off` when off), or a `config_edit` (a toggle writes `on`/`off` if you give them, else `true`/`false`).
* A game's own setting wins over the emulator-wide one, which wins over `default`.

### How the controller is held

For a system with more than one way to hold its controller, add a `controller` option. Kouch shows it above **Play** on the game page, on the **Options** tab and in the Quick Menu's **Controller tools** (see [The controller for this game](/playing/game-page.md#the-controller-for-this-game)):

```json
    { "id": "controller", "label": "Controller", "tab": "controls", "type": "controller", "default": "attachment",
      "values": [
        { "value": "attachment", "label": "With attachment", "icon": "kouch:remote-attachment",
          "needs": ["motion", "pointer", "second_piece"],
          "edits": [{ "file": "{config}/controllers.ini", "format": "ini", "set": { "Pad1.Type": "attachment" } }] },
        { "value": "sideways", "label": "Sideways", "icon": "kouch:remote-sideways", "needs": ["motion"],
          "edits": [{ "file": "{config}/controllers.ini", "format": "ini", "set": { "Pad1.Type": "sideways" } }] }
      ] }
```

* `icon`: one of Kouch's neutral pictures: `kouch:` followed by `remote-upright`, `remote-sideways`, `remote-attachment`, `classic-pad`, `pro-pad`, `split-pair`, `split-half`, `standard-pad`, `touchpad-pad`, `trackpad-pad`, `handheld`, `keyboard`, `mouse`, `cube-pad` or `tablet-pad`. Every value needs one. Or use your own picture inside the `.kod`, as `icons/<file>.png` or `icons/<file>.webp`: at most 256 KB each and 64 in all. Kouch checks each one is really the image type its name says, and a picture that fails shows the standard pad instead. Keep your pictures brand-free: no logos or marks.
* `edits`: the emulator's own settings for that way of holding it, written at launch, with the config-edit rules.
* `needs`: `motion`, `pointer` (pointing by motion) or `second_piece` (something held in the other hand). Kouch shows it as **Needs motion · pointing · a second piece**.

**Per-game defaults.** Some games only work one way. `game_defaults`, keyed by the game's title id (see [Game ids and names](#game-ids-and-names)), sets your profile's value for those games, for any option, not only the controller:

```json
  "game_defaults": { "TITLE01": { "controller": "sideways" } },
```

What wins, highest first: the player's choice for that game, `game_defaults`, the player's choice for the emulator, then `default`.

### Graphics settings

Put every graphics setting the emulator has on the `graphics` tab, and people can change them all in Kouch's [Graphics](/playing/graphics.md) sheet without opening the emulator. Three optional fields keep them consistent between emulators:

* **`role`**: what the setting does: `renderer`, `internal_resolution`, `output_filter`, `aspect`, `anti_aliasing`, `anisotropic`, `vsync`, `frame_limit`, `post_processing`, `hdr`, `docked_mode`, `screen_layout`, `stereo_3d`, `enhancement` or `other`. Kouch groups settings by it.
* **`section`**: a group label inside the tab, like `"Enhancements"` or `"Post-processing"`, matching the emulator's own screens.
* **`restart: true`**: when a change only takes effect after the game restarts. The Quick Menu then offers **Restart game to apply**.

Leave V-Sync off by default. **Presets** set several options with one choice, and each option stays adjustable afterwards:

```json
  "presets": [
    { "id": "performance", "label": "Performance", "values": { "scale": 1, "vsync": false } },
    { "id": "quality", "label": "Quality", "values": { "scale": 2 } }
  ],
```

A preset may only name options the profile has, with values they accept.

## Mods

```json
  "mods": { "dir": "{storage}/mods/{game_hash}", "method": "link" }
```

`dir` is where the emulator looks for a game's mods. It can use `{game_hash}` (the game file's SHA-1), `{title_id}`, `{rom_stem}` and `{game_version}`, with the same reshaping as `game_files`, for example `{title_id|lower}`. Kouch puts each enabled mod in its own folder there before launch (`link` or `copy`) and removes them after. Optionally, `enable` (a config edit) switches the emulator's mod loading on for the session, and `order_file` names a file Kouch writes with the load order.

* **`layout`**: `per_mod` (default), each mod in its own folder under `dir`; or `single`, for an emulator that loads one mod per game straight from `dir`, where the one enabled mod is placed as `dir` itself.
* **`list_entry`**: for an emulator that only loads the mods listed in its own XML settings. Kouch adds an entry per enabled mod for the session and removes only its own entries afterwards; the person's own entries are never touched. `children` can add elements to each entry, filled from the game's options (`{option:<id>}`).

See [Make a mod](/for-creators/creators/mods.md).

## Netplay

```json
  "netplay": { "protocol": "udp", "port": 7000, "host_args": ["--host", "{port}"], "join_args": ["--connect", "{peer_addr}:{port}"] }
```

How to start the emulator's own netplay as host or guest. Kouch carries the traffic over Steam, so `{peer_addr}` is always a local address Kouch provides.

## A complete example

```json
{
  "version": 3,
  "id": "example",
  "name": "Example emulator",
  "origin": { "kind": "github", "repo": "someone/kouch-profiles", "path": "example.kod" },
  "revision": "1.0",
  "platforms": {
    "windows": {
      "launch": { "kind": "exe", "path": "emulator.exe" },
      "source": { "kind": "github_release", "repo": "someone/example-emulator", "asset": "*-windows-x64.zip", "executable": "emulator.exe" }
    },
    "linux": {
      "launch": { "kind": "flatpak", "app_id": "org.example.Emulator" },
      "source": { "kind": "flatpak", "app_id": "org.example.Emulator" }
    }
  },
  "args": ["{rom}"],
  "systems": [{ "id": "example-system", "name": "Example system", "folder": "example" }],
  "data_folder": "example",
  "max_players": 4,
  "players": [
    { "dsu": { "server": 0, "slot": 0 }, "motion": true },
    { "dsu": { "server": 0, "slot": 1 }, "motion": true },
    { "dsu": { "server": 0, "slot": 2 }, "motion": false },
    { "dsu": { "server": 0, "slot": 3 }, "motion": false }
  ],
  "window_mode": "borderless",
  "hotkeys": { "save_state": null, "load_state": null, "reset": null, "screenshot": null, "pause": null, "quit": null },
  "pause": { "method": "suspend" },
  "quit": { "method": "close", "force_kill_after_ms": 5000 },
  "on_disconnect": "hold_neutral",
  "native_gamepads": "disable_in_emulator_config",
  "options": [
    {
      "id": "scale", "label": "Resolution", "tab": "graphics", "type": "choice",
      "values": [{ "value": 1, "label": "Native" }, { "value": 2, "label": "2x" }],
      "default": 1,
      "apply": { "kind": "arg", "args": ["--scale={value}"] }
    }
  ]
}
```

## Test it

1. Save it as `my-emulator.kod` and import it (drop it on the window, or **Settings › Emulators › Import profile**). Fix anything listed under **Problems**.
2. Put a game in its `roms` folder and start it.
3. Open the Quick Menu (**Select + Start**): your players should be listed, and **Save state**, **Load state** and **Screenshot** should work if you set hotkeys.
4. Quit from the Quick Menu, and check that the save landed in the emulator's `saves` folder.

## Share it

A `.kod` made with a newer Kouch than the reader's still opens when the new parts only describe things. Otherwise Kouch says, for example, **This file was made with a newer Kouch (0.3.0). Update Kouch to import it.**

Share the `.kod` file anywhere. Publishing it on GitHub or GitLab, with an `origin` pointing at it, lets people get your fixes as profile updates. A link like `kouch://import-profile?url=https://example.org/my-emulator.kod` opens the import sheet in Kouch directly.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kouch.dev/for-creators/creators/emulator-profiles.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
