import pygame # type: ignore import os import logging import platform # Version actuelle de l'application app_version = "2.2.0.3" def get_operating_system(): """Renvoie le nom du système d'exploitation.""" return platform.system() #log dans la console le système d'exploitation print(f"Système d'exploitation : {get_operating_system()}") def get_application_root(): """Détermine le dossier de l'application de manière portable.""" 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)) 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 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 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 # 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))) # Configuration du logging 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 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 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") 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") RGSX_SETTINGS_PATH = os.path.join(SAVE_FOLDER, "rgsx_settings.json") # URL OTA_SERVER_URL = "https://retrogamesets.fr/softs/" OTA_VERSION_ENDPOINT = os.path.join(OTA_SERVER_URL, "version.json") OTA_UPDATE_ZIP = os.path.join(OTA_SERVER_URL, "RGSX.zip") OTA_data_ZIP = os.path.join(OTA_SERVER_URL, "games.zip") #CHEMINS DES EXECUTABLES 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") # Print des chemins pour debug print(f"RETROBAT_DATA_FOLDER: {RETROBAT_DATA_FOLDER}") print(f"ROMS_FOLDER: {ROMS_FOLDER}") print(f"SAVE_FOLDER: {SAVE_FOLDER}") print(f"RGSX APP_FOLDER: {APP_FOLDER}") print(f"RGSX LOGS_FOLDER: {log_dir}") print(f"RGSX SETTINGS PATH: {RGSX_SETTINGS_PATH}") print(f"GAMELISTXML: {GAMELISTXML}") print(f"GAMELISTXML_WINDOWS: {GAMELISTXML_WINDOWS}") print(f"UPDATE_FOLDER: {UPDATE_FOLDER}") print(f"LANGUAGES_FOLDER: {LANGUAGES_FOLDER}") print(f"JSON_EXTENSIONS: {JSON_EXTENSIONS}") print(f"MUSIC_FOLDER: {MUSIC_FOLDER}") print(f"IMAGES_FOLDER: {IMAGES_FOLDER}") print(f"GAMES_FOLDER: {GAMES_FOLDER}") print(f"SOURCES_FILE: {SOURCES_FILE}") print(f"CONTROLS_CONFIG_PATH: {CONTROLS_CONFIG_PATH}") print(f"HISTORY_PATH: {HISTORY_PATH}") # Constantes pour la répétition automatique dans pause_menu REPEAT_DELAY = 350 # Délai initial avant répétition (ms) - augmenté pour éviter les doubles actions REPEAT_INTERVAL = 120 # Intervalle entre répétitions (ms) - ajusté pour une navigation plus contrôlée REPEAT_ACTION_DEBOUNCE = 150 # Délai anti-rebond pour répétitions (ms) - augmenté pour éviter les doubles actions # Variables d'état platforms = [] current_platform = 0 accessibility_mode = False # Mode accessibilité pour les polices agrandies accessibility_settings = {"font_scale": 1.0} font_scale_options = [0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0] current_font_scale_index = 3 # Index pour 1.0 platform_names = {} # {platform_id: platform_name} games = [] current_game = 0 menu_state = "popup" confirm_choice = False scroll_offset = 0 visible_games = 15 popup_start_time = 0 last_progress_update = 0 needs_redraw = True transition_state = "idle" transition_progress = 0.0 transition_duration = 18 games_count = {} music_enabled = True # Par défaut la musique est activée sources_mode = "rgsx" # Mode des sources de jeux (rgsx/custom) custom_sources_url = "" # URL personnalisée si mode custom # Variables pour la sélection de langue selected_language_index = 0 loading_progress = 0.0 current_loading_system = "" error_message = "" repeat_action = None repeat_start_time = 0 repeat_last_action = 0 repeat_key = None filtered_games = [] search_mode = False search_query = "" filter_active = False extension_confirm_selection = 0 pending_download = None controls_config = {} selected_option = 0 previous_menu_state = None history = [] # Liste des entrées d'historique avec platform, game_name, status, url, progress, message, timestamp download_progress = {} download_tasks = {} # Dictionnaire pour les tâches de téléchargement download_result_message = "" download_result_error = False download_result_start_time = 0 needs_redraw = False current_history_item = 0 history_scroll_offset = 0 # Offset pour le défilement de l'historique visible_history_items = 15 # Nombre d'éléments d'historique visibles (ajusté dynamiquement) confirm_clear_selection = 0 # confirmation clear historique confirm_cancel_selection = 0 # confirmation annulation téléchargement last_state_change_time = 0 # Temps du dernier changement d'état pour debounce debounce_delay = 200 # Délai de debounce en millisecondes platform_dicts = [] # Liste des dictionnaires de plateformes selected_key = (0, 0) # Position du curseur dans le clavier virtuel redownload_confirm_selection = 0 # Sélection pour la confirmation de redownload popup_message = "" # Message à afficher dans les popups popup_timer = 0 # Temps restant pour le popup en millisecondes (0 = inactif) last_frame_time = pygame.time.get_ticks() current_music_name = None music_popup_start_time = 0 selected_games = set() # Indices des jeux sélectionnés pour un téléchargement multiple (menu game) batch_download_indices = [] # File d'attente des indices de jeux à traiter en lot batch_in_progress = False # Indique qu'un lot est en cours batch_pending_game = None # Données du jeu en attente de confirmation d'extension # Indicateurs d'entrée (détectés au démarrage) joystick = False keyboard = False xbox_controller = False playstation_controller = False nintendo_controller = False logitech_controller = False eightbitdo_controller = False steam_controller = False trimui_controller = False generic_controller = False # --- Filtre plateformes (UI) --- selected_filter_index = 0 # index dans la liste visible triée filter_platforms_scroll_offset = 0 # défilement si liste longue filter_platforms_dirty = False # indique si modifications non sauvegardées filter_platforms_selection = [] # copie de travail des plateformes visibles (bool masque?) structure: list of (name, hidden_bool) GRID_COLS = 3 # Number of columns in the platform grid GRID_ROWS = 4 # Number of rows in the platform grid # Résolution de l'écran fallback # Utilisée si la résolution définie dépasse les capacités de l'écran SCREEN_WIDTH = 800 """Largeur de l'écran en pixels.""" SCREEN_HEIGHT = 600 """Hauteur de l'écran en pixels.""" # Polices FONT = None """Police par défaut pour l'affichage, initialisée via init_font().""" progress_font = None """Police pour l'affichage de la progression.""" title_font = None """Police pour les titres.""" search_font = None """Police pour la recherche.""" small_font = None """Police pour les petits textes.""" def init_font(): """Initialise les polices après pygame.init().""" global font, progress_font, title_font, search_font, small_font font_scale = accessibility_settings.get("font_scale", 1.0) try: font_path = os.path.join(APP_FOLDER, "assets", "Pixel-UniCode.ttf") font = pygame.font.Font(font_path, int(36 * font_scale)) title_font = pygame.font.Font(font_path, int(48 * font_scale)) search_font = pygame.font.Font(font_path, int(48 * font_scale)) progress_font = pygame.font.Font(font_path, int(36 * font_scale)) small_font = pygame.font.Font(font_path, int(28 * font_scale)) logger.debug(f"Polices Pixel-UniCode initialisées (font_scale: {font_scale})") except Exception as e: try: font = pygame.font.SysFont("arial", int(48 * font_scale)) title_font = pygame.font.SysFont("arial", int(60 * font_scale)) search_font = pygame.font.SysFont("arial", int(60 * font_scale)) progress_font = pygame.font.SysFont("arial", int(36 * font_scale)) small_font = pygame.font.SysFont("arial", int(28 * font_scale)) logger.debug(f"Polices Arial initialisées (font_scale: {font_scale})") except Exception as e2: logger.error(f"Erreur lors de l'initialisation des polices : {e2}") font = None progress_font = None title_font = None search_font = None small_font = None # Indique si une vérification/installation des mises à jour a déjà été effectuée au démarrage update_checked = False def validate_resolution(): """Valide la résolution de l'écran par rapport aux capacités de l'écran.""" display_info = pygame.display.Info() if SCREEN_WIDTH > display_info.current_w or SCREEN_HEIGHT > display_info.current_h: logger.warning(f"Résolution {SCREEN_WIDTH}x{SCREEN_HEIGHT} dépasse les limites de l'écran") return display_info.current_w, display_info.current_h return SCREEN_WIDTH, SCREEN_HEIGHT