emu-coop

Emu-coop works currently with Zelda 1 and 3, but it’s designed as a general system for describing the memory layout of a game and synchronizing memory accordingly. In principle, it should be possible to add support for any emulated game to emu-coop. You can do this by editing the “.lua” text files that come with it.

Basics

Mode files

Emu-coop comes with a modes directory. Each file in this directory contains one “game mode”. If you want to add support for a new game, add a new “game mode” file.

You can install multiple modes for the same game; if you do, emu-coop will ask you which mode you want to use.

Lua

The mode files, and emu-coop itself, are written in a programming language called “Lua”. It will help if you know Lua, but even if you can’t program you can still edit the Lua files. All you need to know is that Lua files contain three main types of values: numbers, strings and tables.

Lua files are programs, like a .exe file. If you install a mode file, or copypaste Lua into emu-coop, make sure you know and trust the person you got it from.

Debugging

Normally you run emu-coop by running the file coop.lua. But if instead you run debug.lua, It will print extra information and error messages. This can be helpful if you are making a new mode.

Making a game mode

Installing a new game mode

To add support for a new game, you will need a “mode file”. Put the mode file in the modes/ directory, then edit the index.lua file in modes/ to add your new mode to the table there. For example if you added a file named noahs_ark_3d.lua, you would need to add a line saying require "modes.noahs_ark_3d",.

Yes, this part is really inconvenient. Sorry.

Creating a mode: The easy version

The quick way to make a mode is to just make a copy of lttp.lua and change a few things.

Set the “guid” key to a new GUID generated with this website. Set the “name” to whatever your new game/mode is. You’ll see a “match” line; replace the letters ZELDANODENSETSU with the tag from your game’s ROM header. (Every SNES game has a string containing the name of the game at 0xFFC0 which contains the game’s name; on the NES, this still works, but you’ll need to change 0xFFC0 to 0xFFEB). Remove the “running” line for now. (You can use “running” to set a condition so the game only syncs while you’re playing and not during menus and stuff, but you might not need that for your game.)

Now you just need to set up the sync table. The sync table is a list of memory addresses to sync, and a rule for each one describing what to do when the sync happens. The simplest example of a rule would be a line that says

[0x7EF34A] = {},

This means, when the memory address 0x7EF34A changes, send it to your partner and they will change 0x7EF34A on their side to match, no questions asked.

Probably you want a message displayed on your partner’s side when this happens, so add a “name” key to the rule:

[0x7EF34A] = {name="Lantern"},

This will make it print “Partner got the Lantern” on the screen when it syncs.

There’s one more thing, which is that you probably don’t always want to sync. For example, say there’s a memory address that’s always supposed to only go up (experience points, maybe). Say player A sets this to 3 at the same time player B sets this to 4. You want to ignore the 3 and set both values to 4. You can arrange this by setting the sync “kind”:

[0x7EF34A] = {name="Lantern", kind="high"},

The “high” kind will always take the higher value and ignore the lower one. Other “kinds” include “bitOr”, which always takes the bitwise-OR of values (this is good for bit flags), or “delta”, which is what you should use if changes should be “added” or “subtracted” (for example, if player A goes from 0 to 3 and player B goes from 0 to 4, the final value will be 7; if player A goes from 8 to 6 and player B goes from 8 to 12, the final value will be 10).

Just setting these basic keys will let you do a lot of things. If you need to do something more complicated, see the full list of keys and values below.

Mode table

Each mode file will end with the word return followed by a table.

The value returned by the mode file is a mode table. The mode table has the following required keys:

And the following optional keys:

Sync table

The sync table is a mapping of memory addresses to sync rules. Each sync rule is a table, and that table has the following keys (all optional):

Cond table

This is used in a few places above when you need to describe a “condition”. It can take one of three forms:

{"stringtest", addr=0xFFC0, value="ZELDANODENSETSU"}

This will test true if the string given by “value” is found at the address “addr”.

{"test", addr = 0x7E0010, gte = 0x6, lte = 0x13}

This will test true if the value at address “addr” is greater than or equal to “gte” AND less than or equal to “lte”.

Optionally for this version you can add a “size” key, which can be 1, 2 or 4. This is the byte size of the value at address “addr” (if you don’t use a “size” key it will assume 1). The addr and size keys are both ignored for sync rules.

(A function)

Anywhere a cond table can be passed in, a function can be passed instead. The function will take two arguments, value (the tested value for sync rules, or nil otherwise) and size (the byte size of the tested value for sync rules, or nil otherwise).

Custom message table

This is an advanced feature for if you need to send information between partners outside of the basic memory syncing. It’s a table of custom message “name”s to functions; when a custom message with name “name” is received the corresponding function gets called. So if player A has this in their mode file:

custom = {
    hello = function (payload)
        message("Hello from " .. payload)
    end
}

Then if player B’s code calls:

send("hello", "Susan")

Then player A will see “Hello from Susan” displayed on their screen.

Functions

The following functions are available to code written in a mode file.

In addition, you can probably expect the snes9x-rr functions are available. Unless you’re running in FCEUX, maybe? Life is an adventure.

Are you stuck?

If you have problems or this guide didn’t explain well enough, bother mcc#7322 on Discord or @mcclure111 on Twitter and I’ll help you.