From 6b88731d8f1b1116a0b8c498a20541e5a74e5183 Mon Sep 17 00:00:00 2001 From: skymike03 Date: Fri, 25 Jul 2025 17:23:38 +0200 Subject: [PATCH] correction du mappage des controles --- README.md | 18 +- __main__.py | 10 +- config.py | 32 ++- controls.py | 75 +++---- controls_mapper.py | 529 ++++++++++++++++++++++++++------------------ display.py | 4 +- network.py | 30 +-- rom_extensions.json | 420 +++++++++++++++++------------------ utils.py | 4 +- 9 files changed, 611 insertions(+), 511 deletions(-) diff --git a/README.md b/README.md index db54960..1278192 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ RGSX est une application Python basée sur Pygame. - Manette (optionnelle, mais recommandée pour une expérience optimale) ou Clavier. ### Espace disque -- Espace suffisant dans `/userdata/roms/ports/RGSX` pour stocker les ROMs, images et fichiers de configuration. +- Espace suffisant pour stocker les ROMs, images et fichiers de configuration. --- @@ -43,14 +43,14 @@ Entrez la commande : Patientez et regardez le retour à l'écran ou sur la commande (à améliorer). Mettez à jour la liste des jeux via : `Menu > Paramètres de jeux > Mettre à jour la liste des jeux `. -Vous trouverez RGSX dans le système "PORTS" ou "Jeux Amateurs et portages" et dans `/userdata/roms/ports/RGSX` +Vous trouverez RGSX dans le système "PORTS" ou "Jeux Amateurs et portages" et dans `/roms/ports/RGSX` --- ### Méthode 2 : Copie manuelle - Téléchargez le contenu du dépôt en zip : https://github.com/RetroGameSets/RGSX/archive/refs/heads/main.zip -- Extrayez le tout dans `/userdata/roms/ports/RGSX` (le dossier RGSX devra être créé manuellement). Attention de bien respecter la structure indiquée plus bas. +- Extrayez le tout dans `roms/ports/RGSX` (le dossier RGSX devra être créé manuellement). Attention de bien respecter la structure indiquée plus bas. - Mettez à jour la liste des jeux via le menu : `Paramètres de jeux > Mettre à jour la liste`. @@ -59,13 +59,13 @@ Vous trouverez RGSX dans le système "PORTS" ou "Jeux Amateurs et portages" et d --- > ## IMPORTANT > Si vous avez une clé API 1Fichier, vous devez la renseigner dans -> `/userdata/saves/ports/RGSX/1FichierAPI.txt` +> `/saves/ports/RGSX/1FichierAPI.txt` > si vous souhaitez télécharger depuis des liens 1Fichier. --- - Lancez RGSX depuis ports. - Configurez les contrôles. Ils pourront être reconfigurés via le menu pause par la suite si erreur. -- Supprimez le fichier `/userdata/saves/ports/rgsx/controls.json` en cas de problème puis relancez l'application. +- Supprimez le fichier `/saves/ports/rgsx/controls.json` en cas de problème puis relancez l'application. - L'application téléchargera toutes les données nécessaires automatiquement ensuite. --- @@ -106,13 +106,13 @@ Vous trouverez RGSX dans le système "PORTS" ou "Jeux Amateurs et portages" et d ### Logs -Les logs sont enregistrés dans `/userdata/roms/ports/RGSX/logs/RGSX.log` pour diagnostiquer les problèmes. +Les logs sont enregistrés dans `/roms/ports/RGSX/logs/RGSX.log` pour diagnostiquer les problèmes. --- ## 📁 Structure du projet ``` -/userdata/roms/ports/ +/roms/ports/ RGSX-INSTALL.log # LOG d'installation uniquement RGSX/ │ @@ -127,7 +127,7 @@ RGSX/ └── logs/ └── RGSX.log # Fichier de logs. -/userdata/saves/ports/ +/saves/ports/ RGSX/ │ ├── controls.json # Fichier de mappage des contrôles (généré après le 1er demarrage) @@ -145,7 +145,7 @@ RGSX/ ### Signaler un bug -1. Consultez les logs dans `/userdata/roms/ports/RGSX/logs/RGSX.log`. +1. Consultez les logs dans `/roms/ports/RGSX/logs/RGSX.log`. 2. Ouvrez une issue sur GitHub avec une description détaillée et les logs pertinents. ### Proposer une fonctionnalité diff --git a/__main__.py b/__main__.py index 7d2a8e0..12e0c53 100644 --- a/__main__.py +++ b/__main__.py @@ -18,12 +18,10 @@ import config from config import OTA_data_ZIP # Configuration du logging -log_dir = os.path.join(config.APP_FOLDER, "logs") -log_file = os.path.join(log_dir, "RGSX.log") try: - os.makedirs(log_dir, exist_ok=True) + os.makedirs(config.log_dir, exist_ok=True) logging.basicConfig( - filename=log_file, + filename=config.log_file, level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s' ) @@ -32,7 +30,7 @@ except Exception as e: level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s' ) - logging.error(f"Échec de la configuration du logging dans {log_file}: {str(e)}") + logging.error(f"Échec de la configuration du logging dans {config.log_file}: {str(e)}") logger = logging.getLogger(__name__) @@ -310,7 +308,7 @@ async def main(): config.previous_menu_state = config.menu_state config.menu_state = "error" config.error_message = ( - "Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt" + f"Attention il faut renseigner sa clé API (premium only) dans le fichier {os.path.join(config.SAVE_FOLDER, '1fichierAPI.txt')}" ) # Mettre à jour l'entrée temporaire avec l'erreur config.history[-1]["status"] = "Erreur" diff --git a/config.py b/config.py index c023f2e..20605a0 100644 --- a/config.py +++ b/config.py @@ -2,8 +2,6 @@ import pygame # type: ignore import os import logging -logger = logging.getLogger(__name__) - # Version actuelle de l'application app_version = "1.9.7.2" @@ -11,21 +9,30 @@ app_version = "1.9.7.2" current_language = "fr" -# URL -OTA_SERVER_URL = "https://retrogamesets.fr/softs" -OTA_VERSION_ENDPOINT = f"{OTA_SERVER_URL}/version.json" -OTA_UPDATE_ZIP = f"{OTA_SERVER_URL}/RGSX.zip" -OTA_data_ZIP = f"{OTA_SERVER_URL}/rgsx-data.zip" - # Chemins de base -APP_FOLDER = "/userdata/roms/ports/RGSX" -SAVE_FOLDER = "/userdata/saves/ports/rgsx" -UPDATE_FOLDER = f"{APP_FOLDER}/update" +SYSTEM_FOLDER = "/userdata" +ROMS_FOLDER = os.path.join(SYSTEM_FOLDER, "roms") +APP_FOLDER = os.path.join(ROMS_FOLDER, "ports", "RGSX") +UPDATE_FOLDER = os.path.join(APP_FOLDER, "update") +SAVE_FOLDER = os.path.join(SYSTEM_FOLDER, "saves", "ports", "rgsx") +IMAGES_FOLDER = os.path.join(APP_FOLDER, "images", "systemes") +GAMES_FOLDER = os.path.join(APP_FOLDER, "games") CONTROLS_CONFIG_PATH = os.path.join(SAVE_FOLDER, "controls.json") HISTORY_PATH = os.path.join(SAVE_FOLDER, "history.json") LANGUAGE_CONFIG_PATH = os.path.join(SAVE_FOLDER, "language.json") JSON_EXTENSIONS = os.path.join(APP_FOLDER, "rom_extensions.json") +# Configuration du logging +logger = logging.getLogger(__name__) +log_dir = os.path.join(APP_FOLDER, "logs") +log_file = os.path.join(log_dir, "RGSX.log") + +# URL +OTA_SERVER_URL = "https://retrogamesets.fr/softs" +OTA_VERSION_ENDPOINT = os.path.join(OTA_SERVER_URL, "version.json") +OTA_UPDATE_ZIP = os.path.join(OTA_SERVER_URL, "RGSX.zip") +OTA_data_ZIP = os.path.join(OTA_SERVER_URL, "rgsx-data.zip") + # Constantes pour la répétition automatique dans pause_menu REPEAT_DELAY = 350 # Délai initial avant répétition (ms) - augmenté pour éviter les doubles actions @@ -50,6 +57,7 @@ transition_state = "idle" transition_progress = 0.0 transition_duration = 18 games_count = {} +API_KEY_1FICHIER = "" # Initialisation de la variable globale pour la clé API # Variables pour la sélection de langue selected_language_index = 0 @@ -149,4 +157,4 @@ def validate_resolution(): -API_KEY_1FICHIER = "" # Initialisation de la variable globale pour la clé API + diff --git a/controls.py b/controls.py index 583c718..9c45980 100644 --- a/controls.py +++ b/controls.py @@ -45,52 +45,35 @@ def validate_menu_state(state): def load_controls_config(path=CONTROLS_CONFIG_PATH): """Charge la configuration des contrôles depuis un fichier JSON.""" + default_config = { + "confirm": {"type": "key", "key": pygame.K_RETURN}, + "cancel": {"type": "key", "key": pygame.K_ESCAPE}, + "left": {"type": "key", "key": pygame.K_LEFT}, + "right": {"type": "key", "key": pygame.K_RIGHT}, + "up": {"type": "key", "key": pygame.K_UP}, + "down": {"type": "key", "key": pygame.K_DOWN}, + "start": {"type": "key", "key": pygame.K_p}, + "progress": {"type": "key", "key": pygame.K_x}, + "history": {"type": "key", "key": pygame.K_h}, + "page_up": {"type": "key", "key": pygame.K_PAGEUP}, + "page_down": {"type": "key", "key": pygame.K_PAGEDOWN}, + "filter": {"type": "key", "key": pygame.K_f}, + "delete": {"type": "key", "key": pygame.K_BACKSPACE}, + "space": {"type": "key", "key": pygame.K_SPACE} + } + try: with open(path, "r") as f: config_data = json.load(f) - # Vérifier les actions nécessaires - required_actions = ["confirm", "cancel", "up", "down"] - for action in required_actions: + # Vérifier et compléter les actions manquantes + for action, default_mapping in default_config.items(): if action not in config_data: logger.warning(f"Action {action} manquante dans {path}, utilisation de la valeur par défaut") - config_data[action] = { - "type": "key", - "value": { - "confirm": {"type": "key", "value": pygame.K_RETURN}, - "cancel": {"type": "key", "value": pygame.K_ESCAPE}, - "left": {"type": "key", "value": pygame.K_LEFT}, - "right": {"type": "key", "value": pygame.K_RIGHT}, - "up": {"type": "key", "value": pygame.K_UP}, - "down": {"type": "key", "value": pygame.K_DOWN}, - "start": {"type": "key", "value": pygame.K_p}, - "progress": {"type": "key", "value": pygame.K_x}, - "history": {"type": "key", "value": pygame.K_h}, - "page_up": {"type": "key", "value": pygame.K_PAGEUP}, - "page_down": {"type": "key", "value": pygame.K_PAGEDOWN}, - "filter": {"type": "key", "value": pygame.K_f}, - "delete": {"type": "key", "value": pygame.K_BACKSPACE}, - "space": {"type": "key", "value": pygame.K_SPACE} - }[action] - } + config_data[action] = default_mapping return config_data except (FileNotFoundError, json.JSONDecodeError) as e: logger.error(f"Erreur lors de la lecture de {path} : {e}, utilisation de la configuration par défaut") - return { - "confirm": {"type": "key", "value": pygame.K_RETURN}, - "cancel": {"type": "key", "value": pygame.K_ESCAPE}, - "left": {"type": "key", "value": pygame.K_LEFT}, - "right": {"type": "key", "value": pygame.K_RIGHT}, - "up": {"type": "key", "value": pygame.K_UP}, - "down": {"type": "key", "value": pygame.K_DOWN}, - "start": {"type": "key", "value": pygame.K_p}, - "progress": {"type": "key", "value": pygame.K_x}, - "history": {"type": "key", "value": pygame.K_h}, - "page_up": {"type": "key", "value": pygame.K_PAGEUP}, - "page_down": {"type": "key", "value": pygame.K_PAGEDOWN}, - "filter": {"type": "key", "value": pygame.K_f}, - "delete": {"type": "key", "value": pygame.K_BACKSPACE}, - "space": {"type": "key", "value": pygame.K_SPACE} - } + return default_config # Fonction pour vérifier si un événement correspond à une action def is_input_matched(event, action_name): @@ -623,7 +606,7 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = "error" config.error_message = _( "error_api_key" - ).format("/userdata/saves/ports/rgsx/1fichierAPI.txt") + ).format(os.join(config.SAVE_FOLDER,"1fichierAPI.txt")) config.history[-1]["status"] = "Erreur" config.history[-1]["progress"] = 0 config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente" @@ -728,9 +711,9 @@ def handle_controls(event, sources, joystick, screen): if not config.API_KEY_1FICHIER: config.previous_menu_state = config.menu_state config.menu_state = "error" - config.error_message = ( - "Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichierAPI.txt" - ) + logger.warning("clé api absente dans os.path.join(config.SAVE_FOLDER, '1fichierAPI.txt')\n") + config.error_message = _("error_api_key").format(os.path.join(config.SAVE_FOLDER, "1fichierAPI.txt")) + config.history[-1]["status"] = "Erreur" config.history[-1]["progress"] = 0 config.history[-1]["message"] = "Erreur API : Clé API 1fichier absente" @@ -930,11 +913,11 @@ def handle_controls(event, sources, joystick, screen): try: os.remove(config.APP_FOLDER + "/sources.json") logger.debug("Fichier sources.json supprimé avec succès") - if os.path.exists(config.APP_FOLDER + "/games"): - shutil.rmtree(config.APP_FOLDER + "/games") + if os.path.exists(config.GAMES_FOLDER): + shutil.rmtree(config.GAMES_FOLDER) logger.debug("Dossier games supprimé avec succès") - if os.path.exists(config.APP_FOLDER + "/images"): - shutil.rmtree(config.APP_FOLDER + "/images") + if os.path.exists(config.IMAGES_FOLDER): + shutil.rmtree(config.IMAGES_FOLDER) logger.debug("Dossier images supprimé avec succès") config.menu_state = "restart_popup" config.popup_message = _("popup_redownload_success") diff --git a/controls_mapper.py b/controls_mapper.py index 9fe3ab3..d0fc120 100644 --- a/controls_mapper.py +++ b/controls_mapper.py @@ -5,11 +5,12 @@ import logging import config from config import CONTROLS_CONFIG_PATH from display import draw_gradient +import xml.etree.ElementTree as ET logger = logging.getLogger(__name__) # Chemin du fichier de configuration des contrôles -CONTROLS_CONFIG_PATH = "/userdata/saves/ports/rgsx/controls.json" +CONTROLS_CONFIG_PATH = os.path.join(config.SAVE_FOLDER, "controls.json") # Actions internes de RGSX à mapper ACTIONS = [ @@ -36,11 +37,11 @@ SDL_TO_PYGAME_KEY = { 1073741904: pygame.K_LEFT, # Flèche Gauche 1073741903: pygame.K_RIGHT, # Flèche Droite 1073742050: pygame.K_LALT, # Alt gauche - 1073742051: pygame.K_RSHIFT, # Alt droit + 1073742054: pygame.K_RALT, # Alt droit (AltGr) 1073742049: pygame.K_LCTRL, # Ctrl gauche 1073742053: pygame.K_RCTRL, # Ctrl droit 1073742048: pygame.K_LSHIFT, # Shift gauche - 1073742054: pygame.K_RALT, # Shift droit + 1073742052: pygame.K_RSHIFT, # Shift droit } # Noms lisibles pour les touches clavier @@ -156,31 +157,100 @@ KEY_NAMES = { pygame.K_SLASH: "/", } -# Noms lisibles pour les boutons de manette -BUTTON_NAMES = { - 0: "A", - 1: "B", - 2: "X", - 3: "Y", - 4: "LB", - 5: "RB", - 6: "LT", - 7: "RT", - 8: "Select", - 9: "Start", -} +def get_controller_button_names(): + """Récupère les noms des boutons depuis es_input.cfg""" + es_input_path = "/usr/share/emulationstation/es_input.cfg" + button_names = {} + + if not os.path.exists(es_input_path): + return {i: f"Bouton {i}" for i in range(16)} + + try: + tree = ET.parse(es_input_path) + root = tree.getroot() + + # Mapping des noms ES vers des noms lisibles + es_button_names = { + "a": "A", "b": "B", "x": "X", "y": "Y", + "leftshoulder": "LB", "rightshoulder": "RB", + "lefttrigger": "LT", "righttrigger": "RT", + "select": "Select", "start": "Start", + "leftstick": "L3", "rightstick": "R3" + } + + for inputConfig in root.findall("inputConfig"): + if inputConfig.get("type") == "joystick": + for input_tag in inputConfig.findall("input"): + if input_tag.get("type") == "button": + es_name = input_tag.get("name") + button_id = int(input_tag.get("id")) + readable_name = es_button_names.get(es_name, es_name.upper()) + button_names[button_id] = readable_name + break + except Exception as e: + logger.error(f"Erreur parsing es_input.cfg: {e}") + + # Compléter avec des noms génériques + for i in range(16): + if i not in button_names: + button_names[i] = f"Bouton {i}" + + return button_names + +def get_controller_axis_names(): + """Récupère les noms des axes depuis es_input.cfg""" + es_input_path = "/usr/share/emulationstation/es_input.cfg" + axis_names = {} + + if not os.path.exists(es_input_path): + return {(i, d): f"Axe {i}{'+' if d > 0 else '-'}" for i in range(8) for d in [-1, 1]} + + try: + tree = ET.parse(es_input_path) + root = tree.getroot() + + # Mapping des noms ES vers des noms lisibles + es_axis_names = { + "leftx": "Joy G", "lefty": "Joy G", + "rightx": "Joy D", "righty": "Joy D", + "lefttrigger": "LT", "righttrigger": "RT" + } + + for inputConfig in root.findall("inputConfig"): + if inputConfig.get("type") == "joystick": + for input_tag in inputConfig.findall("input"): + if input_tag.get("type") == "axis": + es_name = input_tag.get("name") + axis_id = int(input_tag.get("id")) + value = int(input_tag.get("value", "1")) + direction = 1 if value > 0 else -1 + + if es_name in es_axis_names: + base_name = es_axis_names[es_name] + if "Joy" in base_name: + if "leftx" in es_name or "rightx" in es_name: + axis_names[(axis_id, direction)] = f"{base_name} {'Droite' if direction > 0 else 'Gauche'}" + else: + axis_names[(axis_id, direction)] = f"{base_name} {'Bas' if direction > 0 else 'Haut'}" + else: + axis_names[(axis_id, direction)] = base_name + break + except Exception as e: + logger.error(f"Erreur parsing es_input.cfg: {e}") + + # Compléter avec des noms génériques + for i in range(8): + for d in [-1, 1]: + if (i, d) not in axis_names: + axis_names[(i, d)] = f"Axe {i}{'+' if d > 0 else '-'}" + + return axis_names + +# Charger les noms depuis es_input.cfg +BUTTON_NAMES = get_controller_button_names() +AXIS_NAMES = get_controller_axis_names() + -# Noms pour les axes de joystick -AXIS_NAMES = { - (0, 1): "Joy G Haut", - (0, -1): "Joy G Bas", - (1, 1): "Joy G Gauche", - (1, -1): "Joy G Droite", - (2, 1): "Joy D Haut", - (2, -1): "Joy D Bas", - (3, 1): "Joy D Gauche", - (3, -1): "Joy D Droite", -} # Noms pour la croix directionnelle HAT_NAMES = { @@ -227,7 +297,7 @@ def load_controls_config(): return {} def save_controls_config(controls_config): - #Enregistre la configuration des contrôles dans controls.json + """Enregistre la configuration des contrôles dans controls.json""" try: os.makedirs(os.path.dirname(CONTROLS_CONFIG_PATH), exist_ok=True) with open(CONTROLS_CONFIG_PATH, "w") as f: @@ -236,8 +306,41 @@ def save_controls_config(controls_config): except Exception as e: logger.error(f"Erreur lors de l'enregistrement de controls.json : {e}") +def validate_controls_config(controls_config): + """Valide la structure de la configuration des contrôles""" + required_actions = ["confirm", "cancel", "up", "down", "left", "right"] + + for action in required_actions: + if action not in controls_config: + logger.warning(f"Action {action} manquante dans la configuration") + return False + + mapping = controls_config[action] + if "type" not in mapping: + logger.warning(f"Type manquant pour l'action {action}") + return False + + input_type = mapping["type"] + if input_type == "key" and "key" not in mapping: + logger.warning(f"Clé 'key' manquante pour l'action {action}") + return False + elif input_type == "button" and "button" not in mapping: + logger.warning(f"Clé 'button' manquante pour l'action {action}") + return False + elif input_type == "axis" and ("axis" not in mapping or "direction" not in mapping): + logger.warning(f"Clés 'axis' ou 'direction' manquantes pour l'action {action}") + return False + elif input_type == "hat" and "value" not in mapping: + logger.warning(f"Clé 'value' manquante pour l'action {action}") + return False + elif input_type == "mouse" and "button" not in mapping: + logger.warning(f"Clé 'button' manquante pour l'action {action}") + return False + + return True + def get_readable_input_name(event): - #Retourne un nom lisible pour une entrée (touche, bouton, axe, hat, ou souris) + """Retourne un nom lisible pour une entrée (touche, bouton, axe, hat, ou souris)""" if event.type == pygame.KEYDOWN: key_value = SDL_TO_PYGAME_KEY.get(event.key, event.key) return KEY_NAMES.get(key_value, pygame.key.name(key_value) or f"Touche {key_value}") @@ -245,202 +348,208 @@ def get_readable_input_name(event): return BUTTON_NAMES.get(event.button, f"Bouton {event.button}") elif event.type == pygame.JOYAXISMOTION: if abs(event.value) > 0.5: # Seuil pour détecter un mouvement significatif - return AXIS_NAMES.get((event.axis, 1 if event.value > 0 else -1), f"Axe {event.axis}") + return AXIS_NAMES.get((event.axis, 1 if event.value > 0 else -1), f"Axe {event.axis} {'Positif' if event.value > 0 else 'Négatif'}") elif event.type == pygame.JOYHATMOTION: - return HAT_NAMES.get(event.value, f"D-Pad {event.value}") + if event.value != (0, 0): # Ignorer la position neutre + return HAT_NAMES.get(event.value, f"D-Pad {event.value}") elif event.type == pygame.MOUSEBUTTONDOWN: return MOUSE_BUTTON_NAMES.get(event.button, f"Souris Bouton {event.button}") return "Inconnu" def map_controls(screen): - mapping = True - current_action = 0 - clock = pygame.time.Clock() - while mapping: - clock.tick(100) # 100 FPS + """Interface de mappage des contrôles avec maintien de 3 secondes""" + controls_config = load_controls_config() + current_action_index = 0 + current_input = None + input_held_time = 0 + last_input_name = None + last_frame_time = pygame.time.get_ticks() + config.needs_redraw = True + last_joyhat_time = 0 + + # État des entrées maintenues + held_keys = set() + held_buttons = set() + held_axes = {} + held_hats = {} + held_mouse_buttons = set() + + while current_action_index < len(ACTIONS): + if config.needs_redraw: + progress = min(input_held_time / HOLD_DURATION, 1.0) if current_input else 0.0 + draw_controls_mapping(screen, ACTIONS[current_action_index], last_input_name, current_input is not None, progress) + pygame.display.flip() + config.needs_redraw = False + + current_time = pygame.time.get_ticks() + delta_time = current_time - last_frame_time + last_frame_time = current_time + for event in pygame.event.get(): - # Initialisation des variables de contrôle - controls_config = load_controls_config() - current_action_index = 0 - current_input = None - input_held_time = 0 - last_input_name = None - last_frame_time = pygame.time.get_ticks() - config.needs_redraw = True - last_joyhat_time = 0 # Pour le débouncing des événements JOYHATMOTION - - # Initialiser l'état des boutons et axes pour suivre les relâchements - held_keys = set() - held_buttons = set() - held_axes = {} # {axis: direction} - held_hats = {} # {hat: value} - held_mouse_buttons = set() - - while current_action_index < len(ACTIONS): - if config.needs_redraw: - progress = min(input_held_time / HOLD_DURATION, 1.0) if current_input else 0.0 - draw_controls_mapping(screen, ACTIONS[current_action_index], last_input_name, current_input is not None, progress) - pygame.display.flip() - config.needs_redraw = False - - current_time = pygame.time.get_ticks() - delta_time = current_time - last_frame_time - last_frame_time = current_time - - events = pygame.event.get() - for event in events: - if event.type == pygame.QUIT: - return False - - # Détecter les relâchements pour réinitialiser - if event.type == pygame.KEYUP: - if event.key in held_keys: - held_keys.remove(event.key) - if current_input and current_input["type"] == "key" and current_input["value"] == event.key: - current_input = None - input_held_time = 0 - last_input_name = None - config.needs_redraw = True - logger.debug(f"Touche relâchée: {event.key}") - elif event.type == pygame.JOYBUTTONUP: - if event.button in held_buttons: - held_buttons.remove(event.button) - if current_input and current_input["type"] == "button" and current_input["value"] == event.button: - current_input = None - input_held_time = 0 - last_input_name = None - config.needs_redraw = True - logger.debug(f"Bouton relâché: {event.button}") - elif event.type == pygame.MOUSEBUTTONUP: - if event.button in held_mouse_buttons: - held_mouse_buttons.remove(event.button) - if current_input and current_input["type"] == "mouse" and current_input["value"] == event.button: - current_input = None - input_held_time = 0 - last_input_name = None - config.needs_redraw = True - logger.debug(f"Bouton souris relâché: {event.button}") - elif event.type == pygame.JOYAXISMOTION: - if abs(event.value) < 0.5: # Axe revenu à la position neutre - if event.axis in held_axes: - del held_axes[event.axis] - if current_input and current_input["type"] == "axis" and current_input["value"][0] == event.axis: - current_input = None - input_held_time = 0 - last_input_name = None - config.needs_redraw = True - logger.debug(f"Axe relâché: {event.axis}") - elif event.type == pygame.JOYHATMOTION: - logger.debug(f"JOYHATMOTION détecté: hat={event.hat}, value={event.value}") - if event.value == (0, 0): # D-Pad revenu à la position neutre - if event.hat in held_hats: - del held_hats[event.hat] - if current_input and current_input["type"] == "hat" and current_input["value"] == event.value: - current_input = None - input_held_time = 0 - last_input_name = None - config.needs_redraw = True - logger.debug(f"D-Pad relâché: {event.hat}") - continue # Ignorer les événements (0, 0) pour la détection des nouvelles entrées - - # Détecter les nouvelles entrées - if event.type in (pygame.KEYDOWN, pygame.JOYBUTTONDOWN, pygame.JOYAXISMOTION, pygame.JOYHATMOTION, pygame.MOUSEBUTTONDOWN): - # Appliquer le débouncing pour JOYHATMOTION - if event.type == pygame.JOYHATMOTION and (current_time - last_joyhat_time) < JOYHAT_DEBOUNCE: - logger.debug(f"Événement JOYHATMOTION ignoré (debounce): hat={event.hat}, value={event.value}") - continue - if event.type == pygame.JOYHATMOTION: - last_joyhat_time = current_time - - - input_name = get_readable_input_name(event) - if input_name != "Inconnu": - input_type = { - pygame.KEYDOWN: "key", - pygame.JOYBUTTONDOWN: "button", - pygame.JOYAXISMOTION: "axis", - pygame.JOYHATMOTION: "hat", - pygame.MOUSEBUTTONDOWN: "mouse", - }[event.type] - input_value = ( - SDL_TO_PYGAME_KEY.get(event.key, event.key) if event.type == pygame.KEYDOWN else - event.button if event.type == pygame.JOYBUTTONDOWN else - (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION and abs(event.value) > 0.5 else - event.value if event.type == pygame.JOYHATMOTION else - event.button - ) - - # Vérifier si l'entrée est nouvelle ou différente - if (current_input is None or - (input_type == "key" and current_input["value"] != input_value) or - (input_type == "button" and current_input["value"] != input_value) or - (input_type == "axis" and current_input["value"] != input_value) or - (input_type == "hat" and current_input["value"] != input_value) or - (input_type == "mouse" and current_input["value"] != input_value)): - current_input = {"type": input_type, "value": input_value} - input_held_time = 0 - last_input_name = input_name - config.needs_redraw = True - logger.debug(f"Nouvelle entrée détectée: {input_type}:{input_value} ({input_name})") - - # Mettre à jour les entrées maintenues - if input_type == "key": - held_keys.add(input_value) - elif input_type == "button": - held_buttons.add(input_value) - elif input_type == "axis": - held_axes[input_value[0]] = input_value[1] - elif input_type == "hat": - held_hats[event.hat] = input_value - elif input_type == "mouse": - held_mouse_buttons.add(input_value) - - # Désactivation du passage avec Échap - # Aucun code ici pour empêcher de sauter les actions avec Échap - - # Mettre à jour le temps de maintien - if current_input: - input_held_time += delta_time - if input_held_time >= HOLD_DURATION: - action_name = ACTIONS[current_action_index]["name"] - logger.debug(f"Entrée validée pour {action_name}: {current_input['type']}:{current_input['value']} ({last_input_name})") - controls_config[action_name] = { - "type": current_input["type"], - "value": current_input["value"], - "display": last_input_name - } - current_action_index += 1 + if event.type == pygame.QUIT: + return False + + # Gestion des relâchements + if event.type == pygame.KEYUP and event.key in held_keys: + held_keys.remove(event.key) + if current_input and current_input["type"] == "key" and current_input["value"] == event.key: + current_input = None + input_held_time = 0 + last_input_name = None + config.needs_redraw = True + elif event.type == pygame.JOYBUTTONUP and event.button in held_buttons: + held_buttons.remove(event.button) + if current_input and current_input["type"] == "button" and current_input["value"] == event.button: + current_input = None + input_held_time = 0 + last_input_name = None + config.needs_redraw = True + elif event.type == pygame.JOYAXISMOTION and abs(event.value) < 0.5: + if event.axis in held_axes: + held_direction = held_axes[event.axis] + if current_input and current_input["type"] == "axis" and current_input["value"][0] == event.axis and current_input["value"][1] == held_direction: current_input = None input_held_time = 0 last_input_name = None config.needs_redraw = True - # Réinitialiser les entrées maintenues pour éviter les interférences - held_keys.clear() - held_buttons.clear() - held_axes.clear() - held_hats.clear() - held_mouse_buttons.clear() + del held_axes[event.axis] + elif event.type == pygame.JOYHATMOTION and event.value == (0, 0): + if event.hat in held_hats: + del held_hats[event.hat] + if current_input and current_input["type"] == "hat": + current_input = None + input_held_time = 0 + last_input_name = None + config.needs_redraw = True + continue + elif event.type == pygame.MOUSEBUTTONUP and event.button in held_mouse_buttons: + held_mouse_buttons.remove(event.button) + if current_input and current_input["type"] == "mouse" and current_input["value"] == event.button: + current_input = None + input_held_time = 0 + last_input_name = None config.needs_redraw = True - - pygame.time.wait(10) - - save_controls_config(controls_config) - config.controls_config = controls_config - return True - pass - -def save_controls_config(config): - #Enregistre la configuration des contrôles dans un fichier JSON - try: - with open(CONTROLS_CONFIG_PATH, "w") as f: - json.dump(config, f, indent=4) - logger.debug("Configuration des contrôles enregistrée") - except Exception as e: - logger.error(f"Erreur lors de l'enregistrement de controls.json : {e}") - return False + + # Détection des nouvelles entrées + if event.type in (pygame.KEYDOWN, pygame.JOYBUTTONDOWN, pygame.JOYAXISMOTION, pygame.JOYHATMOTION, pygame.MOUSEBUTTONDOWN): + if event.type == pygame.JOYHATMOTION: + if (current_time - last_joyhat_time) < JOYHAT_DEBOUNCE: + continue + last_joyhat_time = current_time + + input_name = get_readable_input_name(event) + if input_name == "Inconnu": + continue + + # Déterminer le type et la valeur + if event.type == pygame.KEYDOWN: + input_type = "key" + input_value = SDL_TO_PYGAME_KEY.get(event.key, event.key) + elif event.type == pygame.JOYBUTTONDOWN: + input_type = "button" + input_value = event.button + elif event.type == pygame.JOYAXISMOTION and abs(event.value) > 0.5: + input_type = "axis" + direction = 1 if event.value > 0 else -1 + input_value = (event.axis, direction) + # Ignorer si c'est juste un changement de direction du même axe + if event.axis in held_axes and held_axes[event.axis] != direction: + continue + elif event.type == pygame.JOYHATMOTION: + input_type = "hat" + input_value = event.value + elif event.type == pygame.MOUSEBUTTONDOWN: + input_type = "mouse" + input_value = event.button + else: + continue + + # Nouvelle entrée détectée + if (current_input is None or + current_input["type"] != input_type or + current_input["value"] != input_value): + current_input = {"type": input_type, "value": input_value} + input_held_time = 0 + last_input_name = input_name + config.needs_redraw = True + + # Mettre à jour les entrées maintenues + if input_type == "key": + held_keys.add(input_value) + elif input_type == "button": + held_buttons.add(input_value) + elif input_type == "axis": + held_axes[input_value[0]] = input_value[1] + elif input_type == "hat": + held_hats[event.hat] = input_value + elif input_type == "mouse": + held_mouse_buttons.add(input_value) + + # Mise à jour du temps de maintien + if current_input: + input_held_time += delta_time + if input_held_time >= HOLD_DURATION: + action_name = ACTIONS[current_action_index]["name"] + + # Sauvegarder avec la structure attendue par controls.py + if current_input["type"] == "key": + controls_config[action_name] = { + "type": "key", + "key": current_input["value"], + "display": last_input_name + } + elif current_input["type"] == "button": + controls_config[action_name] = { + "type": "button", + "button": current_input["value"], + "display": last_input_name + } + elif current_input["type"] == "axis": + axis, direction = current_input["value"] + controls_config[action_name] = { + "type": "axis", + "axis": axis, + "direction": direction, + "display": last_input_name + } + elif current_input["type"] == "hat": + controls_config[action_name] = { + "type": "hat", + "value": current_input["value"], + "display": last_input_name + } + elif current_input["type"] == "mouse": + controls_config[action_name] = { + "type": "mouse", + "button": current_input["value"], + "display": last_input_name + } + + logger.debug(f"Contrôle mappé: {action_name} -> {controls_config[action_name]}") + current_action_index += 1 + current_input = None + input_held_time = 0 + last_input_name = None + config.needs_redraw = True + + # Réinitialiser les entrées maintenues + held_keys.clear() + held_buttons.clear() + held_axes.clear() + held_hats.clear() + held_mouse_buttons.clear() + + config.needs_redraw = True + + pygame.time.wait(10) + + save_controls_config(controls_config) + config.controls_config = controls_config return True + + def draw_controls_mapping(screen, action, last_input, waiting_for_input, hold_progress): #Affiche l'interface de mappage des contrôles avec une barre de progression pour le maintien draw_gradient(screen, (28, 37, 38), (47, 59, 61)) diff --git a/display.py b/display.py index 4f73d84..f86d2c2 100644 --- a/display.py +++ b/display.py @@ -680,10 +680,10 @@ def draw_history_list(screen): # logger.debug(f"Affichage terminé: {game_name}, status={status_text}") elif status == "Erreur": status_text = _("history_status_error").format(entry.get('message', 'Échec')) - logger.debug(f"Affichage erreur: {game_name}, status={status_text}") + #logger.debug(f"Affichage erreur: {game_name}, status={status_text}") else: status_text = status - logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}") + #logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}") color = THEME_COLORS["fond_lignes"] if i == config.current_history_item else THEME_COLORS["text"] platform_text = truncate_text_end(platform, config.small_font, col_platform_width - 10) diff --git a/network.py b/network.py index c75e8fe..179d3cf 100644 --- a/network.py +++ b/network.py @@ -176,7 +176,9 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas dest_dir = None for platform_dict in config.platform_dicts: if platform_dict["platform"] == platform: - dest_dir = platform_dict.get("folder") + dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", platform.lower().replace(" ", ""))) + logger.debug(f"Répertoire de destination trouvé pour {platform}: {dest_dir}") + #dest_dir = platform_dict.get("folder") break if not dest_dir: dest_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), platform.lower().replace(" ", "")) @@ -204,15 +206,15 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas session.headers.update(headers) # Première requête HEAD pour obtenir la vraie URL - logger.debug(f"Première requête HEAD vers {url}") + #logger.debug(f"Première requête HEAD vers {url}") head_response = session.head(url, timeout=30, allow_redirects=False) - logger.debug(f"HEAD Status: {head_response.status_code}, Headers: {dict(head_response.headers)}") + #logger.debug(f"HEAD Status: {head_response.status_code}, Headers: {dict(head_response.headers)}") # Suivre la redirection manuellement si nécessaire final_url = url if head_response.status_code in [301, 302, 303, 307, 308]: final_url = head_response.headers.get('Location', url) - logger.debug(f"Redirection détectée vers: {final_url}") + #logger.debug(f"Redirection détectée vers: {final_url}") # Requête GET vers l'URL finale avec en-têtes spécifiques download_headers = headers.copy() @@ -368,7 +370,7 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas logger.error(f"Erreur mise à jour progression: {str(e)}") thread.join() - logger.debug(f"Thread joined for {url}, task_id={task_id}") + #logger.debug(f"Thread joined for {url}, task_id={task_id}") return result[0], result[1] async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False, task_id=None): @@ -391,7 +393,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= dest_dir = None for platform_dict in config.platform_dicts: if platform_dict["platform"] == platform: - dest_dir = platform_dict.get("folder") + dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", platform.lower().replace(" ", ""))) break if not dest_dir: logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}") @@ -411,9 +413,9 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= "pretty": 1 } - logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/file/info.cgi pour {url}") + #logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/file/info.cgi pour {url}") response = requests.post("https://api.1fichier.com/v1/file/info.cgi", headers=headers, json=payload, timeout=30) - logger.debug(f"Réponse reçue, status: {response.status_code}") + #logger.debug(f"Réponse reçue, status: {response.status_code}") response.raise_for_status() file_info = response.json() @@ -434,9 +436,9 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= dest_path = os.path.join(dest_dir, sanitized_filename) logger.debug(f"Chemin destination: {dest_path}") - logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/download/get_token.cgi pour {url}") + #logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/download/get_token.cgi pour {url}") response = requests.post("https://api.1fichier.com/v1/download/get_token.cgi", headers=headers, json=payload, timeout=30) - logger.debug(f"Réponse reçue, status: {response.status_code}") + #logger.debug(f"Réponse reçue, status: {response.status_code}") response.raise_for_status() download_info = response.json() @@ -452,12 +454,12 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= retry_delay = 10 # Initialiser la progression avec task_id progress_queue.put((task_id, 0, 0)) # Taille initiale inconnue - logger.debug(f"Progression initiale envoyée: 0% pour {game_name}, task_id={task_id}") + #logger.debug(f"Progression initiale envoyée: 0% pour {game_name}, task_id={task_id}") for attempt in range(retries): try: - logger.debug(f"Tentative {attempt + 1} : Envoi requête GET à {final_url}") + #logger.debug(f"Tentative {attempt + 1} : Envoi requête GET à {final_url}") with requests.get(final_url, stream=True, headers={'User-Agent': 'Mozilla/5.0'}, timeout=30) as response: - logger.debug(f"Réponse reçue, status: {response.status_code}") + #logger.debug(f"Réponse reçue, status: {response.status_code}") response.raise_for_status() total_size = int(response.headers.get('content-length', 0)) logger.debug(f"Taille totale: {total_size} octets") @@ -496,7 +498,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= entry["downloaded_size"] = downloaded entry["total_size"] = total_size config.needs_redraw = True - logger.debug(f"Progression mise à jour: {entry['progress']:.1f}% pour {game_name}") + #logger.debug(f"Progression mise à jour: {entry['progress']:.1f}% pour {game_name}") break progress_queue.put((task_id, downloaded, total_size)) last_update_time = current_time diff --git a/rom_extensions.json b/rom_extensions.json index bdf3fb8..84a77d5 100644 --- a/rom_extensions.json +++ b/rom_extensions.json @@ -1,7 +1,7 @@ [ { "system": "3DO INTERACTIVE MULTIPLAYER", - "folder": "/userdata/roms/3do", + "folder": "3do", "extensions": [ ".iso", ".chd", @@ -10,7 +10,7 @@ }, { "system": "3DS", - "folder": "/userdata/roms/3ds", + "folder": "3ds", "extensions": [ ".3ds", ".3dsx", @@ -23,14 +23,14 @@ }, { "system": "ABUSE", - "folder": "/userdata/roms/abuse", + "folder": "abuse", "extensions": [ ".game" ] }, { "system": "ADAM", - "folder": "/userdata/roms/adam", + "folder": "adam", "extensions": [ ".wav", ".ddp", @@ -55,7 +55,7 @@ }, { "system": "ADVENTURE VISION", - "folder": "/userdata/roms/advision", + "folder": "advision", "extensions": [ ".bin", ".zip", @@ -64,7 +64,7 @@ }, { "system": "AMIGA AGA", - "folder": "/userdata/roms/amiga1200", + "folder": "amiga1200", "extensions": [ ".adf", ".uae", @@ -83,7 +83,7 @@ }, { "system": "AMIGA OCS/ECS", - "folder": "/userdata/roms/amiga500", + "folder": "amiga500", "extensions": [ ".adf", ".uae", @@ -102,7 +102,7 @@ }, { "system": "AMIGA CD32", - "folder": "/userdata/roms/amigacd32", + "folder": "amigacd32", "extensions": [ ".bin", ".cue", @@ -112,7 +112,7 @@ }, { "system": "AMIGA CDTV", - "folder": "/userdata/roms/amigacdtv", + "folder": "amigacdtv", "extensions": [ ".bin", ".cue", @@ -123,7 +123,7 @@ }, { "system": "AMSTRAD CPC", - "folder": "/userdata/roms/amstradcpc", + "folder": "amstradcpc", "extensions": [ ".dsk", ".sna", @@ -137,7 +137,7 @@ }, { "system": "M-1000", - "folder": "/userdata/roms/apfm1000", + "folder": "apfm1000", "extensions": [ ".bin", ".zip", @@ -146,7 +146,7 @@ }, { "system": "APPLE II", - "folder": "/userdata/roms/apple2", + "folder": "apple2", "extensions": [ ".nib", ".do", @@ -167,7 +167,7 @@ }, { "system": "APPLE IIGS", - "folder": "/userdata/roms/apple2gs", + "folder": "apple2gs", "extensions": [ ".2mg", ".do", @@ -200,7 +200,7 @@ }, { "system": "ARCADIA 2001", - "folder": "/userdata/roms/arcadia", + "folder": "arcadia", "extensions": [ ".bin", ".zip", @@ -209,7 +209,7 @@ }, { "system": "ARCHIMEDES", - "folder": "/userdata/roms/archimedes", + "folder": "archimedes", "extensions": [ ".mfi", ".dfi", @@ -246,7 +246,7 @@ }, { "system": "ARDUBOY", - "folder": "/userdata/roms/arduboy", + "folder": "arduboy", "extensions": [ ".hex", ".zip", @@ -255,7 +255,7 @@ }, { "system": "ASTROCADE", - "folder": "/userdata/roms/astrocde", + "folder": "astrocde", "extensions": [ ".bin", ".zip", @@ -264,7 +264,7 @@ }, { "system": "ATARI 2600", - "folder": "/userdata/roms/atari2600", + "folder": "atari2600", "extensions": [ ".a26", ".bin", @@ -274,7 +274,7 @@ }, { "system": "ATARI 5200", - "folder": "/userdata/roms/atari5200", + "folder": "atari5200", "extensions": [ ".rom", ".xfd", @@ -292,7 +292,7 @@ }, { "system": "ATARI 7800", - "folder": "/userdata/roms/atari7800", + "folder": "atari7800", "extensions": [ ".a78", ".bin", @@ -302,7 +302,7 @@ }, { "system": "ATARI 800", - "folder": "/userdata/roms/atari800", + "folder": "atari800", "extensions": [ ".rom", ".xfd", @@ -321,7 +321,7 @@ }, { "system": "ATARI ST", - "folder": "/userdata/roms/atarist", + "folder": "atarist", "extensions": [ ".st", ".msa", @@ -337,7 +337,7 @@ }, { "system": "ATOM", - "folder": "/userdata/roms/atom", + "folder": "atom", "extensions": [ ".wav", ".tap", @@ -365,7 +365,7 @@ }, { "system": "ATOMISWAVE", - "folder": "/userdata/roms/atomiswave", + "folder": "atomiswave", "extensions": [ ".lst", ".bin", @@ -376,7 +376,7 @@ }, { "system": "BBC MICRO", - "folder": "/userdata/roms/bbc", + "folder": "bbc", "extensions": [ ".mfi", ".dfi", @@ -412,7 +412,7 @@ }, { "system": "COMMODORE 128", - "folder": "/userdata/roms/c128", + "folder": "c128", "extensions": [ ".d64", ".d81", @@ -425,7 +425,7 @@ }, { "system": "COMMODORE VIC-20", - "folder": "/userdata/roms/c20", + "folder": "c20", "extensions": [ ".20", ".40", @@ -446,7 +446,7 @@ }, { "system": "COMMODORE 64", - "folder": "/userdata/roms/c64", + "folder": "c64", "extensions": [ ".d64", ".d71", @@ -464,7 +464,7 @@ }, { "system": "CAMPUTERS LYNX", - "folder": "/userdata/roms/camplynx", + "folder": "camplynx", "extensions": [ ".wav", ".tap", @@ -475,21 +475,21 @@ }, { "system": "CANNONBALL", - "folder": "/userdata/roms/cannonball", + "folder": "cannonball", "extensions": [ ".cannonball" ] }, { "system": "CAVE STORY", - "folder": "/userdata/roms/cavestory", + "folder": "cavestory", "extensions": [ ".exe" ] }, { "system": "CD-I", - "folder": "/userdata/roms/cdi", + "folder": "cdi", "extensions": [ ".chd", ".cue", @@ -502,21 +502,21 @@ }, { "system": "C-DOGS SDL", - "folder": "/userdata/roms/cdogs", + "folder": "cdogs", "extensions": [ ".game" ] }, { "system": "COMMANDER GENIUS", - "folder": "/userdata/roms/cgenius", + "folder": "cgenius", "extensions": [ ".cgenius" ] }, { "system": "CHANNEL-F", - "folder": "/userdata/roms/channelf", + "folder": "channelf", "extensions": [ ".zip", ".rom", @@ -526,14 +526,14 @@ }, { "system": "SEGA CHIHIRO", - "folder": "/userdata/roms/chihiro", + "folder": "chihiro", "extensions": [ ".iso" ] }, { "system": "COLOR COMPUTER", - "folder": "/userdata/roms/coco", + "folder": "coco", "extensions": [ ".wav", ".cas", @@ -546,7 +546,7 @@ }, { "system": "COLECOVISION", - "folder": "/userdata/roms/colecovision", + "folder": "colecovision", "extensions": [ ".bin", ".col", @@ -557,7 +557,7 @@ }, { "system": "COMMANDER X16", - "folder": "/userdata/roms/commanderx16", + "folder": "commanderx16", "extensions": [ ".bas", ".img", @@ -566,14 +566,14 @@ }, { "system": "CORSIXTH", - "folder": "/userdata/roms/corsixth", + "folder": "corsixth", "extensions": [ ".game" ] }, { "system": "COMMODORE PLUS4", - "folder": "/userdata/roms/cplus4", + "folder": "cplus4", "extensions": [ ".d64", ".prg", @@ -585,7 +585,7 @@ }, { "system": "CREATIVISION", - "folder": "/userdata/roms/crvision", + "folder": "crvision", "extensions": [ ".bin", ".rom", @@ -595,7 +595,7 @@ }, { "system": "DAPHNE", - "folder": "/userdata/roms/daphne", + "folder": "daphne", "extensions": [ ".daphne", ".squashfs" @@ -603,21 +603,21 @@ }, { "system": "DIABLO", - "folder": "/userdata/roms/devilutionx", + "folder": "devilutionx", "extensions": [ ".mpq" ] }, { "system": "DOOM 3", - "folder": "/userdata/roms/doom3", + "folder": "doom3", "extensions": [ ".d3" ] }, { "system": "DOS (X86)", - "folder": "/userdata/roms/dos", + "folder": "dos", "extensions": [ ".pc", ".dos", @@ -631,7 +631,7 @@ }, { "system": "DREAMCAST", - "folder": "/userdata/roms/dreamcast", + "folder": "dreamcast", "extensions": [ ".cdi", ".cue", @@ -642,7 +642,7 @@ }, { "system": "DXX REBIRTH", - "folder": "/userdata/roms/dxx-rebirth", + "folder": "dxx-rebirth", "extensions": [ ".d1x", ".d2x" @@ -650,7 +650,7 @@ }, { "system": "EASYRPG", - "folder": "/userdata/roms/easyrpg", + "folder": "easyrpg", "extensions": [ ".easyrpg", ".squashfs", @@ -659,7 +659,7 @@ }, { "system": "ECWOLF", - "folder": "/userdata/roms/ecwolf", + "folder": "ecwolf", "extensions": [ ".ecwolf", ".pk3", @@ -668,14 +668,14 @@ }, { "system": "EDUKE32", - "folder": "/userdata/roms/eduke32", + "folder": "eduke32", "extensions": [ ".eduke32" ] }, { "system": "ELECTRON", - "folder": "/userdata/roms/electron", + "folder": "electron", "extensions": [ ".wav", ".csw", @@ -708,28 +708,28 @@ }, { "system": "WOLFENSTEIN - ENEMY TERRITORY", - "folder": "/userdata/roms/etlegacy", + "folder": "etlegacy", "extensions": [ ".etl" ] }, { "system": "FALLOUT COMMUNITY EDITION", - "folder": "/userdata/roms/fallout1-ce", + "folder": "fallout1-ce", "extensions": [ ".f1ce" ] }, { "system": "FALLOUT 2 COMMUNITY EDITION", - "folder": "/userdata/roms/fallout2-ce", + "folder": "fallout2-ce", "extensions": [ ".f2ce" ] }, { "system": "FINAL BURN NEO", - "folder": "/userdata/roms/fbneo", + "folder": "fbneo", "extensions": [ ".zip", ".7z" @@ -737,7 +737,7 @@ }, { "system": "FAMILY COMPUTER DISK SYSTEM", - "folder": "/userdata/roms/fds", + "folder": "fds", "extensions": [ ".fds", ".zip", @@ -746,21 +746,21 @@ }, { "system": "FLASH PLAYER", - "folder": "/userdata/roms/flash", + "folder": "flash", "extensions": [ ".swf" ] }, { "system": "APPLICATIONS", - "folder": "/userdata/roms/flatpak", + "folder": "flatpak", "extensions": [ ".flatpak" ] }, { "system": "FM-7", - "folder": "/userdata/roms/fm7", + "folder": "fm7", "extensions": [ ".wav", ".t77", @@ -782,7 +782,7 @@ }, { "system": "FM-TOWNS", - "folder": "/userdata/roms/fmtowns", + "folder": "fmtowns", "extensions": [ ".bin", ".m3u", @@ -812,21 +812,21 @@ }, { "system": "FUTURE PINBALL", - "folder": "/userdata/roms/fpinball", + "folder": "fpinball", "extensions": [ ".fpt" ] }, { "system": "ION FURY", - "folder": "/userdata/roms/fury", + "folder": "fury", "extensions": [ ".grp" ] }, { "system": "GAMATE", - "folder": "/userdata/roms/gamate", + "folder": "gamate", "extensions": [ ".bin", ".zip", @@ -835,7 +835,7 @@ }, { "system": "GAME AND WATCH", - "folder": "/userdata/roms/gameandwatch", + "folder": "gameandwatch", "extensions": [ ".mgw", ".zip", @@ -844,7 +844,7 @@ }, { "system": "GAME.COM", - "folder": "/userdata/roms/gamecom", + "folder": "gamecom", "extensions": [ ".bin", ".tgc", @@ -854,7 +854,7 @@ }, { "system": "GAMECUBE", - "folder": "/userdata/roms/gamecube", + "folder": "gamecube", "extensions": [ ".gcm", ".iso", @@ -870,7 +870,7 @@ }, { "system": "GAME GEAR", - "folder": "/userdata/roms/gamegear", + "folder": "gamegear", "extensions": [ ".bin", ".gg", @@ -880,7 +880,7 @@ }, { "system": "GAME POCKET COMPUTER", - "folder": "/userdata/roms/gamepock", + "folder": "gamepock", "extensions": [ ".bin", ".zip", @@ -889,7 +889,7 @@ }, { "system": "GAME BOY", - "folder": "/userdata/roms/gb", + "folder": "gb", "extensions": [ ".gb", ".zip", @@ -898,7 +898,7 @@ }, { "system": "GAME BOY (2 PLAYERS)", - "folder": "/userdata/roms/gb2players", + "folder": "gb2players", "extensions": [ ".gb", ".gb2", @@ -909,7 +909,7 @@ }, { "system": "GAME BOY ADVANCE", - "folder": "/userdata/roms/gba", + "folder": "gba", "extensions": [ ".gba", ".zip", @@ -918,7 +918,7 @@ }, { "system": "GAME BOY COLOR", - "folder": "/userdata/roms/gbc", + "folder": "gbc", "extensions": [ ".gbc", ".zip", @@ -927,7 +927,7 @@ }, { "system": "GAME BOY COLOR (2 PLAYERS)", - "folder": "/userdata/roms/gbc2players", + "folder": "gbc2players", "extensions": [ ".gbc", ".gb2", @@ -938,7 +938,7 @@ }, { "system": "GAME MASTER", - "folder": "/userdata/roms/gmaster", + "folder": "gmaster", "extensions": [ ".bin", ".zip", @@ -947,7 +947,7 @@ }, { "system": "GP32", - "folder": "/userdata/roms/gp32", + "folder": "gp32", "extensions": [ ".smc", ".zip", @@ -956,7 +956,7 @@ }, { "system": "GX4000", - "folder": "/userdata/roms/gx4000", + "folder": "gx4000", "extensions": [ ".dsk", ".m3u", @@ -967,7 +967,7 @@ }, { "system": "GZDOOM", - "folder": "/userdata/roms/gzdoom", + "folder": "gzdoom", "extensions": [ ".wad", ".iwad", @@ -977,21 +977,21 @@ }, { "system": "HYDRA CASTLE LABYRINTH", - "folder": "/userdata/roms/hcl", + "folder": "hcl", "extensions": [ ".game" ] }, { "system": "HURRICAN", - "folder": "/userdata/roms/hurrican", + "folder": "hurrican", "extensions": [ ".game" ] }, { "system": "IKEMEN", - "folder": "/userdata/roms/ikemen", + "folder": "ikemen", "extensions": [ ".ikemen", ".pc" @@ -999,7 +999,7 @@ }, { "system": "MATTEL INTELLIVISION", - "folder": "/userdata/roms/intellivision", + "folder": "intellivision", "extensions": [ ".int", ".bin", @@ -1010,14 +1010,14 @@ }, { "system": "RETURN TO CASTLE WOLFENSTEIN", - "folder": "/userdata/roms/iortcw", + "folder": "iortcw", "extensions": [ ".rtcw" ] }, { "system": "JAGUAR", - "folder": "/userdata/roms/jaguar", + "folder": "jaguar", "extensions": [ ".j64", ".jag", @@ -1030,7 +1030,7 @@ }, { "system": "JAGUAR CD", - "folder": "/userdata/roms/jaguarcd", + "folder": "jaguarcd", "extensions": [ ".cue", ".cdi" @@ -1038,14 +1038,14 @@ }, { "system": "JAZZ JACKRABBIT 2", - "folder": "/userdata/roms/jazz2", + "folder": "jazz2", "extensions": [ ".game" ] }, { "system": "LASER 310", - "folder": "/userdata/roms/laser310", + "folder": "laser310", "extensions": [ ".vz", ".wav", @@ -1056,7 +1056,7 @@ }, { "system": "LCD GAMES", - "folder": "/userdata/roms/lcdgames", + "folder": "lcdgames", "extensions": [ ".mgw", ".zip", @@ -1065,7 +1065,7 @@ }, { "system": "LOWRES NX", - "folder": "/userdata/roms/lowresnx", + "folder": "lowresnx", "extensions": [ ".nx", ".zip", @@ -1074,7 +1074,7 @@ }, { "system": "LUTRO", - "folder": "/userdata/roms/lutro", + "folder": "lutro", "extensions": [ ".lutro", ".zip", @@ -1083,7 +1083,7 @@ }, { "system": "ATARI LYNX", - "folder": "/userdata/roms/lynx", + "folder": "lynx", "extensions": [ ".bll", ".lnx", @@ -1095,7 +1095,7 @@ }, { "system": "MACINTOSH", - "folder": "/userdata/roms/macintosh", + "folder": "macintosh", "extensions": [ ".dsk", ".zip", @@ -1135,7 +1135,7 @@ }, { "system": "MAME", - "folder": "/userdata/roms/mame", + "folder": "mame", "extensions": [ ".zip", ".7z" @@ -1143,7 +1143,7 @@ }, { "system": "MASTER SYSTEM", - "folder": "/userdata/roms/mastersystem", + "folder": "mastersystem", "extensions": [ ".bin", ".sms", @@ -1153,7 +1153,7 @@ }, { "system": "MEGA DRIVE", - "folder": "/userdata/roms/megadrive", + "folder": "megadrive", "extensions": [ ".bin", ".gen", @@ -1166,7 +1166,7 @@ }, { "system": "MEGA DUCK / COUGAR BOY", - "folder": "/userdata/roms/megaduck", + "folder": "megaduck", "extensions": [ ".bin", ".zip", @@ -1175,35 +1175,35 @@ }, { "system": "MODEL 2", - "folder": "/userdata/roms/model2", + "folder": "model2", "extensions": [ ".zip" ] }, { "system": "MODEL 3", - "folder": "/userdata/roms/model3", + "folder": "model3", "extensions": [ ".zip" ] }, { "system": "MOONLIGHT EMBEDDED", - "folder": "/userdata/roms/moonlight", + "folder": "moonlight", "extensions": [ ".moonlight" ] }, { "system": "MRBOOM", - "folder": "/userdata/roms/mrboom", + "folder": "mrboom", "extensions": [ ".libretro" ] }, { "system": "MSU-MD", - "folder": "/userdata/roms/msu-md", + "folder": "msu-md", "extensions": [ ".md", ".zip", @@ -1213,7 +1213,7 @@ }, { "system": "MSX1", - "folder": "/userdata/roms/msx1", + "folder": "msx1", "extensions": [ ".dsk", ".mx1", @@ -1228,7 +1228,7 @@ }, { "system": "MSX2", - "folder": "/userdata/roms/msx2", + "folder": "msx2", "extensions": [ ".dsk", ".mx2", @@ -1243,7 +1243,7 @@ }, { "system": "MSX2+", - "folder": "/userdata/roms/msx2+", + "folder": "msx2+", "extensions": [ ".dsk", ".mx2", @@ -1257,7 +1257,7 @@ }, { "system": "MSX TURBO-R", - "folder": "/userdata/roms/msxturbor", + "folder": "msxturbor", "extensions": [ ".dsk", ".mx2", @@ -1270,14 +1270,14 @@ }, { "system": "MUGEN", - "folder": "/userdata/roms/mugen", + "folder": "mugen", "extensions": [ ".pc" ] }, { "system": "OTHELLO MULTIVISION", - "folder": "/userdata/roms/multivision", + "folder": "multivision", "extensions": [ ".bin", ".sg", @@ -1287,7 +1287,7 @@ }, { "system": "NINTENDO 64", - "folder": "/userdata/roms/n64", + "folder": "n64", "extensions": [ ".z64", ".n64", @@ -1298,7 +1298,7 @@ }, { "system": "NINTENDO 64 DISK DRIVE", - "folder": "/userdata/roms/n64dd", + "folder": "n64dd", "extensions": [ ".z64", ".n64", @@ -1309,14 +1309,14 @@ }, { "system": "NAMCO 246/256", - "folder": "/userdata/roms/namco2x6", + "folder": "namco2x6", "extensions": [ ".zip" ] }, { "system": "NAOMI", - "folder": "/userdata/roms/naomi", + "folder": "naomi", "extensions": [ ".lst", ".bin", @@ -1327,7 +1327,7 @@ }, { "system": "NAOMI 2", - "folder": "/userdata/roms/naomi2", + "folder": "naomi2", "extensions": [ ".zip", ".7z" @@ -1335,7 +1335,7 @@ }, { "system": "NINTENDO DS", - "folder": "/userdata/roms/nds", + "folder": "nds", "extensions": [ ".nds", ".bin", @@ -1345,7 +1345,7 @@ }, { "system": "NEO-GEO", - "folder": "/userdata/roms/neogeo", + "folder": "neogeo", "extensions": [ ".7z", ".zip" @@ -1353,7 +1353,7 @@ }, { "system": "NEO-GEO CD", - "folder": "/userdata/roms/neogeocd", + "folder": "neogeocd", "extensions": [ ".cue", ".iso", @@ -1362,7 +1362,7 @@ }, { "system": "NINTENDO ENTERTAINMENT SYSTEM", - "folder": "/userdata/roms/nes", + "folder": "nes", "extensions": [ ".nes", ".unif", @@ -1373,7 +1373,7 @@ }, { "system": "NEO-GEO POCKET", - "folder": "/userdata/roms/ngp", + "folder": "ngp", "extensions": [ ".ngp", ".zip", @@ -1382,7 +1382,7 @@ }, { "system": "NEO-GEO POCKET COLOR", - "folder": "/userdata/roms/ngpc", + "folder": "ngpc", "extensions": [ ".ngc", ".zip", @@ -1391,7 +1391,7 @@ }, { "system": "ODYSSEY2", - "folder": "/userdata/roms/o2em", + "folder": "o2em", "extensions": [ ".bin", ".zip", @@ -1400,35 +1400,35 @@ }, { "system": "OD-COMMANDER", - "folder": "/userdata/roms/odcommander", + "folder": "odcommander", "extensions": [ ".odc" ] }, { "system": "OPENBOR", - "folder": "/userdata/roms/openbor", + "folder": "openbor", "extensions": [ ".pak" ] }, { "system": "JAZZ JACKRABBIT", - "folder": "/userdata/roms/openjazz", + "folder": "openjazz", "extensions": [ ".game" ] }, { "system": "OPENLARA", - "folder": "/userdata/roms/openlara", + "folder": "openlara", "extensions": [ ".croft" ] }, { "system": "PC-8800", - "folder": "/userdata/roms/pc88", + "folder": "pc88", "extensions": [ ".cmt", ".d88", @@ -1438,7 +1438,7 @@ }, { "system": "PC-9800", - "folder": "/userdata/roms/pc98", + "folder": "pc98", "extensions": [ ".d98", ".zip", @@ -1463,7 +1463,7 @@ }, { "system": "PC ENGINE", - "folder": "/userdata/roms/pcengine", + "folder": "pcengine", "extensions": [ ".pce", ".bin", @@ -1473,7 +1473,7 @@ }, { "system": "PC ENGINE CD", - "folder": "/userdata/roms/pcenginecd", + "folder": "pcenginecd", "extensions": [ ".pce", ".cue", @@ -1485,7 +1485,7 @@ }, { "system": "PC-FX", - "folder": "/userdata/roms/pcfx", + "folder": "pcfx", "extensions": [ ".cue", ".ccd", @@ -1498,7 +1498,7 @@ }, { "system": "PDP-1", - "folder": "/userdata/roms/pdp1", + "folder": "pdp1", "extensions": [ ".zip", ".7z", @@ -1509,7 +1509,7 @@ }, { "system": "COMMODORE PET", - "folder": "/userdata/roms/pet", + "folder": "pet", "extensions": [ ".a0", ".b0", @@ -1526,7 +1526,7 @@ }, { "system": "SEGA PICO", - "folder": "/userdata/roms/pico", + "folder": "pico", "extensions": [ ".bin", ".md", @@ -1536,7 +1536,7 @@ }, { "system": "PICO-8", - "folder": "/userdata/roms/pico8", + "folder": "pico8", "extensions": [ ".p8", ".png", @@ -1545,7 +1545,7 @@ }, { "system": "PLUG AND PLAY TV GAMES", - "folder": "/userdata/roms/plugnplay", + "folder": "plugnplay", "extensions": [ ".zip", ".7z" @@ -1553,7 +1553,7 @@ }, { "system": "POKEMON MINI", - "folder": "/userdata/roms/pokemini", + "folder": "pokemini", "extensions": [ ".min", ".zip", @@ -1562,7 +1562,7 @@ }, { "system": "PRBOOM", - "folder": "/userdata/roms/prboom", + "folder": "prboom", "extensions": [ ".wad", ".iwad", @@ -1571,7 +1571,7 @@ }, { "system": "PLAYSTATION 2", - "folder": "/userdata/roms/ps2", + "folder": "ps2", "extensions": [ ".iso", ".mdf", @@ -1587,7 +1587,7 @@ }, { "system": "PLAYSTATION 3", - "folder": "/userdata/roms/ps3", + "folder": "ps3", "extensions": [ ".ps3", ".psn", @@ -1596,7 +1596,7 @@ }, { "system": "PLAYSTATION PORTABLE", - "folder": "/userdata/roms/psp", + "folder": "psp", "extensions": [ ".iso", ".cso", @@ -1606,7 +1606,7 @@ }, { "system": "PLAYSTATION VITA", - "folder": "/userdata/roms/psvita", + "folder": "psvita", "extensions": [ ".zip", ".psvita" @@ -1614,7 +1614,7 @@ }, { "system": "PLAYSTATION", - "folder": "/userdata/roms/psx", + "folder": "psx", "extensions": [ ".cue", ".img", @@ -1630,7 +1630,7 @@ }, { "system": "PV-1000", - "folder": "/userdata/roms/pv1000", + "folder": "pv1000", "extensions": [ ".bin", ".zip", @@ -1639,14 +1639,14 @@ }, { "system": "PYGAME", - "folder": "/userdata/roms/pygame", + "folder": "pygame", "extensions": [ ".pygame" ] }, { "system": "PYXEL", - "folder": "/userdata/roms/pyxel", + "folder": "pyxel", "extensions": [ ".py", ".pyxapp" @@ -1654,35 +1654,35 @@ }, { "system": "QUAKE III", - "folder": "/userdata/roms/quake3", + "folder": "quake3", "extensions": [ ".quake3" ] }, { "system": "RAZE", - "folder": "/userdata/roms/raze", + "folder": "raze", "extensions": [ ".raze" ] }, { "system": "REMINISCENCE", - "folder": "/userdata/roms/reminiscence", + "folder": "reminiscence", "extensions": [ ".rem" ] }, { "system": "RISE OF THE TRIAD", - "folder": "/userdata/roms/rott", + "folder": "rott", "extensions": [ ".rott" ] }, { "system": "SAM COUPÉ", - "folder": "/userdata/roms/samcoupe", + "folder": "samcoupe", "extensions": [ ".cpm", ".dsk", @@ -1696,7 +1696,7 @@ }, { "system": "SATELLAVIEW", - "folder": "/userdata/roms/satellaview", + "folder": "satellaview", "extensions": [ ".bs", ".smc", @@ -1708,7 +1708,7 @@ }, { "system": "SATURN", - "folder": "/userdata/roms/saturn", + "folder": "saturn", "extensions": [ ".cue", ".ccd", @@ -1721,7 +1721,7 @@ }, { "system": "SCUMMVM", - "folder": "/userdata/roms/scummvm", + "folder": "scummvm", "extensions": [ ".scummvm", ".squashfs" @@ -1729,7 +1729,7 @@ }, { "system": "SUPER CASSETTE VISION", - "folder": "/userdata/roms/scv", + "folder": "scv", "extensions": [ ".bin", ".zip", @@ -1738,14 +1738,14 @@ }, { "system": "SDLPOP", - "folder": "/userdata/roms/sdlpop", + "folder": "sdlpop", "extensions": [ ".sdlpop" ] }, { "system": "32X", - "folder": "/userdata/roms/sega32x", + "folder": "sega32x", "extensions": [ ".32x", ".chd", @@ -1758,7 +1758,7 @@ }, { "system": "SEGA CD", - "folder": "/userdata/roms/segacd", + "folder": "segacd", "extensions": [ ".cue", ".iso", @@ -1768,7 +1768,7 @@ }, { "system": "SG-1000", - "folder": "/userdata/roms/sg1000", + "folder": "sg1000", "extensions": [ ".bin", ".sg", @@ -1778,7 +1778,7 @@ }, { "system": "SUPER GAME BOY", - "folder": "/userdata/roms/sgb", + "folder": "sgb", "extensions": [ ".gb", ".gbc", @@ -1788,7 +1788,7 @@ }, { "system": "SINGE", - "folder": "/userdata/roms/singe", + "folder": "singe", "extensions": [ ".daphne", ".squashfs" @@ -1796,7 +1796,7 @@ }, { "system": "SUPER NINTENDO ENTERTAINMENT SYSTEM", - "folder": "/userdata/roms/snes", + "folder": "snes", "extensions": [ ".smc", ".fig", @@ -1812,7 +1812,7 @@ }, { "system": "SUPER DISC (MSU1)", - "folder": "/userdata/roms/snes-msu1", + "folder": "snes-msu1", "extensions": [ ".smc", ".sfc", @@ -1821,7 +1821,7 @@ }, { "system": "SOCRATES", - "folder": "/userdata/roms/socrates", + "folder": "socrates", "extensions": [ ".bin", ".zip", @@ -1830,7 +1830,7 @@ }, { "system": "SOLARUS", - "folder": "/userdata/roms/solarus", + "folder": "solarus", "extensions": [ ".zip", ".solarus" @@ -1838,21 +1838,21 @@ }, { "system": "SONIC MANIA", - "folder": "/userdata/roms/sonic-mania", + "folder": "sonic-mania", "extensions": [ ".sman" ] }, { "system": "SONIC 3 A.I.R.", - "folder": "/userdata/roms/sonic3-air", + "folder": "sonic3-air", "extensions": [ ".s3air" ] }, { "system": "SONIC RETRO ENGINE", - "folder": "/userdata/roms/sonicretro", + "folder": "sonicretro", "extensions": [ ".son", ".scd" @@ -1860,7 +1860,7 @@ }, { "system": "SPECTRAVIDEO SV-328", - "folder": "/userdata/roms/spectravideo", + "folder": "spectravideo", "extensions": [ ".zip", ".7z", @@ -1869,14 +1869,14 @@ }, { "system": "STEAM", - "folder": "/userdata/roms/steam", + "folder": "steam", "extensions": [ ".steam" ] }, { "system": "SUFAMI TURBO", - "folder": "/userdata/roms/sufami", + "folder": "sufami", "extensions": [ ".st", ".fig", @@ -1889,14 +1889,14 @@ }, { "system": "SUPER MARIO WAR", - "folder": "/userdata/roms/superbroswar", + "folder": "superbroswar", "extensions": [ ".game" ] }, { "system": "SUPERGRAFX", - "folder": "/userdata/roms/supergrafx", + "folder": "supergrafx", "extensions": [ ".pce", ".sgx", @@ -1909,7 +1909,7 @@ }, { "system": "SUPERVISION", - "folder": "/userdata/roms/supervision", + "folder": "supervision", "extensions": [ ".sv", ".zip", @@ -1918,7 +1918,7 @@ }, { "system": "SUPER A'CAN", - "folder": "/userdata/roms/supracan", + "folder": "supracan", "extensions": [ ".bin", ".zip", @@ -1927,7 +1927,7 @@ }, { "system": "SEGA SP", - "folder": "/userdata/roms/systemsp", + "folder": "systemsp", "extensions": [ ".lst", ".bin", @@ -1938,14 +1938,14 @@ }, { "system": "THE FORCE ENGINE", - "folder": "/userdata/roms/theforceengine", + "folder": "theforceengine", "extensions": [ ".tfe" ] }, { "system": "THEXTECH", - "folder": "/userdata/roms/thextech", + "folder": "thextech", "extensions": [ ".smbx", ".squashfs" @@ -1953,7 +1953,7 @@ }, { "system": "THOMSON - MO/TO (THEODORE)", - "folder": "/userdata/roms/thomson", + "folder": "thomson", "extensions": [ ".fd", ".sap", @@ -1966,7 +1966,7 @@ }, { "system": "TI-99", - "folder": "/userdata/roms/ti99", + "folder": "ti99", "extensions": [ ".rpk", ".wav", @@ -1976,14 +1976,14 @@ }, { "system": "TIC-80", - "folder": "/userdata/roms/tic80", + "folder": "tic80", "extensions": [ ".tic" ] }, { "system": "TRIFORCE", - "folder": "/userdata/roms/triforce", + "folder": "triforce", "extensions": [ ".gcm", ".iso", @@ -1997,7 +1997,7 @@ }, { "system": "TUTOR", - "folder": "/userdata/roms/tutor", + "folder": "tutor", "extensions": [ ".bin", ".wav", @@ -2007,28 +2007,28 @@ }, { "system": "TYRIAN", - "folder": "/userdata/roms/tyrian", + "folder": "tyrian", "extensions": [ ".game" ] }, { "system": "TYRQUAKE", - "folder": "/userdata/roms/tyrquake", + "folder": "tyrquake", "extensions": [ ".pak" ] }, { "system": "UZEBOX", - "folder": "/userdata/roms/uzebox", + "folder": "uzebox", "extensions": [ ".uze" ] }, { "system": "VC 4000", - "folder": "/userdata/roms/vc4000", + "folder": "vc4000", "extensions": [ ".bin", ".rom", @@ -2040,7 +2040,7 @@ }, { "system": "VECTREX", - "folder": "/userdata/roms/vectrex", + "folder": "vectrex", "extensions": [ ".bin", ".gam", @@ -2051,7 +2051,7 @@ }, { "system": "VIDEO GAME MUSIC PLAYER", - "folder": "/userdata/roms/vgmplay", + "folder": "vgmplay", "extensions": [ ".vgm", ".vgz", @@ -2061,7 +2061,7 @@ }, { "system": "VIDEOPAC+ G7400", - "folder": "/userdata/roms/videopacplus", + "folder": "videopacplus", "extensions": [ ".bin", ".zip", @@ -2070,7 +2070,7 @@ }, { "system": "VIRCON32", - "folder": "/userdata/roms/vircon32", + "folder": "vircon32", "extensions": [ ".v32", ".zip" @@ -2078,7 +2078,7 @@ }, { "system": "VIRTUAL BOY", - "folder": "/userdata/roms/virtualboy", + "folder": "virtualboy", "extensions": [ ".vb", ".zip", @@ -2087,7 +2087,7 @@ }, { "system": "TANDY VIDEO INFORMATION SYSTEM", - "folder": "/userdata/roms/vis", + "folder": "vis", "extensions": [ ".chd", ".cue", @@ -2100,7 +2100,7 @@ }, { "system": "QUAKE II", - "folder": "/userdata/roms/vitaquake2", + "folder": "vitaquake2", "extensions": [ ".pak", ".zip", @@ -2109,14 +2109,14 @@ }, { "system": "VISUAL PINBALL X", - "folder": "/userdata/roms/vpinball", + "folder": "vpinball", "extensions": [ ".vpx" ] }, { "system": "V.SMILE", - "folder": "/userdata/roms/vsmile", + "folder": "vsmile", "extensions": [ ".u1", ".u3", @@ -2127,14 +2127,14 @@ }, { "system": "WASM4", - "folder": "/userdata/roms/wasm4", + "folder": "wasm4", "extensions": [ ".wasm" ] }, { "system": "WII", - "folder": "/userdata/roms/wii", + "folder": "wii", "extensions": [ ".gcm", ".iso", @@ -2151,7 +2151,7 @@ }, { "system": "WII U", - "folder": "/userdata/roms/wiiu", + "folder": "wiiu", "extensions": [ ".wua", ".wup", @@ -2164,7 +2164,7 @@ }, { "system": "WINDOWS", - "folder": "/userdata/roms/windows", + "folder": "windows", "extensions": [ ".pc", ".exe", @@ -2175,7 +2175,7 @@ }, { "system": "INSTALL A NEW WINDOWS GAME", - "folder": "/userdata/roms/windows_installers", + "folder": "windows_installers", "extensions": [ ".exe", ".iso" @@ -2183,7 +2183,7 @@ }, { "system": "WONDERSWAN", - "folder": "/userdata/roms/wswan", + "folder": "wswan", "extensions": [ ".ws", ".zip", @@ -2192,7 +2192,7 @@ }, { "system": "WONDERSWAN COLOR", - "folder": "/userdata/roms/wswanc", + "folder": "wswanc", "extensions": [ ".wsc", ".zip", @@ -2201,7 +2201,7 @@ }, { "system": "SHARP X1", - "folder": "/userdata/roms/x1", + "folder": "x1", "extensions": [ ".dx1", ".zip", @@ -2219,7 +2219,7 @@ }, { "system": "SHARP X68000", - "folder": "/userdata/roms/x68000", + "folder": "x68000", "extensions": [ ".dim", ".img", @@ -2238,14 +2238,14 @@ }, { "system": "HALF-LIFE 1", - "folder": "/userdata/roms/xash3d_fwgs", + "folder": "xash3d_fwgs", "extensions": [ ".game" ] }, { "system": "XBOX", - "folder": "/userdata/roms/xbox", + "folder": "xbox", "extensions": [ ".iso", ".squashfs" @@ -2253,7 +2253,7 @@ }, { "system": "XBOX 360", - "folder": "/userdata/roms/xbox360", + "folder": "xbox360", "extensions": [ ".iso", ".xex", @@ -2263,7 +2263,7 @@ }, { "system": "ATARI XE GAME SYSTEM", - "folder": "/userdata/roms/xegs", + "folder": "xegs", "extensions": [ ".atr", ".dsk", @@ -2277,21 +2277,21 @@ }, { "system": "XRICK", - "folder": "/userdata/roms/xrick", + "folder": "xrick", "extensions": [ ".zip" ] }, { "system": "ZELDA CLASSIC", - "folder": "/userdata/roms/zc210", + "folder": "zc210", "extensions": [ ".qst" ] }, { "system": "ZX81", - "folder": "/userdata/roms/zx81", + "folder": "zx81", "extensions": [ ".tzx", ".p", @@ -2301,7 +2301,7 @@ }, { "system": "ZX SPECTRUM", - "folder": "/userdata/roms/zxspectrum", + "folder": "zxspectrum", "extensions": [ ".tzx", ".tap", diff --git a/utils.py b/utils.py index 8e29383..479e515 100644 --- a/utils.py +++ b/utils.py @@ -86,7 +86,7 @@ def is_extension_supported(filename, platform, extensions_data): dest_dir = None for platform_dict in config.platform_dicts: if platform_dict["platform"] == platform: - dest_dir = platform_dict.get("folder") + dest_dir = os.path.join(config.ROMS_FOLDER, platform_dict.get("folder", platform.lower().replace(" ", ""))) break if not dest_dir: logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}") @@ -300,7 +300,7 @@ def wrap_text(text, font, max_width): def load_system_image(platform_dict): """Charge une image système depuis le chemin spécifié dans system_image.""" - image_path = platform_dict.get("system_image") + image_path = os.path.join(config.IMAGES_FOLDER, platform_dict.get("system_image", "default.png")) platform_name = platform_dict.get("platform", "unknown") #logger.debug(f"Chargement de l'image système pour {platform_name} depuis {image_path}") try: