diff --git a/.gitignore b/.gitignore index 3f9a242..96c7720 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ ports/RGSX/logs/ ports/RGSX/images/ ports/RGSX/games/ ports/RGSX/__pycache__/ +ports/RGSX/scripts/ ports/RGSX/sources.json ports/gamelist.xml windows/gamelist.xml diff --git a/ports/RGSX/__main__.py b/ports/RGSX/__main__.py index 39945c0..4521dbf 100644 --- a/ports/RGSX/__main__.py +++ b/ports/RGSX/__main__.py @@ -183,6 +183,7 @@ try: count = pygame.joystick.get_count() except Exception: count = 0 + joystick_names = [] for i in range(count): try: @@ -197,7 +198,14 @@ if not joystick_names: logger.debug("Aucun joystick détecté, utilisation du clavier par défaut.") config.joystick = False config.keyboard = True + # Si aucune marque spécifique détectée mais un joystick est présent, marquer comme générique + if not any([config.xbox_controller, config.playstation_controller, config.nintendo_controller, + config.eightbitdo_controller, config.steam_controller, config.trimui_controller, + config.logitech_controller]): + config.generic_controller = True + logger.debug("Aucun contrôleur spécifique détecté, utilisation du profil générique") else: + # Des joysticks sont présents, activer le mode joystick et tenter la détection spécifique config.joystick = True config.keyboard = False print(f"Joysticks détectés: {joystick_names}") @@ -243,29 +251,18 @@ else: # Note: virtual keyboard display now depends on controller presence (config.joystick) logger.debug(f"Flags contrôleur: xbox={config.xbox_controller}, ps={config.playstation_controller}, nintendo={config.nintendo_controller}, eightbitdo={config.eightbitdo_controller}, steam={config.steam_controller}, trimui={config.trimui_controller}, logitech={config.logitech_controller}, generic={config.generic_controller}") - # Si aucune marque spécifique détectée mais un joystick est présent, marquer comme générique - if not any([config.xbox_controller, config.playstation_controller, config.nintendo_controller, - config.eightbitdo_controller, config.steam_controller, config.trimui_controller, - config.logitech_controller]): - config.generic_controller = True - logger.debug("Aucun contrôleur spécifique détecté, utilisation du profil générique") - # Initialisation des variables de grille config.current_page = 0 config.selected_platform = 0 -config.selected_key = (0, 0) -config.transition_state = "none" -# Initialisation des variables de répétition -config.repeat_action = None -config.repeat_key = None -config.repeat_start_time = 0 -config.repeat_last_action = 0 - -# Charger la configuration de la musique AVANT d'initialiser l'audio -load_music_config() +# Charger la configuration musique AVANT d'initialiser le mixer pour respecter le paramètre music_enabled +try: + load_music_config() + logger.debug(f"Configuration musique chargée: music_enabled={getattr(config, 'music_enabled', True)}") +except Exception as e: + logger.warning(f"Impossible de charger la configuration musique avant init mixer: {e}") # Initialisation du mixer Pygame (déférée/évitable si musique désactivée) if getattr(config, 'music_enabled', True): @@ -447,6 +444,8 @@ async def main(): ): if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping", "history", "confirm_clear_history"]: config.previous_menu_state = config.menu_state + # Capturer l'état d'origine pour une sortie fiable du menu pause + config.pause_origin_state = config.menu_state config.menu_state = "pause_menu" config.selected_option = 0 config.needs_redraw = True @@ -454,13 +453,25 @@ async def main(): continue if config.menu_state == "pause_menu": - action = handle_controls(event, sources, joystick, screen) - config.needs_redraw = True - #logger.debug(f"Événement transmis à handle_controls dans pause_menu: {event.type}") - continue + # Rien de spécifique ici, capturé par SIMPLE_HANDLE_STATES ci-dessous + pass - # Gestion des événements pour le menu de filtrage des plateformes - if config.menu_state == "filter_platforms": + # États simples factorisés (déclenchent juste handle_controls + redraw) + SIMPLE_HANDLE_STATES = { + "pause_menu", + "pause_controls_menu", + "pause_display_menu", + "pause_games_menu", + "pause_settings_menu", + "pause_api_keys_status", + "filter_platforms", + "display_menu", + "language_select", + "controls_help", + "confirm_cancel_download", + "reload_games_data", + } + if config.menu_state in SIMPLE_HANDLE_STATES: action = handle_controls(event, sources, joystick, screen) config.needs_redraw = True continue @@ -470,24 +481,6 @@ async def main(): if handle_accessibility_events(event): config.needs_redraw = True continue - if config.menu_state == "display_menu": - # Les événements sont gérés dans controls.handle_controls - action = handle_controls(event, sources, joystick, screen) - config.needs_redraw = True - continue - - if config.menu_state == "language_select": - # Gérer les événements du sélecteur de langue via le système unifié - action = handle_controls(event, sources, joystick, screen) - config.needs_redraw = True - continue - - if config.menu_state == "controls_help": - action = handle_controls(event, sources, joystick, screen) - config.needs_redraw = True - #logger.debug(f"Événement transmis à handle_controls dans controls_help: {event.type}") - continue - if config.menu_state == "confirm_clear_history": action = handle_controls(event, sources, joystick, screen) if action == "confirm": @@ -509,7 +502,6 @@ async def main(): if config.menu_state == "reload_games_data": action = handle_controls(event, sources, joystick, screen) config.needs_redraw = True - #logger.debug(f"Événement transmis à handle_controls dans reload_games_data: {event.type}") continue if config.menu_state == "extension_warning": @@ -800,7 +792,21 @@ async def main(): draw_extension_warning(screen) elif config.menu_state == "pause_menu": draw_pause_menu(screen, config.selected_option) - #logger.debug("Rendu de draw_pause_menu") + elif config.menu_state == "pause_controls_menu": + from display import draw_pause_controls_menu + draw_pause_controls_menu(screen, getattr(config, 'pause_controls_selection', 0)) + elif config.menu_state == "pause_display_menu": + from display import draw_pause_display_menu + draw_pause_display_menu(screen, getattr(config, 'pause_display_selection', 0)) + elif config.menu_state == "pause_games_menu": + from display import draw_pause_games_menu + draw_pause_games_menu(screen, getattr(config, 'pause_games_selection', 0)) + elif config.menu_state == "pause_settings_menu": + from display import draw_pause_settings_menu + draw_pause_settings_menu(screen, getattr(config, 'pause_settings_selection', 0)) + elif config.menu_state == "pause_api_keys_status": + from display import draw_pause_api_keys_status + draw_pause_api_keys_status(screen) elif config.menu_state == "filter_platforms": from display import draw_filter_platforms_menu draw_filter_platforms_menu(screen) diff --git a/ports/RGSX/assets/controls/xbox_controller.json b/ports/RGSX/assets/controls/xbox_controller.json index 98f56c5..d79f9d0 100644 --- a/ports/RGSX/assets/controls/xbox_controller.json +++ b/ports/RGSX/assets/controls/xbox_controller.json @@ -66,7 +66,7 @@ "history": { "type": "button", "button": 3, - "display": "Select" + "display": "Y" }, "clear_history": { "type": "button", diff --git a/ports/RGSX/config.py b/ports/RGSX/config.py index f5a69a0..4529ce7 100644 --- a/ports/RGSX/config.py +++ b/ports/RGSX/config.py @@ -13,74 +13,34 @@ except Exception: pygame = None # type: ignore # Version actuelle de l'application -app_version = "2.2.0.8" - -def get_operating_system(): - """Renvoie le nom du système d'exploitation.""" - return platform.system() -#log dans la console le système d'exploitation (désactivé en headless) -if not HEADLESS: - print(f"Système d'exploitation : {get_operating_system()}") +app_version = "2.2.1.0" def get_application_root(): - """Détermine le dossier de l'application de manière portable.""" + """Détermine le dossier de l'application (PORTS)""" try: # Obtenir le chemin absolu du fichier config.py current_file = os.path.abspath(__file__) # Remonter au dossier parent de config.py (par exemple, dossier de l'application) app_root = os.path.dirname(os.path.dirname(current_file)) - if not HEADLESS: - print(f"Dossier de l'application : {app_root}") return app_root except NameError: # Si __file__ n'est pas défini (par exemple, exécution dans un REPL) return os.path.abspath(os.getcwd()) -def get_system_root(): - OPERATING_SYSTEM = get_operating_system() - """Détermine le dossier racine du système de fichiers (par exemple, /userdata ou C:\\).""" - try: - if OPERATING_SYSTEM == "Windows": - # Sur Windows, extraire la lettre de disque - current_path = os.path.abspath(__file__) - drive, _ = os.path.splitdrive(current_path) - system_root = drive + os.sep - if not HEADLESS: - print(f"Dossier racine du système : {system_root}") - return system_root - elif OPERATING_SYSTEM == "Linux": - # tester si c'est batocera : - if os.path.exists("/usr/share/batocera"): - OPERATING_SYSTEM = "Batocera" - - #remonter jusqu'à atteindre /userdata - current_path = os.path.abspath(__file__) - current_dir = current_path - while current_dir != os.path.dirname(current_dir): # Tant qu'on peut remonter - parent_dir = os.path.dirname(current_dir) - if os.path.basename(parent_dir) == "userdata": # Vérifier si le parent est userdata - system_root = parent_dir - if not HEADLESS: - print(f"Dossier racine du système : {system_root}") - return system_root - current_dir = parent_dir - # Si userdata n'est pas trouvé, retourner / - return "/" - else: - return "/" - except NameError: - - return "/" if not OPERATING_SYSTEM == "Windows" else os.path.splitdrive(os.getcwd())[0] + os.sep - +def detect_operating_system(): + """Renvoie le nom du système d'exploitation.""" + OPERATING_SYSTEM = platform.system() + return OPERATING_SYSTEM + # Chemins de base -SYSTEM_FOLDER = get_system_root() APP_FOLDER = os.path.join(get_application_root(), "RGSX") -ROMS_FOLDER = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(APP_FOLDER))), "roms") -SAVE_FOLDER = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(APP_FOLDER))), "saves", "ports", "rgsx") -RETROBAT_DATA_FOLDER = os.path.dirname(os.path.dirname(os.path.dirname(APP_FOLDER))) - +USERDATA_FOLDER = os.path.dirname(os.path.dirname(os.path.dirname(APP_FOLDER))) +ROMS_FOLDER = os.path.join(USERDATA_FOLDER, "roms") +SAVE_FOLDER = os.path.join(USERDATA_FOLDER, "saves", "ports", "rgsx") +GAMELISTXML = os.path.join(ROMS_FOLDER, "ports","gamelist.xml") +GAMELISTXML_WINDOWS = os.path.join(ROMS_FOLDER, "windows","gamelist.xml") # Configuration du logging @@ -88,15 +48,12 @@ logger = logging.getLogger(__name__) log_dir = os.path.join(APP_FOLDER, "logs") log_file = os.path.join(log_dir, "RGSX.log") -# Chemins de base -GAMELISTXML = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(APP_FOLDER))), "roms", "ports", "gamelist.xml") -GAMELISTXML_WINDOWS = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(APP_FOLDER))), "roms", "windows", "gamelist.xml") -#Dossier /roms/ports/rgsx +#Dossier de l'APP : /roms/ports/rgsx UPDATE_FOLDER = os.path.join(APP_FOLDER, "update") LANGUAGES_FOLDER = os.path.join(APP_FOLDER, "languages") MUSIC_FOLDER = os.path.join(APP_FOLDER, "assets", "music") -#Dossier /saves/ports/rgsx +#Dossier de sauvegarde : /saves/ports/rgsx IMAGES_FOLDER = os.path.join(SAVE_FOLDER, "images") GAMES_FOLDER = os.path.join(SAVE_FOLDER, "games") SOURCES_FILE = os.path.join(SAVE_FOLDER, "systems_list.json") @@ -104,7 +61,12 @@ JSON_EXTENSIONS = os.path.join(SAVE_FOLDER, "rom_extensions.json") PRECONF_CONTROLS_PATH = os.path.join(APP_FOLDER, "assets", "controls") CONTROLS_CONFIG_PATH = os.path.join(SAVE_FOLDER, "controls.json") HISTORY_PATH = os.path.join(SAVE_FOLDER, "history.json") -API_KEY_1FICHIER = os.path.join(SAVE_FOLDER, "1FichierAPI.txt") +# Séparation chemin / valeur pour éviter les confusions lors du chargement +API_KEY_1FICHIER_PATH = os.path.join(SAVE_FOLDER, "1FichierAPI.txt") +API_KEY_ALLDEBRID_PATH = os.path.join(SAVE_FOLDER, "AllDebridAPI.txt") +# Valeurs chargées (remplies dynamiquement par utils.load_api_key_*). +API_KEY_1FICHIER = "" +API_KEY_ALLDEBRID = "" RGSX_SETTINGS_PATH = os.path.join(SAVE_FOLDER, "rgsx_settings.json") # URL @@ -117,14 +79,12 @@ OTA_data_ZIP = os.path.join(OTA_SERVER_URL, "games.zip") UNRAR_EXE = os.path.join(APP_FOLDER,"assets", "unrar.exe") XDVDFS_EXE = os.path.join(APP_FOLDER,"assets", "xdvdfs.exe") XDVDFS_LINUX = os.path.join(APP_FOLDER,"assets", "xdvdfs") -unrar_download_exe = os.path.join(OTA_SERVER_URL, "unrar.exe") -xdvdfs_download_exe = os.path.join(OTA_SERVER_URL, "xdvdfs.exe") - - -xdvdfs_download_linux = os.path.join(OTA_SERVER_URL, "xdvdfs") if not HEADLESS: # Print des chemins pour debug + print(f"OPERATING_SYSTEM: {detect_operating_system()}") + print(f"APP_FOLDER: {APP_FOLDER}") + print(f"USERDATA_FOLDER: {USERDATA_FOLDER}") print(f"ROMS_FOLDER: {ROMS_FOLDER}") print(f"SAVE_FOLDER: {SAVE_FOLDER}") print(f"RGSX LOGS_FOLDER: {log_dir}") @@ -133,7 +93,6 @@ if not HEADLESS: print(f"IMAGES_FOLDER: {IMAGES_FOLDER}") print(f"GAMES_FOLDER: {GAMES_FOLDER}") print(f"SOURCES_FILE: {SOURCES_FILE}") - print(f"OPERATING_SYSTEM: {get_operating_system()}") # Constantes pour la répétition automatique dans pause_menu diff --git a/ports/RGSX/controls.py b/ports/RGSX/controls.py index 2199430..556c19f 100644 --- a/ports/RGSX/controls.py +++ b/ports/RGSX/controls.py @@ -1,20 +1,17 @@ -import shutil import pygame # type: ignore -import config -# Constantes pour la répétition automatique - importées de config.py -from config import REPEAT_DELAY, REPEAT_INTERVAL, REPEAT_ACTION_DEBOUNCE -from config import CONTROLS_CONFIG_PATH +import shutil import asyncio import json import os -import sys +import config +from config import REPEAT_DELAY, REPEAT_INTERVAL, REPEAT_ACTION_DEBOUNCE +from config import CONTROLS_CONFIG_PATH from display import draw_validation_transition -from network import download_rom, download_from_1fichier, is_1fichier_url from network import download_rom, download_from_1fichier, is_1fichier_url, request_cancel from utils import ( load_games, check_extension_before_download, is_extension_supported, load_extensions_json, play_random_music, sanitize_filename, - load_api_key_1fichier, load_api_key_alldebrid, save_music_config + save_music_config, load_api_keys ) from history import load_history, clear_history, add_to_history, save_history import logging @@ -34,16 +31,19 @@ VALID_STATES = [ "platform", "game", "confirm_exit", "extension_warning", "pause_menu", "controls_help", "history", "controls_mapping", "reload_games_data", "restart_popup", "error", "loading", "confirm_clear_history", - "language_select", "filter_platforms", "display_menu" + "language_select", "filter_platforms", "display_menu", + # Nouveaux sous-menus hiérarchiques (refonte pause menu) + "pause_controls_menu", # sous-menu Controls (aide, remap) + "pause_display_menu", # sous-menu Display (layout, font size, unsupported, unknown ext, filter) + "pause_games_menu", # sous-menu Games (source mode, update/redownload cache) + "pause_settings_menu", # sous-menu Settings (music on/off, symlink toggle, api keys status) + "pause_api_keys_status" # sous-menu API Keys (affichage statut des clés) ] def validate_menu_state(state): if state not in VALID_STATES: logger.debug(f"État invalide {state}, retour à platform") return "platform" - if state == "history": # Éviter de revenir à history - logger.debug(f"État history non autorisé comme previous_menu_state, retour à platform") - return "platform" return state @@ -133,6 +133,7 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH): # 3) Fallback clavier par défaut logging.getLogger(__name__).info("Aucun fichier utilisateur ou préréglage trouvé, utilisation des contrôles par défaut") return default_config.copy() + except Exception as e: logging.getLogger(__name__).error(f"Erreur load_controls_config: {e}") return default_config.copy() @@ -318,6 +319,8 @@ def handle_controls(event, sources, joystick, screen): config.repeat_last_action = current_time config.needs_redraw = True elif is_input_matched(event, "history"): + # Capturer l'origine si on vient directement des plateformes + config.history_origin = "platform" config.menu_state = "history" config.needs_redraw = True logger.debug("Ouverture history depuis platform") @@ -334,6 +337,8 @@ def handle_controls(event, sources, joystick, screen): config.needs_redraw = True #logger.debug(f"Plateforme sélectionnée: {config.platforms[config.current_platform]}, {len(config.games)} jeux chargés") elif is_input_matched(event, "cancel"): + # Capturer l'origine (plateformes) pour un retour correct si l'utilisateur choisit "Non" + config.confirm_exit_origin = "platform" config.menu_state = "confirm_exit" config.confirm_selection = 0 config.needs_redraw = True @@ -584,14 +589,8 @@ def handle_controls(event, sources, joystick, screen): config.current_history_item = len(config.history) -1 task_id = str(pygame.time.get_ticks()) if is_1fichier_url(url): - config.API_KEY_1FICHIER = load_api_key_1fichier() - if not config.API_KEY_1FICHIER: - # Fallback AllDebrid - try: - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - except Exception: - config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "") - if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""): + keys = load_api_keys() + if not keys.get('1fichier') and not keys.get('alldebrid'): config.history[-1]["status"] = "Erreur" config.history[-1]["message"] = "API NOT FOUND" save_history(config.history) @@ -626,14 +625,8 @@ def handle_controls(event, sources, joystick, screen): config.current_history_item = len(config.history) - 1 # Vérifier d'abord si c'est un lien 1fichier if is_1fichier_url(url): - config.API_KEY_1FICHIER = load_api_key_1fichier() - if not config.API_KEY_1FICHIER: - # Fallback AllDebrid - try: - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - except Exception: - config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "") - if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""): + keys = load_api_keys() + if not keys.get('1fichier') and not keys.get('alldebrid'): config.previous_menu_state = config.menu_state config.menu_state = "error" try: @@ -748,13 +741,8 @@ def handle_controls(event, sources, joystick, screen): )) config.current_history_item = len(config.history) - 1 if is_1fichier_url(url): - if not config.API_KEY_1FICHIER: - # Fallback AllDebrid - try: - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - except Exception: - config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "") - if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""): + keys = load_api_keys() + if not keys.get('1fichier') and not keys.get('alldebrid'): config.previous_menu_state = config.menu_state config.menu_state = "error" try: @@ -821,14 +809,8 @@ def handle_controls(event, sources, joystick, screen): config.current_history_item = len(config.history) -1 task_id = str(pygame.time.get_ticks()) if is_1fichier_url(url): - config.API_KEY_1FICHIER = load_api_key_1fichier() - if not config.API_KEY_1FICHIER: - # Fallback AllDebrid - try: - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - except Exception: - config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "") - if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""): + keys = load_api_keys() + if not keys.get('1fichier') and not keys.get('alldebrid'): config.history[-1]["status"] = "Erreur" config.history[-1]["message"] = "API NOT FOUND" save_history(config.history) @@ -892,14 +874,8 @@ def handle_controls(event, sources, joystick, screen): config.current_history_item = len(config.history) -1 task_id = str(pygame.time.get_ticks()) if is_1fichier_url(url): - config.API_KEY_1FICHIER = load_api_key_1fichier() - if not config.API_KEY_1FICHIER: - # Fallback AllDebrid - try: - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - except Exception: - config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "") - if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""): + keys = load_api_keys() + if not keys.get('1fichier') and not keys.get('alldebrid'): config.history[-1]["status"] = "Erreur" config.history[-1]["message"] = "API NOT FOUND" save_history(config.history) @@ -969,75 +945,6 @@ def handle_controls(event, sources, joystick, screen): config.confirm_clear_selection = 0 # 0 pour "Non", 1 pour "Oui" config.needs_redraw = True logger.debug("Passage à confirm_clear_history depuis history") - elif is_input_matched(event, "confirm"): - if history: - entry = history[config.current_history_item] - platform = entry["platform"] - game_name = entry["game_name"] - for game in config.games: - if game[0] == game_name and config.platforms[config.current_platform] == platform: - config.pending_download = check_extension_before_download(game[1], platform, game_name) - if config.pending_download: - url, platform, game_name, is_zip_non_supported = config.pending_download - # Recalculer le support exact et décider via le flag is_zip_non_supported - is_supported = is_extension_supported( - sanitize_filename(game_name), - platform, - load_extensions_json() - ) - if not is_supported and not is_zip_non_supported: - config.previous_menu_state = config.menu_state - config.menu_state = "extension_warning" - config.extension_confirm_selection = 0 - config.needs_redraw = True - logger.debug(f"Extension non supportée pour retéléchargement, passage à extension_warning pour {game_name}") - else: - task_id = str(pygame.time.get_ticks()) - if is_1fichier_url(url): - if not config.API_KEY_1FICHIER: - # Fallback AllDebrid - try: - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - except Exception: - config.API_KEY_ALLDEBRID = getattr(config, "API_KEY_ALLDEBRID", "") - if not config.API_KEY_1FICHIER and not getattr(config, "API_KEY_ALLDEBRID", ""): - config.previous_menu_state = config.menu_state - config.menu_state = "error" - logger.warning("clé api absente pour 1fichier et AllDebrid") - try: - both_paths = f"{os.path.join(config.SAVE_FOLDER,'1FichierAPI.txt')} or {os.path.join(config.SAVE_FOLDER,'AllDebridAPI.txt')}" - config.error_message = _("error_api_key").format(both_paths) - except Exception: - config.error_message = "Please enter API key (1fichier or AllDebrid)" - - config.history[-1]["status"] = "Erreur" - config.history[-1]["progress"] = 0 - config.history[-1]["message"] = "API NOT FOUND" - save_history(config.history) - config.needs_redraw = True - logger.error("Clé API 1fichier et AllDebrid absentes, retéléchargement impossible.") - config.pending_download = None - return action - task = asyncio.create_task(download_from_1fichier(url, platform, game_name, is_zip_non_supported, task_id)) - else: - task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported, task_id)) - config.download_tasks[task_id] = (task, url, game_name, platform) - config.previous_menu_state = config.menu_state - config.menu_state = "history" - config.needs_redraw = True - logger.debug(f"Retéléchargement: {game_name} pour {platform} depuis {url}, task_id={task_id}") - config.pending_download = None - action = "redownload" - else: - config.menu_state = "error" - try: - config.error_message = _("error_invalid_download_data") - except Exception: - config.error_message = "Invalid download data" - config.pending_download = None - config.needs_redraw = True - logger.error(f"config.pending_download est None pour {game_name}") - break elif is_input_matched(event, "cancel") or is_input_matched(event, "history"): if config.history and config.current_history_item < len(config.history): entry = config.history[config.current_history_item] @@ -1047,7 +954,14 @@ def handle_controls(event, sources, joystick, screen): config.needs_redraw = True logger.debug("Demande d'annulation de téléchargement") return action - config.menu_state = validate_menu_state(config.previous_menu_state) + # Retour à l'origine capturée si disponible sinon previous_menu_state + target = getattr(config, 'history_origin', getattr(config, 'previous_menu_state', 'platform')) + config.menu_state = validate_menu_state(target) + if hasattr(config, 'history_origin'): + try: + delattr(config, 'history_origin') + except Exception: + pass config.current_history_item = 0 config.history_scroll_offset = 0 config.needs_redraw = True @@ -1088,7 +1002,7 @@ def handle_controls(event, sources, joystick, screen): # Confirmation vider l'historique elif config.menu_state == "confirm_clear_history": - logger.debug(f"État confirm_clear_history, confirm_clear_selection={config.confirm_clear_selection}, événement={event.type}, valeur={getattr(event, 'value', None)}") + if is_input_matched(event, "confirm"): # 0 = Non, 1 = Oui if config.confirm_clear_selection == 1: # Oui @@ -1098,7 +1012,6 @@ def handle_controls(event, sources, joystick, screen): config.history_scroll_offset = 0 config.menu_state = "history" config.needs_redraw = True - logger.info("Historique vidé après confirmation") else: # Non config.menu_state = "history" config.needs_redraw = True @@ -1110,8 +1023,6 @@ def handle_controls(event, sources, joystick, screen): config.needs_redraw = True logger.debug("Annulation du vidage de l'historique, retour à history") - # État download_result supprimé - # Confirmation quitter elif config.menu_state == "confirm_exit": if is_input_matched(event, "confirm"): @@ -1128,9 +1039,16 @@ def handle_controls(event, sources, joystick, screen): pass return "quit" else: - config.menu_state = validate_menu_state(config.previous_menu_state) + # Retour à l'état capturé (confirm_exit_origin) sinon previous_menu_state sinon platform + target = getattr(config, 'confirm_exit_origin', getattr(config, 'previous_menu_state', 'platform')) + config.menu_state = validate_menu_state(target) + if hasattr(config, 'confirm_exit_origin'): + try: + delattr(config, 'confirm_exit_origin') + except Exception: + pass config.needs_redraw = True - logger.debug(f"Retour à {config.menu_state} depuis confirm_exit") + logger.debug(f"Retour à {config.menu_state} depuis confirm_exit (annulation)") elif is_input_matched(event, "left") or is_input_matched(event, "right"): config.confirm_selection = 1 - config.confirm_selection config.needs_redraw = True @@ -1141,58 +1059,213 @@ def handle_controls(event, sources, joystick, screen): #logger.debug(f"État pause_menu, selected_option={config.selected_option}, événement={event.type}, valeur={getattr(event, 'value', None)}") # Start toggles back to previous state when already in pause if is_input_matched(event, "start"): - config.menu_state = validate_menu_state(config.previous_menu_state) + target = getattr(config, 'pause_origin_state', getattr(config, 'previous_menu_state', 'platform')) + config.menu_state = validate_menu_state(target) config.needs_redraw = True logger.debug(f"Start: retour à {config.menu_state} depuis pause_menu") elif is_input_matched(event, "up"): config.selected_option = max(0, config.selected_option - 1) config.needs_redraw = True elif is_input_matched(event, "down"): - # Nombre d'options dynamique (inclut éventuellement l'option source des jeux) - total = getattr(config, 'pause_menu_total_options', 11) # fallback 11 (Restart added) + # Menu racine hiérarchique: nombre dynamique (langue + catégories) + total = getattr(config, 'pause_menu_total_options', 7) config.selected_option = min(total - 1, config.selected_option + 1) config.needs_redraw = True elif is_input_matched(event, "confirm"): - if config.selected_option == 0: # Controls - config.previous_menu_state = validate_menu_state(config.previous_menu_state) - config.menu_state = "controls_help" + if config.selected_option == 0: # Language selector direct + config.menu_state = "language_select" + config.previous_menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() config.needs_redraw = True - #logger.debug(f"Passage à controls_help depuis pause_menu") - elif config.selected_option == 1: # Remap controls + elif config.selected_option == 1: # Controls submenu + config.menu_state = "pause_controls_menu" + if not hasattr(config, 'pause_controls_selection'): + config.pause_controls_selection = 0 + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif config.selected_option == 2: # Display submenu + config.menu_state = "pause_display_menu" + if not hasattr(config, 'pause_display_selection'): + config.pause_display_selection = 0 + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif config.selected_option == 3: # Games submenu + config.menu_state = "pause_games_menu" + if not hasattr(config, 'pause_games_selection'): + config.pause_games_selection = 0 + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif config.selected_option == 4: # Settings submenu + config.menu_state = "pause_settings_menu" + if not hasattr(config, 'pause_settings_selection'): + config.pause_settings_selection = 0 + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif config.selected_option == 5: # Restart + from utils import restart_application + restart_application(2000) + elif config.selected_option == 6: # Quit + # Capturer l'origine pause_menu pour retour si annulation + config.confirm_exit_origin = "pause_menu" config.previous_menu_state = validate_menu_state(config.previous_menu_state) - #logger.debug(f"Previous menu state avant controls_mapping: {config.previous_menu_state}") - #Supprimer le fichier de configuration des contrôles s'il existe + config.menu_state = "confirm_exit" + config.confirm_selection = 0 + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif is_input_matched(event, "cancel"): + target = getattr(config, 'pause_origin_state', getattr(config, 'previous_menu_state', 'platform')) + config.menu_state = validate_menu_state(target) + config.needs_redraw = True + logger.debug(f"Retour à {config.menu_state} depuis pause_menu") + + # Sous-menu Controls + elif config.menu_state == "pause_controls_menu": + sel = getattr(config, 'pause_controls_selection', 0) + if is_input_matched(event, "up"): + config.pause_controls_selection = (sel - 1) % 3 + config.needs_redraw = True + elif is_input_matched(event, "down"): + config.pause_controls_selection = (sel + 1) % 3 + config.needs_redraw = True + elif is_input_matched(event, "confirm"): + if sel == 0: # Aide + config.previous_menu_state = "pause_controls_menu" + config.menu_state = "controls_help" + elif sel == 1: # Remap if os.path.exists(config.CONTROLS_CONFIG_PATH): try: os.remove(config.CONTROLS_CONFIG_PATH) - logger.debug(f"Fichier de configuration des contrôles supprimé: {config.CONTROLS_CONFIG_PATH}") except Exception as e: - logger.error(f"Erreur lors de la suppression du fichier de configuration des contrôles: {e}") + logger.error(f"Erreur suppression controls_config: {e}") + config.previous_menu_state = "pause_controls_menu" config.menu_state = "controls_mapping" + else: # Back + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif is_input_matched(event, "cancel") or is_input_matched(event, "start"): + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + + # Sous-menu Display + elif config.menu_state == "pause_display_menu": + sel = getattr(config, 'pause_display_selection', 0) + total = 6 # layout, font, unsupported, unknown, filter, back + if is_input_matched(event, "up"): + config.pause_display_selection = (sel - 1) % total + config.needs_redraw = True + elif is_input_matched(event, "down"): + config.pause_display_selection = (sel + 1) % total + config.needs_redraw = True + elif is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm"): + sel = getattr(config, 'pause_display_selection', 0) + # 0 layout cycle + if sel == 0 and (is_input_matched(event, "left") or is_input_matched(event, "right")): + layouts = [(3,3),(3,4),(4,3),(4,4)] + try: + idx = layouts.index((config.GRID_COLS, config.GRID_ROWS)) + except ValueError: + idx = 0 + idx = (idx + 1) % len(layouts) if is_input_matched(event, "right") else (idx - 1) % len(layouts) + new_cols, new_rows = layouts[idx] + try: + from rgsx_settings import set_display_grid + set_display_grid(new_cols, new_rows) + except Exception as e: + logger.error(f"Erreur set_display_grid: {e}") + config.GRID_COLS = new_cols + config.GRID_ROWS = new_rows + # Redémarrage automatique + try: + from utils import restart_application + config.menu_state = "restart_popup" + config.popup_message = _("popup_restarting") if _ else "Restarting..." + config.popup_timer = 2000 + restart_application(2000) + except Exception as e: + logger.error(f"Erreur restart après layout: {e}") config.needs_redraw = True - logger.debug(f"Passage à controls_mapping depuis pause_menu") - elif config.selected_option == 2: # History + # 1 font size + elif sel == 1 and (is_input_matched(event, "left") or is_input_matched(event, "right")): + from accessibility import save_accessibility_settings + opts = getattr(config, 'font_scale_options', [0.75,1.0,1.25,1.5,1.75]) + idx = getattr(config, 'current_font_scale_index', 1) + idx = max(0, idx-1) if is_input_matched(event, "left") else min(len(opts)-1, idx+1) + if idx != getattr(config, 'current_font_scale_index', 1): + config.current_font_scale_index = idx + scale = opts[idx] + config.accessibility_settings["font_scale"] = scale + try: + save_accessibility_settings(config.accessibility_settings) + except Exception as e: + logger.error(f"Erreur sauvegarde accessibilité: {e}") + try: + config.init_font() + except Exception as e: + logger.error(f"Erreur init polices: {e}") + config.needs_redraw = True + # 2 unsupported toggle + elif sel == 2 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")): + try: + from rgsx_settings import get_show_unsupported_platforms, set_show_unsupported_platforms + current = get_show_unsupported_platforms() + new_val = set_show_unsupported_platforms(not current) + from utils import load_sources + load_sources() + config.popup_message = _("menu_show_unsupported_enabled") if new_val else _("menu_show_unsupported_disabled") + config.popup_timer = 3000 + config.needs_redraw = True + except Exception as e: + logger.error(f"Erreur toggle unsupported: {e}") + # 3 allow unknown extensions + elif sel == 3 and (is_input_matched(event, "left") or is_input_matched(event, "right") or is_input_matched(event, "confirm")): + try: + from rgsx_settings import get_allow_unknown_extensions, set_allow_unknown_extensions + current = get_allow_unknown_extensions() + new_val = set_allow_unknown_extensions(not current) + config.popup_message = _("menu_allow_unknown_ext_enabled") if new_val else _("menu_allow_unknown_ext_disabled") + config.popup_timer = 3000 + config.needs_redraw = True + except Exception as e: + logger.error(f"Erreur toggle allow_unknown_extensions: {e}") + # 4 filter platforms + elif sel == 4 and (is_input_matched(event, "confirm") or is_input_matched(event, "right")): + config.filter_return_to = "pause_display_menu" + config.menu_state = "filter_platforms" + config.selected_filter_index = 0 + config.filter_platforms_scroll_offset = 0 + config.needs_redraw = True + # 5 back + elif sel == 5 and (is_input_matched(event, "confirm")): + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif is_input_matched(event, "cancel") or is_input_matched(event, "start"): + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + + # Sous-menu Games + elif config.menu_state == "pause_games_menu": + sel = getattr(config, 'pause_games_selection', 0) + total = 4 # history, source, redownload, back + if is_input_matched(event, "up"): + config.pause_games_selection = (sel - 1) % total + config.needs_redraw = True + elif is_input_matched(event, "down"): + config.pause_games_selection = (sel + 1) % total + config.needs_redraw = True + elif is_input_matched(event, "confirm") or is_input_matched(event, "left") or is_input_matched(event, "right"): + sel = getattr(config, 'pause_games_selection', 0) + if sel == 0 and is_input_matched(event, "confirm"): # history config.history = load_history() config.current_history_item = 0 config.history_scroll_offset = 0 - config.previous_menu_state = validate_menu_state(config.previous_menu_state) + config.previous_menu_state = "pause_games_menu" config.menu_state = "history" config.needs_redraw = True - logger.debug(f"Passage à history depuis pause_menu") - elif config.selected_option == 3: # Language - config.previous_menu_state = validate_menu_state(config.previous_menu_state) - config.menu_state = "language_select" - config.selected_language_index = 0 - config.needs_redraw = True - logger.debug(f"Passage à language_select depuis pause_menu") - elif config.selected_option == 4: # Display - config.previous_menu_state = validate_menu_state(config.previous_menu_state) - config.menu_state = "display_menu" - if not hasattr(config, 'display_menu_selection'): - config.display_menu_selection = 0 - config.needs_redraw = True - logger.debug("Passage au menu affichage") - elif config.selected_option == 5: # Source toggle (index shifted by removal of filter) + elif sel == 1 and (is_input_matched(event, "confirm") or is_input_matched(event, "left") or is_input_matched(event, "right")): try: from rgsx_settings import get_sources_mode, set_sources_mode current_mode = get_sources_mode() @@ -1208,13 +1281,33 @@ def handle_controls(event, sources, joystick, screen): logger.info(f"Changement du mode des sources vers {new_mode}") except Exception as e: logger.error(f"Erreur changement mode sources: {e}") - elif config.selected_option == 6: # Redownload game cache - config.previous_menu_state = validate_menu_state(config.previous_menu_state) + elif sel == 2 and is_input_matched(event, "confirm"): # redownload cache + config.previous_menu_state = "pause_games_menu" config.menu_state = "reload_games_data" config.redownload_confirm_selection = 0 config.needs_redraw = True - logger.debug(f"Passage à reload_games_data depuis pause_menu") - elif config.selected_option == 7: # Music toggle + elif sel == 3 and is_input_matched(event, "confirm"): # back + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif is_input_matched(event, "cancel") or is_input_matched(event, "start"): + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + + # Sous-menu Settings + elif config.menu_state == "pause_settings_menu": + sel = getattr(config, 'pause_settings_selection', 0) + total = 4 # music, symlink, api keys, back + if is_input_matched(event, "up"): + config.pause_settings_selection = (sel - 1) % total + config.needs_redraw = True + elif is_input_matched(event, "down"): + config.pause_settings_selection = (sel + 1) % total + config.needs_redraw = True + elif is_input_matched(event, "confirm") or is_input_matched(event, "left") or is_input_matched(event, "right"): + sel = getattr(config, 'pause_settings_selection', 0) + if sel == 0 and (is_input_matched(event, "confirm") or is_input_matched(event, "left") or is_input_matched(event, "right")): config.music_enabled = not config.music_enabled save_music_config() if config.music_enabled: @@ -1225,28 +1318,32 @@ def handle_controls(event, sources, joystick, screen): else: pygame.mixer.music.stop() config.needs_redraw = True - logger.info(f"Musique {'activée' if config.music_enabled else 'désactivée'} via menu pause") - elif config.selected_option == 8: # Symlink option + logger.info(f"Musique {'activée' if config.music_enabled else 'désactivée'} via settings") + elif sel == 1 and (is_input_matched(event, "confirm") or is_input_matched(event, "left") or is_input_matched(event, "right")): from rgsx_settings import set_symlink_option, get_symlink_option current_status = get_symlink_option() success, message = set_symlink_option(not current_status) config.popup_message = message config.popup_timer = 3000 if success else 5000 config.needs_redraw = True - logger.info(f"Symlink option {'activée' if not current_status else 'désactivée'} via menu pause") - elif config.selected_option == 9: # Restart - from utils import restart_application - restart_application(2000) - elif config.selected_option == 10: # Quit - config.previous_menu_state = validate_menu_state(config.previous_menu_state) - config.menu_state = "confirm_exit" - config.confirm_selection = 0 + logger.info(f"Symlink option {'activée' if not current_status else 'désactivée'} via settings") + elif sel == 2 and is_input_matched(event, "confirm"): + config.menu_state = "pause_api_keys_status" config.needs_redraw = True - logger.debug(f"Passage à confirm_exit depuis pause_menu") - elif is_input_matched(event, "cancel"): - config.menu_state = validate_menu_state(config.previous_menu_state) + elif sel == 3 and is_input_matched(event, "confirm"): + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + elif is_input_matched(event, "cancel") or is_input_matched(event, "start"): + config.menu_state = "pause_menu" + config.last_state_change_time = pygame.time.get_ticks() + config.needs_redraw = True + + elif config.menu_state == "pause_api_keys_status": + if is_input_matched(event, "cancel") or is_input_matched(event, "confirm") or is_input_matched(event, "start"): + config.menu_state = "pause_settings_menu" + config.last_state_change_time = pygame.time.get_ticks() config.needs_redraw = True - logger.debug(f"Retour à {config.menu_state} depuis pause_menu") # Aide contrôles elif config.menu_state == "controls_help": @@ -1535,9 +1632,10 @@ def handle_controls(event, sources, joystick, screen): # Return either to display menu or pause menu depending on origin target = getattr(config, 'filter_return_to', 'pause_menu') config.menu_state = target - if target == 'display_menu': - # reset display selection to the Filter item for convenience + if target == 'display_menu': # ancien cas (fallback) config.display_menu_selection = 3 + elif target == 'pause_display_menu': # nouveau sous-menu hiérarchique + config.pause_display_selection = 4 # positionner sur Filter else: config.selected_option = 5 # keep pointer on Filter in pause menu config.filter_return_to = None @@ -1546,6 +1644,8 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = target if target == 'display_menu': config.display_menu_selection = 3 + elif target == 'pause_display_menu': + config.pause_display_selection = 4 else: config.selected_option = 5 config.filter_return_to = None @@ -1555,6 +1655,8 @@ def handle_controls(event, sources, joystick, screen): config.menu_state = target if target == 'display_menu': config.display_menu_selection = 3 + elif target == 'pause_display_menu': + config.pause_display_selection = 4 else: config.selected_option = 5 config.filter_return_to = None diff --git a/ports/RGSX/display.py b/ports/RGSX/display.py index acd7138..1dcd2c7 100644 --- a/ports/RGSX/display.py +++ b/ports/RGSX/display.py @@ -1403,54 +1403,178 @@ def draw_display_menu(screen): screen.blit(instruction_surface, instruction_rect) def draw_pause_menu(screen, selected_option): - """Dessine le menu pause avec un style moderne.""" + """Dessine le menu pause racine (catégories).""" screen.blit(OVERLAY, (0, 0)) - from rgsx_settings import get_symlink_option, get_sources_mode, get_show_unsupported_platforms - mode = get_sources_mode() - source_label = _("games_source_rgsx") if mode == "rgsx" else _("games_source_custom") - if config.music_enabled: - music_name = config.current_music_name or "" - music_option = _("menu_music_enabled").format(music_name) - else: - music_option = _("menu_music_disabled") - symlink_option = _("symlink_option_enabled") if get_symlink_option() else _("symlink_option_disabled") + # Nouvel ordre: Language / Controls / Display / Games / Settings / Restart / Quit options = [ - _("menu_controls"), # 0 - _("menu_remap_controls"), # 1 - _("menu_history"), # 2 - _("menu_language"), # 3 - _("menu_display"), # 4 new merged display menu - f"{_('menu_games_source_prefix')}: {source_label}", # 5 (shifted left) - _("menu_redownload_cache"), # 6 - music_option, # 7 - symlink_option, # 8 - _("menu_restart"), # 9 (new) - _("menu_quit") # 10 + _("menu_language") if _ else "Language", # 0 -> sélecteur de langue direct + _("menu_controls"), # 1 -> sous-menu controls + _("menu_display"), # 2 -> sous-menu display + _("menu_games") if _ else "Games", # 3 -> sous-menu games (history + sources + update) + _("menu_settings_category") if _ else "Settings", # 4 -> sous-menu settings + _("menu_restart"), # 5 -> reboot + _("menu_quit") # 6 -> quit ] - menu_width = int(config.screen_width * 0.8) - line_height = config.font.get_height() + 10 - button_height = int(config.screen_height * 0.0463) - margin_top_bottom = 20 - menu_height = len(options) * (button_height + 10) + 2 * margin_top_bottom + menu_width = int(config.screen_width * 0.6) + button_height = int(config.screen_height * 0.048) + margin_top_bottom = 24 + menu_height = len(options) * (button_height + 12) + 2 * margin_top_bottom menu_x = (config.screen_width - menu_width) // 2 menu_y = (config.screen_height - menu_height) // 2 - pygame.draw.rect(screen, THEME_COLORS["button_idle"], (menu_x, menu_y, menu_width, menu_height), border_radius=12) pygame.draw.rect(screen, THEME_COLORS["border"], (menu_x, menu_y, menu_width, menu_height), 2, border_radius=12) - for i, option in enumerate(options): draw_stylized_button( screen, option, menu_x + 20, - menu_y + margin_top_bottom + i * (button_height + 10), + menu_y + margin_top_bottom + i * (button_height + 12), menu_width - 40, button_height, selected=i == selected_option ) - # Stocker le nombre total d'options pour la navigation dynamique config.pause_menu_total_options = len(options) +def _draw_submenu_generic(screen, title, options, selected_index): + """Helper générique pour dessiner un sous-menu hiérarchique.""" + screen.blit(OVERLAY, (0, 0)) + menu_width = int(config.screen_width * 0.72) + button_height = int(config.screen_height * 0.045) + margin_top_bottom = 26 + menu_height = (len(options)+1) * (button_height + 10) + 2 * margin_top_bottom # +1 pour le titre + menu_x = (config.screen_width - menu_width) // 2 + menu_y = (config.screen_height - menu_height) // 2 + pygame.draw.rect(screen, THEME_COLORS["button_idle"], (menu_x, menu_y, menu_width, menu_height), border_radius=14) + pygame.draw.rect(screen, THEME_COLORS["border"], (menu_x, menu_y, menu_width, menu_height), 2, border_radius=14) + # Title + title_surface = config.font.render(title, True, THEME_COLORS["text"]) + title_rect = title_surface.get_rect(center=(config.screen_width//2, menu_y + margin_top_bottom//2 + title_surface.get_height()//2)) + screen.blit(title_surface, title_rect) + # Options + start_y = title_rect.bottom + 10 + for i, opt in enumerate(options): + draw_stylized_button( + screen, + opt, + menu_x + 20, + start_y + i * (button_height + 10), + menu_width - 40, + button_height, + selected=(i == selected_index) + ) + +def draw_pause_controls_menu(screen, selected_index): + options = [ + _("menu_controls"), # aide contrôles (réutilisée) + _("menu_remap_controls"), # remap + _("menu_back") if _ else "Back" + ] + _draw_submenu_generic(screen, _("menu_controls") if _ else "Controls", options, selected_index) + +def draw_pause_display_menu(screen, selected_index): + from rgsx_settings import get_show_unsupported_platforms, get_allow_unknown_extensions + # Layout label + layouts = [(3,3),(3,4),(4,3),(4,4)] + try: + idx = layouts.index((config.GRID_COLS, config.GRID_ROWS)) + except ValueError: + idx = 0 + layout_value = f"{layouts[idx][0]}x{layouts[idx][1]}" + layout_txt = f"{_('submenu_display_layout') if _ else 'Layout'}: < {layout_value} >" + # Font size + opts = getattr(config, 'font_scale_options', [0.75, 1.0, 1.25, 1.5, 1.75]) + cur_idx = getattr(config, 'current_font_scale_index', 1) + font_value = f"{opts[cur_idx]}x" + font_txt = f"{_('submenu_display_font_size') if _ else 'Font Size'}: < {font_value} >" + unsupported = get_show_unsupported_platforms() + status_unsupported = _('status_on') if unsupported else _('status_off') + # Construire label sans statut pour insérer les chevrons proprement + raw_unsupported_label = _('submenu_display_show_unsupported') if _ else 'Show unsupported systems: {status}' + # Retirer éventuel placeholder et ponctuation finale + if '{status}' in raw_unsupported_label: + raw_unsupported_label = raw_unsupported_label.split('{status}')[0].rstrip(' :') + unsupported_txt = f"{raw_unsupported_label}: < {status_unsupported} >" + allow_unknown = get_allow_unknown_extensions() + status_unknown = _('status_on') if allow_unknown else _('status_off') + raw_unknown_label = _('submenu_display_allow_unknown_ext') if _ else 'Hide unknown ext warn: {status}' + if '{status}' in raw_unknown_label: + raw_unknown_label = raw_unknown_label.split('{status}')[0].rstrip(' :') + unknown_txt = f"{raw_unknown_label}: < {status_unknown} >" + filter_txt = _("submenu_display_filter_platforms") if _ else "Filter Platforms" + back_txt = _("menu_back") if _ else "Back" + options = [layout_txt, font_txt, unsupported_txt, unknown_txt, filter_txt, back_txt] + _draw_submenu_generic(screen, _("menu_display"), options, selected_index) + +def draw_pause_games_menu(screen, selected_index): + from rgsx_settings import get_sources_mode + mode = get_sources_mode() + source_label = _("games_source_rgsx") if mode == "rgsx" else _("games_source_custom") + source_txt = f"{_('menu_games_source_prefix')}: < {source_label} >" + update_txt = _("menu_redownload_cache") + # Première entrée: Historique des téléchargements (utiliser la clé menu_history) + history_txt = _("menu_history") if _ else "History" + back_txt = _("menu_back") if _ else "Back" + options = [history_txt, source_txt, update_txt, back_txt] + _draw_submenu_generic(screen, _("menu_games") if _ else "Games", options, selected_index) + +def draw_pause_settings_menu(screen, selected_index): + from rgsx_settings import get_symlink_option + # Music + if config.music_enabled: + music_name = config.current_music_name or "" + music_option = _("menu_music_enabled").format(music_name) + else: + music_option = _("menu_music_disabled") + # Uniformiser en < value > pour les réglages basculables + if ' : ' in music_option: + base, val = music_option.split(' : ',1) + music_option = f"{base} : < {val.strip()} >" + symlink_option = _("symlink_option_enabled") if get_symlink_option() else _("symlink_option_disabled") + if ' ' in symlink_option: + parts = symlink_option.split(' ',1) + # On garde phrase intacte si elle n'a pas de forme label: valeur ; sinon transformer + if ' : ' in symlink_option: + base, val = symlink_option.split(' : ',1) + symlink_option = f"{base} : < {val.strip()} >" + api_keys_txt = _("menu_api_keys_status") if _ else "API Keys" + back_txt = _("menu_back") if _ else "Back" + options = [music_option, symlink_option, api_keys_txt, back_txt] + _draw_submenu_generic(screen, _("menu_settings_category") if _ else "Settings", options, selected_index) + +def draw_pause_api_keys_status(screen): + screen.blit(OVERLAY, (0,0)) + from utils import load_api_keys + keys = load_api_keys() + # Layout simple + lines = [ + _("api_keys_status_title") if _ else "API Keys Status", + ("1fichier", keys.get('1fichier')), + ("AllDebrid", keys.get('alldebrid')) + ] + menu_width = int(config.screen_width * 0.55) + menu_height = int(config.screen_height * 0.35) + menu_x = (config.screen_width - menu_width)//2 + menu_y = (config.screen_height - menu_height)//2 + pygame.draw.rect(screen, THEME_COLORS["button_idle"], (menu_x, menu_y, menu_width, menu_height), border_radius=16) + pygame.draw.rect(screen, THEME_COLORS["border"], (menu_x, menu_y, menu_width, menu_height), 2, border_radius=16) + title_surface = config.font.render(lines[0], True, THEME_COLORS["text"]) + title_rect = title_surface.get_rect(center=(config.screen_width//2, menu_y + 40)) + screen.blit(title_surface, title_rect) + status_on = _("status_present") if _ else "Present" + status_off = _("status_missing") if _ else "Missing" + y = title_rect.bottom + 20 + for provider, present in lines[1:]: + status_txt = status_on if present else status_off + text = f"{provider}: {status_txt}" + surf = config.small_font.render(text, True, THEME_COLORS["text"]) + rect = surf.get_rect(center=(config.screen_width//2, y)) + screen.blit(surf, rect) + y += surf.get_height() + 12 + back_txt = _("menu_back") if _ else "Back" + back_surf = config.small_font.render(back_txt, True, THEME_COLORS["fond_lignes"]) # Indication + back_rect = back_surf.get_rect(center=(config.screen_width//2, menu_y + menu_height - 30)) + screen.blit(back_surf, back_rect) + def draw_filter_platforms_menu(screen): """Affiche le menu de filtrage des plateformes (afficher/masquer).""" from rgsx_settings import load_rgsx_settings @@ -1591,8 +1715,11 @@ def draw_controls_help(screen, previous_state): # États autorisés (même logique qu'avant) allowed_states = { + # États classiques où l'aide était accessible "error", "platform", "game", "confirm_exit", - "extension_warning", "history", "clear_history" + "extension_warning", "history", "clear_history", + # Nouveaux états hiérarchiques pause + "pause_controls_menu", "pause_menu" } if previous_state not in allowed_states: return diff --git a/ports/RGSX/history.py b/ports/RGSX/history.py index 877b9ce..fa21c40 100644 --- a/ports/RGSX/history.py +++ b/ports/RGSX/history.py @@ -51,7 +51,6 @@ def save_history(history): os.makedirs(os.path.dirname(history_path), exist_ok=True) with open(history_path, "w", encoding='utf-8') as f: json.dump(history, f, indent=2, ensure_ascii=False) - logger.debug(f"Historique sauvegardé dans {history_path}") except Exception as e: logger.error(f"Erreur lors de l'écriture de {history_path} : {e}") diff --git a/ports/RGSX/languages/de.json b/ports/RGSX/languages/de.json index 73e2a62..540485c 100644 --- a/ports/RGSX/languages/de.json +++ b/ports/RGSX/languages/de.json @@ -1,15 +1,10 @@ { - "controls_mapping_title": "Steuerung konfigurieren", - "controls_mapping_instruction": "3 Sekunden halten zum Konfigurieren:", - "controls_mapping_waiting": "Warte auf eine Taste oder einen Button...", - "controls_mapping_press": "Drücke eine Taste oder einen Button", "welcome_message": "Willkommen bei RGSX", "disclaimer_line1": "Es ist gefährlich, allein zu gehen, nimm alles, was du brauchst!", "disclaimer_line2": "Aber lade nur Spiele herunter,", "disclaimer_line3": "von denen du die Originale besitzt!", "disclaimer_line4": "RGSX ist nicht verantwortlich für heruntergeladene Inhalte,", "disclaimer_line5": "und hostet keine ROMs.", - "loading_test_connection": "Verbindung wird getestet...", "loading_download_data": "Spiele und Bilder werden heruntergeladen...", "loading_progress": "Fortschritt: {0}%", @@ -19,27 +14,18 @@ "loading_extracting_data": "Der initiale Datenordner wird entpackt...", "loading_load_systems": "Systeme werden geladen...", "error_extract_data_failed": "Herunterladen oder Entpacken des initialen Datenordners fehlgeschlagen.", - "error_sources_load_failed": "Quellen konnten nicht geladen werden. Öffne das Menü und wähle dann Spiele-Cache erneut herunterladen", - "error_no_internet": "Keine Internetverbindung. Überprüfe dein Netzwerk.", - "error_controls_mapping": "Fehler beim Zuordnen der Steuerung", "error_api_key": "Achtung, du musst deinen API-Schlüssel (nur Premium) in der Datei {0} eingeben", - "error_api_key_extended": "Achtung, du musst deinen API-Schlüssel (nur Premium) in der Datei /userdata/saves/ports/rgsx/1FichierAPI.txt einfügen. Öffne die Datei in einem Texteditor und füge den API-Schlüssel ein", "error_invalid_download_data": "Ungültige Downloaddaten", "error_delete_sources": "Fehler beim Löschen der Datei systems_list.json oder Ordner", - "error_extension": "Nicht unterstützte Erweiterung oder Downloadfehler", - "error_no_download": "Keine Downloads ausstehend.", - "platform_no_platform": "Keine Plattform", "platform_page": "Seite {0}/{1}", - "game_no_games": "Keine Spiele verfügbar", "game_count": "{0} ({1} Spiele)", "game_filter": "Aktiver Filter: {0}", "game_search": "Filtern: {0}", "game_header_name": "Name", "game_header_size": "Größe", - "history_title": "Downloads ({0})", "history_empty": "Keine Downloads im Verlauf", "history_column_system": "System", @@ -51,28 +37,21 @@ "history_status_completed": "Abgeschlossen", "history_status_error": "Fehler: {0}", "history_status_canceled": "Abgebrochen", - "download_status": "{0}: {1}", - "download_progress": "{0}% {1} MB / {2} MB", "download_canceled": "Download vom Benutzer abgebrochen.", - "extension_warning_zip": "Die Datei '{0}' ist ein Archiv und Batocera unterstützt keine Archive für dieses System. Die automatische Extraktion der Datei erfolgt nach dem Download, fortfahren?", "extension_warning_unsupported": "Die Dateierweiterung für '{0}' wird laut der Konfiguration es_systems.cfg von Batocera nicht unterstützt. Möchtest du fortfahren?", "extension_warning_enable_unknown_hint": "\nUm diese Meldung auszublenden: \"Warnung bei unbekannter Erweiterung ausblenden\" in Pausenmenü > Anzeige aktivieren", - "confirm_exit": "Anwendung beenden?", "confirm_exit_with_downloads": "Achtung: {0} Download(s) laufen. Trotzdem beenden?", "confirm_clear_history": "Verlauf löschen?", "confirm_redownload_cache": "Spieleliste aktualisieren?", - "popup_redownload_success": "Cache gelöscht, bitte die Anwendung neu starten", "popup_no_cache": "Kein Cache gefunden.\nBitte starte die Anwendung neu, um die Spiele zu laden.", "popup_countdown": "Diese Nachricht schließt in {0} Sekunde{1}", - "language_select_title": "Sprachauswahl", "language_select_instruction": "Verwende die Pfeiltasten zum Navigieren und Enter zum Auswählen", "language_changed": "Sprache geändert zu {0}", - "menu_controls": "Steuerung", "menu_remap_controls": "Steuerung neu zuordnen", "menu_history": "Verlauf", @@ -81,21 +60,13 @@ "menu_display": "Anzeige", "display_layout": "Anzeigelayout", "menu_redownload_cache": "Spieleliste aktualisieren", - "menu_music_toggle": "Musik ein/aus", "menu_music_enabled": "Musik aktiviert: {0}", "menu_music_disabled": "Musik deaktiviert", "menu_restart": "Neustart", "menu_filter_platforms": "Systeme filtern", "filter_platforms_title": "Systemsichtbarkeit", - "filter_all": "Alle anzeigen", - "filter_none": "Alle ausblenden", - "filter_apply": "Anwenden", - "filter_back": "Zurück", "filter_platforms_info": "Sichtbar: {0} | Versteckt: {1} / Gesamt: {2}", "filter_unsaved_warning": "Ungespeicherte Änderungen", - "menu_show_unsupported_on": "Nicht unterstützte Systeme anzeigen: Ja", - "menu_show_unsupported_off": "Nicht unterstützte Systeme anzeigen: Nein", - "menu_show_unsupported_text": "systeme versteckt", "menu_show_unsupported_enabled": "Sichtbarkeit nicht unterstützter Systeme aktiviert", "menu_show_unsupported_disabled": "Sichtbarkeit nicht unterstützter Systeme deaktiviert", "menu_allow_unknown_ext_on": "Warnung bei unbekannter Erweiterung ausblenden: Ja", @@ -103,64 +74,15 @@ "menu_allow_unknown_ext_enabled": "Ausblenden der Warnung bei unbekannter Erweiterung aktiviert", "menu_allow_unknown_ext_disabled": "Ausblenden der Warnung bei unbekannter Erweiterung deaktiviert", "menu_quit": "Beenden", - "button_yes": "Ja", "button_no": "Nein", "button_OK": "OK", "popup_restarting": "Neustart...", - - "controls_hold_message": "3 Sekunden halten für: '{0}'", - "controls_skip_message": "Drücke Esc, um zu überspringen (nur PC)", - "controls_waiting": "Warten...", - "controls_hold": "3 Sekunden halten", - - "controls_action_confirm": "Bestätigen", - "controls_action_cancel": "Abbrechen", - "controls_action_up": "Hoch", - "controls_action_down": "Runter", - "controls_action_left": "Links", - "controls_action_right": "Rechts", - "controls_action_page_up": "Vorherige Seite", - "controls_action_page_down": "Nächste Seite", "controls_action_clear_history": "Mehrfachauswahl / Verlauf leeren", "controls_action_history": "Verlauf", - "controls_action_filter": "Filtern", "controls_action_delete": "Löschen", "controls_action_space": "Leerzeichen", "controls_action_start": "Hilfe / Einstellungen", - - "controls_desc_confirm": "Bestätigen (z.B.: A, Enter)", - "controls_desc_cancel": "Abbrechen/Zurück (z.B.: B, Rücktaste)", - "controls_desc_up": "Nach oben navigieren", - "controls_desc_down": "Nach unten navigieren", - "controls_desc_left": "Nach links navigieren", - "controls_desc_right": "Nach rechts navigieren", - "controls_desc_page_up": "Vorherige Seite/Schnelles Scrollen nach oben (z.B.: BildAuf, LB)", - "controls_desc_page_down": "Nächste Seite/Schnelles Scrollen nach unten (z.B.: BildAb, RB)", - "controls_desc_clear_history": "Mehrfachauswahl (Spieleliste) / Verlauf löschen (Verlaufsmenü) (z.B.: X)", - "controls_desc_history": "Verlauf öffnen (z.B.: H, Y)", - "controls_desc_filter": "Filter öffnen (z.B.: F, Select)", - "controls_desc_delete": "Zeichen löschen (z.B.: LT, Entf)", - "controls_desc_space": "Leerzeichen hinzufügen (z.B.: RT, Leertaste)", - "controls_desc_start": "Pausenmenü öffnen (z.B.: Start, AltGr)", - - - "action_retry": "Wiederholen", - "action_quit": "Beenden", - "action_select": "Auswählen", - "action_history": "Verlauf", - "action_download": "Herunterladen", - "action_filter": "Filtern", - "action_cancel": "Abbrechen", - "action_back": "Zurück", - "action_navigate": "Navigieren", - "action_page": "Seite", - "action_cancel_download": "Download abbrechen", - "action_background": "Hintergrund", - "action_confirm": "Bestätigen", - "action_redownload": "Erneut herunterladen", - "action_clear_history": "Verlauf löschen", - "network_checking_updates": "Updates werden geprüft...", "network_update_available": "Update verfügbar: {0}", "network_extracting_update": "Update wird extrahiert...", @@ -175,7 +97,6 @@ "network_extraction_partial": "Extraktion erfolgreich, aber einige Dateien wurden aufgrund von Fehlern übersprungen: {0}", "network_extraction_success": "Extraktion erfolgreich", "network_zip_extraction_error": "Fehler beim Extrahieren des ZIP {0}: {1}", - "network_permission_error": "Keine Schreibberechtigung für {0}", "network_file_not_found": "Die Datei {0} existiert nicht", "network_cannot_get_filename": "Dateiname konnte nicht abgerufen werden", "network_cannot_get_download_url": "Download-URL konnte nicht abgerufen werden", @@ -191,36 +112,40 @@ "controls_pages": "Seiten", "controls_confirm_select": "Bestätigen/Auswählen", "controls_cancel_back": "Abbrechen/Zurück", - "controls_history": "Verlauf", - "controls_clear_history": "Mehrfachauswahl / Verlauf", "controls_filter_search": "Filtern/Suchen", "network_download_failed": "Download nach {0} Versuchen fehlgeschlagen", "network_api_error": "Fehler bei der API-Anfrage, der Schlüssel könnte falsch sein: {0}", "network_download_error": "Downloadfehler {0}: {1}", "network_download_ok": "Download erfolgreich: {0}", - "utils_extracted": "Extrahiert: {0}", "utils_corrupt_zip": "Beschädigtes ZIP-Archiv: {0}", "utils_permission_denied": "Berechtigung während der Extraktion verweigert: {0}", "utils_extraction_failed": "Extraktion fehlgeschlagen: {0}", "utils_unrar_unavailable": "Befehl unrar nicht verfügbar", "utils_rar_list_failed": "Fehler beim Auflisten der RAR-Dateien: {0}", - "utils_xdvdfs_unavailable": "xdvdfs-Tool nicht verfügbar (fehlend oder keine Berechtigung)", - - "menu_symlink_option": "Symlink-Option", "symlink_option_enabled": "Symlink-Option aktiviert", "symlink_option_disabled": "Symlink-Option deaktiviert", - "symlink_settings_saved_successfully": "Symlink-Einstellungen erfolgreich gespeichert", - "symlink_settings_save_error": "Fehler beim Speichern der Symlink-Einstellungen" - , "menu_games_source_prefix": "Spielquelle", "games_source_rgsx": "RGSX", "sources_mode_rgsx_select_info": "RGSX: Spieleliste aktualisieren", - "games_source_custom": "Benutzerdefiniert" - , + "games_source_custom": "Benutzerdefiniert", "sources_mode_custom_select_info": "Benutzerdefiniert: URL in {0} setzen und Spieleliste aktualisieren", "sources_mode_custom_missing_url": "Keine benutzerdefinierte URL gesetzt (bearbeite {0})", "sources_mode_custom_download_error": "Download der benutzerdefinierten Quelle fehlgeschlagen", "menu_show_unsupported_and_hidden": "{0} nicht unterstützte und ausgeblendete Systeme anzeigen", - "menu_show_unsupported_all_displayed": "Alle Systeme angezeigt" + "menu_show_unsupported_all_displayed": "Alle Systeme angezeigt", + "menu_settings_category": "Einstellungen", + "menu_back": "Zurück", + "submenu_display_layout": "Layout", + "submenu_display_font_size": "Schriftgröße", + "submenu_display_show_unsupported": "Nicht unterstützte Systeme: {status}", + "submenu_display_allow_unknown_ext": "Warnung unbek. Ext verbergen: {status}", + "submenu_display_filter_platforms": "Systeme filtern", + "status_on": "An", + "status_off": "Aus", + "status_present": "Vorhanden", + "status_missing": "Fehlt", + "menu_api_keys_status": "API-Schlüssel", + "api_keys_status_title": "Status der API-Schlüssel", + "menu_games": "Spiele" } \ No newline at end of file diff --git a/ports/RGSX/languages/en.json b/ports/RGSX/languages/en.json index 1d263f0..1781952 100644 --- a/ports/RGSX/languages/en.json +++ b/ports/RGSX/languages/en.json @@ -1,15 +1,10 @@ { - "controls_mapping_title": "Controls configuration", - "controls_mapping_instruction": "Hold for 3s to configure:", - "controls_mapping_waiting": "Waiting for a key or button...", - "controls_mapping_press": "Press a key or button", "welcome_message": "Welcome to RGSX", "disclaimer_line1": "It's dangerous to go alone, take all you need!", "disclaimer_line2": "But only download games", "disclaimer_line3": "that you already own!", "disclaimer_line4": "RGSX is not responsible for downloaded content,", "disclaimer_line5": "and does not host ROMs.", - "loading_test_connection": "Testing connection...", "loading_download_data": "Downloading initial Data folder...", "loading_progress": "Progress: {0}%", @@ -19,27 +14,18 @@ "loading_extracting_data": "Extracting initial Data folder...", "loading_load_systems": "Loading systems...", "error_extract_data_failed": "Failed to download or extract the initial Data folder.", - "error_sources_load_failed": "Failed to load sources. Open the menu then select Redownload Games cache", - "error_no_internet": "No Internet connection. Check your network.", - "error_controls_mapping": "Failed to map controls", "error_api_key": "Please enter your API key (premium only) in the file {0}", - "error_api_key_extended": "Please enter your API key (premium only) in the file /userdata/saves/ports/rgsx/1FichierAPI.txt by opening it in a text editor and pasting your API key", "error_invalid_download_data": "Invalid download data", "error_delete_sources": "Error deleting systems_list.json file or folders", - "error_extension": "Unsupported extension or download error", - "error_no_download": "No pending download.", - "platform_no_platform": "No platform", "platform_page": "Page {0}/{1}", - "game_no_games": "No games available", "game_count": "{0} ({1} games)", "game_filter": "Active filter: {0}", "game_search": "Filter: {0}", "game_header_name": "Name", "game_header_size": "Size", - "history_title": "Downloads ({0})", "history_empty": "No downloads in history", "history_column_system": "System", @@ -51,28 +37,21 @@ "history_status_completed": "Completed", "history_status_error": "Error: {0}", "history_status_canceled": "Canceled", - "download_status": "{0}: {1}", - "download_progress": "{0}% {1} MB / {2} MB", "download_canceled": "Download canceled by user.", - "extension_warning_zip": "The file '{0}' is an archive and Batocera does not support archives for this system. Automatic extraction will occur after download, continue?", "extension_warning_unsupported": "The file extension for '{0}' is not supported by Batocera according to the es_systems.cfg configuration. Do you want to continue?", "extension_warning_enable_unknown_hint": "\nTo hide this message: enable \"Hide unknown extension warning\" in Pause Menu > Display", - "confirm_exit": "Exit application?", "confirm_exit_with_downloads": "Attention: {0} download(s) in progress. Quit anyway?", "confirm_clear_history": "Clear history?", "confirm_redownload_cache": "Update games list?", - "popup_redownload_success": "Cache cleared, please restart the application", "popup_no_cache": "No cache found.\nPlease restart the application to load games.", "popup_countdown": "This message will close in {0} second{1}", - "language_select_title": "Language Selection", "language_select_instruction": "Use arrow keys to navigate and Enter to select", "language_changed": "Language changed to {0}", - "menu_controls": "Controls", "menu_remap_controls": "Remap controls", "menu_history": "History", @@ -81,21 +60,13 @@ "menu_display": "Display", "display_layout": "Display layout", "menu_redownload_cache": "Update games list", - "menu_music_toggle": "Toggle music", "menu_music_enabled": "Music enabled: {0}", "menu_music_disabled": "Music disabled", "menu_restart": "Restart", "menu_filter_platforms": "Filter systems", "filter_platforms_title": "Systems visibility", - "filter_all": "Show all", - "filter_none": "Hide all", - "filter_apply": "Apply", - "filter_back": "Back", "filter_platforms_info": "Visible: {0} | Hidden: {1} / Total: {2}", "filter_unsaved_warning": "Unsaved changes", - "menu_show_unsupported_on": "Show unsupported systems: Yes", - "menu_show_unsupported_off": "Show unsupported systems: No", - "menu_show_unsupported_text": "hidden systems", "menu_show_unsupported_enabled": "Unsupported systems visibility enabled", "menu_show_unsupported_disabled": "Unsupported systems visibility disabled", "menu_allow_unknown_ext_on": "Hide unknown extension warning: Yes", @@ -103,64 +74,15 @@ "menu_allow_unknown_ext_enabled": "Hide unknown extension warning enabled", "menu_allow_unknown_ext_disabled": "Hide unknown extension warning disabled", "menu_quit": "Quit", - "button_yes": "Yes", "button_no": "No", "button_OK": "OK", "popup_restarting": "Restarting...", - - "controls_hold_message": "Hold for 3s for: '{0}'", - "controls_skip_message": "Press Esc to skip (PC only)", - "controls_waiting": "Waiting...", - "controls_hold": "Hold 3s", - - "controls_action_confirm": "Confirm", - "controls_action_cancel": "Cancel", - "controls_action_up": "Up", - "controls_action_down": "Down", - "controls_action_left": "Left", - "controls_action_right": "Right", - "controls_action_page_up": "Previous Page", - "controls_action_page_down": "Next Page", "controls_action_clear_history": "Multi-select / Clear History", "controls_action_history": "History", - "controls_action_filter": "Filter", "controls_action_delete": "Delete", "controls_action_space": "Space", "controls_action_start": "Help / Settings", - - "controls_desc_confirm": "Validate (e.g. A, Enter)", - "controls_desc_cancel": "Cancel/Back (e.g. B, Backspace)", - "controls_desc_up": "Navigate up", - "controls_desc_down": "Navigate down", - "controls_desc_left": "Navigate left", - "controls_desc_right": "Navigate right", - "controls_desc_page_up": "Previous page/Fast scroll up (e.g. PageUp, LB)", - "controls_desc_page_down": "Next page/Fast scroll down (e.g. PageDown, RB)", - "controls_desc_clear_history": "Multi-select (game list) / Clear history (history menu) (e.g. X)", - "controls_desc_history": "Open history (e.g. H, Y)", - "controls_desc_filter": "Open filter (e.g. F, Select)", - "controls_desc_delete": "Delete character (e.g. LT, Delete)", - "controls_desc_space": "Add space (e.g. RT, Space)", - "controls_desc_start": "Open pause menu (e.g. Start, AltGr)", - - - "action_retry": "Retry", - "action_quit": "Quit", - "action_select": "Select", - "action_history": "History", - "action_download": "Download", - "action_filter": "Filter", - "action_cancel": "Cancel", - "action_back": "Back", - "action_navigate": "Navigate", - "action_page": "Page", - "action_cancel_download": "Cancel download", - "action_background": "Background", - "action_confirm": "Confirm", - "action_redownload": "Redownload", - "action_clear_history": "Clear history", - "network_checking_updates": "Checking for updates...", "network_update_available": "Update available: {0}", "network_extracting_update": "Extracting update...", @@ -175,7 +97,6 @@ "network_extraction_success": "Extraction successful", "network_download_extract_ok": "Download and extraction successful of {0}", "network_zip_extraction_error": "Error extracting ZIP {0}: {1}", - "network_permission_error": "No write permission in {0}", "network_file_not_found": "File {0} does not exist", "network_cannot_get_filename": "Cannot retrieve filename", "network_cannot_get_download_url": "Cannot retrieve download URL", @@ -183,14 +104,12 @@ "network_api_error": "API request error, the key may be incorrect: {0}", "network_download_error": "Download error {0}: {1}", "network_download_ok": "Download OK: {0}", - "utils_extracted": "Extracted: {0}", "utils_corrupt_zip": "Corrupted ZIP archive: {0}", "utils_permission_denied": "Permission denied during extraction: {0}", "utils_extraction_failed": "Extraction failed: {0}", "utils_unrar_unavailable": "Unrar command not available", "utils_rar_list_failed": "Failed to list RAR files: {0}", - "utils_xdvdfs_unavailable": "xdvdfs tool unavailable (missing or permission denied)", "download_initializing": "Initializing...", "accessibility_font_size": "Font size: {0}", "confirm_cancel_download": "Cancel current download?", @@ -203,24 +122,30 @@ "controls_pages": "Pages", "controls_confirm_select": "Confirm/Select", "controls_cancel_back": "Cancel/Back", - "controls_history": "History", - "controls_clear_history": "Multi-select / History", "controls_filter_search": "Filter/Search", - - "menu_symlink_option": "Symlink Option", "symlink_option_enabled": "Symlink option enabled", "symlink_option_disabled": "Symlink option disabled", - "symlink_settings_saved_successfully": "Symlink settings saved successfully", - "symlink_settings_save_error": "Error saving symlink settings", - "menu_games_source_prefix": "Game source", "games_source_rgsx": "RGSX", "sources_mode_rgsx_select_info": "RGSX: update the games list", "games_source_custom": "Custom", - "sources_mode_custom_select_info": "Custom mode: set URL in {0} then update games list", "sources_mode_custom_missing_url": "No custom URL set (edit {0})", "sources_mode_custom_download_error": "Custom source download failed", "menu_show_unsupported_and_hidden": "Show unsupported and hidden {0} systems", - "menu_show_unsupported_all_displayed": "Hide all unsupported systems" + "menu_show_unsupported_all_displayed": "Hide all unsupported systems", + "menu_settings_category": "Settings", + "menu_back": "Back", + "submenu_display_layout": "Layout", + "submenu_display_font_size": "Font Size", + "submenu_display_show_unsupported": "Show unsupported systems: {status}", + "submenu_display_allow_unknown_ext": "Hide unknown ext warn: {status}", + "submenu_display_filter_platforms": "Filter systems", + "status_on": "On", + "status_off": "Off", + "status_present": "Present", + "status_missing": "Missing", + "menu_api_keys_status": "API Keys", + "api_keys_status_title": "API Keys Status", + "menu_games": "Games" } \ No newline at end of file diff --git a/ports/RGSX/languages/es.json b/ports/RGSX/languages/es.json index 2100648..194aaad 100644 --- a/ports/RGSX/languages/es.json +++ b/ports/RGSX/languages/es.json @@ -1,15 +1,10 @@ { - "controls_mapping_title": "Configuración de controles", - "controls_mapping_instruction": "Mantén pulsado 3s para configurar:", - "controls_mapping_waiting": "Esperando una tecla o botón...", - "controls_mapping_press": "Pulsa una tecla o botón", "welcome_message": "Bienvenido a RGSX", "disclaimer_line1": "¡Es peligroso ir solo, toma todo lo que necesites!", "disclaimer_line2": "Pero solo descarga juegos", "disclaimer_line3": "de los que poseas los originales.", "disclaimer_line4": "RGSX no es responsable del contenido descargado,", "disclaimer_line5": "y no aloja ROMs.", - "loading_test_connection": "Probando conexión...", "loading_download_data": "Descargando juegos e imágenes...", "loading_progress": "Progreso: {0}%", @@ -19,28 +14,18 @@ "loading_extracting_data": "Extrayendo la carpeta de datos inicial...", "loading_load_systems": "Cargando sistemas...", "error_extract_data_failed": "Error al descargar o extraer la carpeta de datos inicial.", - "error_sources_load_failed": "Error al cargar las fuentes. Abre el menú y selecciona Volver a descargar la caché de juegos", - - "error_no_internet": "Sin conexión a Internet. Verifica tu red.", - "error_controls_mapping": "Error al mapear los controles", "error_api_key": "Atención, debes ingresar tu clave API (solo premium) en el archivo {0}", - "error_api_key_extended": "Atención, debes ingresar tu clave API (solo premium) en el archivo /userdata/saves/ports/rgsx/1FichierAPI.txt, abrirlo en un editor de texto y pegar la clave API", "error_invalid_download_data": "Datos de descarga no válidos", "error_delete_sources": "Error al eliminar el archivo systems_list.json o carpetas", - "error_extension": "Extensión no soportada o error de descarga", - "error_no_download": "No hay descargas pendientes.", - "platform_no_platform": "Ninguna plataforma", "platform_page": "Página {0}/{1}", - "game_no_games": "No hay juegos disponibles", "game_count": "{0} ({1} juegos)", "game_filter": "Filtro activo: {0}", "game_search": "Filtrar: {0}", "game_header_name": "Nombre", "game_header_size": "Tamaño", - "history_title": "Descargas ({0})", "history_empty": "No hay descargas en el historial", "history_column_system": "Sistema", @@ -52,28 +37,21 @@ "history_status_completed": "Completado", "history_status_error": "Error: {0}", "history_status_canceled": "Cancelado", - "download_status": "{0}: {1}", - "download_progress": "{0}% {1} MB / {2} MB", "download_canceled": "Descarga cancelada por el usuario.", - "extension_warning_zip": "El archivo '{0}' es un archivo comprimido y Batocera no soporta archivos comprimidos para este sistema. La extracción automática del archivo se realizará después de la descarga, ¿continuar?", "extension_warning_unsupported": "La extensión del archivo '{0}' no está soportada por Batocera según la configuración es_systems.cfg. ¿Deseas continuar?", "extension_warning_enable_unknown_hint": "\nPara no mostrar este mensaje: activa \"Ocultar aviso de extensión desconocida\" en Menú de pausa > Pantalla", - "confirm_exit": "¿Salir de la aplicación?", "confirm_exit_with_downloads": "Atención: {0} descarga(s) en curso. ¿Salir de todas formas?", "confirm_clear_history": "¿Vaciar el historial?", "confirm_redownload_cache": "¿Actualizar la lista de juegos?", - "popup_redownload_success": "Caché borrada, por favor reinicia la aplicación", "popup_no_cache": "No se encontró caché.\nPor favor, reinicia la aplicación para cargar los juegos.", "popup_countdown": "Este mensaje se cerrará en {0} segundo{1}", - "language_select_title": "Selección de idioma", "language_select_instruction": "Usa las flechas para navegar y Enter para seleccionar", "language_changed": "Idioma cambiado a {0}", - "menu_controls": "Controles", "menu_remap_controls": "Remapear controles", "menu_history": "Historial", @@ -82,21 +60,13 @@ "menu_display": "Pantalla", "display_layout": "Distribución", "menu_redownload_cache": "Actualizar lista de juegos", - "menu_music_toggle": "Activar/Desactivar música", "menu_music_enabled": "Música activada: {0}", "menu_music_disabled": "Música desactivada", "menu_restart": "Reiniciar", "menu_filter_platforms": "Filtrar sistemas", "filter_platforms_title": "Visibilidad de sistemas", - "filter_all": "Mostrar todo", - "filter_none": "Ocultar todo", - "filter_apply": "Aplicar", - "filter_back": "Volver", "filter_platforms_info": "Visibles: {0} | Ocultos: {1} / Total: {2}", "filter_unsaved_warning": "Cambios no guardados", - "menu_show_unsupported_on": "Mostrar sistemas no soportados: Sí", - "menu_show_unsupported_off": "Mostrar sistemas no soportados: No", - "menu_show_unsupported_text": "sistemas ocultos", "menu_show_unsupported_enabled": "Visibilidad de sistemas no soportados activada", "menu_show_unsupported_disabled": "Visibilidad de sistemas no soportados desactivada", "menu_allow_unknown_ext_on": "Ocultar aviso de extensión desconocida: Sí", @@ -104,64 +74,15 @@ "menu_allow_unknown_ext_enabled": "Aviso de extensión desconocida oculto (activado)", "menu_allow_unknown_ext_disabled": "Aviso de extensión desconocida visible (desactivado)", "menu_quit": "Salir", - "button_yes": "Sí", "button_no": "No", "button_OK": "OK", "popup_restarting": "Reiniciando...", - - "controls_hold_message": "Mantén presionado durante 3s para: '{0}'", - "controls_skip_message": "Presiona Esc para omitir (solo PC)", - "controls_waiting": "Esperando...", - "controls_hold": "Mantener 3s", - - "controls_action_confirm": "Confirmar", - "controls_action_cancel": "Cancelar", - "controls_action_up": "Arriba", - "controls_action_down": "Abajo", - "controls_action_left": "Izquierda", - "controls_action_right": "Derecha", - "controls_action_page_up": "Página anterior", - "controls_action_page_down": "Página siguiente", "controls_action_clear_history": "Multi-selección / Vaciar historial", "controls_action_history": "Historial", - "controls_action_filter": "Filtrar", "controls_action_delete": "Eliminar", "controls_action_space": "Espacio", "controls_action_start": "Ayuda / Configuración", - - "controls_desc_confirm": "Validar (ej: A, Enter)", - "controls_desc_cancel": "Cancelar/Volver (ej: B, Retroceso)", - "controls_desc_up": "Navegar hacia arriba", - "controls_desc_down": "Navegar hacia abajo", - "controls_desc_left": "Navegar a izquierda", - "controls_desc_right": "Navegar a derecha", - "controls_desc_page_up": "Página anterior/Desplazamiento rápido arriba (ej: RePág, LB)", - "controls_desc_page_down": "Página siguiente/Desplazamiento rápido abajo (ej: AvPág, RB)", - "controls_desc_clear_history": "Multi-selección (lista juegos) / Vaciar historial (menú historial) (ej: X)", - "controls_desc_history": "Abrir historial (ej: H, Y)", - "controls_desc_filter": "Abrir filtro (ej: F, Select)", - "controls_desc_delete": "Eliminar carácter (ej: LT, Supr)", - "controls_desc_space": "Añadir espacio (ej: RT, Espacio)", - "controls_desc_start": "Abrir el menú de pausa (ej: Start, AltGr)", - - - "action_retry": "Reintentar", - "action_quit": "Salir", - "action_select": "Seleccionar", - "action_history": "Historial", - "action_download": "Descargar", - "action_filter": "Filtrar", - "action_cancel": "Cancelar", - "action_back": "Volver", - "action_navigate": "Navegar", - "action_page": "Página", - "action_cancel_download": "Cancelar la descarga", - "action_background": "Fondo", - "action_confirm": "Confirmar", - "action_redownload": "Volver a descargar", - "action_clear_history": "Vaciar el historial", - "network_checking_updates": "Verificando actualizaciones...", "network_update_available": "Actualización disponible: {0}", "network_extracting_update": "Extrayendo la actualización...", @@ -176,7 +97,6 @@ "network_extraction_partial": "Extracción exitosa, pero algunos archivos fueron omitidos debido a errores: {0}", "network_extraction_success": "Extracción exitosa", "network_zip_extraction_error": "Error al extraer el ZIP {0}: {1}", - "network_permission_error": "Sin permiso de escritura en {0}", "network_file_not_found": "El archivo {0} no existe", "network_cannot_get_filename": "No se pudo obtener el nombre del archivo", "network_cannot_get_download_url": "No se pudo obtener la URL de descarga", @@ -192,28 +112,19 @@ "controls_pages": "Páginas", "controls_confirm_select": "Confirmar/Seleccionar", "controls_cancel_back": "Cancelar/Volver", - "controls_history": "Historial", - "controls_clear_history": "Multi-selección / Historial", "controls_filter_search": "Filtrar/Buscar", "network_download_failed": "Error en la descarga tras {0} intentos", "network_api_error": "Error en la solicitud de API, la clave puede ser incorrecta: {0}", "network_download_error": "Error en la descarga {0}: {1}", "network_download_ok": "Descarga exitosa: {0}", - "utils_extracted": "Extraído: {0}", "utils_corrupt_zip": "Archivo ZIP corrupto: {0}", "utils_permission_denied": "Permiso denegado durante la extracción: {0}", "utils_extraction_failed": "Error en la extracción: {0}", "utils_unrar_unavailable": "Comando unrar no disponible", "utils_rar_list_failed": "Error al listar los archivos RAR: {0}", - "utils_xdvdfs_unavailable": "Herramienta xdvdfs no disponible (faltante o sin permisos)", - - "menu_symlink_option": "Opción Symlink", "symlink_option_enabled": "Opción symlink habilitada", "symlink_option_disabled": "Opción symlink deshabilitada", - "symlink_settings_saved_successfully": "Configuración symlink guardada con éxito", - "symlink_settings_save_error": "Error al guardar la configuración symlink", - "menu_games_source_prefix": "Origen de juegos", "games_source_rgsx": "RGSX", "sources_mode_rgsx_select_info": "RGSX: actualizar la lista de juegos", @@ -222,5 +133,19 @@ "sources_mode_custom_missing_url": "No se ha establecido URL personalizada (editar {0})", "sources_mode_custom_download_error": "Fallo en la descarga de la fuente personalizada", "menu_show_unsupported_and_hidden": "Mostrar {0} sistemas no soportados y ocultos", - "menu_show_unsupported_all_displayed": "Todos los sistemas mostrados" + "menu_show_unsupported_all_displayed": "Todos los sistemas mostrados", + "menu_settings_category": "Ajustes", + "menu_back": "Volver", + "submenu_display_layout": "Disposición", + "submenu_display_font_size": "Tamaño Fuente", + "submenu_display_show_unsupported": "Mostrar sistemas no soportados: {status}", + "submenu_display_allow_unknown_ext": "Ocultar aviso ext. desconocida: {status}", + "submenu_display_filter_platforms": "Filtrar sistemas", + "status_on": "Sí", + "status_off": "No", + "status_present": "Presente", + "status_missing": "Ausente", + "menu_api_keys_status": "Claves API", + "api_keys_status_title": "Estado de las claves API", + "menu_games": "Juegos" } \ No newline at end of file diff --git a/ports/RGSX/languages/fr.json b/ports/RGSX/languages/fr.json index 8d97c5a..f3b595e 100644 --- a/ports/RGSX/languages/fr.json +++ b/ports/RGSX/languages/fr.json @@ -5,7 +5,6 @@ "disclaimer_line3": "dont vous possédez les originaux !", "disclaimer_line4": "RGSX n'est pas responsable des contenus téléchargés,", "disclaimer_line5": "et n'heberge pas de ROMs.", - "loading_test_connection": "Test de la connexion...", "loading_download_data": "Téléchargement des jeux et images...", "loading_progress": "Progression : {0}%", @@ -15,28 +14,18 @@ "loading_extracting_data": "Extraction du dossier de données initial...", "loading_load_systems": "Chargement des systèmes...", "error_extract_data_failed": "Échec du téléchargement ou de l'extraction du dossier de données initial.", - "error_sources_load_failed": "Échec de chargement des sources, ouvrir le menu puis sélectionner Retélécharger le cache des jeux", - - "error_no_internet": "Pas de connexion Internet. Vérifiez votre réseau.", - "error_controls_mapping": "Échec du mappage des contrôles", "error_api_key": "Attention il faut renseigner sa clé API (premium only) dans le fichier {0}", - "error_api_key_extended": "Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1FichierAPI.txt à ouvrir dans un éditeur de texte et coller la clé API", "error_invalid_download_data": "Données de téléchargement invalides", "error_delete_sources": "Erreur lors de la suppression du fichier systems_list.json ou dossiers", - "error_extension": "Extension non supportée ou erreur de téléchargement", - "error_no_download": "Aucun téléchargement en attente.", - "platform_no_platform": "Aucune plateforme", "platform_page": "Page {0}/{1}", - "game_no_games": "Aucun jeu disponible", "game_count": "{0} ({1} jeux)", "game_filter": "Filtre actif : {0}", "game_search": "Filtrer : {0}", "game_header_name": "Nom", "game_header_size": "Taille", - "history_title": "Téléchargements ({0})", "history_empty": "Aucun téléchargement dans l'historique", "history_column_system": "Système", @@ -48,28 +37,21 @@ "history_status_completed": "Terminé", "history_status_error": "Erreur : {0}", "history_status_canceled": "Annulé", - "download_status": "{0} : {1}", - "download_progress": "{0}% {1} Mo / {2} Mo", "download_canceled": "Téléchargement annulé par l'utilisateur.", - "extension_warning_zip": "Le fichier '{0}' est une archive et Batocera ne prend pas en charge les archives pour ce système. L'extraction automatique du fichier aura lieu après le téléchargement, continuer ?", "extension_warning_unsupported": "L'extension du fichier '{0}' n'est pas supportée par Batocera d'après la configuration es_systems.cfg. Voulez-vous continuer ?", "extension_warning_enable_unknown_hint": "\nPour ne plus afficher ce messager : Activer l'option \"Masquer avertissement\" dans le Menu Pause>Display", - "confirm_exit": "Quitter l'application ?", "confirm_exit_with_downloads": "Attention : {0} téléchargement(s) en cours. Quitter quand même ?", "confirm_clear_history": "Vider l'historique ?", "confirm_redownload_cache": "Mettre à jour la liste des jeux ?", - "popup_redownload_success": "Le cache a été effacé, merci de relancer l'application", "popup_no_cache": "Aucun cache trouvé.\nVeuillez redémarrer l'application pour charger les jeux.", "popup_countdown": "Ce message se fermera dans {0} seconde{1}", - "language_select_title": "Sélection de la langue", "language_select_instruction": "Utilisez les flèches pour naviguer et Entrée pour sélectionner", "language_changed": "Langue changée pour {0}", - "menu_controls": "Contrôles", "menu_remap_controls": "Remapper les contrôles", "menu_history": "Historique", @@ -79,85 +61,28 @@ "display_layout": "Disposition", "menu_redownload_cache": "Mettre à jour la liste des jeux", "menu_quit": "Quitter", - "menu_music_toggle": "Activer/Désactiver la musique", "menu_music_enabled": "Musique activée : {0}", - "menu_music_disabled": "Musique désactivée", + "menu_music_disabled": "Musique désactivée", "menu_restart": "Redémarrer", "menu_filter_platforms": "Filtrer les systèmes", "filter_platforms_title": "Affichage des systèmes", - "filter_all": "Tout afficher", - "filter_none": "Tout masquer", - "filter_apply": "Appliquer", - "filter_back": "Retour", "filter_platforms_info": "Visibles: {0} | Masqués: {1} / Total: {2}", "filter_unsaved_warning": "Modifications non sauvegardées", - "menu_show_unsupported_on": "Afficher systèmes non supportés : Oui", - "menu_show_unsupported_off": "Afficher systèmes non supportés : Non", - "menu_show_unsupported_text": "systèmes cachés", "menu_show_unsupported_enabled": "Affichage systèmes non supportés activé", "menu_show_unsupported_disabled": "Affichage systèmes non supportés désactivé", "menu_allow_unknown_ext_on": "Masquer avertissement extension inconnue : Oui", "menu_allow_unknown_ext_off": "Masquer avertissement extension inconnue : Non", "menu_allow_unknown_ext_enabled": "Masquer avertissement extension inconnue activé", "menu_allow_unknown_ext_disabled": "Masquer avertissement extension inconnue désactivé", - "button_yes": "Oui", "button_no": "Non", "button_OK": "OK", "popup_restarting": "Redémarrage...", - - "controls_hold_message": "Maintenez pendant 3s pour : '{0}'", - "controls_skip_message": "Appuyez sur Échap pour passer(Pc only)", - "controls_waiting": "Attente...", - "controls_hold": "Maintenez 3s", - - "controls_action_confirm": "Confirmer", - "controls_action_cancel": "Annuler", - "controls_action_up": "Haut", - "controls_action_down": "Bas", - "controls_action_left": "Gauche", - "controls_action_right": "Droite", - "controls_action_page_up": "Page Précédente", - "controls_action_page_down": "Page Suivante", "controls_action_clear_history": "Multi-sélection / Vider Historique", "controls_action_history": "Historique", - "controls_action_filter": "Filtrer", "controls_action_delete": "Supprimer", "controls_action_space": "Espace", "controls_action_start": "Aide / Réglages", - - "controls_desc_confirm": "Valider (ex: A, Entrée)", - "controls_desc_cancel": "Annuler/Retour (ex: B, Echap)", - "controls_desc_up": "Naviguer vers le haut", - "controls_desc_down": "Naviguer vers le bas", - "controls_desc_left": "Naviguer à gauche", - "controls_desc_right": "Naviguer à droite", - "controls_desc_page_up": "Défilement Rapide - (ex: PageUp, LB)", - "controls_desc_page_down": "Défilement Rapide + (ex: PageDown, RB)", - "controls_desc_history": "Ouvrir l'historique (ex: H, Y)", - "controls_desc_clear_history": "Multi-sélection (liste jeux) / Vider historique (menu historique) (ex: X)", - "controls_desc_filter": "Mode Filtre : Ouvrir/Valider (ex: F, Select)", - "controls_desc_delete": "Mode Filtre : Supprimer caractère (ex: LT, Suppr)", - "controls_desc_space": "Mode Filtre : Ajouter espace (ex: RT, Espace)", - "controls_desc_start": "Ouvrir le menu pause (ex: Start, AltGr)", - - - "action_retry": "Retenter", - "action_quit": "Quitter", - "action_select": "Sélectionner", - "action_history": "Historique", - "action_download": "Télécharger", - "action_filter": "Filtrer", - "action_cancel": "Annuler", - "action_back": "Retour", - "action_navigate": "Naviguer", - "action_page": "Page", - "action_cancel_download": "Annuler le téléchargement", - "action_background": "Arrière plan", - "action_confirm": "Confirmer", - "action_redownload": "Retélécharger", - "action_clear_history": "Vider l'historique", - "network_checking_updates": "Vérification des mises à jour...", "network_update_available": "Mise à jour disponible : {0}", "network_extracting_update": "Extraction de la mise à jour...", @@ -172,10 +97,9 @@ "network_extraction_partial": "Extraction réussie, mais certains fichiers ont été ignorés en raison d'erreurs : {0}", "network_extraction_success": "Extraction réussie", "network_zip_extraction_error": "Erreur lors de l'extraction du ZIP {0}: {1}", - "network_permission_error": "Pas de permission d'écriture dans {0}", "network_file_not_found": "Le fichier {0} n'existe pas", "network_cannot_get_filename": "Impossible de récupérer le nom du fichier", - "network_cannot_get_download_url": "Impossible de récupérer l'URL de téléchargement", + "network_cannot_get_download_url": "Impossible de récupérer l'URL de téléchargement", "download_initializing": "Initialisation en cours...", "accessibility_font_size": "Taille de police : {0}", "confirm_cancel_download": "Annuler le téléchargement en cours ?", @@ -188,32 +112,19 @@ "controls_pages": "Pages", "controls_confirm_select": "Confirmer/Sélectionner", "controls_cancel_back": "Annuler/Retour", - "controls_history": "Historique", - "controls_clear_history": "Multi-sélection / Historique", "controls_filter_search": "Filtrer/Rechercher", "network_download_failed": "Échec du téléchargement après {0} tentatives", "network_api_error": "Erreur lors de la requête API, la clé est peut-être incorrecte: {0}", "network_download_error": "Erreur téléchargement {0}: {1}", "network_download_ok": "Téléchargement ok : {0}", - "utils_extracted": "Extracted: {0}", "utils_corrupt_zip": "Archive ZIP corrompue: {0}", "utils_permission_denied": "Permission refusée lors de l'extraction: {0}", "utils_extraction_failed": "Échec de l'extraction: {0}", "utils_unrar_unavailable": "Commande unrar non disponible", "utils_rar_list_failed": "Échec de la liste des fichiers RAR: {0}", - "utils_xdvdfs_unavailable": "Outil xdvdfs indisponible (manquant ou permission refusée)", - "controls_mapping_title": "Configuration des contrôles", - "controls_mapping_instruction": "Maintenir 3s pour configurer :", - "controls_mapping_waiting": "En attente d'une touche ou d'un bouton...", - "controls_mapping_press": "Appuyez sur une touche ou un bouton", - - "menu_symlink_option": "Option Symlink", "symlink_option_enabled": "Option symlink activée", "symlink_option_disabled": "Option symlink désactivée", - "symlink_settings_saved_successfully": "Paramètres symlink sauvegardés avec succès", - "symlink_settings_save_error": "Erreur lors de la sauvegarde des paramètres symlink", - "menu_games_source_prefix": "Source des jeux", "games_source_rgsx": "RGSX", "sources_mode_rgsx_select_info": "RGSX : mettre à jour la liste des jeux", @@ -222,5 +133,19 @@ "sources_mode_custom_missing_url": "Aucune URL personnalisée définie (modifier {0})", "sources_mode_custom_download_error": "Échec du téléchargement de la source personnalisée", "menu_show_unsupported_and_hidden": "Afficher les systèmes non supportés et masqués : {0}", - "menu_show_unsupported_all_displayed": "Tous les systèmes sont affichés" + "menu_show_unsupported_all_displayed": "Tous les systèmes sont affichés", + "menu_settings_category": "Réglages", + "menu_back": "Retour", + "submenu_display_layout": "Disposition", + "submenu_display_font_size": "Taille Police", + "submenu_display_show_unsupported": "Afficher systèmes non supportés : {status}", + "submenu_display_allow_unknown_ext": "Masquer avert. ext inconnue : {status}", + "submenu_display_filter_platforms": "Filtrer systèmes", + "status_on": "Oui", + "status_off": "Non", + "status_present": "Présente", + "status_missing": "Absente", + "menu_api_keys_status": "Clés API", + "api_keys_status_title": "Statut des clés API", + "menu_games": "Jeux" } \ No newline at end of file diff --git a/ports/RGSX/languages/it.json b/ports/RGSX/languages/it.json index 886e850..8d19418 100644 --- a/ports/RGSX/languages/it.json +++ b/ports/RGSX/languages/it.json @@ -1,224 +1,151 @@ { - "controls_mapping_title": "Configurazione dei controlli", - "controls_mapping_instruction": "Tieni premuto per 3s per configurare:", - "controls_mapping_waiting": "In attesa di un tasto o pulsante...", - "controls_mapping_press": "Premi un tasto o un pulsante", - "welcome_message": "Benvenuto in RGSX", - "disclaimer_line1": "È pericoloso andare da soli, prendi tutto ciò che ti serve!", - "disclaimer_line2": "Ma scarica solo giochi", - "disclaimer_line3": "che possiedi già!", - "disclaimer_line4": "RGSX non è responsabile dei contenuti scaricati,", - "disclaimer_line5": "e non ospita ROM.", - - "loading_test_connection": "Verifica connessione...", - "loading_download_data": "Download cartella Dati iniziale...", - "loading_progress": "Progresso: {0}%", - "loading_check_updates": "Verifica aggiornamenti... Attendere...", - "error_check_updates_failed": "Impossibile verificare gli aggiornamenti.", - "loading_downloading_games_images": "Download giochi e immagini...", - "loading_extracting_data": "Estrazione cartella Dati iniziale...", - "loading_load_systems": "Caricamento sistemi...", - "error_extract_data_failed": "Errore nel download o nell'estrazione della cartella Dati iniziale.", - "error_sources_load_failed": "Errore nel caricamento delle sorgenti. Apri il menu e seleziona Aggiorna cache giochi", - - "error_no_internet": "Nessuna connessione Internet. Controlla la rete.", - "error_controls_mapping": "Impossibile mappare i controlli", - "error_api_key": "Inserisci la tua API key (solo premium) nel file {0}", - "error_api_key_extended": "Inserisci la tua API key (solo premium) nel file /userdata/saves/ports/rgsx/1FichierAPI.txt aprendolo in un editor e incollando la chiave", - "error_invalid_download_data": "Dati di download non validi", - "error_delete_sources": "Errore nell'eliminazione del file systems_list.json o delle cartelle", - "error_extension": "Estensione non supportata o errore di download", - "error_no_download": "Nessun download in sospeso.", - - "platform_no_platform": "Nessuna piattaforma", - "platform_page": "Pagina {0}/{1}", - - "game_no_games": "Nessun gioco disponibile", - "game_count": "{0} ({1} giochi)", - "game_filter": "Filtro attivo: {0}", - "game_search": "Filtro: {0}", - "game_header_name": "Nome", - "game_header_size": "Dimensione", - - "history_title": "Download ({0})", - "history_empty": "Nessun download nella cronologia", - "history_column_system": "Sistema", - "history_column_game": "Nome del gioco", - "history_column_size": "Dimensione", - "history_column_status": "Stato", - "history_status_downloading": "Download: {0}%", - "history_status_extracting": "Estrazione: {0}%", - "history_status_completed": "Completato", - "history_status_error": "Errore: {0}", - "history_status_canceled": "Annullato", - - "download_status": "{0}: {1}", - "download_progress": "{0}% {1} MB / {2} MB", - "download_canceled": "Download annullato dall'utente.", - - "extension_warning_zip": "Il file '{0}' è un archivio e Batocera non supporta archivi per questo sistema. L'estrazione automatica avverrà dopo il download, continuare?", - "extension_warning_unsupported": "L'estensione del file '{0}' non è supportata da Batocera secondo la configurazione di es_systems.cfg. Vuoi continuare?", - "extension_warning_enable_unknown_hint": "\nPer non visualizzare questo messaggio: abilita \"Nascondi avviso estensione sconosciuta\" in Menu Pausa > Schermo", - - "confirm_exit": "Uscire dall'applicazione?", - "confirm_exit_with_downloads": "Attenzione: {0} download in corso. Uscire comunque?", - "confirm_clear_history": "Cancellare la cronologia?", - "confirm_redownload_cache": "Aggiornare l'elenco dei giochi?", - - "popup_redownload_success": "Cache pulita, riavvia l'applicazione", - "popup_no_cache": "Nessuna cache trovata.\nRiavvia l'applicazione per caricare i giochi.", - "popup_countdown": "Questo messaggio si chiuderà tra {0} secondo{1}", - - "language_select_title": "Selezione lingua", - "language_select_instruction": "Usa le frecce per navigare e Invio per selezionare", - "language_changed": "Lingua cambiata in {0}", - - "menu_controls": "Controlli", - "menu_remap_controls": "Rimappa controlli", - "menu_history": "Cronologia", - "menu_language": "Lingua", - "menu_accessibility": "Accessibilità", - "menu_display": "Schermo", - "display_layout": "Layout schermo", - "menu_redownload_cache": "Aggiorna elenco giochi", - "menu_music_toggle": "Attiva/Disattiva musica", - "menu_music_enabled": "Musica attivata: {0}", - "menu_music_disabled": "Musica disattivata", - "menu_restart": "Riavvia", - "menu_filter_platforms": "Filtra sistemi", - "filter_platforms_title": "Visibilità sistemi", - "filter_all": "Mostra tutto", - "filter_none": "Nascondi tutto", - "filter_apply": "Applica", - "filter_back": "Indietro", - "filter_platforms_info": "Visibili: {0} | Nascosti: {1} / Totale: {2}", - "filter_unsaved_warning": "Modifiche non salvate", - "menu_show_unsupported_on": "Mostra sistemi non supportati: Sì", - "menu_show_unsupported_off": "Mostra sistemi non supportati: No", - "menu_show_unsupported_text": "sistemi nascosti", - "menu_show_unsupported_enabled": "Visibilità sistemi non supportati abilitata", - "menu_show_unsupported_disabled": "Visibilità sistemi non supportati disabilitata", - "menu_allow_unknown_ext_on": "Nascondi avviso estensione sconosciuta: Sì", - "menu_allow_unknown_ext_off": "Nascondi avviso estensione sconosciuta: No", - "menu_allow_unknown_ext_enabled": "Nascondi avviso estensione sconosciuta abilitato", - "menu_allow_unknown_ext_disabled": "Nascondi avviso estensione sconosciuta disabilitato", - "menu_quit": "Esci", - - "button_yes": "Sì", - "button_no": "No", - "button_OK": "OK", - "popup_restarting": "Riavvio...", - - "controls_hold_message": "Tieni premuto per 3s per: '{0}'", - "controls_skip_message": "Premi Esc per ignorare (solo PC)", - "controls_waiting": "In attesa...", - "controls_hold": "Tieni premuto 3s", - - "controls_action_confirm": "Conferma", - "controls_action_cancel": "Annulla", - "controls_action_up": "Su", - "controls_action_down": "Giù", - "controls_action_left": "Sinistra", - "controls_action_right": "Destra", - "controls_action_page_up": "Pagina precedente", - "controls_action_page_down": "Pagina successiva", - "controls_action_clear_history": "Selezione multipla / Cancella cronologia", - "controls_action_history": "Cronologia", - "controls_action_filter": "Filtro", - "controls_action_delete": "Elimina", - "controls_action_space": "Spazio", - "controls_action_start": "Aiuto / Impostazioni", - - "controls_desc_confirm": "Conferma (es. A, Invio)", - "controls_desc_cancel": "Annulla/Indietro (es. B, Backspace)", - "controls_desc_up": "Naviga in alto", - "controls_desc_down": "Naviga in basso", - "controls_desc_left": "Naviga a sinistra", - "controls_desc_right": "Naviga a destra", - "controls_desc_page_up": "Pagina precedente/Scorrimento veloce su (es. PagSu, LB)", - "controls_desc_page_down": "Pagina successiva/Scorrimento veloce giù (es. PagGiù, RB)", - "controls_desc_clear_history": "Selezione multipla (lista giochi) / Cancella cronologia (menu cronologia) (es. X)", - "controls_desc_history": "Apri cronologia (es. H, Y)", - "controls_desc_filter": "Apri filtro (es. F, Select)", - "controls_desc_delete": "Elimina carattere (es. LT, Canc)", - "controls_desc_space": "Aggiungi spazio (es. RT, Spazio)", - "controls_desc_start": "Apri menu pausa (es. Start, AltGr)", - - "action_retry": "Riprova", - "action_quit": "Esci", - "action_select": "Seleziona", - "action_history": "Cronologia", - "action_download": "Scarica", - "action_filter": "Filtro", - "action_cancel": "Annulla", - "action_back": "Indietro", - "action_navigate": "Naviga", - "action_page": "Pagina", - "action_cancel_download": "Annulla download", - "action_background": "Sfondo", - "action_confirm": "Conferma", - "action_redownload": "Scarica di nuovo", - "action_clear_history": "Cancella cronologia", - - "network_checking_updates": "Verifica aggiornamenti...", - "network_update_available": "Aggiornamento disponibile: {0}", - "network_extracting_update": "Estrazione aggiornamento...", - "network_update_completed": "Aggiornamento completato", - "network_update_success": "Aggiornamento a {0} completato con successo. Riavvia l'applicazione.", - "network_update_success_message": "Aggiornamento completato con successo", - "network_no_update_available": "Nessun aggiornamento disponibile", - "network_update_error": "Errore durante l'aggiornamento: {0}", - "network_check_update_error": "Errore durante il controllo degli aggiornamenti: {0}", - "network_extraction_failed": "Impossibile estrarre l'aggiornamento: {0}", - "network_extraction_partial": "Estrazione riuscita, ma alcuni file sono stati ignorati a causa di errori: {0}", - "network_extraction_success": "Estrazione riuscita", - "network_download_extract_ok": "Download ed estrazione riusciti di {0}", - "network_zip_extraction_error": "Errore durante l'estrazione dello ZIP {0}: {1}", - "network_permission_error": "Nessun permesso di scrittura in {0}", - "network_file_not_found": "Il file {0} non esiste", - "network_cannot_get_filename": "Impossibile ottenere il nome del file", - "network_cannot_get_download_url": "Impossibile ottenere l'URL di download", - "network_download_failed": "Download fallito dopo {0} tentativi", - "network_api_error": "Errore richiesta API, la chiave potrebbe essere errata: {0}", - "network_download_error": "Errore download {0}: {1}", - "network_download_ok": "Download OK: {0}", - - "utils_extracted": "Estratto: {0}", - "utils_corrupt_zip": "Archivio ZIP corrotto: {0}", - "utils_permission_denied": "Permesso negato durante l'estrazione: {0}", - "utils_extraction_failed": "Estrazione fallita: {0}", - "utils_unrar_unavailable": "Comando unrar non disponibile", - "utils_rar_list_failed": "Impossibile elencare i file RAR: {0}", - "utils_xdvdfs_unavailable": "Strumento xdvdfs non disponibile (mancante o permesso negato)", - "download_initializing": "Inizializzazione...", - "accessibility_font_size": "Dimensione carattere: {0}", - "confirm_cancel_download": "Annullare il download corrente?", - "controls_help_title": "Guida ai controlli", - "controls_category_navigation": "Navigazione", - "controls_category_main_actions": "Azioni principali", - "controls_category_downloads": "Download", - "controls_category_search": "Ricerca", - "controls_navigation": "Navigazione", - "controls_pages": "Pagine", - "controls_confirm_select": "Conferma/Seleziona", - "controls_cancel_back": "Annulla/Indietro", - "controls_history": "Cronologia", - "controls_clear_history": "Selezione multipla / Cronologia", - "controls_filter_search": "Filtro/Ricerca", - - "menu_symlink_option": "Opzione Symlink", - "symlink_option_enabled": "Opzione symlink abilitata", - "symlink_option_disabled": "Opzione symlink disabilitata", - "symlink_settings_saved_successfully": "Impostazioni symlink salvate con successo", - "symlink_settings_save_error": "Errore nel salvataggio delle impostazioni symlink", - - "menu_games_source_prefix": "Sorgente giochi", - "games_source_rgsx": "RGSX", - "sources_mode_rgsx_select_info": "RGSX: aggiorna l'elenco dei giochi", - "games_source_custom": "Personalizzato", - "sources_mode_custom_select_info": "Modalità personalizzata: imposta l'URL in {0} poi aggiorna l'elenco giochi", - "sources_mode_custom_missing_url": "Nessun URL personalizzato impostato (modifica {0})", - "sources_mode_custom_download_error": "Download sorgente personalizzata fallito", - "menu_show_unsupported_and_hidden": "Mostra {0} sistemi non supportati e nascosti", - "menu_show_unsupported_all_displayed": "Tutti i sistemi visualizzati" -} + "welcome_message": "Benvenuto in RGSX", + "disclaimer_line1": "È pericoloso andare da soli, prendi tutto ciò che ti serve!", + "disclaimer_line2": "Ma scarica solo giochi", + "disclaimer_line3": "che possiedi già!", + "disclaimer_line4": "RGSX non è responsabile dei contenuti scaricati,", + "disclaimer_line5": "e non ospita ROM.", + "loading_test_connection": "Verifica connessione...", + "loading_download_data": "Download cartella Dati iniziale...", + "loading_progress": "Progresso: {0}%", + "loading_check_updates": "Verifica aggiornamenti... Attendere...", + "error_check_updates_failed": "Impossibile verificare gli aggiornamenti.", + "loading_downloading_games_images": "Download giochi e immagini...", + "loading_extracting_data": "Estrazione cartella Dati iniziale...", + "loading_load_systems": "Caricamento sistemi...", + "error_extract_data_failed": "Errore nel download o nell'estrazione della cartella Dati iniziale.", + "error_no_internet": "Nessuna connessione Internet. Controlla la rete.", + "error_api_key": "Inserisci la tua API key (solo premium) nel file {0}", + "error_invalid_download_data": "Dati di download non validi", + "error_delete_sources": "Errore nell'eliminazione del file systems_list.json o delle cartelle", + "platform_no_platform": "Nessuna piattaforma", + "platform_page": "Pagina {0}/{1}", + "game_no_games": "Nessun gioco disponibile", + "game_count": "{0} ({1} giochi)", + "game_filter": "Filtro attivo: {0}", + "game_search": "Filtro: {0}", + "game_header_name": "Nome", + "game_header_size": "Dimensione", + "history_title": "Download ({0})", + "history_empty": "Nessun download nella cronologia", + "history_column_system": "Sistema", + "history_column_game": "Nome del gioco", + "history_column_size": "Dimensione", + "history_column_status": "Stato", + "history_status_downloading": "Download: {0}%", + "history_status_extracting": "Estrazione: {0}%", + "history_status_completed": "Completato", + "history_status_error": "Errore: {0}", + "history_status_canceled": "Annullato", + "download_status": "{0}: {1}", + "download_canceled": "Download annullato dall'utente.", + "extension_warning_zip": "Il file '{0}' è un archivio e Batocera non supporta archivi per questo sistema. L'estrazione automatica avverrà dopo il download, continuare?", + "extension_warning_unsupported": "L'estensione del file '{0}' non è supportata da Batocera secondo la configurazione di es_systems.cfg. Vuoi continuare?", + "extension_warning_enable_unknown_hint": "\nPer non visualizzare questo messaggio: abilita \"Nascondi avviso estensione sconosciuta\" in Menu Pausa > Schermo", + "confirm_exit": "Uscire dall'applicazione?", + "confirm_exit_with_downloads": "Attenzione: {0} download in corso. Uscire comunque?", + "confirm_clear_history": "Cancellare la cronologia?", + "confirm_redownload_cache": "Aggiornare l'elenco dei giochi?", + "popup_redownload_success": "Cache pulita, riavvia l'applicazione", + "popup_no_cache": "Nessuna cache trovata.\nRiavvia l'applicazione per caricare i giochi.", + "popup_countdown": "Questo messaggio si chiuderà tra {0} secondo{1}", + "language_select_title": "Selezione lingua", + "language_select_instruction": "Usa le frecce per navigare e Invio per selezionare", + "language_changed": "Lingua cambiata in {0}", + "menu_controls": "Controlli", + "menu_remap_controls": "Rimappa controlli", + "menu_history": "Cronologia", + "menu_language": "Lingua", + "menu_accessibility": "Accessibilità", + "menu_display": "Schermo", + "display_layout": "Layout schermo", + "menu_redownload_cache": "Aggiorna elenco giochi", + "menu_music_enabled": "Musica attivata: {0}", + "menu_music_disabled": "Musica disattivata", + "menu_restart": "Riavvia", + "menu_filter_platforms": "Filtra sistemi", + "filter_platforms_title": "Visibilità sistemi", + "filter_platforms_info": "Visibili: {0} | Nascosti: {1} / Totale: {2}", + "filter_unsaved_warning": "Modifiche non salvate", + "menu_show_unsupported_enabled": "Visibilità sistemi non supportati abilitata", + "menu_show_unsupported_disabled": "Visibilità sistemi non supportati disabilitata", + "menu_allow_unknown_ext_on": "Nascondi avviso estensione sconosciuta: Sì", + "menu_allow_unknown_ext_off": "Nascondi avviso estensione sconosciuta: No", + "menu_allow_unknown_ext_enabled": "Nascondi avviso estensione sconosciuta abilitato", + "menu_allow_unknown_ext_disabled": "Nascondi avviso estensione sconosciuta disabilitato", + "menu_quit": "Esci", + "button_yes": "Sì", + "button_no": "No", + "button_OK": "OK", + "popup_restarting": "Riavvio...", + "controls_action_clear_history": "Selezione multipla / Cancella cronologia", + "controls_action_history": "Cronologia", + "controls_action_delete": "Elimina", + "controls_action_space": "Spazio", + "controls_action_start": "Aiuto / Impostazioni", + "network_checking_updates": "Verifica aggiornamenti...", + "network_update_available": "Aggiornamento disponibile: {0}", + "network_extracting_update": "Estrazione aggiornamento...", + "network_update_completed": "Aggiornamento completato", + "network_update_success": "Aggiornamento a {0} completato con successo. Riavvia l'applicazione.", + "network_update_success_message": "Aggiornamento completato con successo", + "network_no_update_available": "Nessun aggiornamento disponibile", + "network_update_error": "Errore durante l'aggiornamento: {0}", + "network_check_update_error": "Errore durante il controllo degli aggiornamenti: {0}", + "network_extraction_failed": "Impossibile estrarre l'aggiornamento: {0}", + "network_extraction_partial": "Estrazione riuscita, ma alcuni file sono stati ignorati a causa di errori: {0}", + "network_extraction_success": "Estrazione riuscita", + "network_download_extract_ok": "Download ed estrazione riusciti di {0}", + "network_zip_extraction_error": "Errore durante l'estrazione dello ZIP {0}: {1}", + "network_file_not_found": "Il file {0} non esiste", + "network_cannot_get_filename": "Impossibile ottenere il nome del file", + "network_cannot_get_download_url": "Impossibile ottenere l'URL di download", + "network_download_failed": "Download fallito dopo {0} tentativi", + "network_api_error": "Errore richiesta API, la chiave potrebbe essere errata: {0}", + "network_download_error": "Errore download {0}: {1}", + "network_download_ok": "Download OK: {0}", + "utils_extracted": "Estratto: {0}", + "utils_corrupt_zip": "Archivio ZIP corrotto: {0}", + "utils_permission_denied": "Permesso negato durante l'estrazione: {0}", + "utils_extraction_failed": "Estrazione fallita: {0}", + "utils_unrar_unavailable": "Comando unrar non disponibile", + "utils_rar_list_failed": "Impossibile elencare i file RAR: {0}", + "download_initializing": "Inizializzazione...", + "accessibility_font_size": "Dimensione carattere: {0}", + "confirm_cancel_download": "Annullare il download corrente?", + "controls_help_title": "Guida ai controlli", + "controls_category_navigation": "Navigazione", + "controls_category_main_actions": "Azioni principali", + "controls_category_downloads": "Download", + "controls_category_search": "Ricerca", + "controls_navigation": "Navigazione", + "controls_pages": "Pagine", + "controls_confirm_select": "Conferma/Seleziona", + "controls_cancel_back": "Annulla/Indietro", + "controls_filter_search": "Filtro/Ricerca", + "symlink_option_enabled": "Opzione symlink abilitata", + "symlink_option_disabled": "Opzione symlink disabilitata", + "menu_games_source_prefix": "Sorgente giochi", + "games_source_rgsx": "RGSX", + "sources_mode_rgsx_select_info": "RGSX: aggiorna l'elenco dei giochi", + "games_source_custom": "Personalizzato", + "sources_mode_custom_select_info": "Modalità personalizzata: imposta l'URL in {0} poi aggiorna l'elenco giochi", + "sources_mode_custom_missing_url": "Nessun URL personalizzato impostato (modifica {0})", + "sources_mode_custom_download_error": "Download sorgente personalizzata fallito", + "menu_show_unsupported_and_hidden": "Mostra {0} sistemi non supportati e nascosti", + "menu_show_unsupported_all_displayed": "Tutti i sistemi visualizzati", + "menu_settings_category": "Impostazioni", + "menu_back": "Indietro", + "submenu_display_layout": "Layout", + "submenu_display_font_size": "Dimensione Carattere", + "submenu_display_show_unsupported": "Mostra sistemi non supportati: {status}", + "submenu_display_allow_unknown_ext": "Nascondi avviso est. sconosciuta: {status}", + "submenu_display_filter_platforms": "Filtra sistemi", + "status_on": "Sì", + "status_off": "No", + "status_present": "Presente", + "status_missing": "Assente", + "menu_api_keys_status": "Chiavi API", + "api_keys_status_title": "Stato delle chiavi API", + "menu_games": "Giochi" +} \ No newline at end of file diff --git a/ports/RGSX/languages/pt.json b/ports/RGSX/languages/pt.json index e7ef277..e96b7f6 100644 --- a/ports/RGSX/languages/pt.json +++ b/ports/RGSX/languages/pt.json @@ -1,15 +1,10 @@ { - "controls_mapping_title": "Configuração dos controles", - "controls_mapping_instruction": "Mantenha pressionado por 3s para configurar:", - "controls_mapping_waiting": "Aguardando uma tecla ou botão...", - "controls_mapping_press": "Pressione uma tecla ou botão", "welcome_message": "Bem-vindo ao RGSX", "disclaimer_line1": "É perigoso ir sozinho, leve tudo o que precisar!", "disclaimer_line2": "Mas baixe apenas jogos", "disclaimer_line3": "que você já possui!", "disclaimer_line4": "O RGSX não é responsável pelo conteúdo baixado,", "disclaimer_line5": "e não hospeda ROMs.", - "loading_test_connection": "Testando conexão...", "loading_download_data": "Baixando pasta inicial Data...", "loading_progress": "Progresso: {0}%", @@ -19,27 +14,18 @@ "loading_extracting_data": "Extraindo pasta inicial Data...", "loading_load_systems": "Carregando sistemas...", "error_extract_data_failed": "Falha ao baixar ou extrair a pasta inicial Data.", - "error_sources_load_failed": "Falha ao carregar fontes. Abra o menu e selecione Redownload Games cache", - "error_no_internet": "Sem conexão com a Internet. Verifique sua rede.", - "error_controls_mapping": "Falha ao mapear controles", "error_api_key": "Insira sua chave API (somente premium) no arquivo {0}", - "error_api_key_extended": "Insira sua chave API (somente premium) no arquivo /userdata/saves/ports/rgsx/1FichierAPI.txt abrindo-o em um editor de texto e colando sua chave", "error_invalid_download_data": "Dados de download inválidos", "error_delete_sources": "Erro ao deletar arquivo sources.json ou pastas", - "error_extension": "Extensão não suportada ou erro no download", - "error_no_download": "Nenhum download pendente.", - "platform_no_platform": "Sem plataforma", "platform_page": "Página {0}/{1}", - "game_no_games": "Nenhum jogo disponível", "game_count": "{0} ({1} jogos)", "game_filter": "Filtro ativo: {0}", "game_search": "Filtro: {0}", "game_header_name": "Nome", "game_header_size": "Tamanho", - "history_title": "Downloads ({0})", "history_empty": "Nenhum download no histórico", "history_column_system": "Sistema", @@ -51,28 +37,21 @@ "history_status_completed": "Concluído", "history_status_error": "Erro: {0}", "history_status_canceled": "Cancelado", - "download_status": "{0}: {1}", - "download_progress": "{0}% {1} MB / {2} MB", "download_canceled": "Download cancelado pelo usuário.", - "extension_warning_zip": "O arquivo '{0}' é um arquivo compactado e o Batocera não suporta arquivos compactados para este sistema. A extração automática ocorrerá após o download, continuar?", "extension_warning_unsupported": "A extensão do arquivo '{0}' não é suportada pelo Batocera segundo a configuração es_systems.cfg. Deseja continuar?", "extension_warning_enable_unknown_hint": "\nPara não ver esta mensagem: ative \"Ocultar aviso de extensão desconhecida\" em Menu de Pausa > Exibição", - "confirm_exit": "Sair da aplicação?", "confirm_exit_with_downloads": "Atenção: {0} download(s) em andamento. Sair mesmo assim?", "confirm_clear_history": "Limpar histórico?", "confirm_redownload_cache": "Atualizar lista de jogos?", - "popup_redownload_success": "Cache limpo, reinicie a aplicação", "popup_no_cache": "Nenhum cache encontrado.\nReinicie a aplicação para carregar os jogos.", "popup_countdown": "Esta mensagem fechará em {0} segundo{1}", - "language_select_title": "Seleção de Idioma", "language_select_instruction": "Use as setas para navegar e Enter para selecionar", "language_changed": "Idioma alterado para {0}", - "menu_controls": "Controles", "menu_remap_controls": "Remapear controles", "menu_history": "Histórico", @@ -81,21 +60,13 @@ "menu_display": "Exibição", "display_layout": "Layout de exibição", "menu_redownload_cache": "Atualizar lista de jogos", - "menu_music_toggle": "Ativar/Desativar música", "menu_music_enabled": "Música ativada: {0}", "menu_music_disabled": "Música desativada", "menu_restart": "Reiniciar", "menu_filter_platforms": "Filtrar sistemas", "filter_platforms_title": "Visibilidade dos sistemas", - "filter_all": "Mostrar todos", - "filter_none": "Ocultar todos", - "filter_apply": "Aplicar", - "filter_back": "Voltar", "filter_platforms_info": "Visíveis: {0} | Ocultos: {1} / Total: {2}", "filter_unsaved_warning": "Alterações não salvas", - "menu_show_unsupported_on": "Mostrar sistemas não suportados: Sim", - "menu_show_unsupported_off": "Mostrar sistemas não suportados: Não", - "menu_show_unsupported_text": "sistemas ocultos", "menu_show_unsupported_enabled": "Visibilidade de sistemas não suportados ativada", "menu_show_unsupported_disabled": "Visibilidade de sistemas não suportados desativada", "menu_allow_unknown_ext_on": "Ocultar aviso de extensão desconhecida: Sim", @@ -103,63 +74,15 @@ "menu_allow_unknown_ext_enabled": "Aviso de extensão desconhecida oculto (ativado)", "menu_allow_unknown_ext_disabled": "Aviso de extensão desconhecida visível (desativado)", "menu_quit": "Sair", - "button_yes": "Sim", "button_no": "Não", "button_OK": "OK", "popup_restarting": "Reiniciando...", - - "controls_hold_message": "Mantenha pressionado por 3s para: '{0}'", - "controls_skip_message": "Pressione Esc para ignorar (somente PC)", - "controls_waiting": "Aguardando...", - "controls_hold": "Segure 3s", - - "controls_action_confirm": "Confirmar", - "controls_action_cancel": "Cancelar", - "controls_action_up": "Cima", - "controls_action_down": "Baixo", - "controls_action_left": "Esquerda", - "controls_action_right": "Direita", - "controls_action_page_up": "Página anterior", - "controls_action_page_down": "Próxima página", "controls_action_clear_history": "Seleção múltipla / Limpar histórico", "controls_action_history": "Histórico", - "controls_action_filter": "Filtro", "controls_action_delete": "Deletar", "controls_action_space": "Espaço", "controls_action_start": "Ajuda / Configurações", - - "controls_desc_confirm": "Validar (ex: A, Enter)", - "controls_desc_cancel": "Cancelar/Voltar (ex: B, Backspace)", - "controls_desc_up": "Navegar para cima", - "controls_desc_down": "Navegar para baixo", - "controls_desc_left": "Navegar para a esquerda", - "controls_desc_right": "Navegar para a direita", - "controls_desc_page_up": "Página anterior/Rolagem rápida para cima (ex: PageUp, LB)", - "controls_desc_page_down": "Próxima página/Rolagem rápida para baixo (ex: PageDown, RB)", - "controls_desc_clear_history": "Seleção múltipla (lista) / Limpar histórico (histórico) (ex: X)", - "controls_desc_history": "Abrir histórico (ex: H, Y)", - "controls_desc_filter": "Abrir filtro (ex: F, Select)", - "controls_desc_delete": "Deletar caractere (ex: LT, Delete)", - "controls_desc_space": "Adicionar espaço (ex: RT, Space)", - "controls_desc_start": "Abrir menu de pausa (ex: Start, AltGr)", - - "action_retry": "Repetir", - "action_quit": "Sair", - "action_select": "Selecionar", - "action_history": "Histórico", - "action_download": "Baixar", - "action_filter": "Filtrar", - "action_cancel": "Cancelar", - "action_back": "Voltar", - "action_navigate": "Navegar", - "action_page": "Página", - "action_cancel_download": "Cancelar download", - "action_background": "Segundo plano", - "action_confirm": "Confirmar", - "action_redownload": "Rebaixar", - "action_clear_history": "Limpar histórico", - "network_checking_updates": "Verificando atualizações...", "network_update_available": "Atualização disponível: {0}", "network_extracting_update": "Extraindo atualização...", @@ -174,7 +97,6 @@ "network_extraction_success": "Extração concluída", "network_download_extract_ok": "Download e extração concluídos de {0}", "network_zip_extraction_error": "Erro ao extrair ZIP {0}: {1}", - "network_permission_error": "Sem permissão de escrita em {0}", "network_file_not_found": "Arquivo {0} não existe", "network_cannot_get_filename": "Não foi possível obter o nome do arquivo", "network_cannot_get_download_url": "Não foi possível obter a URL de download", @@ -182,14 +104,12 @@ "network_api_error": "Erro na requisição da API, a chave pode estar incorreta: {0}", "network_download_error": "Erro de download {0}: {1}", "network_download_ok": "Download OK: {0}", - "utils_extracted": "Extraído: {0}", "utils_corrupt_zip": "Arquivo ZIP corrompido: {0}", "utils_permission_denied": "Permissão negada durante extração: {0}", "utils_extraction_failed": "Extração falhou: {0}", "utils_unrar_unavailable": "Comando unrar não disponível", "utils_rar_list_failed": "Falha ao listar arquivos RAR: {0}", - "utils_xdvdfs_unavailable": "Ferramenta xdvdfs indisponível (faltando ou sem permissão)", "download_initializing": "Inicializando...", "accessibility_font_size": "Tamanho da fonte: {0}", "confirm_cancel_download": "Cancelar download atual?", @@ -202,16 +122,9 @@ "controls_pages": "Páginas", "controls_confirm_select": "Confirmar/Selecionar", "controls_cancel_back": "Cancelar/Voltar", - "controls_history": "Histórico", - "controls_clear_history": "Seleção múltipla / Histórico", "controls_filter_search": "Filtrar/Buscar", - - "menu_symlink_option": "Opção de Symlink", "symlink_option_enabled": "Opção de symlink ativada", "symlink_option_disabled": "Opção de symlink desativada", - "symlink_settings_saved_successfully": "Configurações de symlink salvas com sucesso", - "symlink_settings_save_error": "Erro ao salvar configurações de symlink", - "menu_games_source_prefix": "Fonte de jogos", "games_source_rgsx": "RGSX", "sources_mode_rgsx_select_info": "RGSX: atualizar a lista de jogos", @@ -220,5 +133,19 @@ "sources_mode_custom_missing_url": "Nenhuma URL personalizada definida (edite {0})", "sources_mode_custom_download_error": "Falha no download da fonte personalizada", "menu_show_unsupported_and_hidden": "Mostrar {0} sistemas não suportados e ocultos", - "menu_show_unsupported_all_displayed": "Todos os sistemas exibidos" + "menu_show_unsupported_all_displayed": "Todos os sistemas exibidos", + "menu_settings_category": "Configurações", + "menu_back": "Voltar", + "submenu_display_layout": "Layout", + "submenu_display_font_size": "Tamanho da Fonte", + "submenu_display_show_unsupported": "Mostrar sistemas não suportados: {status}", + "submenu_display_allow_unknown_ext": "Ocultar aviso ext. desconhecida: {status}", + "submenu_display_filter_platforms": "Filtrar sistemas", + "status_on": "Sim", + "status_off": "Não", + "status_present": "Presente", + "status_missing": "Ausente", + "menu_api_keys_status": "Chaves API", + "api_keys_status_title": "Status das chaves API", + "menu_games": "Jogos" } \ No newline at end of file diff --git a/ports/RGSX/network.py b/ports/RGSX/network.py index 0e0a8ae..0d7ebc9 100644 --- a/ports/RGSX/network.py +++ b/ports/RGSX/network.py @@ -15,7 +15,7 @@ try: except Exception: pygame = None # type: ignore from config import OTA_VERSION_ENDPOINT,APP_FOLDER, UPDATE_FOLDER, OTA_UPDATE_ZIP -from utils import sanitize_filename, extract_zip, extract_rar, load_api_key_1fichier, load_api_key_alldebrid, normalize_platform_name +from utils import sanitize_filename, extract_zip, extract_rar, load_api_key_1fichier, load_api_key_alldebrid, normalize_platform_name, load_api_keys from history import save_history import logging import datetime @@ -318,8 +318,8 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas # Spécifique: si le système est "BIOS" on force le dossier BIOS if platform_folder == "bios" or platform == "BIOS" or platform == "- BIOS by TMCTV -": - dest_dir = config.RETROBAT_DATA_FOLDER - logger.debug(f"Plateforme 'BIOS' détectée, destination forcée vers RETROBAT_DATA_FOLDER: {dest_dir}") + dest_dir = config.USERDATA_FOLDER + logger.debug(f"Plateforme 'BIOS' détectée, destination forcée vers USERDATA_FOLDER: {dest_dir}") os.makedirs(dest_dir, exist_ok=True) if not os.access(dest_dir, os.W_OK): @@ -634,13 +634,16 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas return result[0], result[1] async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False, task_id=None): - config.API_KEY_1FICHIER = load_api_key_1fichier() - if not config.API_KEY_1FICHIER: - # Fallback: essayer AllDebrid - config.API_KEY_ALLDEBRID = load_api_key_alldebrid() - logger.debug(f"Clé API 1fichier absente, fallback AllDebrid: {'présente' if config.API_KEY_ALLDEBRID else 'absente'}") + # Charger/rafraîchir les clés API (mtime aware) + keys_info = load_api_keys() + config.API_KEY_1FICHIER = keys_info.get('1fichier', '') + config.API_KEY_ALLDEBRID = keys_info.get('alldebrid', '') + if not config.API_KEY_1FICHIER and config.API_KEY_ALLDEBRID: + logger.debug("Clé 1fichier absente, utilisation fallback AllDebrid") + elif not config.API_KEY_1FICHIER and not config.API_KEY_ALLDEBRID: + logger.debug("Aucune clé API disponible (1fichier ni AllDebrid)") logger.debug(f"Début téléchargement 1fichier: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}, task_id={task_id}") - logger.debug(f"Clé API 1fichier: {'présente' if config.API_KEY_1FICHIER else 'absente'}") + logger.debug(f"Clé API 1fichier: {'présente' if config.API_KEY_1FICHIER else 'absente'} / AllDebrid: {'présente' if config.API_KEY_ALLDEBRID else 'absente'} (reloaded={keys_info.get('reloaded')})") result = [None, None] # Créer une queue spécifique pour cette tâche @@ -673,8 +676,8 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= # Spécifique: si le système est "- BIOS by TMCTV -" on force le dossier BIOS if platform_folder == "bios" or platform == "BIOS" or platform == "- BIOS by TMCTV -": - dest_dir = config.RETROBAT_DATA_FOLDER - logger.debug(f"Plateforme '- BIOS by TMCTV -' détectée, destination forcée vers RETROBAT_DATA_FOLDER: {dest_dir}") + dest_dir = config.USERDATA_FOLDER + logger.debug(f"Plateforme '- BIOS by TMCTV -' détectée, destination forcée vers USERDATA_FOLDER: {dest_dir}") logger.debug(f"Vérification répertoire destination: {dest_dir}") os.makedirs(dest_dir, exist_ok=True) @@ -685,6 +688,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= # Choisir la stratégie d'accès: 1fichier direct via API, sinon AllDebrid pour débrider if config.API_KEY_1FICHIER: + logger.debug("Mode téléchargement sélectionné: 1fichier (API directe)") headers = { "Authorization": f"Bearer {config.API_KEY_1FICHIER}", "Content-Type": "application/json" @@ -726,6 +730,7 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= logger.debug(f"URL de téléchargement obtenue via 1fichier: {final_url}") else: # AllDebrid: débrider l'URL 1fichier vers une URL directe + logger.debug("Mode téléchargement sélectionné: AllDebrid (fallback, débridage 1fichier)") if not getattr(config, 'API_KEY_ALLDEBRID', ''): logger.error("Aucune clé API (1fichier/AllDebrid) disponible") result[0] = False diff --git a/ports/RGSX/utils.py b/ports/RGSX/utils.py index 559aee2..bebafb9 100644 --- a/ports/RGSX/utils.py +++ b/ports/RGSX/utils.py @@ -122,14 +122,14 @@ def load_extensions_json(): def _detect_es_systems_cfg_paths(): """Retourne une liste de chemins possibles pour es_systems.cfg selon l'OS. - - RetroBat (Windows): {config.RETROBAT_DATA_FOLDER}\\system\\templates\\emulationstation\\es_systems.cfg + - RetroBat (Windows): {config.USERDATA_FOLDER}\\system\\templates\\emulationstation\\es_systems.cfg - Batocera (Linux): /usr/share/emulationstation/es_systems.cfg Ajoute aussi les fichiers customs: /userdata/system/configs/emulationstation/es_systems_*.cfg """ candidates = [] try: if platform.system() == 'Windows': - base = getattr(config, 'RETROBAT_DATA_FOLDER', None) + base = getattr(config, 'USERDATA_FOLDER', None) if base: candidates.append(os.path.join(base, 'system', 'templates', 'emulationstation', 'es_systems.cfg')) else: @@ -1250,66 +1250,82 @@ def set_music_popup(music_name): config.music_popup_start_time = pygame.time.get_ticks() / 1000 # Temps actuel en secondes config.needs_redraw = True # Forcer le redraw pour afficher le nom de la musique -def load_api_key_1fichier(): - """Charge la clé API 1fichier depuis le dossier de sauvegarde, crée le fichier si absent.""" - API_KEY_1FICHIER = os.path.join(config.SAVE_FOLDER, "1FichierAPI.txt") - logger.debug(f"Chemin du fichier de clé API: {API_KEY_1FICHIER}") - logger.debug(f"Tentative de chargement de la clé API depuis: {API_KEY_1FICHIER}") - try: - # Vérifie si le fichier existe déjà - if not os.path.exists(API_KEY_1FICHIER): - logger.info(f"Fichier de clé API non trouvé") - # Crée le dossier parent si nécessaire - os.makedirs(config.SAVE_FOLDER, exist_ok=True) - # Crée le fichier vide si absent - with open(API_KEY_1FICHIER, "w") as f: - f.write("") - logger.info(f"Fichier de clé API créé : {API_KEY_1FICHIER}") - return "" - except OSError as e: - logger.error(f"Erreur lors de la création du fichier de clé API : {e}") - return "" - # Lit la clé API depuis le fichier - try: - with open(API_KEY_1FICHIER, "r", encoding="utf-8") as f: - api_key = f.read().strip() - logger.debug(f"Clé API 1fichier lue: '{api_key}' (longueur: {len(api_key)})") - if not api_key: - logger.warning("Clé API 1fichier vide, veuillez la renseigner dans le fichier pour pouvoir utiliser les fonctionnalités de téléchargement sur 1fichier.") - API_KEY_1FICHIER = api_key - config.API_KEY_1FICHIER = api_key - logger.debug(f"Clé API 1fichier chargée dans la configuration : '{config.API_KEY_1FICHIER}'") - return api_key - except OSError as e: - logger.error(f"Erreur lors de la lecture de la clé API : {e}") - return "" +def load_api_keys(force: bool = False): + """Charge les clés API (1fichier, AllDebrid) en une seule passe. -def load_api_key_alldebrid(): - """Charge la clé API AllDebrid depuis le dossier de sauvegarde, crée le fichier si absent.""" + - Crée les fichiers vides s'ils n'existent pas + - Met à jour config.API_KEY_1FICHIER et config.API_KEY_ALLDEBRID + - Utilise un cache basé sur le mtime pour éviter des relectures + - force=True ignore le cache et relit systématiquement + + Retourne: { '1fichier': str, 'alldebrid': str, 'reloaded': bool } + """ try: - api_file = os.path.join(config.SAVE_FOLDER, "AllDebridAPI.txt") - logger.debug(f"Chemin du fichier de clé API AllDebrid: {api_file}") - if not os.path.exists(api_file): - logger.info("Fichier de clé API AllDebrid non trouvé") - os.makedirs(config.SAVE_FOLDER, exist_ok=True) - with open(api_file, "w", encoding="utf-8") as f: - f.write("") - logger.info(f"Fichier de clé API AllDebrid créé : {api_file}") - return "" - with open(api_file, "r", encoding="utf-8") as f: - api_key = f.read().strip() - logger.debug(f"Clé API AllDebrid lue: '{api_key}' (longueur: {len(api_key)})") - if not api_key: - logger.warning("Clé API AllDebrid vide, renseignez-la dans AllDebridAPI.txt pour activer le débridage.") - # Stocke dans la config pour usage global - try: - config.API_KEY_ALLDEBRID = api_key - except Exception: - pass - return api_key + paths = { + '1fichier': getattr(config, 'API_KEY_1FICHIER_PATH', ''), + 'alldebrid': getattr(config, 'API_KEY_ALLDEBRID_PATH', ''), + } + cache_attr = '_api_keys_cache' + if not hasattr(config, cache_attr): + setattr(config, cache_attr, {'1fichier_mtime': None, 'alldebrid_mtime': None}) + cache_data = getattr(config, cache_attr) + reloaded = False + + for key_name, path in paths.items(): + if not path: + continue + # Création fichier vide si absent + try: + if not os.path.exists(path): + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, 'w', encoding='utf-8') as f: + f.write("") + except Exception as ce: + logger.error(f"Impossible de préparer le fichier clé {key_name}: {ce}") + continue + try: + mtime = os.path.getmtime(path) + except Exception: + mtime = None + cache_key = f"{key_name}_mtime" + if force or (mtime is not None and mtime != cache_data.get(cache_key)): + # Lecture + try: + with open(path, 'r', encoding='utf-8') as f: + value = f.read().strip() + except Exception as re: + logger.error(f"Erreur lecture clé {key_name}: {re}") + value = "" + # Assignation dans config + if key_name == '1fichier': + config.API_KEY_1FICHIER = value + else: + config.API_KEY_ALLDEBRID = value + cache_data[cache_key] = mtime + reloaded = True + return { + '1fichier': getattr(config, 'API_KEY_1FICHIER', ''), + 'alldebrid': getattr(config, 'API_KEY_ALLDEBRID', ''), + 'reloaded': reloaded + } except Exception as e: - logger.error(f"Erreur lors du chargement de la clé API AllDebrid: {e}") - return "" + logger.error(f"Erreur load_api_keys: {e}") + return { + '1fichier': getattr(config, 'API_KEY_1FICHIER', ''), + 'alldebrid': getattr(config, 'API_KEY_ALLDEBRID', ''), + 'reloaded': False + } + +# Wrappers rétro-compatibilité (dépréciés) +def load_api_key_1fichier(force: bool = False): # pragma: no cover + return load_api_keys(force).get('1fichier', '') + +def load_api_key_alldebrid(force: bool = False): # pragma: no cover + return load_api_keys(force).get('alldebrid', '') + +# Ancien nom conservé comme alias +def ensure_api_keys_loaded(force: bool = False): # pragma: no cover + return load_api_keys(force) def load_music_config(): """Charge la configuration musique depuis rgsx_settings.json."""