1
0
forked from Mirrors/RGSX

ajout d'une fonction historique

This commit is contained in:
skymike03
2025-07-07 02:48:05 +02:00
parent d791db2c17
commit 4b348a75b0
6 changed files with 867 additions and 382 deletions

View File

@@ -9,11 +9,12 @@ import logging
import requests import requests
import sys import sys
import json import json
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_scrollbar, draw_confirm_dialog, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_message, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_scrollbar, draw_confirm_dialog, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_message, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history, draw_clear_history_dialog
from network import test_internet, download_rom, check_extension_before_download, extract_zip from network import test_internet, download_rom, check_extension_before_download, extract_zip
from controls import handle_controls from controls import handle_controls, validate_menu_state
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS
from utils import truncate_text_end, load_system_image, load_games from utils import truncate_text_end, load_system_image, load_games
from history import load_history
import config import config
# Configuration du logging # Configuration du logging
@@ -104,6 +105,10 @@ config.repeat_key = None
config.repeat_start_time = 0 config.repeat_start_time = 0
config.repeat_last_action = 0 config.repeat_last_action = 0
# Chargement de l'historique
config.history = load_history()
logger.debug(f"Historique chargé: {len(config.history)} entrées")
# Vérification et chargement de la configuration des contrôles # Vérification et chargement de la configuration des contrôles
config.controls_config = load_controls_config() config.controls_config = load_controls_config()
if not config.controls_config: if not config.controls_config:
@@ -287,10 +292,10 @@ async def main():
(event.type == pygame.KEYDOWN and start_config.get("type") == "key" and event.key == start_config.get("value")) or (event.type == pygame.KEYDOWN and start_config.get("type") == "key" and event.key == start_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and start_config.get("type") == "button" and event.button == start_config.get("value")) or (event.type == pygame.JOYBUTTONDOWN and start_config.get("type") == "button" and event.button == start_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and start_config.get("type") == "axis" and event.axis == start_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == start_config.get("value")[1]) or (event.type == pygame.JOYAXISMOTION and start_config.get("type") == "axis" and event.axis == start_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == start_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and start_config.get("type") == "hat" and event.value == start_config.get("value")) or (event.type == pygame.JOYHATMOTION and start_config.get("type") == "hat" and event.value == tuple(start_config.get("value"))) or
(event.type == pygame.MOUSEBUTTONDOWN and start_config.get("type") == "mouse" and event.button == start_config.get("value")) (event.type == pygame.MOUSEBUTTONDOWN and start_config.get("type") == "mouse" and event.button == start_config.get("value"))
): ):
if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping"]: if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping", "history", "confirm_clear_history"]:
config.previous_menu_state = config.menu_state config.previous_menu_state = config.menu_state
config.menu_state = "pause_menu" config.menu_state = "pause_menu"
config.selected_pause_option = 0 config.selected_pause_option = 0
@@ -313,7 +318,7 @@ async def main():
(event.type == pygame.KEYDOWN and up_config and event.key == up_config.get("value")) or (event.type == pygame.KEYDOWN and up_config and event.key == up_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and up_config and up_config.get("type") == "button" and event.button == up_config.get("value")) or (event.type == pygame.JOYBUTTONDOWN and up_config and up_config.get("type") == "button" and event.button == up_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and up_config and up_config.get("type") == "axis" and event.axis == up_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == up_config.get("value")[1]) or (event.type == pygame.JOYAXISMOTION and up_config and up_config.get("type") == "axis" and event.axis == up_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == up_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and up_config and up_config.get("type") == "hat" and event.value == up_config.get("value")) (event.type == pygame.JOYHATMOTION and up_config and up_config.get("type") == "hat" and event.value == tuple(up_config.get("value")))
): ):
config.selected_pause_option = max(0, config.selected_pause_option - 1) config.selected_pause_option = max(0, config.selected_pause_option - 1)
config.repeat_action = "up" config.repeat_action = "up"
@@ -326,9 +331,9 @@ async def main():
(event.type == pygame.KEYDOWN and down_config and event.key == down_config.get("value")) or (event.type == pygame.KEYDOWN and down_config and event.key == down_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and down_config and down_config.get("type") == "button" and event.button == down_config.get("value")) or (event.type == pygame.JOYBUTTONDOWN and down_config and down_config.get("type") == "button" and event.button == down_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and down_config and down_config.get("type") == "axis" and event.axis == down_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == down_config.get("value")[1]) or (event.type == pygame.JOYAXISMOTION and down_config and down_config.get("type") == "axis" and event.axis == down_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == down_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and down_config and down_config.get("type") == "hat" and event.value == down_config.get("value")) (event.type == pygame.JOYHATMOTION and down_config and down_config.get("type") == "hat" and event.value == tuple(down_config.get("value")))
): ):
config.selected_pause_option = min(2, config.selected_pause_option + 1) config.selected_pause_option = min(3, config.selected_pause_option + 1)
config.repeat_action = "down" config.repeat_action = "down"
config.repeat_start_time = current_time + REPEAT_DELAY config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time config.repeat_last_action = current_time
@@ -339,15 +344,17 @@ async def main():
(event.type == pygame.KEYDOWN and confirm_config and event.key == confirm_config.get("value")) or (event.type == pygame.KEYDOWN and confirm_config and event.key == confirm_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and confirm_config and confirm_config.get("type") == "button" and event.button == confirm_config.get("value")) or (event.type == pygame.JOYBUTTONDOWN and confirm_config and confirm_config.get("type") == "button" and event.button == confirm_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and confirm_config and confirm_config.get("type") == "axis" and event.axis == confirm_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == confirm_config.get("value")[1]) or (event.type == pygame.JOYAXISMOTION and confirm_config and confirm_config.get("type") == "axis" and event.axis == confirm_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == confirm_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and confirm_config and confirm_config.get("type") == "hat" and event.value == confirm_config.get("value")) (event.type == pygame.JOYHATMOTION and confirm_config and confirm_config.get("type") == "hat" and event.value == tuple(confirm_config.get("value")))
): ):
if config.selected_pause_option == 0: if config.selected_pause_option == 0:
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "controls_help" config.menu_state = "controls_help"
config.needs_redraw = True config.needs_redraw = True
logger.debug("Menu pause: Aide sélectionnée") logger.debug("Menu pause: Aide sélectionnée")
elif config.selected_pause_option == 1: elif config.selected_pause_option == 1:
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
if map_controls(screen): if map_controls(screen):
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning"] else "platform" config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning", "history"] else "platform"
config.controls_config = load_controls_config() config.controls_config = load_controls_config()
logger.debug(f"Mappage des contrôles terminé, retour à {config.menu_state}") logger.debug(f"Mappage des contrôles terminé, retour à {config.menu_state}")
else: else:
@@ -356,15 +363,25 @@ async def main():
config.needs_redraw = True config.needs_redraw = True
logger.debug("Échec du mappage des contrôles") logger.debug("Échec du mappage des contrôles")
elif config.selected_pause_option == 2: elif config.selected_pause_option == 2:
running = False config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "history"
config.current_history_item = 0
config.history_scroll_offset = 0
config.needs_redraw = True
logger.debug("Menu pause: Historique sélectionné")
elif config.selected_pause_option == 3:
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "confirm_exit"
config.confirm_selection = 0
config.needs_redraw = True
logger.debug("Menu pause: Quitter sélectionné") logger.debug("Menu pause: Quitter sélectionné")
elif ( elif (
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or (event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or (event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or (event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == cancel_config.get("value")) (event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == tuple(cancel_config.get("value")))
): ):
config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning"] else "platform" config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "download_progress", "download_result", "confirm_exit", "extension_warning", "history"] else "platform"
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Menu pause: Annulation, retour à {config.menu_state}") logger.debug(f"Menu pause: Annulation, retour à {config.menu_state}")
@@ -388,7 +405,7 @@ async def main():
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Menu pause: Répétition haut, selected_option={config.selected_pause_option}") logger.debug(f"Menu pause: Répétition haut, selected_option={config.selected_pause_option}")
elif config.repeat_action == "down": elif config.repeat_action == "down":
config.selected_pause_option = min(2, config.selected_pause_option + 1) config.selected_pause_option = min(3, config.selected_pause_option + 1)
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Menu pause: Répétition bas, selected_option={config.selected_pause_option}") logger.debug(f"Menu pause: Répétition bas, selected_option={config.selected_pause_option}")
config.repeat_start_time = current_time + REPEAT_INTERVAL config.repeat_start_time = current_time + REPEAT_INTERVAL
@@ -399,14 +416,24 @@ async def main():
cancel_config = config.controls_config.get("cancel", {}) cancel_config = config.controls_config.get("cancel", {})
if ( if (
(event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or (event.type == pygame.KEYDOWN and cancel_config and event.key == cancel_config.get("value")) or
(event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) (event.type == pygame.JOYBUTTONDOWN and cancel_config and cancel_config.get("type") == "button" and event.button == cancel_config.get("value")) or
(event.type == pygame.JOYAXISMOTION and cancel_config and cancel_config.get("type") == "axis" and event.axis == cancel_config.get("value")[0] and abs(event.value) > 0.5 and (1 if event.value > 0 else -1) == cancel_config.get("value")[1]) or
(event.type == pygame.JOYHATMOTION and cancel_config and cancel_config.get("type") == "hat" and event.value == tuple(cancel_config.get("value")))
): ):
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "pause_menu" config.menu_state = "pause_menu"
config.needs_redraw = True config.needs_redraw = True
logger.debug("Controls_help: Annulation, retour à pause_menu") logger.debug("Controls_help: Annulation, retour à pause_menu")
continue continue
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "extension_warning"]: # Gérer confirm_clear_history explicitement
if config.menu_state == "confirm_clear_history":
action = handle_controls(event, sources, joystick, screen)
config.needs_redraw = True
logger.debug(f"Événement transmis à handle_controls dans confirm_clear_history: {event.type}")
continue
if config.menu_state in ["platform", "game", "error", "confirm_exit", "download_progress", "download_result", "extension_warning", "history"]:
action = handle_controls(event, sources, joystick, screen) action = handle_controls(event, sources, joystick, screen)
config.needs_redraw = True config.needs_redraw = True
if action == "quit": if action == "quit":
@@ -430,8 +457,31 @@ async def main():
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported)) task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform) config.download_tasks[task] = (task, url, game_name, platform)
config.menu_state = "download_progress" config.menu_state = "download_progress"
config.pending_download = None # Réinitialiser après démarrage du téléchargement
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Téléchargement démarré pour {game_name}, passage à download_progress") logger.debug(f"Téléchargement démarré pour {game_name}, passage à download_progress")
elif action == "redownload" and config.menu_state == "history" and config.history:
entry = config.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:
url = game[1]
is_supported, message, is_zip_non_supported = check_extension_before_download(url, platform, game_name)
if not is_supported:
config.pending_download = (url, platform, game_name, is_zip_non_supported)
config.menu_state = "extension_warning"
config.extension_confirm_selection = 0
config.needs_redraw = True
logger.debug(f"Extension non reconnue pour retéléchargement, passage à extension_warning pour {game_name}")
else:
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform)
config.menu_state = "download_progress"
config.pending_download = None # Réinitialiser après démarrage du téléchargement
config.needs_redraw = True
logger.debug(f"Retéléchargement démarré pour {game_name}, passage à download_progress")
break
# Gestion des téléchargements # Gestion des téléchargements
if config.download_tasks: if config.download_tasks:
@@ -444,6 +494,7 @@ async def main():
config.download_result_start_time = pygame.time.get_ticks() config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result" config.menu_state = "download_result"
config.download_progress.clear() # Réinitialiser download_progress config.download_progress.clear() # Réinitialiser download_progress
config.pending_download = None # Réinitialiser après téléchargement
config.needs_redraw = True config.needs_redraw = True
del config.download_tasks[task_id] del config.download_tasks[task_id]
logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}") logger.debug(f"Téléchargement terminé: {game_name}, succès={success}, message={message}")
@@ -453,14 +504,16 @@ async def main():
config.download_result_start_time = pygame.time.get_ticks() config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result" config.menu_state = "download_result"
config.download_progress.clear() # Réinitialiser download_progress config.download_progress.clear() # Réinitialiser download_progress
config.pending_download = None # Réinitialiser après téléchargement
config.needs_redraw = True config.needs_redraw = True
del config.download_tasks[task_id] del config.download_tasks[task_id]
logger.error(f"Erreur dans tâche de téléchargement: {str(e)}") logger.error(f"Erreur dans tâche de téléchargement: {str(e)}")
# Gestion de la fin du popup download_result # Gestion de la fin du popup download_result
if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000: if config.menu_state == "download_result" and current_time - config.download_result_start_time > 3000:
config.menu_state = "game" config.menu_state = config.previous_menu_state if config.previous_menu_state in ["platform", "game", "history"] else "game"
config.download_progress.clear() # Réinitialiser download_progress config.download_progress.clear() # Réinitialiser download_progress
config.pending_download = None # Réinitialiser après affichage du résultat
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Fin popup download_result, retour à {config.menu_state}") logger.debug(f"Fin popup download_result, retour à {config.menu_state}")
@@ -500,7 +553,18 @@ async def main():
elif config.menu_state == "controls_help": elif config.menu_state == "controls_help":
draw_controls_help(screen, config.previous_menu_state) draw_controls_help(screen, config.previous_menu_state)
logger.debug("Rendu de draw_controls_help") logger.debug("Rendu de draw_controls_help")
elif config.menu_state == "history":
draw_history(screen)
logger.debug("Rendu de draw_history")
elif config.menu_state == "confirm_clear_history":
draw_clear_history_dialog(screen)
logger.debug("Rendu de confirm_clear_history")
else:
# Gestion des états non valides
config.menu_state = "platform"
draw_platform_grid(screen)
config.needs_redraw = True
logger.error(f"État de menu non valide détecté: {config.menu_state}, retour à platform")
draw_controls(screen, config.menu_state) draw_controls(screen, config.menu_state)
pygame.display.flip() pygame.display.flip()
config.needs_redraw = False config.needs_redraw = False

View File

@@ -45,6 +45,16 @@ pending_download = None
controls_config = {} controls_config = {}
selected_pause_option = 0 selected_pause_option = 0
previous_menu_state = None previous_menu_state = None
history = [] # Liste des entrées de l'historique
current_history_item = 0 # Index de l'élément sélectionné dans l'historique
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
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
is_non_pc = True # Indicateur pour plateforme non-PC (par exemple, console)
# Résolution de l'écran # Résolution de l'écran
screen_width = 800 screen_width = 800
@@ -68,6 +78,8 @@ small_font = None
CONTROLS_CONFIG_PATH = "/userdata/saves/ports/rgsx/controls.json" CONTROLS_CONFIG_PATH = "/userdata/saves/ports/rgsx/controls.json"
"""Chemin du fichier de configuration des contrôles.""" """Chemin du fichier de configuration des contrôles."""
HISTORY_PATH = "/userdata/saves/ports/rgsx/history.json"
"""Chemin du fichier de l'historique des téléchargements."""
def init_font(): def init_font():
"""Initialise les polices après pygame.init().""" """Initialise les polices après pygame.init()."""

View File

@@ -3,10 +3,12 @@ import config
from config import CONTROLS_CONFIG_PATH from config import CONTROLS_CONFIG_PATH
import asyncio import asyncio
import math import math
import json
from display import draw_validation_transition from display import draw_validation_transition
from network import download_rom, check_extension_before_download from network import download_rom, check_extension_before_download
from controls_mapper import get_readable_input_name from controls_mapper import get_readable_input_name
from utils import load_games # Ajout de l'import from utils import load_games
from history import load_history, clear_history
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -18,15 +20,57 @@ JOYHAT_DEBOUNCE = 200 # Délai anti-rebond pour JOYHATMOTION (ms)
JOYAXIS_DEBOUNCE = 50 # Délai anti-rebond pour JOYAXISMOTION (ms) JOYAXIS_DEBOUNCE = 50 # Délai anti-rebond pour JOYAXISMOTION (ms)
REPEAT_ACTION_DEBOUNCE = 50 # Délai anti-rebond pour répétitions up/down/left/right (ms) REPEAT_ACTION_DEBOUNCE = 50 # Délai anti-rebond pour répétitions up/down/left/right (ms)
# Liste des états valides (mise à jour)
VALID_STATES = [
"platform", "game", "download_progress", "download_result", "confirm_exit",
"extension_warning", "pause_menu", "controls_help", "history", "remap_controls",
"error", "loading", "confirm_clear_history" # Ajout du nouvel état
]
def validate_menu_state(state):
"""Valide l'état du menu et retourne un état par défaut si non valide."""
return state if state in VALID_STATES else "platform"
def load_controls_config(path=CONTROLS_CONFIG_PATH): def load_controls_config(path=CONTROLS_CONFIG_PATH):
"""Charge la configuration des contrôles depuis un fichier JSON.""" """Charge la configuration des contrôles depuis un fichier JSON."""
try: try:
with open(path, "r") as f: with open(path, "r") as f:
return json.load(f) config_data = json.load(f)
# Vérifier les actions nécessaires
required_actions = ["confirm", "cancel", "left", "right"]
for action in required_actions:
if action not in config_data:
logger.warning(f"Action {action} manquante dans {path}, utilisation de la valeur par défaut")
config_data[action] = {
"type": "key",
"value": {
"confirm": pygame.K_RETURN,
"cancel": pygame.K_ESCAPE,
"left": pygame.K_LEFT,
"right": pygame.K_RIGHT
}[action]
}
return config_data
except (FileNotFoundError, json.JSONDecodeError) as e: except (FileNotFoundError, json.JSONDecodeError) as e:
logging.error(f"Erreur lors de la lecture de {path} : {e}") logger.error(f"Erreur lors de la lecture de {path} : {e}, utilisation de la configuration par défaut")
return {} return {
"confirm": {"type": "key", "value": pygame.K_RETURN},
"cancel": {"type": "key", "value": pygame.K_ESCAPE},
"left": {"type": "key", "value": pygame.K_LEFT},
"right": {"type": "key", "value": pygame.K_RIGHT},
"up": {"type": "key", "value": pygame.K_UP},
"down": {"type": "key", "value": pygame.K_DOWN},
"start": {"type": "key", "value": pygame.K_p},
"progress": {"type": "key", "value": pygame.K_t},
"page_up": {"type": "key", "value": pygame.K_PAGEUP},
"page_down": {"type": "key", "value": pygame.K_PAGEDOWN},
"filter": {"type": "key", "value": pygame.K_f},
"delete": {"type": "key", "value": pygame.K_BACKSPACE},
"space": {"type": "key", "value": pygame.K_SPACE}
}
def is_input_matched(event, action_name): def is_input_matched(event, action_name):
"""Vérifie si l'événement correspond à l'action configurée.""" """Vérifie si l'événement correspond à l'action configurée."""
if not config.controls_config.get(action_name): if not config.controls_config.get(action_name):
@@ -53,7 +97,6 @@ def is_input_matched(event, action_name):
logger.debug(f"Vérification axis: event_axis={event_axis}, event_value={event_value}, input_value={input_value}, result={result}") logger.debug(f"Vérification axis: event_axis={event_axis}, event_value={event_value}, input_value={input_value}, result={result}")
return result return result
elif input_type == "hat" and event_type == pygame.JOYHATMOTION: elif input_type == "hat" and event_type == pygame.JOYHATMOTION:
# Convertir input_value en tuple pour comparaison
input_value_tuple = tuple(input_value) if isinstance(input_value, list) else input_value input_value_tuple = tuple(input_value) if isinstance(input_value, list) else input_value
logger.debug(f"Vérification hat: event_value={event_value}, input_value={input_value_tuple}") logger.debug(f"Vérification hat: event_value={event_value}, input_value={input_value_tuple}")
return event_value == input_value_tuple return event_value == input_value_tuple
@@ -64,11 +107,15 @@ def is_input_matched(event, action_name):
def handle_controls(event, sources, joystick, screen): def handle_controls(event, sources, joystick, screen):
"""Gère un événement clavier/joystick/souris et la répétition automatique. """Gère un événement clavier/joystick/souris et la répétition automatique.
Retourne 'quit', 'download', ou None. Retourne 'quit', 'download', 'redownload', ou None.
""" """
action = None action = None
current_time = pygame.time.get_ticks() current_time = pygame.time.get_ticks()
# Valider previous_menu_state avant tout traitement
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
logger.debug(f"Validation initiale: previous_menu_state={config.previous_menu_state}")
# Debounce général # Debounce général
if current_time - config.last_state_change_time < config.debounce_delay: if current_time - config.last_state_change_time < config.debounce_delay:
return action return action
@@ -100,14 +147,25 @@ def handle_controls(event, sources, joystick, screen):
if is_input_matched(event, action_name): if is_input_matched(event, action_name):
logger.debug(f"Action mappée détectée: {action_name}, input={get_readable_input_name(event)}") logger.debug(f"Action mappée détectée: {action_name}, input={get_readable_input_name(event)}")
# Menu pause
if is_input_matched(event, "start") and config.menu_state not in ("pause_menu", "controls_help", "history", "remap_controls"):
config.previous_menu_state = config.menu_state
config.menu_state = "pause_menu"
config.selected_option = 0
config.needs_redraw = True
logger.debug(f"Passage à pause_menu depuis {config.previous_menu_state}")
return action
# Erreur # Erreur
if config.menu_state == "error": if config.menu_state == "error":
if is_input_matched(event, "confirm"): if is_input_matched(event, "confirm"):
config.menu_state = "loading" config.menu_state = "loading"
config.needs_redraw = True
logger.debug("Sortie erreur avec Confirm") logger.debug("Sortie erreur avec Confirm")
elif is_input_matched(event, "cancel"): elif is_input_matched(event, "cancel"):
config.menu_state = "confirm_exit" config.menu_state = "confirm_exit"
config.confirm_selection = 0 config.confirm_selection = 0
config.needs_redraw = True
# Plateformes # Plateformes
elif config.menu_state == "platform": elif config.menu_state == "platform":
@@ -167,17 +225,29 @@ def handle_controls(event, sources, joystick, screen):
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True config.needs_redraw = True
elif is_input_matched(event, "page_down"): elif is_input_matched(event, "page_down"):
if (config.current_page + 1) * 9 < len(config.platforms): if (config.current_page + 1) * 9 < len(config.platforms):
config.current_page += 1 config.current_page += 1
config.selected_platform = config.current_page * 9 + row * 3 config.selected_platform = config.current_page * 9 + row * 3
if config.selected_platform >= len(config.platforms): if config.selected_platform >= len(config.platforms):
config.selected_platform = len(config.platforms) - 1 config.selected_platform = len(config.platforms) - 1
config.repeat_action = None # Réinitialiser la répétition config.repeat_action = None
config.repeat_key = None config.repeat_key = None
config.repeat_start_time = 0 config.repeat_start_time = 0
config.repeat_last_action = current_time config.repeat_last_action = current_time
config.needs_redraw = True config.needs_redraw = True
logger.debug("Page suivante, répétition réinitialisée") logger.debug("Page suivante, répétition réinitialisée")
elif is_input_matched(event, "page_up"):
if config.current_page > 0:
config.current_page -= 1
config.selected_platform = config.current_page * 9 + row * 3
if config.selected_platform >= len(config.platforms):
config.selected_platform = len(config.platforms) - 1
config.repeat_action = None
config.repeat_key = None
config.repeat_start_time = 0
config.repeat_last_action = current_time
config.needs_redraw = True
logger.debug("Page précédente, répétition réinitialisée")
elif is_input_matched(event, "progress"): elif is_input_matched(event, "progress"):
if config.download_tasks: if config.download_tasks:
config.menu_state = "download_progress" config.menu_state = "download_progress"
@@ -186,352 +256,445 @@ def handle_controls(event, sources, joystick, screen):
elif is_input_matched(event, "confirm"): elif is_input_matched(event, "confirm"):
if config.platforms: if config.platforms:
config.current_platform = config.selected_platform config.current_platform = config.selected_platform
config.games = load_games(config.platforms[config.current_platform]) # Appel à load_games depuis utils config.games = load_games(config.platforms[config.current_platform])
config.filtered_games = config.games config.filtered_games = config.games
config.filter_active = False config.filter_active = False
config.current_game = 0 config.current_game = 0
config.scroll_offset = 0 config.scroll_offset = 0
draw_validation_transition(screen, config.current_platform) # Animation de transition draw_validation_transition(screen, config.current_platform)
config.menu_state = "game" config.menu_state = "game"
config.needs_redraw = True config.needs_redraw = True
logger.debug(f"Plateforme sélectionnée: {config.platforms[config.current_platform]}, {len(config.games)} jeux chargés") logger.debug(f"Plateforme sélectionnée: {config.platforms[config.current_platform]}, {len(config.games)} jeux chargés")
elif is_input_matched(event, "cancel"): elif is_input_matched(event, "cancel"):
config.menu_state = "confirm_exit" config.menu_state = "confirm_exit"
config.confirm_selection = 0 config.confirm_selection = 0
config.needs_redraw = True
# Jeux # Jeux
elif config.menu_state == "game": elif config.menu_state == "game":
if config.search_mode: games = config.filtered_games if config.filter_active or config.search_mode else config.games
if config.is_non_pc: if config.search_mode and config.is_non_pc:
keyboard_layout = [ keyboard_layout = [
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
['A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
['Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'], ['Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'],
['W', 'X', 'C', 'V', 'B', 'N'] ['W', 'X', 'C', 'V', 'B', 'N']
] ]
row, col = config.selected_key row, col = config.selected_key
max_row = len(keyboard_layout) - 1 max_row = len(keyboard_layout) - 1
max_col = len(keyboard_layout[row]) - 1 max_col = len(keyboard_layout[row]) - 1
if is_input_matched(event, "up"): if is_input_matched(event, "up"):
if row > 0: if row > 0:
config.selected_key = (row - 1, min(col, len(keyboard_layout[row - 1]) - 1)) config.selected_key = (row - 1, min(col, len(keyboard_layout[row - 1]) - 1))
config.repeat_action = "up" config.repeat_action = "up"
config.repeat_start_time = current_time + REPEAT_DELAY config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "down"):
if row < max_row:
config.selected_key = (row + 1, min(col, len(keyboard_layout[row + 1]) - 1))
config.repeat_action = "down"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "left"):
if col > 0:
config.selected_key = (row, col - 1)
config.repeat_action = "left"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "right"):
if col < max_col:
config.selected_key = (row, col + 1)
config.repeat_action = "right"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "confirm"):
key = keyboard_layout[row][col]
if len(config.search_query) < 50:
config.search_query += key.lower()
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
elif is_input_matched(event, "delete"):
config.search_query = config.search_query[:-1]
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True config.needs_redraw = True
elif is_input_matched(event, "space"): elif is_input_matched(event, "down"):
config.search_query += " " if row < max_row:
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games config.selected_key = (row + 1, min(col, len(keyboard_layout[row + 1]) - 1))
config.current_game = 0 config.repeat_action = "down"
config.scroll_offset = 0 config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True config.needs_redraw = True
elif is_input_matched(event, "cancel"): elif is_input_matched(event, "left"):
config.search_mode = False if col > 0:
config.search_query = "" config.selected_key = (row, col - 1)
config.filtered_games = config.games config.repeat_action = "left"
config.filter_active = False config.repeat_start_time = current_time + REPEAT_DELAY
config.current_game = 0 config.repeat_last_action = current_time
config.scroll_offset = 0 config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True config.needs_redraw = True
logger.debug("Filtre annulé") elif is_input_matched(event, "right"):
elif is_input_matched(event, "filter"): if col < max_col:
config.search_mode = False config.selected_key = (row, col + 1)
config.filter_active = bool(config.search_query) config.repeat_action = "right"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True config.needs_redraw = True
else:
if is_input_matched(event, "confirm"):
config.search_mode = False
config.filter_active = bool(config.search_query)
config.needs_redraw = True
elif is_input_matched(event, "cancel"):
config.search_mode = False
config.search_query = ""
config.filtered_games = config.games
config.filter_active = False
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
logger.debug("Filtre annulé")
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
config.search_query = config.search_query[:-1]
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
elif event.key == pygame.K_SPACE:
config.search_query += " "
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
elif event.unicode.isprintable() and len(config.search_query) < 50:
config.search_query += event.unicode
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()] if config.search_query else config.games
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
else:
if is_input_matched(event, "down"):
config.current_game = min(config.current_game + 1, len(config.filtered_games) - 1)
if config.current_game >= config.scroll_offset + config.visible_games:
config.scroll_offset += 1
config.repeat_action = "down"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "up"):
config.current_game = max(config.current_game - 1, 0)
if config.current_game < config.scroll_offset:
config.scroll_offset -= 1
config.repeat_action = "up"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "page_up"):
config.current_game = max(config.current_game - config.visible_games, 0)
config.scroll_offset = max(config.scroll_offset - config.visible_games, 0)
config.repeat_action = "page_up"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "page_down"):
config.current_game = min(config.current_game + config.visible_games, len(config.filtered_games) - 1)
config.scroll_offset = min(config.scroll_offset + config.visible_games, len(config.filtered_games) - config.visible_games)
config.repeat_action = "page_down"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "confirm"): elif is_input_matched(event, "confirm"):
if config.filtered_games: config.search_query += keyboard_layout[row][col]
action = "download" config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()]
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
logger.debug(f"Recherche mise à jour: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
elif is_input_matched(event, "delete"):
if config.search_query:
config.search_query = config.search_query[:-1]
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()]
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
logger.debug(f"Suppression caractère: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
elif is_input_matched(event, "space"):
config.search_query += " "
config.filtered_games = [game for game in config.games if config.search_query.lower() in game[0].lower()]
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
logger.debug(f"Espace ajouté: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
elif is_input_matched(event, "cancel"):
config.search_mode = False
config.search_query = ""
config.selected_key = (0, 0)
config.filtered_games = config.games
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
logger.debug("Sortie du mode recherche")
else:
if is_input_matched(event, "up"):
if config.current_game > 0:
config.current_game -= 1
config.repeat_action = "up"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "down"):
if config.current_game < len(games) - 1:
config.current_game += 1
config.repeat_action = "down"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "page_up"):
config.current_game = max(0, config.current_game - config.visible_games)
config.repeat_action = None
config.repeat_key = None
config.repeat_start_time = 0
config.repeat_last_action = current_time
config.needs_redraw = True
logger.debug("Page précédente dans la liste des jeux")
elif is_input_matched(event, "page_down"):
config.current_game = min(len(games) - 1, config.current_game + config.visible_games)
config.repeat_action = None
config.repeat_key = None
config.repeat_start_time = 0
config.repeat_last_action = current_time
config.needs_redraw = True
logger.debug("Page suivante dans la liste des jeux")
elif is_input_matched(event, "filter"): elif is_input_matched(event, "filter"):
config.search_mode = True config.search_mode = True
config.search_query = "" config.search_query = ""
config.filtered_games = config.games config.filtered_games = config.games
config.current_game = 0
config.scroll_offset = 0
config.selected_key = (0, 0) config.selected_key = (0, 0)
config.needs_redraw = True config.needs_redraw = True
logger.debug("Entrée en mode recherche") logger.debug("Entrée en mode recherche")
elif is_input_matched(event, "cancel"):
config.menu_state = "platform"
config.current_game = 0
config.scroll_offset = 0
config.filter_active = False
config.filtered_games = config.games
config.needs_redraw = True
logger.debug("Retour à platform, filtre réinitialisé")
elif is_input_matched(event, "progress"): elif is_input_matched(event, "progress"):
if config.download_tasks: if config.download_tasks:
config.menu_state = "download_progress" config.menu_state = "download_progress"
config.needs_redraw = True config.needs_redraw = True
logger.debug("Retour à download_progress depuis game") logger.debug("Retour à download_progress depuis game")
elif is_input_matched(event, "confirm"):
if games:
config.pending_download = check_extension_before_download(games[config.current_game][0], config.platforms[config.current_platform], games[config.current_game][1])
if config.pending_download:
url, platform, game_name, is_zip_non_supported = config.pending_download
if is_zip_non_supported:
config.menu_state = "extension_warning"
config.extension_confirm_selection = 0
config.needs_redraw = True
logger.debug(f"Extension non supportée, passage à extension_warning pour {game_name}")
else:
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform) # Stocker tuple de 4 éléments
config.menu_state = "download_progress"
config.needs_redraw = True
logger.debug(f"Début du téléchargement: {game_name} pour {platform} depuis {url}")
config.pending_download = None # Réinitialiser après démarrage
action = "download"
else:
config.menu_state = "error"
config.error_message = "Extension non supportée ou erreur de téléchargement"
config.pending_download = None
config.needs_redraw = True
logger.error(f"config.pending_download est None pour {games[config.current_game][0]}")
elif is_input_matched(event, "cancel"):
config.menu_state = "platform"
config.current_game = 0
config.scroll_offset = 0
config.needs_redraw = True
logger.debug("Retour à platform")
# Download progress elif config.menu_state == "history":
history = config.history
if is_input_matched(event, "up"):
if config.current_history_item > 0:
config.current_history_item -= 1
config.repeat_action = "up"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "down"):
if config.current_history_item < len(history) - 1:
config.current_history_item += 1
config.repeat_action = "down"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "page_up"):
config.current_history_item = max(0, config.current_history_item - config.visible_history_items)
config.repeat_action = None
config.repeat_key = None
config.repeat_start_time = 0
config.repeat_last_action = current_time
config.needs_redraw = True
logger.debug("Page précédente dans l'historique")
elif is_input_matched(event, "page_down"):
config.current_history_item = min(len(history) - 1, config.current_history_item + config.visible_history_items)
config.repeat_action = None
config.repeat_key = None
config.repeat_start_time = 0
config.repeat_last_action = current_time
config.needs_redraw = True
logger.debug("Page suivante dans l'historique")
elif is_input_matched(event, "progress"):
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "confirm_clear_history"
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"]
# Rechercher l'URL dans config.games
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_name, platform, game[1])
if config.pending_download:
url, platform, game_name, is_zip_non_supported = config.pending_download
if is_zip_non_supported:
config.previous_menu_state = validate_menu_state(config.previous_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 = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform)
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "download_progress"
config.needs_redraw = True
logger.debug(f"Retéléchargement: {game_name} pour {platform} depuis {url}")
config.pending_download = None
action = "redownload"
else:
config.menu_state = "error"
config.error_message = "Extension non supportée ou erreur de retéléchargement"
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"):
config.menu_state = validate_menu_state(config.previous_menu_state)
config.current_history_item = 0
config.history_scroll_offset = 0
config.needs_redraw = True
logger.debug(f"Retour à {config.menu_state} depuis history")
# Ajouter un nouvel état "confirm_clear_history" après l'état "confirm_exit"
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"):
logger.debug(f"Action confirm détectée dans confirm_clear_history")
if config.confirm_clear_selection == 1: # Oui
clear_history()
config.history = []
config.current_history_item = 0
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
logger.debug("Annulation du vidage de l'historique, retour à history")
elif is_input_matched(event, "left"):
logger.debug(f"Action left détectée dans confirm_clear_history")
config.confirm_clear_selection = 1 # Sélectionner "Non"
config.needs_redraw = True
logger.debug(f"Changement sélection confirm_clear_history: {config.confirm_clear_selection}")
elif is_input_matched(event, "right"):
logger.debug(f"Action right détectée dans confirm_clear_history")
config.confirm_clear_selection = 0 # Sélectionner "Oui"
config.needs_redraw = True
logger.debug(f"Changement sélection confirm_clear_history: {config.confirm_clear_selection}")
elif is_input_matched(event, "cancel"):
logger.debug(f"Action cancel détectée dans confirm_clear_history")
config.menu_state = "history"
config.needs_redraw = True
logger.debug("Annulation du vidage de l'historique, retour à history")
# Progression téléchargement
elif config.menu_state == "download_progress": elif config.menu_state == "download_progress":
if is_input_matched(event, "cancel"): if is_input_matched(event, "cancel"):
if config.download_tasks: for task in config.download_tasks:
task = list(config.download_tasks.keys())[0] task.cancel()
config.download_tasks[task][0].cancel() config.download_tasks.clear()
url = config.download_tasks[task][1] config.download_progress.clear()
game_name = config.download_tasks[task][2]
if url in config.download_progress:
del config.download_progress[url]
del config.download_tasks[task]
config.download_result_message = f"Téléchargement annulé : {game_name}"
config.download_result_error = True
config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result"
elif is_input_matched(event, "progress"):
config.menu_state = "game"
config.needs_redraw = True
logger.debug("Retour à game depuis download_progress")
# Confirmation de sortie
elif config.menu_state == "confirm_exit":
if is_input_matched(event, "left"):
config.confirm_selection = 1
config.needs_redraw = True
logger.debug("Sélection Oui")
elif is_input_matched(event, "right"):
config.confirm_selection = 0
config.needs_redraw = True
logger.debug("Sélection Non")
elif is_input_matched(event, "confirm"):
if config.confirm_selection == 1:
logger.debug("Retour de 'quit' pour fermer l'application")
return "quit"
else:
config.menu_state = "platform"
config.needs_redraw = True
logger.debug("Retour à platform depuis confirm_exit")
elif is_input_matched(event, "cancel"):
config.menu_state = "platform"
config.needs_redraw = True
logger.debug("Annulation confirm_exit")
# Avertissement d'extension
elif config.menu_state == "extension_warning":
if is_input_matched(event, "left"):
config.extension_confirm_selection = 1
config.needs_redraw = True
logger.debug("Sélection Oui (extension_warning)")
elif is_input_matched(event, "right"):
config.extension_confirm_selection = 0
config.needs_redraw = True
logger.debug("Sélection Non (extension_warning)")
elif is_input_matched(event, "confirm"):
if config.extension_confirm_selection == 1:
if config.pending_download:
url, platform, game_name, is_zip_non_supported = config.pending_download
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported=is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform)
config.menu_state = "download_progress"
config.pending_download = None
config.needs_redraw = True
else:
config.menu_state = "game"
config.needs_redraw = True
else:
config.menu_state = "game"
config.pending_download = None
config.needs_redraw = True
logger.debug("Téléchargement annulé (extension_warning)")
elif is_input_matched(event, "cancel"):
config.menu_state = "game"
config.pending_download = None config.pending_download = None
config.menu_state = validate_menu_state(config.previous_menu_state)
config.needs_redraw = True config.needs_redraw = True
logger.debug("Annulation extension_warning") logger.debug(f"Téléchargement annulé, retour à {config.menu_state}")
elif is_input_matched(event, "progress"):
config.menu_state = validate_menu_state(config.previous_menu_state)
config.needs_redraw = True
logger.debug(f"Retour à {config.menu_state} depuis download_progress")
# Résultat téléchargement # Résultat téléchargement
elif config.menu_state == "download_result": elif config.menu_state == "download_result":
if is_input_matched(event, "confirm"): if is_input_matched(event, "confirm"):
config.menu_state = "game" config.menu_state = validate_menu_state(config.previous_menu_state)
config.popup_timer = 0
config.pending_download = None
config.needs_redraw = True config.needs_redraw = True
logger.debug("Retour à game depuis download_result") logger.debug(f"Retour à {config.menu_state} depuis download_result")
# Enregistrer la touche pour la répétition # Confirmation quitter
if config.repeat_action in ["up", "down", "page_up", "page_down", "left", "right"]: elif config.menu_state == "confirm_exit":
if event.type == pygame.KEYDOWN: if is_input_matched(event, "confirm"):
config.repeat_key = event.key if config.confirm_selection == 1:
elif event.type == pygame.JOYBUTTONDOWN: return "quit"
config.repeat_key = event.button else:
elif event.type == pygame.JOYAXISMOTION: config.menu_state = validate_menu_state(config.previous_menu_state)
config.repeat_key = (event.axis, 1 if event.value > 0 else -1) config.needs_redraw = True
elif event.type == pygame.JOYHATMOTION: logger.debug(f"Retour à {config.menu_state} depuis confirm_exit")
config.repeat_key = event.value elif is_input_matched(event, "left") or is_input_matched(event, "right"):
config.confirm_selection = 1 - config.confirm_selection
config.needs_redraw = True
logger.debug(f"Changement sélection confirm_exit: {config.confirm_selection}")
# Avertissement extension
elif config.menu_state == "extension_warning":
if is_input_matched(event, "confirm"):
if config.extension_confirm_selection == 1:
if config.pending_download and len(config.pending_download) == 4:
url, platform, game_name, is_zip_non_supported = config.pending_download
task = asyncio.create_task(download_rom(url, platform, game_name, is_zip_non_supported))
config.download_tasks[task] = (task, url, game_name, platform)
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "download_progress"
config.needs_redraw = True
logger.debug(f"Téléchargement confirmé après avertissement: {game_name} pour {platform} depuis {url}")
config.pending_download = None
action = "download"
else:
config.menu_state = "error"
config.error_message = "Données de téléchargement invalides"
config.pending_download = None
config.needs_redraw = True
logger.error("config.pending_download invalide")
else:
config.pending_download = None
config.menu_state = validate_menu_state(config.previous_menu_state)
config.needs_redraw = True
logger.debug(f"Retour à {config.menu_state} depuis extension_warning")
elif is_input_matched(event, "left") or is_input_matched(event, "right"):
config.extension_confirm_selection = 1 - config.extension_confirm_selection
config.needs_redraw = True
logger.debug(f"Changement sélection extension_warning: {config.extension_confirm_selection}")
elif is_input_matched(event, "cancel"):
config.pending_download = None
config.menu_state = validate_menu_state(config.previous_menu_state)
config.needs_redraw = True
logger.debug(f"Retour à {config.menu_state} depuis extension_warning")
# Menu pause
elif config.menu_state == "pause_menu":
if is_input_matched(event, "up"):
config.selected_option = max(0, config.selected_option - 1)
config.repeat_action = "up"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
config.needs_redraw = True
elif is_input_matched(event, "down"):
config.selected_option = min(3, config.selected_option + 1)
config.repeat_action = "down"
config.repeat_start_time = current_time + REPEAT_DELAY
config.repeat_last_action = current_time
config.repeat_key = event.key if event.type == pygame.KEYDOWN else event.button if event.type == pygame.JOYBUTTONDOWN else (event.axis, 1 if event.value > 0 else -1) if event.type == pygame.JOYAXISMOTION else event.value
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"
config.needs_redraw = True
logger.debug(f"Passage à controls_help depuis pause_menu")
elif config.selected_option == 1: # Remap controls
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "remap_controls"
config.needs_redraw = True
logger.debug(f"Passage à remap_controls depuis pause_menu")
elif config.selected_option == 2: # 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.menu_state = "history"
config.needs_redraw = True
logger.debug(f"Passage à history depuis pause_menu")
elif config.selected_option == 3: # Quit
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
config.menu_state = "confirm_exit"
config.confirm_selection = 0
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)
config.needs_redraw = True
logger.debug(f"Retour à {config.menu_state} depuis pause_menu")
elif event.type in (pygame.KEYUP, pygame.JOYBUTTONUP): # Aide contrôles
if config.menu_state in ("game", "platform") and is_input_matched(event, config.repeat_action): elif config.menu_state == "controls_help":
config.repeat_action = None if is_input_matched(event, "cancel"):
config.repeat_key = None config.menu_state = validate_menu_state(config.previous_menu_state)
config.repeat_start_time = 0 config.needs_redraw = True
config.needs_redraw = True logger.debug(f"Retour à {config.menu_state} depuis controls_help")
# Gestion de la répétition automatique # Remap controls
if config.menu_state in ("game", "platform") and config.repeat_action: elif config.menu_state == "remap_controls":
if current_time >= config.repeat_start_time: if is_input_matched(event, "cancel"):
if config.repeat_action in ["up", "down", "left", "right"] and current_time - config.repeat_last_action < REPEAT_ACTION_DEBOUNCE: config.menu_state = "pause_menu"
return action config.needs_redraw = True
logger.debug("Retour à pause_menu depuis remap_controls")
last_repeat_time = config.repeat_start_time - REPEAT_INTERVAL # Gestion de la répétition automatique (relâchement)
if event.type in (pygame.KEYUP, pygame.JOYBUTTONUP, pygame.JOYAXISMOTION, pygame.JOYHATMOTION):
if event.type == pygame.JOYAXISMOTION and abs(event.value) > 0.5:
return action
if event.type == pygame.JOYHATMOTION and event.value != (0, 0):
return action
config.repeat_action = None
config.repeat_key = None
config.repeat_start_time = 0
logger.debug("Répétition arrêtée")
return action
async def handle_repeat_actions():
"""Gère la répétition automatique des actions."""
current_time = pygame.time.get_ticks()
if config.repeat_action and config.repeat_key and current_time > config.repeat_start_time:
if current_time - config.repeat_last_action > REPEAT_ACTION_DEBOUNCE:
logger.debug(f"Répétition action: {config.repeat_action}")
event_dict = {
"type": pygame.KEYDOWN if isinstance(config.repeat_key, int) and config.repeat_key < 1000 else pygame.JOYBUTTONDOWN if isinstance(config.repeat_key, int) else pygame.JOYAXISMOTION if isinstance(config.repeat_key, tuple) and len(config.repeat_key) == 2 else pygame.JOYHATMOTION,
"key": config.repeat_key if isinstance(config.repeat_key, int) and config.repeat_key < 1000 else None,
"button": config.repeat_key if isinstance(config.repeat_key, int) and config.repeat_key >= 1000 else None,
"axis": config.repeat_key[0] if isinstance(config.repeat_key, tuple) and len(config.repeat_key) == 2 else None,
"value": config.repeat_key[1] if isinstance(config.repeat_key, tuple) and len(config.repeat_key) == 2 else config.repeat_key if isinstance(config.repeat_key, tuple) else None
}
handle_controls(event_dict, None, None, None)
config.repeat_last_action = current_time config.repeat_last_action = current_time
if config.menu_state == "game": config.repeat_start_time = current_time + REPEAT_INTERVAL
if config.repeat_action == "down":
config.current_game = min(config.current_game + 1, len(config.filtered_games) - 1)
if config.current_game >= config.scroll_offset + config.visible_games:
config.scroll_offset += 1
config.needs_redraw = True
elif config.repeat_action == "up":
config.current_game = max(config.current_game - 1, 0)
if config.current_game < config.scroll_offset:
config.scroll_offset -= 1
config.needs_redraw = True
elif config.repeat_action == "page_down":
config.current_game = min(config.current_game + config.visible_games, len(config.filtered_games) - 1)
config.scroll_offset = min(config.scroll_offset + config.visible_games, len(config.filtered_games) - config.visible_games)
config.needs_redraw = True
elif config.repeat_action == "page_up":
config.current_game = max(config.current_game - config.visible_games, 0)
config.scroll_offset = max(config.scroll_offset - config.visible_games, 0)
config.needs_redraw = True
elif config.menu_state == "platform":
max_index = min(9, len(config.platforms) - config.current_page * 9) - 1
current_grid_index = config.selected_platform - config.current_page * 9
row = current_grid_index // 3
if config.repeat_action == "down":
if current_grid_index + 3 <= max_index:
config.selected_platform += 3
config.needs_redraw = True
elif config.repeat_action == "up":
if current_grid_index - 3 >= 0:
config.selected_platform -= 3
config.needs_redraw = True
elif config.repeat_action == "left":
if current_grid_index % 3 != 0:
config.selected_platform -= 1
config.needs_redraw = True
elif config.current_page > 0:
config.current_page -= 1
config.selected_platform = config.current_page * 9 + row * 3 + 2
if config.selected_platform >= len(config.platforms):
config.selected_platform = len(config.platforms) - 1
config.needs_redraw = True
elif config.repeat_action == "right":
if current_grid_index % 3 != 2 and current_grid_index < max_index:
config.selected_platform += 1
config.needs_redraw = True
elif (config.current_page + 1) * 9 < len(config.platforms):
config.current_page += 1
config.selected_platform = config.current_page * 9 + row * 3
if config.selected_platform >= len(config.platforms):
config.selected_platform = len(config.platforms) - 1
config.needs_redraw = True
config.repeat_start_time = last_repeat_time + REPEAT_INTERVAL
if config.repeat_start_time < current_time:
config.repeat_start_time = current_time + REPEAT_INTERVAL
return action

View File

@@ -3,6 +3,7 @@ import config
import math import math
from utils import truncate_text_end, wrap_text, load_system_image, load_games from utils import truncate_text_end, wrap_text, load_system_image, load_games
import logging import logging
from history import load_history # Ajout de l'import
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -223,18 +224,18 @@ def draw_game_list(screen):
line_height = config.font.get_height() + 10 line_height = config.font.get_height() + 10
margin_top_bottom = 20 margin_top_bottom = 20
extra_margin_top = 5 # Marge supplémentaire pour éviter le chevauchement avec le titre extra_margin_top = 5
extra_margin_bottom = 40 # Marge supplémentaire en bas pour éloigner du texte des contrôles extra_margin_bottom = 40
title_height = max(config.title_font.get_height(), config.search_font.get_height(), config.small_font.get_height()) + 20 # Hauteur du titre avec padding réduit title_height = max(config.title_font.get_height(), config.search_font.get_height(), config.small_font.get_height()) + 20
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
games_per_page = available_height // line_height games_per_page = available_height // line_height
config.visible_games = games_per_page # Mettre à jour config.visible_games
max_text_width = max([config.font.size(truncate_text_end(game[0] if isinstance(game, (list, tuple)) else game, config.font, config.screen_width - 80))[0] for game in games], default=300) max_text_width = max([config.font.size(truncate_text_end(game[0] if isinstance(game, (list, tuple)) else game, config.font, config.screen_width - 80))[0] for game in games], default=300)
rect_width = max_text_width + 40 rect_width = max_text_width + 40
rect_height = games_per_page * line_height + 2 * margin_top_bottom rect_height = games_per_page * line_height + 2 * margin_top_bottom
rect_x = (config.screen_width - rect_width) // 2 rect_x = (config.screen_width - rect_width) // 2
rect_y = title_height + extra_margin_top + (config.screen_height - title_height - extra_margin_top - extra_margin_bottom - rect_height) // 2 rect_y = title_height + extra_margin_top + (config.screen_height - title_height - extra_margin_top - extra_margin_bottom - rect_height) // 2
# Limiter scroll_offset pour éviter l'espace vide
config.scroll_offset = max(0, min(config.scroll_offset, max(0, len(games) - games_per_page))) config.scroll_offset = max(0, min(config.scroll_offset, max(0, len(games) - games_per_page)))
if config.current_game < config.scroll_offset: if config.current_game < config.scroll_offset:
config.scroll_offset = config.current_game config.scroll_offset = config.current_game
@@ -243,7 +244,6 @@ def draw_game_list(screen):
screen.blit(OVERLAY, (0, 0)) screen.blit(OVERLAY, (0, 0))
# Afficher le titre ou le texte de recherche/filtre
if config.search_mode: if config.search_mode:
search_text = f"Filtrer : {config.search_query}_" search_text = f"Filtrer : {config.search_query}_"
title_surface = config.search_font.render(search_text, True, (255, 255, 255)) title_surface = config.search_font.render(search_text, True, (255, 255, 255))
@@ -272,7 +272,6 @@ def draw_game_list(screen):
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10) pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
screen.blit(title_surface, title_rect) screen.blit(title_surface, title_rect)
# Afficher le rectangle de fond et la liste des jeux
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10) pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10) pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
@@ -288,7 +287,126 @@ def draw_game_list(screen):
draw_scrollbar(screen) draw_scrollbar(screen)
if config.search_mode and config.is_non_pc: if config.search_mode and config.is_non_pc:
draw_virtual_keyboard(screen) draw_virtual_keyboard(screen)
def draw_history_list(screen):
"""Affiche l'historique des téléchargements sous forme de tableau avec système, nom du jeu et état."""
logger.debug("Début de draw_history_list")
history = config.history if hasattr(config, 'history') else load_history()
history_count = len(history)
if not history:
logger.debug("Aucun historique disponible")
message = "Aucun téléchargement dans l'historique"
lines = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(lines) * line_height
margin_top_bottom = 20
rect_height = text_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in lines], default=300)
rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
screen.blit(OVERLAY, (0, 0))
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i, line in enumerate(lines):
text_surface = config.font.render(line, True, (255, 255, 255))
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text_surface, text_rect)
return
line_height = config.small_font.get_height() + 10
margin_top_bottom = 20
extra_margin_top = 5
extra_margin_bottom = 40
title_height = config.title_font.get_height() + 20
available_height = config.screen_height - title_height - extra_margin_top - extra_margin_bottom - 2 * margin_top_bottom
items_per_page = available_height // line_height
config.visible_history_items = items_per_page # Mettre à jour config.visible_history_items
# Calculer la largeur des colonnes
col_platform_width = config.screen_width // 4 # ~25% pour le système
col_game_width = config.screen_width // 2 # ~50% pour le nom du jeu
col_status_width = config.screen_width // 4 # ~25% pour l'état
max_text_width = col_platform_width + col_game_width + col_status_width
rect_width = max_text_width + 40
rect_height = items_per_page * line_height + 2 * margin_top_bottom
rect_x = (config.screen_width - rect_width) // 2
rect_y = title_height + extra_margin_top + (config.screen_height - title_height - extra_margin_top - extra_margin_bottom - rect_height) // 2
config.history_scroll_offset = max(0, min(config.history_scroll_offset, max(0, len(history) - items_per_page)))
if config.current_history_item < config.history_scroll_offset:
config.history_scroll_offset = config.current_history_item
elif config.current_history_item >= config.history_scroll_offset + items_per_page:
config.history_scroll_offset = config.current_history_item - items_per_page + 1
screen.blit(OVERLAY, (0, 0))
title_text = f"Historique des téléchargements ({history_count})"
title_surface = config.title_font.render(title_text, True, (255, 255, 255))
title_rect = title_surface.get_rect(center=(config.screen_width // 2, title_surface.get_height() // 2 + 10))
title_rect_inflated = title_rect.inflate(40, 20)
title_rect_inflated.topleft = ((config.screen_width - title_rect_inflated.width) // 2, 0)
pygame.draw.rect(screen, (50, 50, 50, 200), title_rect_inflated, border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
screen.blit(title_surface, title_rect)
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
# En-têtes du tableau
headers = ["Système", "Nom du jeu", "État"]
header_y = rect_y + margin_top_bottom - line_height // 2
header_x_positions = [
rect_x + 20 + col_platform_width // 2,
rect_x + 20 + col_platform_width + col_game_width // 2,
rect_x + 20 + col_platform_width + col_game_width + col_status_width // 2
]
for header, x_pos in zip(headers, header_x_positions):
text_surface = config.small_font.render(header, True, (255, 255, 255))
text_rect = text_surface.get_rect(center=(x_pos, header_y))
screen.blit(text_surface, text_rect)
# Lignes du tableau
for i in range(config.history_scroll_offset, min(config.history_scroll_offset + items_per_page, len(history))):
entry = history[i]
platform = entry["platform"]
game_name = entry["game_name"]
status = entry["status"]
color = (0, 150, 255) if i == config.current_history_item else (255, 255, 255)
platform_text = truncate_text_end(platform, config.small_font, col_platform_width - 10)
game_text = truncate_text_end(game_name, config.small_font, col_game_width - 10)
status_text = truncate_text_end(status, config.small_font, col_status_width - 10)
y_pos = rect_y + margin_top_bottom + (i - config.history_scroll_offset + 1) * line_height + line_height // 2
platform_surface = config.small_font.render(platform_text, True, color)
game_surface = config.small_font.render(game_text, True, color)
status_surface = config.small_font.render(status_text, True, color)
platform_rect = platform_surface.get_rect(center=(header_x_positions[0], y_pos))
game_rect = game_surface.get_rect(center=(header_x_positions[1], y_pos))
status_rect = status_surface.get_rect(center=(header_x_positions[2], y_pos))
screen.blit(platform_surface, platform_rect)
screen.blit(game_surface, game_rect)
screen.blit(status_surface, status_rect)
logger.debug(f"Entrée historique affichée : index={i}, platform={platform_text}, game={game_text}, status={status_text}, selected={i == config.current_history_item}")
draw_history_scrollbar(screen)
def draw_history_scrollbar(screen):
"""Affiche la barre de défilement pour l'historique."""
if len(config.history) <= config.visible_history_items:
return
game_area_height = config.screen_height - 150
scrollbar_height = game_area_height * (config.visible_history_items / len(config.history))
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.history_scroll_offset / max(1, len(config.history) - config.visible_history_items))
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
def draw_virtual_keyboard(screen): def draw_virtual_keyboard(screen):
"""Affiche un clavier virtuel pour la saisie dans search_mode, centré verticalement.""" """Affiche un clavier virtuel pour la saisie dans search_mode, centré verticalement."""
keyboard_layout = [ keyboard_layout = [
@@ -352,10 +470,10 @@ def draw_progress_screen(screen):
text_height = len(title_lines) * line_height text_height = len(title_lines) * line_height
margin_top_bottom = 20 margin_top_bottom = 20
bar_height = int(config.screen_height * 0.0278) # ~30px pour 1080p bar_height = int(config.screen_height * 0.0278) # ~30px pour 1080p
percent_height = line_height # Hauteur pour le texte de progression percent_height = line_height
rect_height = text_height + bar_height + percent_height + 3 * margin_top_bottom rect_height = text_height + bar_height + percent_height + 3 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in title_lines], default=300) max_text_width = max([config.font.size(line)[0] for line in title_lines], default=300)
bar_width = max_text_width # Ajuster la barre à la largeur du texte bar_width = max_text_width
rect_width = max_text_width + 40 rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2 rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2 rect_y = (config.screen_height - rect_height) // 2
@@ -389,7 +507,7 @@ def draw_progress_screen(screen):
percent_rect = percent_render.get_rect(center=(config.screen_width // 2, text_y + i * line_height + line_height // 2)) percent_rect = percent_render.get_rect(center=(config.screen_width // 2, text_y + i * line_height + line_height // 2))
screen.blit(percent_render, percent_rect) screen.blit(percent_render, percent_rect)
logger.debug(f"Texte de progression affiché : texte={line}, position={percent_rect}, taille={percent_render.get_size()}") logger.debug(f"Texte de progression affiché : texte={line}, position={percent_rect}, taille={percent_render.get_size()}")
def draw_scrollbar(screen): def draw_scrollbar(screen):
"""Affiche la barre de défilement à droite de lécran.""" """Affiche la barre de défilement à droite de lécran."""
if len(config.filtered_games) <= config.visible_games: if len(config.filtered_games) <= config.visible_games:
@@ -400,11 +518,11 @@ def draw_scrollbar(screen):
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.scroll_offset / max(1, len(config.filtered_games) - config.visible_games)) scrollbar_y = 120 + (game_area_height - scrollbar_height) * (config.scroll_offset / max(1, len(config.filtered_games) - config.visible_games))
pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height)) pygame.draw.rect(screen, (255, 255, 255), (config.screen_width - 25, scrollbar_y, 15, scrollbar_height))
def draw_confirm_dialog(screen): def draw_clear_history_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour quitter.""" """Affiche la boîte de dialogue de confirmation pour vider l'historique."""
screen.blit(OVERLAY, (0, 0)) screen.blit(OVERLAY, (0, 0))
message = "Voulez-vous vraiment quitter ?" message = "Vider l'historique ?"
wrapped_message = wrap_text(message, config.font, config.screen_width - 80) wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5 line_height = config.font.get_height() + 5
text_height = len(wrapped_message) * line_height text_height = len(wrapped_message) * line_height
@@ -424,14 +542,14 @@ def draw_confirm_dialog(screen):
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect) screen.blit(text, text_rect)
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_selection == 1 else (255, 255, 255)) yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_clear_selection == 1 else (255, 255, 255))
no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_selection == 0 else (255, 255, 255)) no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_clear_selection == 0 else (255, 255, 255))
yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2)) yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2))
no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2)) no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2))
screen.blit(yes_text, yes_rect) screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect) screen.blit(no_text, no_rect)
def draw_popup_message(screen, message, is_error): def draw_popup_message(screen, message, is_error):
"""Affiche une popup avec un message de résultat.""" """Affiche une popup avec un message de résultat."""
screen.blit(OVERLAY, (0, 0)) screen.blit(OVERLAY, (0, 0))
@@ -526,12 +644,12 @@ def draw_extension_warning(screen):
def draw_controls(screen, menu_state): def draw_controls(screen, menu_state):
"""Affiche les contrôles sur une seule ligne en bas de lécran pour tous les états du menu.""" """Affiche les contrôles sur une seule ligne en bas de lécran pour tous les états du menu."""
start_button = get_control_display('start', 'START') start_button = get_control_display('start', 'START')
control_text = f"Menu {menu_state} - {start_button} : Options - Controls" control_text = f"Menu {menu_state} - {start_button} : Options - History - Controls"
max_width = config.screen_width - 40 max_width = config.screen_width - 40
wrapped_controls = wrap_text(control_text, config.small_font, max_width) wrapped_controls = wrap_text(control_text, config.small_font, max_width)
line_height = config.small_font.get_height() + 5 line_height = config.small_font.get_height() + 5
rect_height = len(wrapped_controls) * line_height + 20 rect_height = len(wrapped_controls) * line_height + 20
rect_y = config.screen_height - rect_height - 40 # Marge inférieure de 40px rect_y = config.screen_height - rect_height - 5
for i, line in enumerate(wrapped_controls): for i, line in enumerate(wrapped_controls):
text_surface = config.small_font.render(line, True, (255, 255, 255)) text_surface = config.small_font.render(line, True, (255, 255, 255))
@@ -561,12 +679,13 @@ def draw_validation_transition(screen, platform_index):
pygame.time.wait(10) pygame.time.wait(10)
def draw_pause_menu(screen, selected_option): def draw_pause_menu(screen, selected_option):
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Quitter.""" """Dessine le menu pause avec les options Aide, Configurer contrôles, Historique, Quitter."""
screen.blit(OVERLAY, (0, 0)) screen.blit(OVERLAY, (0, 0))
options = [ options = [
"Controls", "Controls",
"Remap controls", "Remap controls",
"History",
"Quit" "Quit"
] ]
@@ -629,7 +748,6 @@ def draw_controls_help(screen, previous_state):
common_controls["space"]() common_controls["space"]()
] if config.search_mode and config.is_non_pc else []), ] if config.search_mode and config.is_non_pc else []),
*( [ *( [
f"Saisir texte : Filtrer" if config.search_mode else
f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}", f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}",
f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}", f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}",
common_controls["filter"]("Filtrer") common_controls["filter"]("Filtrer")
@@ -650,6 +768,14 @@ def draw_controls_help(screen, previous_state):
], ],
"extension_warning": [ "extension_warning": [
common_controls["confirm"]("Confirmer") common_controls["confirm"]("Confirmer")
],
"history": [
common_controls["confirm"]("Retélécharger"),
common_controls["cancel"]("Retour"),
common_controls["progress"]("Vider l'historique"),
f"{common_controls['up']('Naviguer')} / {common_controls['down']('Naviguer')}",
f"{common_controls['page_up']('Page')} / {common_controls['page_down']('Page')}",
common_controls["start"]()
] ]
} }
@@ -681,4 +807,120 @@ def draw_controls_help(screen, previous_state):
for i, line in enumerate(wrapped_controls): for i, line in enumerate(wrapped_controls):
text = config.font.render(line, True, (255, 255, 255)) text = config.font.render(line, True, (255, 255, 255))
text_rect = text.get_rect(center=(config.screen_width // 2, popup_y + 40 + i * line_height)) text_rect = text.get_rect(center=(config.screen_width // 2, popup_y + 40 + i * line_height))
screen.blit(text, text_rect) screen.blit(text, text_rect)
def draw_history(screen):
"""Affiche la liste de l'historique des téléchargements."""
if not config.history:
text = config.font.render("Aucun téléchargement dans l'historique", True, (255, 255, 255))
screen.blit(text, (config.screen_width // 2 - text.get_width() // 2, config.screen_height // 2))
return
# Calculer le nombre d'éléments visibles
item_height = config.small_font.get_height() + 10
config.visible_history_items = (config.screen_height - 200) // item_height
max_scroll = max(0, len(config.history) - config.visible_history_items)
config.history_scroll_offset = max(0, min(config.history_scroll_offset, max_scroll))
# Cadre semi-transparent
panel_width = config.screen_width - 100
panel_height = config.visible_history_items * item_height + 20
panel_x = (config.screen_width - panel_width) // 2
panel_y = (config.screen_height - panel_height) // 2
panel_surface = pygame.Surface((panel_width, panel_height), pygame.SRCALPHA)
panel_surface.fill((0, 0, 0, 128))
screen.blit(panel_surface, (panel_x, panel_y))
pygame.draw.rect(screen, (255, 255, 255), (panel_x, panel_y, panel_width, panel_height), 2)
# Afficher les colonnes
headers = ["Système", "Nom du jeu", "État"]
col_widths = [panel_width // 4, panel_width // 2, panel_width // 4]
header_y = panel_y + 10
for i, header in enumerate(headers):
text = config.small_font.render(header, True, (255, 255, 255))
screen.blit(text, (panel_x + sum(col_widths[:i]) + 10, header_y))
# Afficher les entrées
start_index = config.history_scroll_offset
end_index = min(start_index + config.visible_history_items, len(config.history))
for i, entry in enumerate(config.history[start_index:end_index]):
y = panel_y + 40 + i * item_height
color = (255, 255, 0) if i + start_index == config.current_history_item else (255, 255, 255)
system = config.platform_names.get(entry["platform"], entry["platform"])
system_text = truncate_text_end(system, config.small_font, col_widths[0] - 20)
game_text = truncate_text_end(entry["game_name"], config.small_font, col_widths[1] - 20)
status_text = entry["status"]
texts = [system_text, game_text, status_text]
for j, text in enumerate(texts):
rendered = config.small_font.render(text, True, color)
screen.blit(rendered, (panel_x + sum(col_widths[:j]) + 10, y))
if i + start_index == config.current_history_item:
pygame.draw.rect(screen, (255, 255, 0), (panel_x + 5, y - 5, panel_width - 10, item_height), 1)
# Barre de défilement
if len(config.history) > config.visible_history_items:
draw_scrollbar(screen, config.history_scroll_offset, len(config.history), config.visible_history_items, panel_x + panel_width - 10, panel_y, panel_height)
def draw_confirm_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour quitter."""
screen.blit(OVERLAY, (0, 0))
message = "Voulez-vous vraiment quitter ?"
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(wrapped_message) * line_height
button_height = line_height + 20
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in wrapped_message], default=300)
rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, (255, 255, 255))
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect)
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_selection == 1 else (255, 255, 255))
no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_selection == 0 else (255, 255, 255))
yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2))
no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2))
screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect)
def draw_clear_history_dialog(screen):
"""Affiche la boîte de dialogue de confirmation pour vider l'historique."""
screen.blit(OVERLAY, (0, 0))
message = "Vider l'historique ?"
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
line_height = config.font.get_height() + 5
text_height = len(wrapped_message) * line_height
button_height = line_height + 20
margin_top_bottom = 20
rect_height = text_height + button_height + 2 * margin_top_bottom
max_text_width = max([config.font.size(line)[0] for line in wrapped_message], default=300)
rect_width = max_text_width + 40
rect_x = (config.screen_width - rect_width) // 2
rect_y = (config.screen_height - rect_height) // 2
pygame.draw.rect(screen, (50, 50, 50, 200), (rect_x, rect_y, rect_width, rect_height), border_radius=10)
pygame.draw.rect(screen, (255, 255, 255), (rect_x, rect_y, rect_width, rect_height), 2, border_radius=10)
for i, line in enumerate(wrapped_message):
text = config.font.render(line, True, (255, 255, 255))
text_rect = text.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
screen.blit(text, text_rect)
yes_text = config.font.render("Oui", True, (0, 150, 255) if config.confirm_clear_selection == 1 else (255, 255, 255))
no_text = config.font.render("Non", True, (0, 150, 255) if config.confirm_clear_selection == 0 else (255, 255, 255))
yes_rect = yes_text.get_rect(center=(config.screen_width // 2 - 100, rect_y + text_height + margin_top_bottom + line_height // 2))
no_rect = no_text.get_rect(center=(config.screen_width // 2 + 100, rect_y + text_height + margin_top_bottom + line_height // 2))
screen.blit(yes_text, yes_rect)
screen.blit(no_text, no_rect)

View File

@@ -1,7 +1,8 @@
import json import json
import os import os
import logging import logging
from config import HISTORY_FILE_PATH import config
from config import HISTORY_PATH
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -10,7 +11,7 @@ DEFAULT_HISTORY_PATH = "/userdata/saves/ports/rgsx/history.json"
def init_history(): def init_history():
"""Initialise le fichier history.json s'il n'existe pas.""" """Initialise le fichier history.json s'il n'existe pas."""
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH) history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
if not os.path.exists(history_path): if not os.path.exists(history_path):
try: try:
os.makedirs(os.path.dirname(history_path), exist_ok=True) os.makedirs(os.path.dirname(history_path), exist_ok=True)
@@ -24,7 +25,7 @@ def init_history():
def load_history(): def load_history():
"""Charge l'historique depuis history.json.""" """Charge l'historique depuis history.json."""
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH) history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
try: try:
with open(history_path, "r") as f: with open(history_path, "r") as f:
history = json.load(f) history = json.load(f)
@@ -40,7 +41,7 @@ def load_history():
def save_history(history): def save_history(history):
"""Sauvegarde l'historique dans history.json.""" """Sauvegarde l'historique dans history.json."""
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH) history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
try: try:
with open(history_path, "w") as f: with open(history_path, "w") as f:
json.dump(history, f, indent=2) json.dump(history, f, indent=2)
@@ -48,7 +49,7 @@ def save_history(history):
except Exception as e: except Exception as e:
logger.error(f"Erreur lors de l'écriture de {history_path} : {e}") logger.error(f"Erreur lors de l'écriture de {history_path} : {e}")
def add_download_to_history(platform, game_name, status): def add_to_history(platform, game_name, status):
"""Ajoute une entrée à l'historique.""" """Ajoute une entrée à l'historique."""
history = load_history() history = load_history()
history.append({ history.append({
@@ -61,7 +62,7 @@ def add_download_to_history(platform, game_name, status):
def clear_history(): def clear_history():
"""Vide l'historique.""" """Vide l'historique."""
history_path = getattr(config, 'HISTORY_FILE_PATH', DEFAULT_HISTORY_PATH) history_path = getattr(config, 'HISTORY_PATH', DEFAULT_HISTORY_PATH)
try: try:
with open(history_path, "w") as f: with open(history_path, "w") as f:
json.dump([], f) json.dump([], f)

View File

@@ -6,10 +6,12 @@ import threading
import pygame import pygame
import zipfile import zipfile
import json import json
import time
from urllib.parse import urljoin, unquote from urllib.parse import urljoin, unquote
import asyncio import asyncio
import config import config
from utils import sanitize_filename from utils import sanitize_filename
from history import add_to_history, load_history
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -330,12 +332,12 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
if not success: if not success:
raise Exception(f"Échec de l'extraction de l'archive: {msg}") raise Exception(f"Échec de l'extraction de l'archive: {msg}")
result[0] = True result[0] = True
result[1] = f"Téléchargé et extrait : {game_name}" result[1] = f"Downloaded / extracted : {game_name}"
else: else:
os.chmod(dest_path, 0o644) os.chmod(dest_path, 0o644)
logger.debug(f"Téléchargement terminé: {dest_path}") logger.debug(f"Téléchargement terminé: {dest_path}")
result[0] = True result[0] = True
result[1] = f"Téléchargé : {game_name}" result[1] = f"Download_OK : {game_name}"
except Exception as e: except Exception as e:
logger.error(f"Erreur téléchargement {url}: {str(e)}") logger.error(f"Erreur téléchargement {url}: {str(e)}")
if url in config.download_progress: if url in config.download_progress:
@@ -348,7 +350,15 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
finally: finally:
logger.debug(f"Thread téléchargement terminé pour {url}") logger.debug(f"Thread téléchargement terminé pour {url}")
with lock: with lock:
config.download_result_message = result[1]
config.download_result_error = not result[0]
config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result"
config.needs_redraw = True # Forcer le redraw config.needs_redraw = True # Forcer le redraw
# Enregistrement dans l'historique
add_to_history(platform, game_name, "Download_OK" if result[0] else "Erreur")
config.history = load_history() # Recharger l'historique
logger.debug(f"Enregistrement dans l'historique: platform={platform}, game_name={game_name}, status={'Download_OK' if result[0] else 'Erreur'}")
thread = threading.Thread(target=download_thread) thread = threading.Thread(target=download_thread)
logger.debug(f"Démarrage thread pour {url}") logger.debug(f"Démarrage thread pour {url}")
@@ -359,37 +369,30 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
thread.join() thread.join()
logger.debug(f"Thread rejoint pour {url}") logger.debug(f"Thread rejoint pour {url}")
with threading.Lock():
config.download_result_message = result[1]
config.download_result_error = not result[0]
config.download_result_start_time = pygame.time.get_ticks()
config.menu_state = "download_result"
config.needs_redraw = True # Forcer le redraw
logger.debug(f"Transition vers download_result, message={result[1]}, erreur={not result[0]}")
return result[0], result[1] return result[0], result[1]
def check_extension_before_download(url, platform, game_name): def check_extension_before_download(game_name, platform, url):
"""Vérifie l'extension avant de lancer le téléchargement.""" """Vérifie l'extension avant de lancer le téléchargement et retourne un tuple de 4 éléments."""
try: try:
sanitized_name = sanitize_filename(game_name) sanitized_name = sanitize_filename(game_name)
extensions_data = load_extensions_json() extensions_data = load_extensions_json()
if not extensions_data: if not extensions_data:
logger.error(f"Fichier {JSON_EXTENSIONS} vide ou introuvable") logger.error(f"Fichier {JSON_EXTENSIONS} vide ou introuvable")
return False, "Fichier de configuration des extensions introuvable", False return None
is_supported = is_extension_supported(sanitized_name, platform, extensions_data) is_supported = is_extension_supported(sanitized_name, platform, extensions_data)
extension = os.path.splitext(sanitized_name)[1].lower() extension = os.path.splitext(sanitized_name)[1].lower()
is_archive = extension in (".zip", ".rar") is_archive = extension in (".zip", ".rar")
if is_supported: if is_supported:
logger.debug(f"L'extension de {sanitized_name} est supportée pour {platform}") logger.debug(f"L'extension de {sanitized_name} est supportée pour {platform}")
return True, "", False return (url, platform, game_name, False)
else: else:
if is_archive: if is_archive:
logger.debug(f"Fichier {extension.upper()} détecté pour {sanitized_name}, extraction automatique prévue") logger.debug(f"Fichier {extension.upper()} détecté pour {sanitized_name}, extraction automatique prévue")
return False, f"Fichiers {extension.upper()} non supportés par cette plateforme, extraction automatique après le téléchargement.", True return (url, platform, game_name, True)
logger.debug(f"L'extension de {sanitized_name} n'est pas supportée pour {platform}") logger.debug(f"L'extension de {sanitized_name} n'est pas supportée pour {platform}")
return False, f"L'extension de {sanitized_name} n'est pas supportée pour {platform}", False return None
except Exception as e: except Exception as e:
logger.error(f"Erreur vérification extension {url}: {str(e)}") logger.error(f"Erreur vérification extension {url}: {str(e)}")
return False, str(e), False return None