Skip to content

Special controls for MAME: 6 button arcade layout, 3 over 3 - #15180

Open
MizterB wants to merge 1 commit into
batocera-linux:masterfrom
MizterB:arcade_6_layout
Open

Special controls for MAME: 6 button arcade layout, 3 over 3#15180
MizterB wants to merge 1 commit into
batocera-linux:masterfrom
MizterB:arcade_6_layout

Conversation

@MizterB

@MizterB MizterB commented Jan 19, 2026

Copy link
Copy Markdown
Contributor

Additional special controls for MAME, for automatically mapping games to a 6 button, 3x3 arcade layout. Button assignments are based on the default layout used by keyboadToPads.

There are 2 variants:
arcade33: Is a standard RetroPad layout. In its default state, MAME's BUTTON1 is mapped to the bottom-left button:

4 5 6 
1 2 3

arcade33top1: Is a traditional MAME layout. In it's default state, MAME's BUTTON1 mapped to the top-left button:

1 2 3
4 5 6 

Both variants automatically remap fighting games and Neo-Geo as needed.

@dmanlfc

dmanlfc commented Jan 20, 2026

Copy link
Copy Markdown
Collaborator

@nadenislamarre

@dmanlfc

dmanlfc commented Jan 25, 2026

Copy link
Copy Markdown
Collaborator

currently PR merges are on hold. keep an eye on Master for the transition. the wip from Bryan is here as an fyi - master...bryanforbes:batocera.linux:feature/improve-es-features

@MizterB
MizterB marked this pull request as draft February 3, 2026 20:41
@MizterB
MizterB marked this pull request as ready for review February 3, 2026 20:50
@dmanlfc dmanlfc added the 43 Batocera 43 label Feb 8, 2026
@MizterB

MizterB commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on current master, no conflicts — the configgen refactor since didn't touch any of this. Re-tested the mapping on both the libretro and standalone MAME generators and it's still correct.

Should be good for another look now that merges are moving again. Thanks @dmanlfc

@Mikhailzrick

Mikhailzrick commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Interesting.

Wanted to chime in because sine I was the one that did a lot of the 6 face button controller mappings. Using kbtopads I set them up on my arcade(3x3 layout) in the same way.

WEST, NORTH, L1
SOUTH, EAST, R1

As of v43.1, This has correct mapping for mame standalone out of the box, using traditional 6 button fighting games like street fighter 2 as a test. Libretro out of the box mappings are different however, so it would be nice to get them consistent.

IMO, there's less potential confusion if we recommend just mapping arcade 3x3's to the same inputs we recommend for 6 face button controllers, rather than different I think. It could simplify a lot. Arguably 4x4's could even just use:

WEST, NORTH, L1, L2
SOUTH, EAST, R1, R2

