Repository URL to install this package:
|
Version:
0.5.7.1 ▾
|
# Standard Library
import subprocess
from gettext import gettext as _
# Lutris Modules
from lutris.runners.runner import Runner
from lutris.util import system
from lutris.util.display import DISPLAY_MANAGER
from lutris.util.joypad import get_controller_mappings
from lutris.util.log import logger
DEFAULT_MEDNAFEN_SCALER = "nn4x"
class mednafen(Runner):
human_name = _("Mednafen")
description = _("Multi-system emulator including NES, GB(A), PC Engine support.")
platforms = [
_("Nintendo Game Boy (Color)"),
_("Nintendo Game Boy Advance"),
_("Sega Game Gear"),
_("Sega Genesis/Mega Drive"),
_("Atari Lynx"),
_("Sega Master System"),
_("SNK Neo Geo Pocket (Color)"),
_("Nintendo NES"),
_("NEC PC Engine TurboGrafx-16"),
_("NEC PC-FX"),
_("Sony PlayStation"),
_("Sega Saturn"),
_("Nintendo SNES"),
_("Bandai WonderSwan"),
_("Nintendo Virtual Boy"),
]
machine_choices = (
(_("Game Boy (Color)"), "gb"),
(_("Game Boy Advance"), "gba"),
(_("Game Gear"), "gg"),
(_("Genesis/Mega Drive"), "md"),
(_("Lynx"), "lynx"),
(_("Master System"), "sms"),
(_("Neo Geo Pocket (Color)"), "gnp"),
(_("NES"), "nes"),
(_("PC Engine"), "pce_fast"),
(_("PC-FX"), "pcfx"),
(_("PlayStation"), "psx"),
(_("Saturn"), "ss"),
(_("SNES"), "snes"),
(_("WonderSwan"), "wswan"),
(_("Virtual Boy"), "vb"),
)
runner_executable = "mednafen/bin/mednafen"
game_options = [
{
"option": "main_file",
"type": "file",
"label": _("ROM file"),
"help":
_("The game data, commonly called a ROM image. \n"
"Mednafen supports GZIP and ZIP compressed ROMs."),
},
{
"option": "machine",
"type": "choice",
"label": _("Machine type"),
"choices": machine_choices,
"help": _("The emulated machine."),
},
]
runner_options = [
{
"option": "fs",
"type": "bool",
"label": _("Fullscreen"),
"default": False
},
{
"option":
"stretch",
"type":
"choice",
"label":
_("Aspect ratio"),
"choices": (
(_("Disabled"), "0"),
(_("Stretched"), "full"),
(_("Preserve aspect ratio"), "aspect"),
(_("Integer scale"), "aspect_int"),
(_("Multiple of 2 scale"), "aspect_mult2"),
),
"default":
"aspect_int",
},
{
"option":
"scaler",
"type":
"choice",
"label":
_("Video scaler"),
"choices": (
("none", "none"),
("hq2x", "hq2x"),
("hq3x", "hq3x"),
("hq4x", "hq4x"),
("scale2x", "scale2x"),
("scale3x", "scale3x"),
("scale4x", "scale4x"),
("2xsai", "2xsai"),
("super2xsai", "super2xsai"),
("supereagle", "supereagle"),
("nn2x", "nn2x"),
("nn3x", "nn3x"),
("nn4x", "nn4x"),
("nny2x", "nny2x"),
("nny3x", "nny3x"),
("nny4x", "nny4x"),
),
"default":
DEFAULT_MEDNAFEN_SCALER,
},
{
"option":
"sound_device",
"type":
"choice",
"label":
_("Sound device"),
"choices": (
(_("Mednafen default"), "default"),
(_("ALSA default"), "sexyal-literal-default"),
("hw:0", "hw:0,0"),
("hw:1", "hw:1,0"),
("hw:2", "hw:2,0"),
),
"default":
"sexyal-literal-default"
},
{
"option": "dont_map_controllers",
"type": "bool",
"label": _("Use default Mednafen controller configuration"),
"default": False,
},
]
def get_platform(self):
machine = self.game_config.get("machine")
if machine:
for index, choice in enumerate(self.machine_choices):
if choice[1] == machine:
return self.platforms[index]
return ""
def find_joysticks(self):
""" Detect connected joysticks and return their ids """
joy_ids = []
if not self.is_installed:
return []
output = subprocess.Popen(
[self.get_executable(), "dummy"],
stdout=subprocess.PIPE,
universal_newlines=True,
).communicate()[0]
ouput = output.split("\n")
found = False
joy_list = []
for line in ouput:
if found and "Joystick" in line:
joy_list.append(line)
else:
found = False
if "Initializing joysticks" in line:
found = True
for joy in joy_list:
index = joy.find("Unique ID:")
joy_id = joy[index + 11:]
logger.debug("Joystick found id %s ", joy_id)
joy_ids.append(joy_id)
return joy_ids
@staticmethod
def set_joystick_controls(joy_ids, machine):
""" Setup joystick mappings per machine """
# Get the controller mappings
controller_mappings = get_controller_mappings()
if not controller_mappings:
logger.warning("No controller detected for joysticks %s.", joy_ids)
return []
# TODO currently only supports the first controller. Add support for other controllers.
mapping = controller_mappings[0][1]
# Construct a dictionnary of button codes to parse to mendafen
map_code = {
"a": "",
"b": "",
"c": "",
"x": "",
"y": "",
"z": "",
"back": "",
"start": "",
"leftshoulder": "",
"rightshoulder": "",
"lefttrigger": "",
"righttrigger": "",
"leftstick": "",
"rightstick": "",
"select": "",
"shoulder_l": "",
"shoulder_r": "",
"i": "",
"ii": "",
"iii": "",
"iv": "",
"v": "",
"vi": "",
"run": "",
"ls": "",
"rs": "",
"fire1": "",
"fire2": "",
"option_1": "",
"option_2": "",
"cross": "",
"circle": "",
"square": "",
"triangle": "",
"r1": "",
"r2": "",
"l1": "",
"l2": "",
"option": "",
"l": "",
"r": "",
"right-x": "",
"right-y": "",
"left-x": "",
"left-y": "",
"up-x": "",
"up-y": "",
"down-x": "",
"down-y": "",
"up-l": "",
"up-r": "",
"down-l": "",
"down-r": "",
"left-l": "",
"left-r": "",
"right-l": "",
"right-r": "",
"lstick_up": "0000c001",
"lstick_down": "00008001",
"lstick_right": "00008000",
"lstick_left": "0000c000",
"rstick_up": "0000c003",
"rstick_down": "00008003",
"rstick_left": "0000c002",
"rstick_right": "00008002",
"dpup": "0000c005",
"dpdown": "00008005",
"dpleft": "0000c004",
"dpright": "00008004",
}
# Insert the button mapping number into the map_codes
for button in mapping.keys:
bttn_id = mapping.keys[button]
if bttn_id[0] == "b": # it's a button
map_code[button] = "000000" + bttn_id[1:].zfill(2)
# Duplicate button names that are emulated in mednanfen
map_code["up"] = map_code["dpup"] #
map_code["down"] = map_code["dpdown"] #
map_code["left"] = map_code["dpleft"] # Multiple systems
map_code["right"] = map_code["dpright"]
map_code["select"] = map_code["back"] #
map_code["shoulder_r"] = map_code["rightshoulder"] # GBA
map_code["shoulder_l"] = map_code["leftshoulder"] #
map_code["i"] = map_code["b"] #
map_code["ii"] = map_code["a"] #
map_code["iii"] = map_code["leftshoulder"]
map_code["iv"] = map_code["y"] # PCEngine and PCFX
map_code["v"] = map_code["x"] #
map_code["vi"] = map_code["rightshoulder"]
map_code["run"] = map_code["start"] #
map_code["ls"] = map_code["leftshoulder"] #
map_code["rs"] = map_code["rightshoulder"] # Saturn
map_code["c"] = map_code["righttrigger"] #
map_code["z"] = map_code["lefttrigger"] #
map_code["fire1"] = map_code["a"] # Master System
map_code["fire2"] = map_code["b"] #
map_code["option_1"] = map_code["x"] # Lynx
map_code["option_2"] = map_code["y"] #
map_code["r1"] = map_code["rightshoulder"] #
map_code["r2"] = map_code["righttrigger"] #
map_code["l1"] = map_code["leftshoulder"] #
map_code["l2"] = map_code["lefttrigger"] # PlayStation
map_code["cross"] = map_code["a"] #
map_code["circle"] = map_code["b"] #
map_code["square"] = map_code["x"] #
map_code["triangle"] = map_code["y"] #
map_code["option"] = map_code["select"] # NeoGeo pocket
map_code["l"] = map_code["leftshoulder"] # SNES
map_code["r"] = map_code["rightshoulder"] #
map_code["right-x"] = map_code["dpright"] #
map_code["left-x"] = map_code["dpleft"] #
map_code["up-x"] = map_code["dpup"] #
map_code["down-x"] = map_code["dpdown"] # Wonder Swan
map_code["right-y"] = map_code["lstick_right"]
map_code["left-y"] = map_code["lstick_left"] #
map_code["up-y"] = map_code["lstick_up"] #
map_code["down-y"] = map_code["lstick_down"] #
map_code["up-l"] = map_code["dpup"] #
map_code["down-l"] = map_code["dpdown"] #
map_code["left-l"] = map_code["dpleft"] #
map_code["right-l"] = map_code["dpright"] #
map_code["up-r"] = map_code["rstick_up"] #
map_code["down-r"] = map_code["rstick_down"] # Virtual boy
map_code["left-r"] = map_code["rstick_left"] #
map_code["right-r"] = map_code["rstick_right"] #
map_code["lt"] = map_code["leftshoulder"] #
map_code["rt"] = map_code["rightshoulder"] #
# Define which buttons to use for each machine
layout = {
"nes": ["a", "b", "start", "select", "up", "down", "left", "right"],
"gb": ["a", "b", "start", "select", "up", "down", "left", "right"],
"gba": [
"a",
"b",
"shoulder_r",
"shoulder_l",
"start",
"select",
"up",
"down",
"left",
"right",
],
"pce": [
"i",
"ii",
"iii",
"iv",
"v",
"vi",
"run",
"select",
"up",
"down",
"left",
"right",
],
"ss": [
"a",
"b",
"c",
"x",
"y",
"z",
"ls",
"rs",
"start",
"up",
"down",
"left",
"right",
],
"gg": ["button1", "button2", "start", "up", "down", "left", "right"],
"md": [
"a",
"b",
"c",
"x",
"y",
"z",
"start",
"up",
"down",
"left",
"right",
],
"sms": ["fire1", "fire2", "up", "down", "left", "right"],
"lynx": ["a", "b", "option_1", "option_2", "up", "down", "left", "right"],
"psx": [
"cross",
"circle",
"square",
"triangle",
"l1",
"l2",
"r1",
"r2",
"start",
"select",
"lstick_up",
"lstick_down",
"lstick_right",
"lstick_left",
"rstick_up",
"rstick_down",
"rstick_left",
"rstick_right",
"up",
"down",
"left",
"right",
],
"pcfx": [
"i",
"ii",
"iii",
"iv",
"v",
"vi",
"run",
"select",
"up",
"down",
"left",
"right",
],
"ngp": ["a", "b", "option", "up", "down", "left", "right"],
"snes": [
"a",
"b",
"x",
"y",
"l",
"r",
"start",
"select",
"up",
"down",
"left",
"right",
],
"wswan": [
"a",
"b",
"right-x",
"right-y",
"left-x",
"left-y",
"up-x",
"up-y",
"down-x",
"down-y",
"start",
],
"vb": [
"up-l",
"down-l",
"left-l",
"right-l",
"up-r",
"down-r",
"left-r",
"right-r",
"a",
"b",
"lt",
"rt",
],
}
# Select a the gamepad type
controls = []
if machine in ["gg", "lynx", "wswan", "gb", "gba", "vb"]:
gamepad = "builtin.gamepad"
elif machine in ["md"]:
gamepad = "port1.gamepad6"
controls.append("-md.input.port1")
controls.append("gamepad6")
elif machine in ["psx"]:
gamepad = "port1.dualshock"
controls.append("-psx.input.port1")
controls.append("dualshock")
else:
gamepad = "port1.gamepad"
# Construct the controlls options
for button in layout[machine]:
controls.append("-{}.input.{}.{}".format(machine, gamepad, button))
controls.append("joystick {} {}".format(joy_ids[0], map_code[button]))
return controls
def play(self):
"""Runs the game"""
rom = self.game_config.get("main_file") or ""
machine = self.game_config.get("machine") or ""
fullscreen = self.runner_config.get("fs") or "0"
if fullscreen is True:
fullscreen = "1"
elif fullscreen is False:
fullscreen = "0"
stretch = self.runner_config.get("stretch") or "0"
scaler = self.runner_config.get("scaler") or DEFAULT_MEDNAFEN_SCALER
sound_device = self.runner_config.get("sound_device")
xres, yres = DISPLAY_MANAGER.get_current_resolution()
options = [
"-fs",
fullscreen,
"-force_module",
machine,
"-sound.device",
sound_device,
"-" + machine + ".xres",
xres,
"-" + machine + ".yres",
yres,
"-" + machine + ".stretch",
stretch,
"-" + machine + ".special",
scaler,
"-" + machine + ".videoip",
"1",
]
joy_ids = self.find_joysticks()
dont_map_controllers = self.runner_config.get("dont_map_controllers")
if joy_ids and not dont_map_controllers:
controls = self.set_joystick_controls(joy_ids, machine)
for control in controls:
options.append(control)
if not system.path_exists(rom):
return {"error": "FILE_NOT_FOUND", "file": rom}
command = [self.get_executable()]
for option in options:
command.append(option)
command.append(rom)
return {"command": command}