diff --git a/ports/RGSX/__main__.py b/ports/RGSX/__main__.py index a7c4f8a..ceb9475 100644 --- a/ports/RGSX/__main__.py +++ b/ports/RGSX/__main__.py @@ -19,7 +19,7 @@ from display import ( from language import handle_language_menu_events, _ from network import test_internet, download_rom, is_1fichier_url, download_from_1fichier, check_for_updates from controls import handle_controls, validate_menu_state, process_key_repeats, get_emergency_controls -from controls_mapper import load_controls_config, save_controls_config, map_controls, draw_controls_mapping, ACTIONS +from controls_mapper import load_controls_config, save_controls_config, map_controls, draw_controls_mapping, get_actions from utils import ( detect_non_pc, load_sources, check_extension_before_download, extract_zip_data, play_random_music, load_accessibility_settings, load_music_config @@ -639,7 +639,7 @@ async def main(): logger.debug("Initialisation de config.controls_config avec un dictionnaire vide") # Forcer l'affichage de l'interface de mappage des contrôles - action = ACTIONS[0] + action = get_actions()[0] draw_controls_mapping(screen, action, None, True, 0.0) pygame.display.flip() logger.debug("Interface de mappage des contrôles affichée") diff --git a/ports/RGSX/config.py b/ports/RGSX/config.py index 8101080..3ac2f01 100644 --- a/ports/RGSX/config.py +++ b/ports/RGSX/config.py @@ -4,7 +4,7 @@ import sys import logging # Version actuelle de l'application -app_version = "1.9.8.4" +app_version = "1.9.8.5" def get_application_root(): """Détermine le dossier de l'application de manière portable.""" diff --git a/ports/RGSX/controls_mapper.py b/ports/RGSX/controls_mapper.py index 0667404..90e4225 100644 --- a/ports/RGSX/controls_mapper.py +++ b/ports/RGSX/controls_mapper.py @@ -3,6 +3,7 @@ import json import os import logging import config +import language from config import CONTROLS_CONFIG_PATH from display import draw_gradient import xml.etree.ElementTree as ET @@ -13,23 +14,37 @@ logger = logging.getLogger(__name__) CONTROLS_CONFIG_PATH = os.path.join(config.SAVE_FOLDER, "controls.json") # Actions internes de RGSX à mapper -ACTIONS = [ - {"name": "confirm", "display": "Confirmer", "description": "Valider (Recommandé: Entrée, A/Croix)"}, - {"name": "cancel", "display": "Annuler", "description": "Annuler/Retour (Recommandé: Retour Arrière, B/Rond)"}, - {"name": "up", "display": "Haut", "description": "Naviguer vers le haut"}, - {"name": "down", "display": "Bas", "description": "Naviguer vers le bas"}, - {"name": "left", "display": "Gauche", "description": "Naviguer à gauche"}, - {"name": "right", "display": "Droite", "description": "Naviguer à droite"}, - {"name": "start", "display": "Start", "description": "Menu pause / Paramètres (Recommandé: Start, AltGr)"}, - {"name": "filter", "display": "Filtrer", "description": "Ouvrir filtre (Recommandé: F, Select)"}, - {"name": "page_up", "display": "Page Précédente", "description": "Page précédente/Défilement Rapide Haut (Recommandé: PageUp, LB/L1)"}, - {"name": "page_down", "display": "Page Suivante", "description": "Page suivante/Défilement Rapide Bas (Recommandé: PageDown, RB/R1)"}, - {"name": "history", "display": "Historique", "description": "Ouvrir l'historique (Recommandé: H, Y/Carré)"}, - {"name": "progress", "display": "Progression", "description": "Historique : Effacer la liste (Recommandé: X/Triangle)"}, - {"name": "delete", "display": "Supprimer", "description": "Mode Fitre : Supprimer caractère en mode recherche (Recommandé: DEL, LT/L2)"}, - {"name": "space", "display": "Espace", "description": "Mode Filtre : Ajouter espace (Recommandé: Espace, RT/R2)"}, + +# Actions internes de RGSX à mapper (labels et descriptions traduits dynamiquement) +ACTION_DEFS = [ + {"name": "confirm"}, + {"name": "cancel"}, + {"name": "up"}, + {"name": "down"}, + {"name": "left"}, + {"name": "right"}, + {"name": "start"}, + {"name": "filter"}, + {"name": "page_up"}, + {"name": "page_down"}, + {"name": "history"}, + {"name": "progress"}, + {"name": "delete"}, + {"name": "space"}, ] +def get_actions(lang=None): + """Retourne la liste des actions avec labels/descriptions traduits selon la langue courante.""" + actions = [] + for a in ACTION_DEFS: + name = a["name"] + display = language.get_text(f"controls_action_{name}", name.capitalize()) + description = language.get_text(f"controls_desc_{name}", "") + actions.append({"name": name, "display": display, "description": description}) + return actions + +# ...existing code... + # Mappage des valeurs SDL vers les constantes Pygame SDL_TO_PYGAME_KEY = { 1073741906: pygame.K_UP, # Flèche Haut @@ -335,10 +350,11 @@ def map_controls(screen): held_hats = {} held_mouse_buttons = set() - while current_action_index < len(ACTIONS): + actions = get_actions() + while current_action_index < len(actions): if config.needs_redraw: progress = min(input_held_time / HOLD_DURATION, 1.0) if current_input else 0.0 - draw_controls_mapping(screen, ACTIONS[current_action_index], last_input_name, current_input is not None, progress) + draw_controls_mapping(screen, actions[current_action_index], last_input_name, current_input is not None, progress) pygame.display.flip() config.needs_redraw = False @@ -450,7 +466,7 @@ def map_controls(screen): if current_input: input_held_time += delta_time if input_held_time >= HOLD_DURATION: - action_name = ACTIONS[current_action_index]["name"] + action_name = actions[current_action_index]["name"] # Sauvegarder avec la structure attendue par controls.py if current_input["type"] == "key": @@ -522,22 +538,24 @@ def draw_controls_mapping(screen, action, last_input, waiting_for_input, hold_pr border_width = 4 shadow_offset = 8 - # Titre principal - title_text = "Configuration des contrôles" + # Titre principal (traduction) + title_text = language.get_text("controls_mapping_title", "Configuration des contrôles") title_surface = config.title_font.render(title_text, True, (255, 255, 255)) title_rect = title_surface.get_rect(center=(config.screen_width // 2, 80)) screen.blit(title_surface, title_rect) - # Instructions - instruction_text = "Maintenez pendant 3s pour configurer :" - description_text = action['description'] + # Instructions (traduction) + instruction_text = language.get_text("controls_mapping_instruction", "Maintenez pendant 3s pour configurer :") + description_text = action.get('description', '') instruction_surface = config.small_font.render(instruction_text, True, (255, 255, 255)) description_surface = config.font.render(description_text, True, (200, 200, 200)) instruction_width, instruction_height = instruction_surface.get_size() description_width, description_height = description_surface.get_size() - # Input détecté - input_text = last_input or (f"En attente d'une touche ou bouton..." if waiting_for_input else "Appuyez sur une touche ou un bouton") + # Input détecté (traduction) + waiting_text = language.get_text("controls_mapping_waiting", "En attente d'une touche ou bouton...") + press_text = language.get_text("controls_mapping_press", "Appuyez sur une touche ou un bouton") + input_text = last_input or (waiting_text if waiting_for_input else press_text) input_surface = config.small_font.render(input_text, True, (0, 255, 0) if last_input else (255, 255, 255)) input_width, input_height = input_surface.get_size() diff --git a/ports/RGSX/display.py b/ports/RGSX/display.py index 10970f1..f917335 100644 --- a/ports/RGSX/display.py +++ b/ports/RGSX/display.py @@ -1250,23 +1250,15 @@ def draw_pause_menu(screen, selected_option): # Menu aide contrôles def draw_controls_help(screen, previous_state): """Affiche la liste des contrôles avec un style moderne.""" - # Définir les noms d'actions traduits en dehors des f-strings pour éviter les problèmes de syntaxe start_text = _("controls_action_start") - progress_text = _("controls_action_progress") - up_text = _("controls_action_up") - down_text = _("controls_action_down") - filter_text = _("controls_action_filter") history_text = _("controls_action_history") delete_text = _("controls_action_delete") space_text = _("controls_action_space") - - # Catégories de contrôles nav_text = _("controls_navigation") pages_text = _("controls_pages") confirm_select_text = _("controls_confirm_select") cancel_back_text = _("controls_cancel_back") - history_text = _("controls_history") - clear_history_text = _("controls_clear_history") + clear_history_text = _("controls_action_delete_history") filter_search_text = _("controls_filter_search") control_categories = { diff --git a/ports/RGSX/languages/de.json b/ports/RGSX/languages/de.json index 535af51..bc370c6 100644 --- a/ports/RGSX/languages/de.json +++ b/ports/RGSX/languages/de.json @@ -1,4 +1,8 @@ { + "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,", @@ -92,7 +96,7 @@ "controls_action_right": "Rechts", "controls_action_page_up": "Vorherige Seite", "controls_action_page_down": "Nächste Seite", - "controls_action_progress": "Fortschritt", + "controls_action_delete_history": "Verlauf leeren", "controls_action_history": "Verlauf", "controls_action_filter": "Filtern", "controls_action_delete": "Löschen", @@ -107,7 +111,7 @@ "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_progress": "Fortschritt anzeigen (z.B.: X)", + "controls_desc_delete_history": "Verlauf löschen (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)", diff --git a/ports/RGSX/languages/en.json b/ports/RGSX/languages/en.json index f6c833a..13eaee7 100644 --- a/ports/RGSX/languages/en.json +++ b/ports/RGSX/languages/en.json @@ -1,4 +1,8 @@ { + "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", @@ -92,7 +96,7 @@ "controls_action_right": "Right", "controls_action_page_up": "Previous Page", "controls_action_page_down": "Next Page", - "controls_action_progress": "Progress", + "controls_action_delete_history": "Clear History", "controls_action_history": "History", "controls_action_filter": "Filter", "controls_action_delete": "Delete", @@ -107,7 +111,7 @@ "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_progress": "View progress (e.g. X)", + "controls_desc_delete_history": "Clear History (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)", diff --git a/ports/RGSX/languages/es.json b/ports/RGSX/languages/es.json index 769b6d7..5d091ee 100644 --- a/ports/RGSX/languages/es.json +++ b/ports/RGSX/languages/es.json @@ -1,4 +1,8 @@ { + "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", @@ -92,7 +96,7 @@ "controls_action_right": "Derecha", "controls_action_page_up": "Página anterior", "controls_action_page_down": "Página siguiente", - "controls_action_progress": "Progreso", + "controls_action_delete_history": "Vaciar Historial", "controls_action_history": "Historial", "controls_action_filter": "Filtrar", "controls_action_delete": "Eliminar", @@ -107,7 +111,7 @@ "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_progress": "Ver progreso (ej: X)", + "controls_desc_delete_history": "Borrar 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)", diff --git a/ports/RGSX/languages/fr.json b/ports/RGSX/languages/fr.json index 062117f..b65f991 100644 --- a/ports/RGSX/languages/fr.json +++ b/ports/RGSX/languages/fr.json @@ -92,7 +92,7 @@ "controls_action_right": "Droite", "controls_action_page_up": "Page Précédente", "controls_action_page_down": "Page Suivante", - "controls_action_progress": "Progression", + "controls_action_delete_history": "Vider Historique", "controls_action_history": "Historique", "controls_action_filter": "Filtrer", "controls_action_delete": "Supprimer", @@ -107,7 +107,7 @@ "controls_desc_right": "Naviguer à droite", "controls_desc_page_up": "Page précédente/Défilement Rapide Haut (ex: PageUp, LB)", "controls_desc_page_down": "Page suivante/Défilement Rapide Bas (ex: PageDown, RB)", - "controls_desc_progress": "Voir progression (ex: X)", + "controls_desc_delete_history": "Effacer Historique (ex: X)", "controls_desc_history": "Ouvrir l'historique (ex: H, Y)", "controls_desc_filter": "Ouvrir filtre (ex: F, Select)", "controls_desc_delete": "Supprimer caractère (ex: LT, Suppr)", @@ -176,5 +176,9 @@ "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_rar_list_failed": "Échec de la liste des fichiers RAR: {0}", + "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" } \ No newline at end of file diff --git a/ports/RGSX/utils.py b/ports/RGSX/utils.py index ee66fe1..02ce0d9 100644 --- a/ports/RGSX/utils.py +++ b/ports/RGSX/utils.py @@ -354,65 +354,76 @@ def extract_zip(zip_path, dest_dir, url): zip_ref.testzip() # Vérifier l'intégrité de l'archive total_size = sum(info.file_size for info in zip_ref.infolist() if not info.is_dir()) logger.info(f"Taille totale à extraire: {total_size} octets") - if total_size == 0: - logger.warning("ZIP vide ou ne contenant que des dossiers") - return True, "ZIP vide extrait avec succès" + lock = threading.Lock() + # Lister les ISO avant extraction + iso_before = set() + for root, dirs, files in os.walk(dest_dir): + for file in files: + if file.lower().endswith('.iso'): + iso_before.add(os.path.abspath(os.path.join(root, file))) - extracted_size = 0 - os.makedirs(dest_dir, exist_ok=True) - chunk_size = 2048 # Réduire pour plus de mises à jour - last_save_time = time.time() - save_interval = 0.5 # Sauvegarder toutes les 0.5 secondes - for info in zip_ref.infolist(): - if info.is_dir(): - continue - file_path = os.path.join(dest_dir, info.filename) - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with zip_ref.open(info) as source, open(file_path, 'wb') as dest: - file_size = info.file_size - file_extracted = 0 - while True: - chunk = source.read(chunk_size) - if not chunk: - break - dest.write(chunk) - file_extracted += len(chunk) - extracted_size += len(chunk) - current_time = time.time() - with lock: - # Vérifier si config.history est une liste avant d'itérer - if isinstance(config.history, list): - for entry in config.history: - # Vérifier si l'entrée a les clés nécessaires et correspond à notre téléchargement - if "status" in entry and entry["status"] in ["Téléchargement", "Extracting", "downloading"]: - # Chercher par URL si disponible - if "url" in entry and entry["url"] == url: - # Calculer le pourcentage correctement et le limiter entre 0 et 100 - progress_percent = int(extracted_size / total_size * 100) if total_size > 0 else 0 - progress_percent = max(0, min(100, progress_percent)) - - entry["status"] = "Extracting" - entry["progress"] = progress_percent - entry["message"] = "Extraction en cours" - - if current_time - last_save_time >= save_interval: - save_history(config.history) - last_save_time = current_time - # logger.debug(f"Extraction en cours: {info.filename}, file_extracted={file_extracted}/{file_size}, total_extracted={extracted_size}/{total_size}, progression={progress_percent:.1f}%") - - config.needs_redraw = True - break - os.chmod(file_path, 0o644) - # Vérifier si c'est un dossier xbox et le traiter si nécessaire - xbox_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), "xbox") - if dest_dir == xbox_dir: - success, error_msg = handle_xbox(dest_dir) - if not success: - return False, error_msg - - for root, dirs, files in os.walk(dest_dir): - for dir_name in dirs: - os.chmod(os.path.join(root, dir_name), 0o755) + with zipfile.ZipFile(zip_path, 'r') as zip_ref: + zip_ref.testzip() # Vérifier l'intégrité de l'archive + total_size = sum(info.file_size for info in zip_ref.infolist() if not info.is_dir()) + logger.info(f"Taille totale à extraire: {total_size} octets") + if total_size == 0: + logger.warning("ZIP vide ou ne contenant que des dossiers") + return True, "ZIP vide extrait avec succès" + + extracted_size = 0 + os.makedirs(dest_dir, exist_ok=True) + chunk_size = 2048 # Réduire pour plus de mises à jour + last_save_time = time.time() + save_interval = 0.5 # Sauvegarder toutes les 0.5 secondes + for info in zip_ref.infolist(): + if info.is_dir(): + continue + file_path = os.path.join(dest_dir, info.filename) + os.makedirs(os.path.dirname(file_path), exist_ok=True) + with zip_ref.open(info) as source, open(file_path, 'wb') as dest: + file_size = info.file_size + file_extracted = 0 + while True: + chunk = source.read(chunk_size) + if not chunk: + break + dest.write(chunk) + file_extracted += len(chunk) + extracted_size += len(chunk) + current_time = time.time() + with lock: + if isinstance(config.history, list): + for entry in config.history: + if "status" in entry and entry["status"] in ["Téléchargement", "Extracting", "downloading"]: + if "url" in entry and entry["url"] == url: + progress_percent = int(extracted_size / total_size * 100) if total_size > 0 else 0 + progress_percent = max(0, min(100, progress_percent)) + entry["status"] = "Extracting" + entry["progress"] = progress_percent + entry["message"] = "Extraction en cours" + if current_time - last_save_time >= save_interval: + save_history(config.history) + last_save_time = current_time + config.needs_redraw = True + break + os.chmod(file_path, 0o644) + # Vérifier si c'est un dossier xbox et le traiter si nécessaire + xbox_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), "xbox") + if dest_dir == xbox_dir: + # Lister les ISO après extraction + iso_after = set() + for root, dirs, files in os.walk(dest_dir): + for file in files: + if file.lower().endswith('.iso'): + iso_after.add(os.path.abspath(os.path.join(root, file))) + new_isos = list(iso_after - iso_before) + if new_isos: + success, error_msg = handle_xbox(dest_dir, new_isos) + if not success: + return False, error_msg + else: + logger.warning("Aucun nouvel ISO détecté après extraction pour conversion Xbox.") + # On ne retourne pas d'erreur fatale ici, on continue try: os.remove(zip_path) @@ -657,7 +668,7 @@ def handle_ps3(dest_dir): return True, None -def handle_xbox(dest_dir): +def handle_xbox(dest_dir, iso_files): """Gère la conversion des fichiers Xbox extraits.""" logger.debug(f"Traitement spécifique Xbox dans: {dest_dir}")