But this would need testing. ( I don't have anything with this layout)

I have found some other arcade games specifically that are not mapped correctly regardless of the internal ES mapping, soul calibur/souledge, and Mortal Kombat games(MK4 even uses different mappings than the others, the button assaignments are different).

@Mikhailzrick

Mikhailzrick commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Soul Edge/SoulCalibur has unique mapping:

A(horizontal slash), B(vertical slash), K(kick)
G(guard

Mortal Kombat most closely follows:

HP(high punch), G(guard), HK(high kick)
LP(low punch), R(run), LK(low kick)

All but the first motal kombat games have run, in MK1, both guard and run both just guard.

This is keeping with the universal mantra of batocera following cardinal direction above all else.

There may be more, but this is all I've found so far.

@aderumier

Copy link
Copy Markdown
Contributor

@Mikhailzrick

mame generator is already setting specific control for capcom games, motal kombat , kille instict, neogeo ... based on csv rom list.Are you talking about libretro-mame ? (as this PR is about mame standalone)

https://github.com/batocera-linux/batocera.linux/blob/master/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py

    capcomList = set(mameCapcom.read_text().split())
    mkList = set(mameMKombat.read_text().split())
    kiList = set(mameKInstinct.read_text().split())
    neogeoList = set(mameNeogeo.read_text().split())
    twinstickList = set(mameTwinstick.read_text().split())
    qbertList = set(mameRotatedstick.read_text().split())


    romName = rom_path.stem
    if romName in capcomList:
        if controllerType in [ "auto", "snes" ]:
            return "sfsnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType == "fightstick":
            return "sfstick"
    elif romName in mkList:
        if controllerType in [ "auto", "snes" ]:
            return "mksnes"
        if controllerType == "megadrive":
            return "mkmegadrive"
        if controllerType == "fightstick":
            return "mkstick"
    elif romName in kiList:
        if controllerType in [ "auto", "snes" ]:
            return "kisnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType == "fightstick":
            return "sfstick"
    elif romName in  neogeoList:
        return "neomini"
    elif romName in  twinstickList:
        return "twinstick"
    elif romName in  qbertList:
        return "qbert"
    else:
        if controllerType == "fightstick":
            return "fightstick"
        if controllerType == "megadrive":
            return "mddefault"


    return "default"

@aderumier

Copy link
Copy Markdown
Contributor

Additional special controls for MAME, for automatically mapping games to a 6 button, 3x3 arcade layout. Button assignments are based on the default layout used by keyboadToPads.

There are 2 variants: arcade33: Is a standard RetroPad layout. In its default state, MAME's BUTTON1 is mapped to the bottom-left button:

4 5 6 
1 2 3

arcade33top1: Is a traditional MAME layout. In it's default state, MAME's BUTTON1 mapped to the top-left button:

1 2 3
4 5 6 

Both variants automatically remap fighting games and Neo-Geo as needed.

looking at your
arcade33top1 mapping in mameControls.csv, they are exactly the same than the current sfsnes mapping, (which is the default for capcom games like street fighter 2)

arcade33top1,BUTTON1,y
arcade33top1,BUTTON2,x
arcade33top1,BUTTON3,pageup
arcade33top1,BUTTON4,b
arcade33top1,BUTTON5,a
arcade33top1,BUTTON6,pagedown
sfsnes,BUTTON1,y
sfsnes,BUTTON2,x
sfsnes,BUTTON3,pageup
sfsnes,BUTTON4,b
sfsnes,BUTTON5,a
sfsnes,BUTTON6,pagedown

@aderumier

Copy link
Copy Markdown
Contributor

do we have a real usecase for inverted rows ? I mean, if we add a new layout in mame, it should be supported by other arcade emu like flycast for example.

we already support the 2 more common layout:

classic:

Y X L L2
B A R R2

modern:
Y X R L
B A R2 L2

and AFAIK, all premade arcade cabinet && arcade sticks are using theses 2 layouts.
so inverted row seem strange, until it's wired manually.

@Mikhailzrick

Mikhailzrick commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@Mikhailzrick

mame generator is already setting specific control for capcom games, motal kombat , kille instict, neogeo ... based on csv rom list.Are you talking about libretro-mame ? (as this PR is about mame standalone)

https://github.com/batocera-linux/batocera.linux/blob/master/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py

    capcomList = set(mameCapcom.read_text().split())
    mkList = set(mameMKombat.read_text().split())
    kiList = set(mameKInstinct.read_text().split())
    neogeoList = set(mameNeogeo.read_text().split())
    twinstickList = set(mameTwinstick.read_text().split())
    qbertList = set(mameRotatedstick.read_text().split())


    romName = rom_path.stem
    if romName in capcomList:
        if controllerType in [ "auto", "snes" ]:
            return "sfsnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType == "fightstick":
            return "sfstick"
    elif romName in mkList:
        if controllerType in [ "auto", "snes" ]:
            return "mksnes"
        if controllerType == "megadrive":
            return "mkmegadrive"
        if controllerType == "fightstick":
            return "mkstick"
    elif romName in kiList:
        if controllerType in [ "auto", "snes" ]:
            return "kisnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType == "fightstick":
            return "sfstick"
    elif romName in  neogeoList:
        return "neomini"
    elif romName in  twinstickList:
        return "twinstick"
    elif romName in  qbertList:
        return "qbert"
    else:
        if controllerType == "fightstick":
            return "fightstick"
        if controllerType == "megadrive":
            return "mddefault"


    return "default"

Mame stand alone since that’s what the PR is for. I’m not really concerned about libretro mame atm since I’m not using it. But I have identified some inconsistencies with it if anyone else wants to take a look.

For mame stand alone. I had to change the inputs for MK in the csv file, I can only assume whoever determines the defaults does not know the real arcade input mappings, and beyond that there is need for a new one for soul edge/soul caliber since it uses the default which is completely wrong.

additionally MK4 doesn’t use the same internal mapping as all the other MK games so it needs its own handling separate from the other MK games. Completely separate mappings in the csv

@MizterB

MizterB commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

do we have a real usecase for inverted rows ? I mean, if we add a new layout in mame, it should be supported by other arcade emu like flycast for example.

we already support the 2 more common layout:

classic:

Y X L L2 B A R R2

modern: Y X R L B A R2 L2

and AFAIK, all premade arcade cabinet && arcade sticks are using theses 2 layouts. so inverted row seem strange, until it's wired manually.

I disagree with this - historically, 3 over 3 is very common arcade cabinet layout for MAME. In fact, as I mentioned at the top, the default IPAC keymap in https://github.com/batocera-linux/batocera.linux/blob/master/package/batocera/controllers/pads/keyboardtopads/inputs/UltimarcIPAC2ultimarcIPAC2.vd209.p0420.yml reflects this, as it exposes only 6 gameplay buttons.

While Batocera has done some great work to standardize keymaps for certain manufacturers and groups of games, it doesn't cover everything. Individual MAME game drivers have their own longstanding keymaps, and many assumed a stacked button layout, with BUTTON1 being the top-left. This is the layout that arcade33top1 supports.

Batocera's default abstraction across all systems assumes the primary button is SOUTH, which puts BUTTON1 bottom-left. This is great for gamepads, but isn't fully faithful to assumptions built into MAME. This is the layout that arcade33 supports.

The point here is to give the end-user flexibility. As 20+ year MAME user, I want Batocera in an arcade cabinet to easily preserve MAME behaviors.

At the same time, if someone prefers a keymap that is more faithful to the Batocera retropad abstraction, I have provided that option as well.

Best of both worlds.

Also - to be clear - this PR is not just about MAME standalone. It also works perfectly fine with LR-MAME.

@aderumier

Copy link
Copy Markdown
Contributor

do we have a real usecase for inverted rows ? I mean, if we add a new layout in mame, it should be supported by other arcade emu like flycast for example.
we already support the 2 more common layout:
classic:
Y X L L2
B A R R2
modern:
Y X R L
B A R2 L2
and AFAIK, all premade arcade cabinet && arcade sticks are using theses 2 layouts. so inverted row seem strange, until it's wired manually.

I disagree with this - historically, 3 over 3 is very common arcade cabinet layout for MAME. In fact, as I mentioned at the top, the default IPAC keymap in https://github.com/batocera-linux/batocera.linux/blob/master/package/batocera/controllers/pads/keyboardtopads/inputs/UltimarcIPAC2ultimarcIPAC2.vd209.p0420.yml reflects this, as it exposes only 6 gameplay buttons.

yes, 3 over 3 is very common, but want I want to said, if that the sfsnes mapping, is already done for 3 over 3.
(as you can see, it's exactly the same mapping of arcade33top1).

so, you arcade33 mapping is just the 2 rows inverted .

my point is mainly that you'll have problem with other arcade emu like flycast, namco2x6,... because the default layout is arcade33top1 almost everywhere since the snes era. (including also all other console emu)

While Batocera has done some great work to standardize keymaps for certain manufacturers and groups of games, it doesn't cover everything. Individual MAME game drivers have their own longstanding keymaps, and many assumed a stacked button layout, with BUTTON1 being the top-left. This is the layout that arcade33top1 supports.

Batocera's default abstraction across all systems assumes the primary button is SOUTH, which puts BUTTON1 bottom-left. This is great for gamepads, but isn't fully faithful to assumptions built into MAME. This is the layout that arcade33 supports.

Well, indeed, but it's really depend of how you are wiring your encoder. I'm owning an ipac mysel too, and I known that I can't have a corect mapping in every emulator without using the classic layout wired, with button1 wired on top-right.

but if you think that it's a common wiring, no problem ! maybe it should avoid duplication of sfsnes with arcade33top1.

Also - to be clear - this PR is not just about MAME standalone. It also works perfectly fine with LR-MAME.

ah yes, sorry

@aderumier

Copy link
Copy Markdown
Contributor

@Mikhailzrick
mame generator is already setting specific control for capcom games, motal kombat , kille instict, neogeo ... based on csv rom list.Are you talking about libretro-mame ? (as this PR is about mame standalone)
https://github.com/batocera-linux/batocera.linux/blob/master/package/batocera/core/batocera-configgen/configgen/configgen/generators/mame/mameGenerator.py

    capcomList = set(mameCapcom.read_text().split())
    mkList = set(mameMKombat.read_text().split())
    kiList = set(mameKInstinct.read_text().split())
    neogeoList = set(mameNeogeo.read_text().split())
    twinstickList = set(mameTwinstick.read_text().split())
    qbertList = set(mameRotatedstick.read_text().split())


    romName = rom_path.stem
    if romName in capcomList:
        if controllerType in [ "auto", "snes" ]:
            return "sfsnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType == "fightstick":
            return "sfstick"
    elif romName in mkList:
        if controllerType in [ "auto", "snes" ]:
            return "mksnes"
        if controllerType == "megadrive":
            return "mkmegadrive"
        if controllerType == "fightstick":
            return "mkstick"
    elif romName in kiList:
        if controllerType in [ "auto", "snes" ]:
            return "kisnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType == "fightstick":
            return "sfstick"
    elif romName in  neogeoList:
        return "neomini"
    elif romName in  twinstickList:
        return "twinstick"
    elif romName in  qbertList:
        return "qbert"
    else:
        if controllerType == "fightstick":
            return "fightstick"
        if controllerType == "megadrive":
            return "mddefault"


    return "default"

Mame stand alone since that’s what the PR is for. I’m not really concerned about libretro mame atm since I’m not using it. But I have identified some inconsistencies with it if anyone else wants to take a look.

For mame stand alone. I had to change the inputs for MK in the csv file, I can only assume whoever determines the defaults does not know the real arcade input mappings, and beyond that there is need for a new one for soul edge/soul caliber since it uses the default which is completely wrong.

additionally MK4 doesn’t use the same internal mapping as all the other MK games so it needs its own handling separate from the other MK games. Completely separate mappings in the csv

for mortalkombat, I don't have tested it yet, but the csv look strange. AFAIK, the mapping is done for mk1->3, not 4. with the real cabinet with T buttons in X . + the run button in mk3.

I known that console port have done a mapping like:

low_punch high_punch block
low_kick high_kick run

but I don't known how it's implement in the csv ?

for soulcalibur, it should need a custom mapping indeed.

@aderumier

aderumier commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

@MizterB

thinking about that, is it only to fix the mapping with the new keyboardtopad feature ?

because, maybe the original problem is the mapping in /usr/share/keyboardToPads/inputs/*.yml , where the row are inverted ?

(fixed them, should fix mapping in other emulator too)

Edit:

I have looked the yml from keyboardtomap,
and they seem to correctly map the row

for ipac for example:

"key:leftctrl": "btn:west" (button1)
"key:leftalt": "btn:north" (button2)
"key:space": "btn:tl" (button3)

"key:leftshift": "btn:south" (button4)
"key:z": "btn:east" (button5)
"key:x": "btn:tr" (button6)

That's match the default street fighter layout in mame

@MizterB

MizterB commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @aderumier. This option is MAME/LR-MAME only — it doesn't touch flycast or the other arcade emus, so I don't think the cross-emulator consistency concern applies here. And the arcade33 vs arcade33top1 choice only changes the generic default bucket: capcom, MK, KI and Neo-Geo all route to the same scheme in both modes, so the fighters land on the same buttons either way.

On the sfsnes overlap — arcade33top1's rows do match it, but sfsnes isn't user-selectable (it's only returned internally for capcom), so this fills a gap there was no way to reach before. Happy to alias arcade33top1 to sfsnes in code if you'd rather drop the duplicate rows.

For the Mortal Kombat mappings, whether the underlying button semantics are right is really a separate discussion — I'd suggest a dedicated PR for that. My mkarcade33 rows just position the existing scheme onto a 3-over-3 panel; happy to walk through them if anything looks off.

@aderumier

Copy link
Copy Markdown
Contributor

Thanks @aderumier. This option is MAME/LR-MAME only — it doesn't touch flycast or the other arcade emus, so I don't think the cross-emulator consistency concern applies here. And the arcade33 vs arcade33top1 choice only changes the generic default bucket: capcom, MK, KI and Neo-Geo all route to the same scheme in both modes, so the fighters land on the same buttons either way.

On the sfsnes overlap — arcade33top1's rows do match it, but sfsnes isn't user-selectable (it's only returned internally for capcom), so this fills a gap there was no way to reach before. Happy to alias arcade33top1 to sfsnes in code if you'd rather drop the duplicate rows.

it's selectable with "snes" model

      altlayout:
        prompt: SPECIAL CONTROL LAYOUTS
        description: Controls for 5/6 button games and other unique controls
        choices:
          Default Only: default
          SNES Style: snes

for the generics games, indeed it's different, because we don't have "snes"

    else:
        if controllerType == "fightstick":
            return "fightstick"
        if controllerType == "megadrive":
            return "mddefault"
        if controllerType == "arcade33":
            return "arcade33"
        if controllerType == "arcade33top1":
            return "arcade33top1"
            
    return "default"

we have fighstick, but it's modern layout only (with button3= R1 button6=R2).
I think it's because for gamepad, it want button1=A button2=B .

so, indeed this make sense to have something different than "snes"

I'm still unsure about both arcade33 + arcade33top1, because than mean that we should have the same for "fightstick".
if I understand, it's only if user want to use top row vs bottom row for generic game ?

For the Mortal Kombat mappings, whether the underlying button semantics are right is really a separate discussion — I'd suggest a dedicated PR for that. My mkarcade33 rows just position the existing scheme onto a 3-over-3 panel; happy to walk through them if anything looks off.

what I don't understand for example, is

    elif romName in mkList:
        if controllerType in [ "auto", "snes" ]:
            return "mksnes"
        if controllerType == "megadrive":
            return "mkmegadrive"
        if controllerType == "fightstick":
            return "mkstick"
        if controllerType in ["arcade33", "arcade33top1"]:
            return "mkarcade33"

controller arcade33top1 is same layout than controllertype snes,
so mkarcade33 = mksnes ,it should be the same.
That mean, it's more a problem with the mksnes layout (it's really looking strange to be hoenst)

it should be more something like:

        if controllerType in [ "auto", "snes","arcade33", "arcade33top1" ]:
            return "mksnes"

with fix to mksnes mapping

another example for capcom

    if romName in capcomList:
        if controllerType in [ "auto", "snes", "fightstick" ]:
            return "sfsnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType in ["arcade33", "arcade33top1"]:
            return "arcade33top1"`

both controller type arcade33 && arcade33top1 are use layout arcade33top1, which is the same than default sfsnes layout

so it could be

        if controllerType in [ "auto", "snes", "fightstick "arcade33", "arcade33top1" ]:
            return "sfsnes"

(and fighstick look strange too here, as I don't see how it can work for button3 && button6

killer instinct:

        if controllerType in [ "auto", "snes", "fightstick" ]:
            return "kisnes"
        if controllerType == "megadrive":
            return "megadrive"
        if controllerType in ["arcade33", "arcade33top1"]:
            return "arcade33top1"

they seem to use also a default layout, I'm not sure, but maybe the kisnes is wrong too ?

I'll try to tests next week on my side, with both standalone && libretro, with different games, with default vs snes vs fighstick vs arcade33.
It think they are a little bit of mess with the specific games mapping

@Mikhailzrick

Copy link
Copy Markdown
Contributor

I’m in 100% agreement the current mame / libretro-mame input mappings are kind of a mess and seem to have just been an accumulation of fixes here and there. But now it’s just a mess.

something worth considering in the setting names is to prefix the target hardware:

pad: snes
stick: 4x4
stick: 4x4 alternative(or inverted maybe)
stick: 3x3
stick: 3x3 alternative(or inverted maybe)

Otherwise it’s a tad ambiguous. Especially with the new keyboardtopads, I’m not even sure some of the existing layouts are even really needed.

a key thing is knowing what a “standard” input layout should be for arcade owners, regardless of what a default ipac or encoder sets up, it’s still mapped to ES and that should be the consistent part.

my ipac uses the arrow keys, and q,e,r,t,y,u for player 1. For player 2 (wasd for directional) and f,g,h,j,k,l. But that doesn’t really matter because in ES it’s then mapped to the typical gamepad centric layout:

West, north, L1
South, east, R1

if someone is mapping their arcade inputs differently, might be good to know what it is and why.

@aderumier

Copy link
Copy Markdown
Contributor

I think we also need to have group for games with <=3 buttons or 4 buttons.

3 button can set in row for arcade panel , but need 2 + 1 in gamepad

4 buttons games can be set to 1 row with 8 buttons acade panel, but need 2 +2 for 6 buttons panel or gamepad

I think it's also miss for 2 buttons games on gamepad, a X-A layout. (instead A-B or X-Y).

I have begin to look at warped polygon guy presets (he have done the 3500 mames games manually), most of the game can be grouped like now (6button fighter, 2/3/4 buttons games, neogeo, ...) then they are also specific games too (mortal kombat, soul calibur , racing games that need pedal, .....).

also, killer instinct look like a classic capcom 6 button layout, I'm not sure why it was separated with a strange layout.

I'll do tests next week

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

43 Batocera 43

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants