forked from Mirrors/RGSX
Ajout fonction 1fichier avec api + fonction de mise a jour du cache des jeux et corrections de bugs
This commit is contained in:
178
__main__.py
178
__main__.py
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
os.environ["SDL_FBDEV"] = "/dev/fb0"
|
||||
import pygame
|
||||
import pygame # type: ignore
|
||||
import asyncio
|
||||
import platform
|
||||
import subprocess
|
||||
@@ -9,7 +9,7 @@ import logging
|
||||
import requests
|
||||
import sys
|
||||
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, draw_history, draw_clear_history_dialog
|
||||
from display import init_display, draw_loading_screen, draw_error_screen, draw_platform_grid, draw_progress_screen, draw_controls, draw_gradient, draw_virtual_keyboard, draw_popup_message, draw_extension_warning, draw_pause_menu, draw_controls_help, draw_game_list, draw_history_list, draw_clear_history_dialog, draw_confirm_dialog, draw_redownload_game_cache_dialog, draw_popup
|
||||
from network import test_internet, download_rom, check_extension_before_download, extract_zip
|
||||
from controls import handle_controls, validate_menu_state
|
||||
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, ACTIONS
|
||||
@@ -44,8 +44,8 @@ OTA_data_ZIP = f"{OTA_SERVER_URL}/rgsx-data.zip"
|
||||
|
||||
# Constantes pour la répétition automatique dans pause_menu
|
||||
REPEAT_DELAY = 300 # Délai initial avant répétition (ms)
|
||||
REPEAT_INTERVAL = 100 # Intervalle entre répétitions (ms)
|
||||
REPEAT_ACTION_DEBOUNCE = 50 # Délai anti-rebond pour répétitions (ms)
|
||||
REPEAT_INTERVAL = 150 # Intervalle entre répétitions (ms), augmenté pour réduire la fréquence
|
||||
REPEAT_ACTION_DEBOUNCE = 100 # Délai anti-rebond pour répétitions (ms), augmenté pour éviter les répétitions excessives
|
||||
|
||||
# Initialisation de Pygame et des polices
|
||||
pygame.init()
|
||||
@@ -276,6 +276,20 @@ async def main():
|
||||
if config.menu_state == "download_progress" and current_time - last_redraw_time >= 100:
|
||||
config.needs_redraw = True
|
||||
last_redraw_time = current_time
|
||||
|
||||
# Dans __main__.py, dans la boucle principale
|
||||
current_time = pygame.time.get_ticks()
|
||||
delta_time = current_time - config.last_frame_time
|
||||
config.last_frame_time = current_time
|
||||
if config.menu_state == "restart_popup" and config.popup_timer > 0:
|
||||
config.popup_timer -= delta_time
|
||||
config.needs_redraw = True
|
||||
if config.popup_timer <= 0:
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.popup_message = ""
|
||||
config.popup_timer = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Fermeture automatique du popup, retour à {config.menu_state}")
|
||||
|
||||
# Gestion des événements
|
||||
events = pygame.event.get()
|
||||
@@ -298,117 +312,15 @@ async def main():
|
||||
if config.menu_state not in ["pause_menu", "controls_help", "controls_mapping", "history", "confirm_clear_history"]:
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "pause_menu"
|
||||
config.selected_pause_option = 0
|
||||
config.selected_option = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Ouverture menu pause depuis {config.previous_menu_state}")
|
||||
continue
|
||||
|
||||
|
||||
if config.menu_state == "pause_menu":
|
||||
current_time = pygame.time.get_ticks()
|
||||
if event.type in (pygame.KEYDOWN, pygame.JOYBUTTONDOWN, pygame.JOYAXISMOTION, pygame.JOYHATMOTION):
|
||||
up_config = config.controls_config.get("up", {})
|
||||
down_config = config.controls_config.get("down", {})
|
||||
confirm_config = config.controls_config.get("confirm", {})
|
||||
cancel_config = config.controls_config.get("cancel", {})
|
||||
|
||||
if current_time - config.last_state_change_time < config.debounce_delay:
|
||||
continue
|
||||
|
||||
if (
|
||||
(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.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 == tuple(up_config.get("value")))
|
||||
):
|
||||
config.selected_pause_option = max(0, config.selected_pause_option - 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
|
||||
logger.debug(f"Menu pause: Haut, selected_option={config.selected_pause_option}, repeat_action={config.repeat_action}")
|
||||
elif (
|
||||
(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.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 == tuple(down_config.get("value")))
|
||||
):
|
||||
config.selected_pause_option = min(3, config.selected_pause_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
|
||||
logger.debug(f"Menu pause: Bas, selected_option={config.selected_pause_option}, repeat_action={config.repeat_action}")
|
||||
elif (
|
||||
(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.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 == tuple(confirm_config.get("value")))
|
||||
):
|
||||
if config.selected_pause_option == 0:
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.menu_state = "controls_help"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Menu pause: Aide sélectionnée")
|
||||
elif config.selected_pause_option == 1:
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
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", "history"] else "platform"
|
||||
config.controls_config = load_controls_config()
|
||||
logger.debug(f"Mappage des contrôles terminé, retour à {config.menu_state}")
|
||||
else:
|
||||
config.menu_state = "error"
|
||||
config.error_message = "Échec du mappage des contrôles"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Échec du mappage des contrôles")
|
||||
elif config.selected_pause_option == 2:
|
||||
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é")
|
||||
elif (
|
||||
(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.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.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
|
||||
logger.debug(f"Menu pause: Annulation, retour à {config.menu_state}")
|
||||
|
||||
elif event.type in (pygame.KEYUP, pygame.JOYBUTTONUP):
|
||||
if (
|
||||
(event.type == pygame.KEYUP and is_input_matched(event, "up") or is_input_matched(event, "down")) or
|
||||
(event.type == pygame.JOYBUTTONUP and is_input_matched(event, "up") or is_input_matched(event, "down"))
|
||||
):
|
||||
config.repeat_action = None
|
||||
config.repeat_key = None
|
||||
config.repeat_start_time = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug("Menu pause: Touche relâchée, répétition arrêtée")
|
||||
|
||||
if config.repeat_action in ["up", "down"] and current_time >= config.repeat_start_time:
|
||||
if current_time - config.repeat_last_action < REPEAT_ACTION_DEBOUNCE:
|
||||
continue
|
||||
config.repeat_last_action = current_time
|
||||
if config.repeat_action == "up":
|
||||
config.selected_pause_option = max(0, config.selected_pause_option - 1)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Menu pause: Répétition haut, selected_option={config.selected_pause_option}")
|
||||
elif config.repeat_action == "down":
|
||||
config.selected_pause_option = min(3, config.selected_pause_option + 1)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Menu pause: Répétition bas, selected_option={config.selected_pause_option}")
|
||||
config.repeat_start_time = current_time + REPEAT_INTERVAL
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Événement transmis à handle_controls dans pause_menu: {event.type}")
|
||||
|
||||
continue
|
||||
|
||||
@@ -432,6 +344,11 @@ async def main():
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Événement transmis à handle_controls dans confirm_clear_history: {event.type}")
|
||||
continue
|
||||
if config.menu_state == "redownload_game_cache":
|
||||
action = handle_controls(event, sources, joystick, screen)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Événement transmis à handle_controls dans redownload_game_cache: {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)
|
||||
@@ -522,43 +439,50 @@ async def main():
|
||||
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
|
||||
if config.menu_state == "controls_mapping":
|
||||
draw_controls_mapping(screen, ACTIONS[0], None, False, 0.0)
|
||||
logger.debug("Rendu initial de draw_controls_mapping")
|
||||
# logger.debug("Rendu initial de draw_controls_mapping")
|
||||
elif config.menu_state == "loading":
|
||||
draw_loading_screen(screen)
|
||||
logger.debug("Rendu de draw_loading_screen")
|
||||
# logger.debug("Rendu de draw_loading_screen")
|
||||
elif config.menu_state == "error":
|
||||
draw_error_screen(screen)
|
||||
logger.debug("Rendu de draw_error_screen")
|
||||
# logger.debug("Rendu de draw_error_screen")
|
||||
elif config.menu_state == "platform":
|
||||
draw_platform_grid(screen)
|
||||
logger.debug("Rendu de draw_platform_grid")
|
||||
# logger.debug("Rendu de draw_platform_grid")
|
||||
elif config.menu_state == "game":
|
||||
draw_game_list(screen)
|
||||
logger.debug("Rendu de draw_game_list")
|
||||
# logger.debug("Rendu de draw_game_list")
|
||||
elif config.menu_state == "download_progress":
|
||||
draw_progress_screen(screen)
|
||||
logger.debug("Rendu de draw_progress_screen")
|
||||
# logger.debug("Rendu de draw_progress_screen")
|
||||
elif config.menu_state == "download_result":
|
||||
draw_popup_message(screen, config.download_result_message, config.download_result_error)
|
||||
logger.debug("Rendu de draw_popup_message")
|
||||
# logger.debug("Rendu de draw_popup_message")
|
||||
elif config.menu_state == "confirm_exit":
|
||||
draw_confirm_dialog(screen)
|
||||
logger.debug("Rendu de draw_confirm_dialog")
|
||||
# logger.debug("Rendu de draw_confirm_dialog")
|
||||
elif config.menu_state == "extension_warning":
|
||||
draw_extension_warning(screen)
|
||||
logger.debug("Rendu de draw_extension_warning")
|
||||
# logger.debug("Rendu de draw_extension_warning")
|
||||
elif config.menu_state == "pause_menu":
|
||||
draw_pause_menu(screen, config.selected_pause_option)
|
||||
draw_pause_menu(screen, config.selected_option)
|
||||
logger.debug("Rendu de draw_pause_menu")
|
||||
elif config.menu_state == "controls_help":
|
||||
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")
|
||||
draw_history_list(screen)
|
||||
# logger.debug("Rendu de draw_history_list")
|
||||
elif config.menu_state == "confirm_clear_history":
|
||||
draw_clear_history_dialog(screen)
|
||||
logger.debug("Rendu de confirm_clear_history")
|
||||
# logger.debug("Rendu de confirm_clear_history")
|
||||
elif config.menu_state == "redownload_game_cache":
|
||||
draw_redownload_game_cache_dialog(screen) # Fonction existante
|
||||
elif config.menu_state == "restart_popup":
|
||||
draw_popup(screen) # Nouvelle fonction
|
||||
elif config.menu_state == "confirm_clear_history":
|
||||
draw_clear_history_dialog(screen) # Fonction existante
|
||||
|
||||
else:
|
||||
# Gestion des états non valides
|
||||
config.menu_state = "platform"
|
||||
@@ -624,14 +548,14 @@ async def main():
|
||||
logger.debug(f"Erreur OTA : {message}")
|
||||
else:
|
||||
loading_step = "check_data"
|
||||
config.current_loading_system = "Téléchargement des données ..."
|
||||
config.current_loading_system = "Téléchargement des jeux et images ..."
|
||||
config.loading_progress = 10.0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Étape chargement : {loading_step}, progress={config.loading_progress}")
|
||||
elif loading_step == "check_data":
|
||||
games_data_dir = "/userdata/roms/ports/RGSX/games"
|
||||
is_data_empty = not os.path.exists(games_data_dir) or not any(os.scandir(games_data_dir))
|
||||
logger.debug(f"Dossier Data directory {games_data_dir} is {'empty' if is_data_empty else 'not empty'}")
|
||||
#logger.debug(f"Dossier Data directory {games_data_dir} is {'empty' if is_data_empty else 'not empty'}")
|
||||
|
||||
if is_data_empty:
|
||||
config.current_loading_system = "Téléchargement du Dossier Data initial..."
|
||||
|
||||
34
config.py
34
config.py
@@ -1,11 +1,11 @@
|
||||
import pygame
|
||||
import pygame # type: ignore
|
||||
import os
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "1.5.0"
|
||||
app_version = "1.7.0"
|
||||
|
||||
# Variables d'état
|
||||
platforms = []
|
||||
@@ -43,7 +43,7 @@ filter_active = False
|
||||
extension_confirm_selection = 0
|
||||
pending_download = None
|
||||
controls_config = {}
|
||||
selected_pause_option = 0
|
||||
selected_option = 0
|
||||
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
|
||||
@@ -55,10 +55,13 @@ 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)
|
||||
redownload_confirm_selection = 0 # Sélection pour la confirmation de redownload
|
||||
popup_message = "" # Message à afficher dans les popups
|
||||
popup_timer = 0 # Temps restant pour le popup en millisecondes (0 = inactif)
|
||||
last_frame_time = pygame.time.get_ticks()
|
||||
|
||||
# Résolution de l'écran
|
||||
screen_width = 800
|
||||
screen_height = 600
|
||||
# Résolution de l'écran fallback
|
||||
# Utilisée si la résolution définie dépasse les capacités de l'écran
|
||||
SCREEN_WIDTH = 800
|
||||
"""Largeur de l'écran en pixels."""
|
||||
SCREEN_HEIGHT = 600
|
||||
@@ -100,9 +103,24 @@ def init_font():
|
||||
small_font = None
|
||||
|
||||
def validate_resolution():
|
||||
"""Valide la résolution de l'écran par rapport aux capacités du matériel."""
|
||||
"""Valide la résolution de l'écran par rapport aux capacités de l'écran."""
|
||||
display_info = pygame.display.Info()
|
||||
if SCREEN_WIDTH > display_info.current_w or SCREEN_HEIGHT > display_info.current_h:
|
||||
logger.warning(f"Résolution {SCREEN_WIDTH}x{SCREEN_HEIGHT} dépasse les limites de l'écran")
|
||||
return display_info.current_w, display_info.current_h
|
||||
return SCREEN_WIDTH, SCREEN_HEIGHT
|
||||
return SCREEN_WIDTH, SCREEN_HEIGHT
|
||||
|
||||
|
||||
def load_api_key_1fichier():
|
||||
"""Charge la clé API 1fichier depuis /userdata/saves/ports/rgsx/1fichierAPI.txt, crée le fichier si absent."""
|
||||
api_path = "/userdata/saves/ports/rgsx/1fichierAPI.txt"
|
||||
if not os.path.exists(api_path):
|
||||
# Crée le fichier vide si absent
|
||||
with open(api_path, "w") as f:
|
||||
f.write("")
|
||||
return ""
|
||||
with open(api_path, "r") as f:
|
||||
key = f.read().strip()
|
||||
return key
|
||||
|
||||
API_KEY_1FICHIER = load_api_key_1fichier()
|
||||
247
controls.py
247
controls.py
@@ -1,11 +1,13 @@
|
||||
import pygame
|
||||
import shutil
|
||||
import pygame # type: ignore
|
||||
import config
|
||||
from config import CONTROLS_CONFIG_PATH
|
||||
import asyncio
|
||||
import math
|
||||
import json
|
||||
import os
|
||||
from display import draw_validation_transition
|
||||
from network import download_rom, check_extension_before_download
|
||||
from network import download_rom, check_extension_before_download, download_from_1fichier, is_1fichier_url, is_extension_supported,load_extensions_json,sanitize_filename
|
||||
from controls_mapper import get_readable_input_name
|
||||
from utils import load_games
|
||||
from history import load_history, clear_history
|
||||
@@ -24,7 +26,7 @@ REPEAT_ACTION_DEBOUNCE = 50 # Délai anti-rebond pour répétitions up/down/lef
|
||||
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
|
||||
"redownload_game_cache", "restart_popup", "error", "loading", "confirm_clear_history"
|
||||
]
|
||||
|
||||
def validate_menu_state(state):
|
||||
@@ -63,7 +65,8 @@ def load_controls_config(path=CONTROLS_CONFIG_PATH):
|
||||
"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},
|
||||
"progress": {"type": "key", "value": pygame.K_x},
|
||||
"history": {"type": "key", "value": pygame.K_h},
|
||||
"page_up": {"type": "key", "value": pygame.K_PAGEUP},
|
||||
"page_down": {"type": "key", "value": pygame.K_PAGEDOWN},
|
||||
"filter": {"type": "key", "value": pygame.K_f},
|
||||
@@ -86,22 +89,22 @@ def is_input_matched(event, action_name):
|
||||
event_value = event.get("value") if isinstance(event, dict) else getattr(event, "value", None)
|
||||
|
||||
if input_type == "key" and event_type in (pygame.KEYDOWN, pygame.KEYUP):
|
||||
logger.debug(f"Vérification key: event_key={event_key}, input_value={input_value}")
|
||||
#logger.debug(f"Vérification key: event_key={event_key}, input_value={input_value}")
|
||||
return event_key == input_value
|
||||
elif input_type == "button" and event_type in (pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP):
|
||||
logger.debug(f"Vérification button: event_button={event_button}, input_value={input_value}")
|
||||
#logger.debug(f"Vérification button: event_button={event_button}, input_value={input_value}")
|
||||
return event_button == input_value
|
||||
elif input_type == "axis" and event_type == pygame.JOYAXISMOTION:
|
||||
axis, direction = input_value
|
||||
result = event_axis == axis and abs(event_value) > 0.5 and (1 if event_value > 0 else -1) == direction
|
||||
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
|
||||
elif input_type == "hat" and event_type == pygame.JOYHATMOTION:
|
||||
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
|
||||
elif input_type == "mouse" and event_type == pygame.MOUSEBUTTONDOWN:
|
||||
logger.debug(f"Vérification mouse: event_button={event_button}, input_value={input_value}")
|
||||
#logger.debug(f"Vérification mouse: event_button={event_button}, input_value={input_value}")
|
||||
return event_button == input_value
|
||||
return False
|
||||
|
||||
@@ -114,14 +117,14 @@ def handle_controls(event, sources, joystick, screen):
|
||||
|
||||
# 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}")
|
||||
#logger.debug(f"Validation initiale: previous_menu_state={config.previous_menu_state}")
|
||||
|
||||
# Debounce général
|
||||
if current_time - config.last_state_change_time < config.debounce_delay:
|
||||
return action
|
||||
|
||||
# Log des événements reçus
|
||||
logger.debug(f"Événement reçu: type={event.type}, value={getattr(event, 'value', None)}")
|
||||
#logger.debug(f"Événement reçu: type={event.type}, value={getattr(event, 'value', None)}")
|
||||
|
||||
# --- CLAVIER, MANETTE, SOURIS ---
|
||||
if event.type in (pygame.KEYDOWN, pygame.JOYBUTTONDOWN, pygame.JOYAXISMOTION, pygame.JOYHATMOTION, pygame.MOUSEBUTTONDOWN):
|
||||
@@ -143,12 +146,12 @@ def handle_controls(event, sources, joystick, screen):
|
||||
return "quit"
|
||||
|
||||
# Vérification des actions mappées
|
||||
for action_name in ["up", "down", "left", "right"]:
|
||||
if is_input_matched(event, action_name):
|
||||
logger.debug(f"Action mappée détectée: {action_name}, input={get_readable_input_name(event)}")
|
||||
#for action_name in ["up", "down", "left", "right"]:
|
||||
#if is_input_matched(event, action_name):
|
||||
#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"):
|
||||
if is_input_matched(event, "start") and config.menu_state not in ("pause_menu", "controls_help", "remap_controls", "redownload_game_cache"):
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "pause_menu"
|
||||
config.selected_option = 0
|
||||
@@ -159,13 +162,9 @@ def handle_controls(event, sources, joystick, screen):
|
||||
# Erreur
|
||||
if config.menu_state == "error":
|
||||
if is_input_matched(event, "confirm"):
|
||||
config.menu_state = "loading"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Sortie erreur avec Confirm")
|
||||
elif is_input_matched(event, "cancel"):
|
||||
config.menu_state = "confirm_exit"
|
||||
config.confirm_selection = 0
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.needs_redraw = True
|
||||
logger.debug("Sortie du menu erreur avec Confirm")
|
||||
|
||||
# Plateformes
|
||||
elif config.menu_state == "platform":
|
||||
@@ -235,7 +234,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
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
|
||||
@@ -247,12 +246,16 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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")
|
||||
#logger.debug("Page précédente, répétition réinitialisée")
|
||||
elif is_input_matched(event, "progress"):
|
||||
if config.download_tasks:
|
||||
config.menu_state = "download_progress"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Retour à download_progress depuis platform")
|
||||
elif is_input_matched(event, "history"):
|
||||
config.menu_state = "history"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Ouverture history depuis platform")
|
||||
elif is_input_matched(event, "confirm"):
|
||||
if config.platforms:
|
||||
config.current_platform = config.selected_platform
|
||||
@@ -264,7 +267,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
draw_validation_transition(screen, config.current_platform)
|
||||
config.menu_state = "game"
|
||||
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"):
|
||||
config.menu_state = "confirm_exit"
|
||||
config.confirm_selection = 0
|
||||
@@ -329,14 +332,14 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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)}")
|
||||
#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)}")
|
||||
#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 = ""
|
||||
@@ -346,7 +349,45 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.scroll_offset = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug("Sortie du mode recherche")
|
||||
elif config.search_mode and not config.is_non_pc:
|
||||
# Gestion de la recherche sur PC
|
||||
if event.type == pygame.KEYDOWN:
|
||||
# Saisie de texte alphanumérique
|
||||
if event.unicode.isalnum() or event.unicode == ' ':
|
||||
config.search_query += event.unicode
|
||||
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)}")
|
||||
# Gestion de la suppression
|
||||
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)}")
|
||||
# Gestion de la validation
|
||||
elif is_input_matched(event, "confirm"):
|
||||
config.search_mode = False
|
||||
config.filter_active = True # Conserver le filtre actif
|
||||
config.current_game = 0
|
||||
config.scroll_offset = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Validation de la recherche: query={config.search_query}, jeux filtrés={len(config.filtered_games)}")
|
||||
# Gestion de l'annulation
|
||||
elif is_input_matched(event, "cancel"):
|
||||
config.search_mode = False
|
||||
config.search_query = ""
|
||||
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
|
||||
@@ -370,7 +411,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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")
|
||||
#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
|
||||
@@ -378,7 +419,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
logger.debug("Page suivante dans la liste des jeux")
|
||||
#logger.debug("Page suivante dans la liste des jeux")
|
||||
elif is_input_matched(event, "filter"):
|
||||
config.search_mode = True
|
||||
config.search_query = ""
|
||||
@@ -390,26 +431,56 @@ def handle_controls(event, sources, joystick, screen):
|
||||
logger.debug("Entrée en mode recherche")
|
||||
elif is_input_matched(event, "progress"):
|
||||
if config.download_tasks:
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "download_progress"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Retour à download_progress depuis game")
|
||||
logger.debug(f"Retour à download_progress depuis {config.previous_menu_state}")
|
||||
elif is_input_matched(event, "history"):
|
||||
config.menu_state = "history"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Ouverture history 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])
|
||||
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:
|
||||
is_supported = is_extension_supported(
|
||||
sanitize_filename(game_name),
|
||||
platform,
|
||||
load_extensions_json()
|
||||
)
|
||||
if not is_supported:
|
||||
config.previous_menu_state = config.menu_state # Ajouter cette ligne
|
||||
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
|
||||
if is_1fichier_url(url):
|
||||
if not config.API_KEY_1FICHIER:
|
||||
config.previous_menu_state = config.menu_state # Ajouter cette ligne
|
||||
config.menu_state = "error"
|
||||
config.error_message = (
|
||||
"Attention il faut renseigner sa clé API (premium only) dans le fichier /userdata/saves/ports/rgsx/1fichier.api à ouvrir dans un editeur de texte et coller la clé API"
|
||||
)
|
||||
config.needs_redraw = True
|
||||
logger.error("Clé API 1fichier absente, téléchargement impossible.")
|
||||
config.pending_download = None
|
||||
return action
|
||||
loop = asyncio.get_running_loop()
|
||||
task = loop.run_in_executor(None, download_from_1fichier, url, platform, game_name, is_zip_non_supported)
|
||||
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 = config.menu_state # Ajouter cette ligne
|
||||
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
|
||||
config.pending_download = None
|
||||
action = "download"
|
||||
else:
|
||||
config.menu_state = "error"
|
||||
@@ -423,7 +494,11 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.scroll_offset = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug("Retour à platform")
|
||||
|
||||
elif is_input_matched(event, "redownload_game_cache"):
|
||||
config.previous_menu_state = config.menu_state
|
||||
config.menu_state = "redownload_game_cache"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Passage à redownload_game_cache depuis game")
|
||||
elif config.menu_state == "history":
|
||||
history = config.history
|
||||
if is_input_matched(event, "up"):
|
||||
@@ -449,7 +524,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
logger.debug("Page précédente dans l'historique")
|
||||
#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
|
||||
@@ -457,7 +532,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_start_time = 0
|
||||
config.repeat_last_action = current_time
|
||||
config.needs_redraw = True
|
||||
logger.debug("Page suivante dans l'historique")
|
||||
#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"
|
||||
@@ -469,14 +544,13 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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.previous_menu_state = config.menu_state # Remplacer cette ligne
|
||||
config.menu_state = "extension_warning"
|
||||
config.extension_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
@@ -484,7 +558,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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.previous_menu_state = config.menu_state # Remplacer cette ligne
|
||||
config.menu_state = "download_progress"
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Retéléchargement: {game_name} pour {platform} depuis {url}")
|
||||
@@ -503,6 +577,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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)}")
|
||||
@@ -521,20 +596,21 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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")
|
||||
#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}")
|
||||
#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")
|
||||
#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}")
|
||||
#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")
|
||||
#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":
|
||||
if is_input_matched(event, "cancel"):
|
||||
@@ -572,7 +648,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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}")
|
||||
#logger.debug(f"Changement sélection confirm_exit: {config.confirm_selection}")
|
||||
|
||||
# Avertissement extension
|
||||
elif config.menu_state == "extension_warning":
|
||||
@@ -602,15 +678,15 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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}")
|
||||
#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":
|
||||
logger.debug(f"État pause_menu, selected_option={config.selected_option}, événement={event.type}, valeur={getattr(event, 'value', None)}")
|
||||
if is_input_matched(event, "up"):
|
||||
config.selected_option = max(0, config.selected_option - 1)
|
||||
config.repeat_action = "up"
|
||||
@@ -618,14 +694,17 @@ def handle_controls(event, sources, joystick, screen):
|
||||
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
|
||||
logger.debug(f"Navigation vers le haut: selected_option={config.selected_option}")
|
||||
elif is_input_matched(event, "down"):
|
||||
config.selected_option = min(3, config.selected_option + 1)
|
||||
config.selected_option = min(4, 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
|
||||
logger.debug(f"Navigation vers le bas: selected_option={config.selected_option}")
|
||||
elif is_input_matched(event, "confirm"):
|
||||
logger.debug(f"Confirmation dans pause_menu avec selected_option={config.selected_option}")
|
||||
if config.selected_option == 0: # Controls
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.menu_state = "controls_help"
|
||||
@@ -644,7 +723,13 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.menu_state = "history"
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Passage à history depuis pause_menu")
|
||||
elif config.selected_option == 3: # Quit
|
||||
elif config.selected_option == 3: # Redownload game cache
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.menu_state = "redownload_game_cache"
|
||||
config.redownload_confirm_selection = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Passage à redownload_game_cache depuis pause_menu")
|
||||
elif config.selected_option == 4: # Quit
|
||||
config.previous_menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.menu_state = "confirm_exit"
|
||||
config.confirm_selection = 0
|
||||
@@ -668,6 +753,66 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.menu_state = "pause_menu"
|
||||
config.needs_redraw = True
|
||||
logger.debug("Retour à pause_menu depuis remap_controls")
|
||||
|
||||
elif config.menu_state == "redownload_game_cache":
|
||||
if is_input_matched(event, "left") or is_input_matched(event, "right"):
|
||||
config.redownload_confirm_selection = 1 - config.redownload_confirm_selection
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Changement sélection redownload_game_cache: {config.redownload_confirm_selection}")
|
||||
elif is_input_matched(event, "confirm"):
|
||||
logger.debug(f"Action confirm dans redownload_game_cache, sélection={config.redownload_confirm_selection}")
|
||||
if config.redownload_confirm_selection == 1: # Oui
|
||||
logger.debug("Début du redownload des jeux")
|
||||
config.download_tasks.clear()
|
||||
config.download_progress.clear()
|
||||
config.pending_download = None
|
||||
if os.path.exists("/userdata/roms/ports/RGSX/sources.json"):
|
||||
try:
|
||||
os.remove("/userdata/roms/ports/RGSX/sources.json")
|
||||
logger.debug("Fichier sources.json supprimé avec succès")
|
||||
if os.path.exists("/userdata/roms/ports/RGSX/games"):
|
||||
shutil.rmtree("/userdata/roms/ports/RGSX/games")
|
||||
logger.debug("Dossier games supprimé avec succès")
|
||||
if os.path.exists("/userdata/roms/ports/RGSX/images"):
|
||||
shutil.rmtree("/userdata/roms/ports/RGSX/images")
|
||||
logger.debug("Dossier images supprimé avec succès")
|
||||
config.menu_state = "restart_popup"
|
||||
config.popup_message = "Redownload des jeux effectué.\nVeuillez redémarrer l'application pour voir les changements."
|
||||
config.popup_timer = 5000 # 5 secondes
|
||||
config.needs_redraw = True
|
||||
logger.debug("Passage à restart_popup")
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de la suppression du fichier sources.json ou dossiers: {e}")
|
||||
config.menu_state = "error"
|
||||
config.error_message = "Erreur lors de la suppression du fichier sources.json ou dossiers"
|
||||
config.needs_redraw = True
|
||||
return action
|
||||
else:
|
||||
logger.debug("Fichier sources.json non trouvé, passage à restart_popup")
|
||||
config.menu_state = "restart_popup"
|
||||
config.popup_message = "Aucun cache trouvé.\nVeuillez redémarrer l'application pour charger les jeux."
|
||||
config.popup_timer = 5000 # 5 secondes
|
||||
config.needs_redraw = True
|
||||
logger.debug("Passage à restart_popup")
|
||||
else: # Non
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Annulation du redownload, retour à {config.menu_state}")
|
||||
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 redownload_game_cache")
|
||||
|
||||
|
||||
# Popup de redémarrage
|
||||
elif config.menu_state == "restart_popup":
|
||||
if is_input_matched(event, "confirm") or is_input_matched(event, "cancel"):
|
||||
config.menu_state = validate_menu_state(config.previous_menu_state)
|
||||
config.popup_message = ""
|
||||
config.popup_timer = 0
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Retour manuel à {config.menu_state} depuis restart_popup")
|
||||
|
||||
|
||||
# Gestion de la répétition automatique (relâchement)
|
||||
if event.type in (pygame.KEYUP, pygame.JOYBUTTONUP, pygame.JOYAXISMOTION, pygame.JOYHATMOTION):
|
||||
@@ -678,7 +823,7 @@ def handle_controls(event, sources, joystick, screen):
|
||||
config.repeat_action = None
|
||||
config.repeat_key = None
|
||||
config.repeat_start_time = 0
|
||||
logger.debug("Répétition arrêtée")
|
||||
#logger.debug("Répétition arrêtée")
|
||||
|
||||
return action
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import pygame
|
||||
import pygame # type: ignore
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
@@ -19,9 +19,10 @@ ACTIONS = [
|
||||
{"name": "down", "display": "Bas", "description": "Naviguer vers le bas"},
|
||||
{"name": "left", "display": "Gauche", "description": "Naviguer à gauche"},
|
||||
{"name": "right", "display": "Droite", "description": "Naviguer à droite"},
|
||||
{"name": "page_up", "display": "Page Précédente", "description": "Page précédente (ex: PageUp, LB)"},
|
||||
{"name": "page_down", "display": "Page Suivante", "description": "Page suivante (ex: PageDown, RB)"},
|
||||
{"name": "page_up", "display": "Page Précédente", "description": "Page précédente/Défilement Rapide Haut (ex: PageUp, LB)"},
|
||||
{"name": "page_down", "display": "Page Suivante", "description": "Page suivante/Défilement Rapide Bas (ex: PageDown, RB)"},
|
||||
{"name": "progress", "display": "Progression", "description": "Voir progression (ex: X)"},
|
||||
{"name": "history", "display": "Historique", "description": "Ouvrir l'historique (ex: H, Y)"},
|
||||
{"name": "filter", "display": "Filtrer", "description": "Ouvrir filtre (ex: F, Select)"},
|
||||
{"name": "delete", "display": "Supprimer", "description": "Supprimer caractère (ex: LT, Suppr)"},
|
||||
{"name": "space", "display": "Espace", "description": "Ajouter espace (ex: RT, Espace)"},
|
||||
@@ -200,7 +201,7 @@ MOUSE_BUTTON_NAMES = {
|
||||
HOLD_DURATION = 1000
|
||||
|
||||
def load_controls_config():
|
||||
"""Charge la configuration des contrôles depuis controls.json."""
|
||||
#Charge la configuration des contrôles depuis controls.json
|
||||
try:
|
||||
if os.path.exists(CONTROLS_CONFIG_PATH):
|
||||
with open(CONTROLS_CONFIG_PATH, "r") as f:
|
||||
@@ -215,7 +216,7 @@ def load_controls_config():
|
||||
return {}
|
||||
|
||||
def save_controls_config(controls_config):
|
||||
"""Enregistre la configuration des contrôles dans controls.json."""
|
||||
#Enregistre la configuration des contrôles dans controls.json
|
||||
try:
|
||||
os.makedirs(os.path.dirname(CONTROLS_CONFIG_PATH), exist_ok=True)
|
||||
with open(CONTROLS_CONFIG_PATH, "w") as f:
|
||||
@@ -225,7 +226,7 @@ def save_controls_config(controls_config):
|
||||
logger.error(f"Erreur lors de l'enregistrement de controls.json : {e}")
|
||||
|
||||
def get_readable_input_name(event):
|
||||
"""Retourne un nom lisible pour une entrée (touche, bouton, axe, hat, ou souris)."""
|
||||
#Retourne un nom lisible pour une entrée (touche, bouton, axe, hat, ou souris)
|
||||
if event.type == pygame.KEYDOWN:
|
||||
key_value = SDL_TO_PYGAME_KEY.get(event.key, event.key)
|
||||
return KEY_NAMES.get(key_value, pygame.key.name(key_value) or f"Touche {key_value}")
|
||||
@@ -240,7 +241,6 @@ def get_readable_input_name(event):
|
||||
return MOUSE_BUTTON_NAMES.get(event.button, f"Souris Bouton {event.button}")
|
||||
return "Inconnu"
|
||||
|
||||
ACTIONS = ["start", "confirm", "cancel"]
|
||||
|
||||
def map_controls(screen):
|
||||
mapping = True
|
||||
@@ -249,7 +249,7 @@ def map_controls(screen):
|
||||
while mapping:
|
||||
clock.tick(100) # 100 FPS
|
||||
for event in pygame.event.get():
|
||||
"""Interface de mappage des contrôles avec validation par maintien de 3 secondes."""
|
||||
#Interface de mappage des contrôles avec validation par maintien de 3 secondes
|
||||
controls_config = load_controls_config()
|
||||
current_action_index = 0
|
||||
current_input = None
|
||||
@@ -411,21 +411,21 @@ def map_controls(screen):
|
||||
pass
|
||||
|
||||
def save_controls_config(config):
|
||||
"""Enregistre la configuration des contrôles dans un fichier JSON."""
|
||||
#Enregistre la configuration des contrôles dans un fichier JSON
|
||||
try:
|
||||
with open(CONTROLS_CONFIG_PATH, "w") as f:
|
||||
json.dump(config, f, indent=4)
|
||||
logging.debug("Configuration des contrôles enregistrée")
|
||||
logger.debug("Configuration des contrôles enregistrée")
|
||||
except Exception as e:
|
||||
logging.error(f"Erreur lors de l'enregistrement de controls.json : {e}")
|
||||
logger.error(f"Erreur lors de l'enregistrement de controls.json : {e}")
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw_controls_mapping(screen, action, last_input, waiting_for_input, hold_progress):
|
||||
"""Affiche l'interface de mappage des contrôles avec une barre de progression pour le maintien."""
|
||||
#Affiche l'interface de mappage des contrôles avec une barre de progression pour le maintien
|
||||
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
|
||||
|
||||
max_width = config.screen_width // 1.2
|
||||
max_width = config.screen_width // 1.2
|
||||
padding_horizontal = 40
|
||||
padding_vertical = 30
|
||||
padding_between = 10
|
||||
@@ -434,18 +434,18 @@ def draw_controls_mapping(screen, action, last_input, waiting_for_input, hold_pr
|
||||
shadow_offset = 8
|
||||
|
||||
# Instructions
|
||||
instruction_text = f"Maintenez une touche/bouton pendant 3s pour '{action['display']}'"
|
||||
instruction_text = f"Maintenez pendant 3s pour : '{action['display']}'"
|
||||
description_text = action['description']
|
||||
skip_text = "Appuyez sur Échap pour passer"
|
||||
instruction_surface = config.font.render(instruction_text, True, (255, 255, 255))
|
||||
description_surface = config.font.render(description_text, True, (200, 200, 200))
|
||||
skip_text = "Appuyez sur Échap pour passer(Pc only)"
|
||||
instruction_surface = config.small_font.render(instruction_text, True, (255, 255, 255))
|
||||
description_surface = config.small_font.render(description_text, True, (200, 200, 200))
|
||||
skip_surface = config.font.render(skip_text, True, (255, 255, 255))
|
||||
instruction_width, instruction_height = instruction_surface.get_size()
|
||||
description_width, description_height = description_surface.get_size()
|
||||
skip_width, skip_height = skip_surface.get_size()
|
||||
|
||||
# Input détecté
|
||||
input_text = last_input or (f"En attente d'une entrée..." if waiting_for_input else "Maintenez une touche/bouton")
|
||||
input_text = last_input or (f"Attente..." if waiting_for_input else "Maintenez 3s")
|
||||
input_surface = config.font.render(input_text, True, (0, 255, 0) if last_input else (255, 255, 255))
|
||||
input_width, input_height = input_surface.get_size()
|
||||
|
||||
@@ -483,7 +483,7 @@ def draw_controls_mapping(screen, action, last_input, waiting_for_input, hold_pr
|
||||
input_rect = input_surface.get_rect(center=(config.screen_width // 2, start_y + input_height // 2))
|
||||
screen.blit(input_surface, input_rect)
|
||||
start_y += input_height + padding_between
|
||||
skip_rect = skip_surface.get_rect(center=(config.screen_width // 2, start_y + skip_height // 2))
|
||||
skip_rect = skip_surface.get_rect(center=(config.screen_width // 2, start_y + skip_height // 2))
|
||||
screen.blit(skip_surface, skip_rect)
|
||||
|
||||
# Barre de progression pour le maintien
|
||||
@@ -491,7 +491,7 @@ def draw_controls_mapping(screen, action, last_input, waiting_for_input, hold_pr
|
||||
bar_height = 20
|
||||
bar_x = (config.screen_width - bar_width) // 2
|
||||
bar_y = start_y + skip_height + 20
|
||||
pygame.draw.rect(screen, (100, 100, 100), (bar_x, bar_y, bar_width, bar_height))
|
||||
pygame.draw.rect(screen, (100, 100, 100), (bar_x, bar_y, bar_width, bar_height))
|
||||
progress_width = bar_width * hold_progress
|
||||
pygame.draw.rect(screen, (0, 255, 0), (bar_x, bar_y, progress_width, bar_height))
|
||||
pygame.draw.rect(screen, (0, 255, 0), (bar_x, bar_y, progress_width, bar_height))
|
||||
pygame.draw.rect(screen, (255, 255, 255), (bar_x, bar_y, bar_width, bar_height), 2)
|
||||
475
display.py
475
display.py
@@ -1,15 +1,15 @@
|
||||
import pygame
|
||||
import pygame # type: ignore
|
||||
import config
|
||||
import math
|
||||
from utils import truncate_text_end, wrap_text, load_system_image, load_games
|
||||
from utils import truncate_text_middle, wrap_text, load_system_image, load_games
|
||||
import logging
|
||||
from history import load_history # Ajout de l'import
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Cache global pour l'overlay semi-transparent
|
||||
OVERLAY = None # Initialisé dans init_display()
|
||||
|
||||
#Général, résolution, overlay
|
||||
def init_display():
|
||||
"""Initialise l'écran et les ressources globales."""
|
||||
global OVERLAY
|
||||
@@ -26,6 +26,7 @@ def init_display():
|
||||
logger.debug(f"Écran initialisé avec résolution : {screen_width}x{screen_height}")
|
||||
return screen
|
||||
|
||||
#Fond d'ecran dégradé
|
||||
def draw_gradient(screen, top_color, bottom_color):
|
||||
"""Dessine un fond dégradé vertical."""
|
||||
height = screen.get_height()
|
||||
@@ -36,6 +37,30 @@ def draw_gradient(screen, top_color, bottom_color):
|
||||
color = top_color.lerp(bottom_color, ratio)
|
||||
pygame.draw.line(screen, color, (0, y), (screen.get_width(), y))
|
||||
|
||||
#Transistion d'image lors de la selection d'un systeme
|
||||
def draw_validation_transition(screen, platform_index):
|
||||
"""Affiche une animation de transition pour la sélection d’une plateforme."""
|
||||
platform_dict = config.platform_dicts[platform_index]
|
||||
image = load_system_image(platform_dict)
|
||||
if not image:
|
||||
return
|
||||
orig_width, orig_height = image.get_width(), image.get_height()
|
||||
base_size = int(config.screen_width * 0.0781) # ~150px pour 1920p
|
||||
start_time = pygame.time.get_ticks()
|
||||
duration = 500
|
||||
while pygame.time.get_ticks() - start_time < duration:
|
||||
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
|
||||
elapsed = pygame.time.get_ticks() - start_time
|
||||
scale = 2.0 + (2.0 * elapsed / duration) if elapsed < duration / 2 else 3.0 - (2.0 * elapsed / duration)
|
||||
new_width = int(base_size * scale)
|
||||
new_height = int(base_size * scale)
|
||||
scaled_image = pygame.transform.smoothscale(image, (new_width, new_height))
|
||||
image_rect = scaled_image.get_rect(center=(config.screen_width // 2, config.screen_height // 2))
|
||||
screen.blit(scaled_image, image_rect)
|
||||
pygame.display.flip()
|
||||
pygame.time.wait(10)
|
||||
|
||||
#Ecran de chargement
|
||||
def draw_loading_screen(screen):
|
||||
"""Affiche l’écran de chargement avec le disclaimer en haut, le texte de chargement et la barre de progression."""
|
||||
disclaimer_lines = [
|
||||
@@ -52,7 +77,7 @@ def draw_loading_screen(screen):
|
||||
border_width = 3
|
||||
shadow_offset = 6
|
||||
|
||||
line_height = config.font.get_height() + padding_between
|
||||
line_height = config.small_font.get_height() + padding_between
|
||||
total_height = line_height * len(disclaimer_lines) - padding_between
|
||||
rect_width = config.screen_width - 2 * margin_horizontal
|
||||
rect_height = total_height + 2 * padding_vertical
|
||||
@@ -73,9 +98,9 @@ def draw_loading_screen(screen):
|
||||
|
||||
max_text_width = rect_width - 2 * padding_vertical
|
||||
for i, line in enumerate(disclaimer_lines):
|
||||
wrapped_lines = wrap_text(line, config.font, max_text_width)
|
||||
wrapped_lines = wrap_text(line, config.small_font, max_text_width)
|
||||
for j, wrapped_line in enumerate(wrapped_lines):
|
||||
text_surface = config.font.render(wrapped_line, True, (255, 255, 255))
|
||||
text_surface = config.small_font.render(wrapped_line, True, (255, 255, 255))
|
||||
text_rect = text_surface.get_rect(center=(
|
||||
config.screen_width // 2,
|
||||
rect_y + padding_vertical + (i * len(wrapped_lines) + j + 0.5) * line_height - padding_between // 2
|
||||
@@ -83,11 +108,11 @@ def draw_loading_screen(screen):
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
loading_y = rect_y + rect_height + int(config.screen_height * 0.0926) # ~100px pour 1080p
|
||||
text = config.font.render(truncate_text_end(f"{config.current_loading_system}", config.font, config.screen_width - 2 * margin_horizontal), True, (255, 255, 255))
|
||||
text = config.small_font.render(truncate_text_middle(f"{config.current_loading_system}", config.small_font, config.screen_width - 2 * margin_horizontal), True, (255, 255, 255))
|
||||
text_rect = text.get_rect(center=(config.screen_width // 2, loading_y))
|
||||
screen.blit(text, text_rect)
|
||||
|
||||
progress_text = config.font.render(f"Progression : {int(config.loading_progress)}%", True, (255, 255, 255))
|
||||
progress_text = config.small_font.render(f"Progression : {int(config.loading_progress)}%", True, (255, 255, 255))
|
||||
progress_rect = progress_text.get_rect(center=(config.screen_width // 2, loading_y + int(config.screen_height * 0.0463))) # ~50px pour 1080p
|
||||
screen.blit(progress_text, progress_rect)
|
||||
|
||||
@@ -97,6 +122,7 @@ def draw_loading_screen(screen):
|
||||
pygame.draw.rect(screen, (100, 100, 100), (config.screen_width // 2 - bar_width // 2, loading_y + int(config.screen_height * 0.0926), bar_width, bar_height))
|
||||
pygame.draw.rect(screen, (0, 255, 0), (config.screen_width // 2 - bar_width // 2, loading_y + int(config.screen_height * 0.0926), progress_width, bar_height))
|
||||
|
||||
#Ecran d'erreur
|
||||
def draw_error_screen(screen):
|
||||
"""Affiche l’écran d’erreur."""
|
||||
error_font = pygame.font.SysFont("arial", 28)
|
||||
@@ -106,10 +132,20 @@ def draw_error_screen(screen):
|
||||
text = error_font.render(line, True, (255, 0, 0))
|
||||
text_rect = text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 - (len(wrapped_message) // 2 - i) * line_height))
|
||||
screen.blit(text, text_rect)
|
||||
retry_text = config.font.render(f"{get_control_display('confirm', 'Entrée/A')} : retenter, {get_control_display('cancel', 'Échap/B')} : quitter", True, (255, 255, 255))
|
||||
retry_rect = retry_text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 + int(config.screen_height * 0.0926))) # ~100px pour 1080p
|
||||
screen.blit(retry_text, retry_rect)
|
||||
# Afficher uniquement "Valider"
|
||||
confirm_text = config.small_font.render("Valider", True, (0, 150, 255))
|
||||
confirm_rect = confirm_text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 + int(config.screen_height * 0.0926)))
|
||||
screen.blit(confirm_text, confirm_rect)
|
||||
|
||||
#Recuperer les noms d'affichage des controles
|
||||
def get_control_display(action, default):
|
||||
"""Récupère le nom d'affichage d'une action depuis controls_config."""
|
||||
if not config.controls_config:
|
||||
logger.warning(f"controls_config vide pour l'action {action}, utilisation de la valeur par défaut")
|
||||
return default
|
||||
return config.controls_config.get(action, {}).get('display', default)
|
||||
|
||||
#Grille des systemes 3x3
|
||||
def draw_platform_grid(screen):
|
||||
"""Affiche la grille des plateformes avec un titre en haut."""
|
||||
# Configuration du titre
|
||||
@@ -149,7 +185,7 @@ def draw_platform_grid(screen):
|
||||
y_positions = [margin_top + row_height * i + row_height // 2 for i in range(num_rows)]
|
||||
|
||||
start_idx = config.current_page * systems_per_page
|
||||
logger.debug(f"Page {config.current_page}, start_idx: {start_idx}, total_platforms: {len(config.platforms)}")
|
||||
#logger.debug(f"Page {config.current_page}, start_idx: {start_idx}, total_platforms: {len(config.platforms)}")
|
||||
|
||||
for idx in range(start_idx, start_idx + systems_per_page):
|
||||
if idx >= len(config.platforms):
|
||||
@@ -196,9 +232,10 @@ def draw_platform_grid(screen):
|
||||
|
||||
screen.blit(image, image_rect)
|
||||
|
||||
#Liste des jeux
|
||||
def draw_game_list(screen):
|
||||
"""Affiche la liste des jeux avec défilement et rectangle de fond."""
|
||||
logger.debug("Début de draw_game_list")
|
||||
#logger.debug("Début de draw_game_list")
|
||||
|
||||
platform = config.platforms[config.current_platform]
|
||||
platform_name = config.platform_names.get(platform, platform)
|
||||
@@ -228,25 +265,26 @@ def draw_game_list(screen):
|
||||
screen.blit(text_surface, text_rect)
|
||||
return
|
||||
|
||||
line_height = config.font.get_height() + 10
|
||||
line_height = config.small_font.get_height() + 10
|
||||
#header_height = line_height
|
||||
margin_top_bottom = 20
|
||||
extra_margin_top = 5
|
||||
extra_margin_bottom = 40
|
||||
title_height = max(config.title_font.get_height(), config.search_font.get_height(), config.small_font.get_height()) + 20
|
||||
extra_margin_top = 20
|
||||
extra_margin_bottom = 60 # Aligné sur draw_history_list
|
||||
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
|
||||
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)
|
||||
rect_width = max_text_width + 40
|
||||
rect_height = games_per_page * line_height + 2 * margin_top_bottom
|
||||
items_per_page = available_height // line_height
|
||||
|
||||
rect_height = items_per_page * line_height + 2 * margin_top_bottom
|
||||
rect_width = int(0.95 * config.screen_width)
|
||||
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.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) - items_per_page)))
|
||||
if config.current_game < config.scroll_offset:
|
||||
config.scroll_offset = config.current_game
|
||||
elif config.current_game >= config.scroll_offset + games_per_page:
|
||||
config.scroll_offset = config.current_game - games_per_page + 1
|
||||
elif config.current_game >= config.scroll_offset + items_per_page:
|
||||
config.scroll_offset = config.current_game - items_per_page + 1
|
||||
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
@@ -281,33 +319,68 @@ def draw_game_list(screen):
|
||||
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 in range(config.scroll_offset, min(config.scroll_offset + games_per_page, len(games))):
|
||||
for i in range(config.scroll_offset, min(config.scroll_offset + items_per_page, len(games))):
|
||||
game_name = games[i][0] if isinstance(games[i], (list, tuple)) else games[i]
|
||||
color = (0, 150, 255) if i == config.current_game else (255, 255, 255)
|
||||
game_text = truncate_text_end(game_name, config.font, config.screen_width - 80)
|
||||
text_surface = config.font.render(game_text, True, color)
|
||||
game_text = truncate_text_middle(game_name, config.small_font, rect_width - 40)
|
||||
text_surface = config.small_font.render(game_text, True, color)
|
||||
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + (i - config.scroll_offset) * line_height + line_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
logger.debug(f"Jeu affiché : texte={game_text}, position={text_rect}, selected={i == config.current_game}")
|
||||
#logger.debug(f"Jeu affiché : texte={game_text}, position={text_rect}, selected={i == config.current_game}")
|
||||
|
||||
draw_scrollbar(screen)
|
||||
if config.search_mode and config.is_non_pc:
|
||||
draw_virtual_keyboard(screen)
|
||||
# Afficher la barre de scroll si besoin
|
||||
if len(games) > items_per_page:
|
||||
try:
|
||||
draw_game_scrollbar(
|
||||
screen,
|
||||
config.scroll_offset,
|
||||
len(games),
|
||||
items_per_page,
|
||||
rect_x + rect_width - 10,
|
||||
rect_y,
|
||||
rect_height
|
||||
)
|
||||
except NameError as e:
|
||||
logger.error(f"Erreur : draw_game_scrollbar non défini: {str(e)}")
|
||||
|
||||
#Barre de défilement des jeux
|
||||
def draw_game_scrollbar(screen, scroll_offset, total_items, visible_items, x, y, height):
|
||||
"""Affiche la barre de défilement pour la liste des jeux."""
|
||||
if total_items <= visible_items:
|
||||
return
|
||||
game_area_height = height
|
||||
scrollbar_height = game_area_height * (visible_items / total_items)
|
||||
scrollbar_y = y + (game_area_height - scrollbar_height) * (scroll_offset / max(1, total_items - visible_items))
|
||||
pygame.draw.rect(screen, (255, 255, 255), (x, scrollbar_y, 15, scrollbar_height))
|
||||
|
||||
#Liste historique téléchargement
|
||||
|
||||
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)
|
||||
|
||||
# Définir les largeurs des colonnes (valeurs fixes pour simplifier, ajustez si nécessaire)
|
||||
col_platform_width = int((0.95 * config.screen_width - 60) * 0.33)
|
||||
col_game_width = int((0.95 * config.screen_width - 60) * 0.50)
|
||||
col_status_width = int((0.95 * config.screen_width - 60) * 0.17)
|
||||
rect_width = int(0.95 * config.screen_width) # 95% de la largeur de l'écran
|
||||
|
||||
# Hauteur des lignes et en-tête
|
||||
line_height = config.small_font.get_height() + 10
|
||||
header_height = line_height
|
||||
margin_top_bottom = 20
|
||||
extra_margin_top = 20
|
||||
extra_margin_bottom = 60
|
||||
title_height = config.title_font.get_height() + 20
|
||||
|
||||
# Cas où l'historique est vide
|
||||
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
|
||||
@@ -324,33 +397,26 @@ def draw_history_list(screen):
|
||||
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
|
||||
# Calculer la hauteur disponible et les éléments par page
|
||||
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
|
||||
# Calculer les dimensions du rectangle
|
||||
rect_height = header_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
|
||||
|
||||
# Gestion du défilement
|
||||
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
|
||||
|
||||
# Fond et cadre
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
# Titre
|
||||
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))
|
||||
@@ -360,12 +426,13 @@ def draw_history_list(screen):
|
||||
pygame.draw.rect(screen, (255, 255, 255), title_rect_inflated, 2, border_radius=10)
|
||||
screen.blit(title_surface, title_rect)
|
||||
|
||||
# Cadre du tableau
|
||||
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_y = rect_y + margin_top_bottom + header_height // 2
|
||||
header_x_positions = [
|
||||
rect_x + 20 + col_platform_width // 2,
|
||||
rect_x + 20 + col_platform_width + col_game_width // 2,
|
||||
@@ -376,18 +443,18 @@ def draw_history_list(screen):
|
||||
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))):
|
||||
# Afficher les entrées de l'historique
|
||||
for idx, i in enumerate(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"]
|
||||
platform = entry.get("platform", "Inconnu")
|
||||
game_name = entry.get("game_name", "Inconnu")
|
||||
status = entry.get("status", "Inconnu")
|
||||
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)
|
||||
platform_text = truncate_text_middle(platform, config.small_font, col_platform_width - 10)
|
||||
game_text = truncate_text_middle(game_name, config.small_font, col_game_width - 10)
|
||||
status_text = truncate_text_middle(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
|
||||
y_pos = rect_y + margin_top_bottom + header_height + idx * 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)
|
||||
@@ -399,20 +466,68 @@ def draw_history_list(screen):
|
||||
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}")
|
||||
#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)
|
||||
# Scrollbar
|
||||
if len(history) > items_per_page:
|
||||
try:
|
||||
draw_history_scrollbar(
|
||||
screen,
|
||||
config.history_scroll_offset,
|
||||
len(history),
|
||||
items_per_page,
|
||||
rect_x + rect_width - 10,
|
||||
rect_y,
|
||||
rect_height
|
||||
)
|
||||
except NameError as e:
|
||||
logger.error(f"Erreur : draw_history_scrollbar non défini: {str(e)}")
|
||||
|
||||
def draw_history_scrollbar(screen):
|
||||
"""Affiche la barre de défilement pour l'historique."""
|
||||
if len(config.history) <= config.visible_history_items:
|
||||
#Barre de défilement de l'historique
|
||||
def draw_history_scrollbar(screen, scroll_offset, total_items, visible_items, x, y, height):
|
||||
"""Affiche la barre de défilement à droite de l’écran."""
|
||||
if len(config.filtered_games) <= config.visible_games:
|
||||
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))
|
||||
scrollbar_height = game_area_height * (config.visible_games / len(config.filtered_games))
|
||||
scrollbar_y = 120 + (game_area_height - scrollbar_height) * (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))
|
||||
|
||||
#Ecran confirmation vider historique
|
||||
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)
|
||||
|
||||
#Affichage du clavier virtuel sur non pc
|
||||
def draw_virtual_keyboard(screen):
|
||||
"""Affiche un clavier virtuel pour la saisie dans search_mode, centré verticalement."""
|
||||
keyboard_layout = [
|
||||
@@ -450,9 +565,10 @@ def draw_virtual_keyboard(screen):
|
||||
text_rect = text.get_rect(center=key_rect.center)
|
||||
screen.blit(text, text_rect)
|
||||
|
||||
#Ecran de progression de téléchargement/extraction
|
||||
def draw_progress_screen(screen):
|
||||
"""Affiche l'écran de progression des téléchargements avec taille en Mo."""
|
||||
logger.debug("Début de draw_progress_screen")
|
||||
#logger.debug("Début de draw_progress_screen")
|
||||
|
||||
if not config.download_tasks:
|
||||
logger.debug("Aucune tâche de téléchargement active")
|
||||
@@ -466,11 +582,11 @@ def draw_progress_screen(screen):
|
||||
downloaded_size = progress["downloaded_size"]
|
||||
total_size = progress["total_size"]
|
||||
progress_percent = progress["progress_percent"]
|
||||
logger.debug(f"Progression : game_name={game_name}, url={url}, status={status}, progress_percent={progress_percent}, downloaded_size={downloaded_size}, total_size={total_size}")
|
||||
#logger.debug(f"Progression : game_name={game_name}, url={url}, status={status}, progress_percent={progress_percent}, downloaded_size={downloaded_size}, total_size={total_size}")
|
||||
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
title_text = f"{status} : {truncate_text_end(game_name, config.font, config.screen_width - 200)}"
|
||||
title_text = f"{status} : {truncate_text_middle(game_name, config.font, config.screen_width - 200)}"
|
||||
title_lines = wrap_text(title_text, config.font, config.screen_width - 80)
|
||||
line_height = config.font.get_height() + 5
|
||||
text_height = len(title_lines) * line_height
|
||||
@@ -491,7 +607,7 @@ def draw_progress_screen(screen):
|
||||
title_render = config.font.render(line, True, (255, 255, 255))
|
||||
title_rect = title_render.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2))
|
||||
screen.blit(title_render, title_rect)
|
||||
logger.debug(f"Titre affiché : texte={line}, position={title_rect}, taille={title_render.get_size()}")
|
||||
#logger.debug(f"Titre affiché : texte={line}, position={title_rect}, taille={title_render.get_size()}")
|
||||
|
||||
bar_y = rect_y + text_height + margin_top_bottom
|
||||
progress_width = 0
|
||||
@@ -500,7 +616,7 @@ def draw_progress_screen(screen):
|
||||
progress_width = int(bar_width * (progress_percent / 100))
|
||||
pygame.draw.rect(screen, (0, 150, 255), (rect_x + 20, bar_y, progress_width, bar_height))
|
||||
pygame.draw.rect(screen, (255, 255, 255), (rect_x + 20, bar_y, bar_width, bar_height), 2)
|
||||
logger.debug(f"Barre de progression affichée : position=({rect_x + 20}, {bar_y}), taille=({bar_width}, {bar_height}), progress_width={progress_width}")
|
||||
#logger.debug(f"Barre de progression affichée : position=({rect_x + 20}, {bar_y}), taille=({bar_width}, {bar_height}), progress_width={progress_width}")
|
||||
|
||||
downloaded_mb = downloaded_size / (1024 * 1024)
|
||||
total_mb = total_size / (1024 * 1024)
|
||||
@@ -512,64 +628,25 @@ def draw_progress_screen(screen):
|
||||
percent_render = config.font.render(line, True, (255, 255, 255))
|
||||
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)
|
||||
logger.debug(f"Texte de progression affiché : texte={line}, position={percent_rect}, taille={percent_render.get_size()}")
|
||||
|
||||
def draw_scrollbar(screen):
|
||||
"""Affiche la barre de défilement à droite de l’écran."""
|
||||
if len(config.filtered_games) <= config.visible_games:
|
||||
return
|
||||
|
||||
game_area_height = config.screen_height - 150
|
||||
scrollbar_height = game_area_height * (config.visible_games / len(config.filtered_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))
|
||||
|
||||
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)
|
||||
|
||||
#Ecran popup resultat téléchargement
|
||||
def draw_popup_message(screen, message, is_error):
|
||||
"""Affiche une popup avec un message de résultat."""
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
wrapped_message = wrap_text(message, config.font, config.screen_width - 80)
|
||||
line_height = config.font.get_height() + 5
|
||||
if message is None:
|
||||
message = "Téléchargement annulé"
|
||||
logger.debug(f"Message popup : {message}, is_error={is_error}")
|
||||
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 80)
|
||||
line_height = config.small_font.get_height() + 5
|
||||
for i, line in enumerate(wrapped_message):
|
||||
text = config.font.render(line, True, (255, 0, 0) if is_error else (0, 255, 0))
|
||||
text = config.small_font.render(line, True, (255, 0, 0) if is_error else (0, 255, 0))
|
||||
text_rect = text.get_rect(center=(config.screen_width // 2, config.screen_height // 2 - (len(wrapped_message) // 2 - i) * line_height))
|
||||
screen.blit(text, text_rect)
|
||||
|
||||
#Ecran avertissement extension non supportée téléchargement
|
||||
def draw_extension_warning(screen):
|
||||
"""Affiche un avertissement pour une extension non reconnue ou un fichier ZIP."""
|
||||
logger.debug("Début de draw_extension_warning")
|
||||
#logger.debug("Début de draw_extension_warning")
|
||||
|
||||
if not config.pending_download:
|
||||
logger.error("config.pending_download est None ou vide dans extension_warning")
|
||||
@@ -612,7 +689,7 @@ def draw_extension_warning(screen):
|
||||
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)
|
||||
logger.debug(f"Lignes affichées : {[(rect.center, text_surface.get_size()) for rect, text_surface in zip([text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) for i in range(len(lines))], [config.font.render(line, True, (255, 255, 255)) for line in lines])]}")
|
||||
#logger.debug(f"Lignes affichées : {[(rect.center, text_surface.get_size()) for rect, text_surface in zip([text_surface.get_rect(center=(config.screen_width // 2, rect_y + margin_top_bottom + i * line_height + line_height // 2)) for i in range(len(lines))], [config.font.render(line, True, (255, 255, 255)) for line in lines])]}")
|
||||
|
||||
yes_text = "[Oui]" if config.extension_confirm_selection == 1 else "Oui"
|
||||
no_text = "[Non]" if config.extension_confirm_selection == 0 else "Non"
|
||||
@@ -625,7 +702,7 @@ def draw_extension_warning(screen):
|
||||
|
||||
screen.blit(yes_surface, yes_rect)
|
||||
screen.blit(no_surface, no_rect)
|
||||
logger.debug(f"Boutons affichés : Oui={yes_rect}, Non={no_rect}, selection={config.extension_confirm_selection}")
|
||||
#logger.debug(f"Boutons affichés : Oui={yes_rect}, Non={no_rect}, selection={config.extension_confirm_selection}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors du rendu de extension_warning : {str(e)}")
|
||||
@@ -647,10 +724,11 @@ def draw_extension_warning(screen):
|
||||
error_rect = error_surface.get_rect(center=(config.screen_width // 2, rect_y + 20 + i * line_height + line_height // 2))
|
||||
screen.blit(error_surface, error_rect)
|
||||
|
||||
#Affichage des controles en bas de page
|
||||
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."""
|
||||
start_button = get_control_display('start', 'START')
|
||||
control_text = f"Menu {menu_state} - {start_button} : Options - History - Controls"
|
||||
control_text = f"{start_button} : Options - History - Controls"
|
||||
max_width = config.screen_width - 40
|
||||
wrapped_controls = wrap_text(control_text, config.small_font, max_width)
|
||||
line_height = config.small_font.get_height() + 5
|
||||
@@ -662,40 +740,20 @@ def draw_controls(screen, menu_state):
|
||||
text_rect = text_surface.get_rect(center=(config.screen_width // 2, rect_y + 10 + i * line_height + line_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
def draw_validation_transition(screen, platform_index):
|
||||
"""Affiche une animation de transition pour la sélection d’une plateforme."""
|
||||
platform_dict = config.platform_dicts[platform_index]
|
||||
image = load_system_image(platform_dict)
|
||||
if not image:
|
||||
return
|
||||
orig_width, orig_height = image.get_width(), image.get_height()
|
||||
base_size = int(config.screen_width * 0.0781) # ~150px pour 1920p
|
||||
start_time = pygame.time.get_ticks()
|
||||
duration = 500
|
||||
while pygame.time.get_ticks() - start_time < duration:
|
||||
draw_gradient(screen, (28, 37, 38), (47, 59, 61))
|
||||
elapsed = pygame.time.get_ticks() - start_time
|
||||
scale = 2.0 + (2.0 * elapsed / duration) if elapsed < duration / 2 else 3.0 - (2.0 * elapsed / duration)
|
||||
new_width = int(base_size * scale)
|
||||
new_height = int(base_size * scale)
|
||||
scaled_image = pygame.transform.smoothscale(image, (new_width, new_height))
|
||||
image_rect = scaled_image.get_rect(center=(config.screen_width // 2, config.screen_height // 2))
|
||||
screen.blit(scaled_image, image_rect)
|
||||
pygame.display.flip()
|
||||
pygame.time.wait(10)
|
||||
|
||||
#Menu pause
|
||||
def draw_pause_menu(screen, selected_option):
|
||||
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Historique, Quitter."""
|
||||
"""Dessine le menu pause avec les options Aide, Configurer contrôles, Historique, Redownload game cache, Quitter."""
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
options = [
|
||||
"Controls",
|
||||
"Remap controls",
|
||||
"History",
|
||||
"Redownload Games cache",
|
||||
"Quit"
|
||||
]
|
||||
|
||||
menu_width = int(config.screen_width * 0.2083) # ~400px pour 1920p
|
||||
menu_width = int(config.screen_width * 0.8) # ~400px pour 1920p
|
||||
line_height = config.font.get_height() + 10
|
||||
text_height = len(options) * line_height
|
||||
margin_top_bottom = 20
|
||||
@@ -712,13 +770,7 @@ def draw_pause_menu(screen, selected_option):
|
||||
text_rect = text_surface.get_rect(center=(config.screen_width // 2, menu_y + margin_top_bottom + i * line_height + line_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
def get_control_display(action, default):
|
||||
"""Récupère le nom d'affichage d'une action depuis controls_config."""
|
||||
if not config.controls_config:
|
||||
logger.warning(f"controls_config vide pour l'action {action}, utilisation de la valeur par défaut")
|
||||
return default
|
||||
return config.controls_config.get(action, {}).get('display', default)
|
||||
|
||||
#Menu aide controles
|
||||
def draw_controls_help(screen, previous_state):
|
||||
"""Affiche la liste des contrôles pour l'état précédent du menu."""
|
||||
common_controls = {
|
||||
@@ -731,6 +783,7 @@ def draw_controls_help(screen, previous_state):
|
||||
"page_up": lambda action: f"{get_control_display('page_up', 'Q/LB')} : {action}",
|
||||
"page_down": lambda action: f"{get_control_display('page_down', 'E/RB')} : {action}",
|
||||
"filter": lambda action: f"{get_control_display('filter', 'Select')} : {action}",
|
||||
"history": lambda action: f"{get_control_display('history', 'H')} : {action}",
|
||||
"delete": lambda: f"{get_control_display('delete', 'Retour Arrière')} : Supprimer",
|
||||
"space": lambda: f"{get_control_display('space', 'Espace')} : Espace"
|
||||
}
|
||||
@@ -744,11 +797,13 @@ def draw_controls_help(screen, previous_state):
|
||||
common_controls["confirm"]("Sélectionner"),
|
||||
common_controls["cancel"]("Quitter"),
|
||||
common_controls["start"](),
|
||||
common_controls["history"]("Historique"),
|
||||
*( [common_controls["progress"]("Progression")] if config.download_tasks else [])
|
||||
],
|
||||
"game": [
|
||||
common_controls["confirm"](f"{'Valider' if config.search_mode else 'Télécharger'}"),
|
||||
common_controls["cancel"](f"{'Annuler' if config.search_mode else 'Retour'}"),
|
||||
common_controls["history"]("Historique"),
|
||||
*( [
|
||||
common_controls["delete"](),
|
||||
common_controls["space"]()
|
||||
@@ -814,63 +869,13 @@ def draw_controls_help(screen, previous_state):
|
||||
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))
|
||||
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)
|
||||
|
||||
#Menu Quitter Appli
|
||||
def draw_confirm_dialog(screen):
|
||||
"""Affiche la boîte de dialogue de confirmation pour quitter."""
|
||||
global OVERLAY
|
||||
logger.debug("Rendu de draw_confirm_dialog")
|
||||
#logger.debug("Rendu de draw_confirm_dialog")
|
||||
# Vérifier si OVERLAY est valide, sinon le recréer
|
||||
if OVERLAY is None or OVERLAY.get_size() != (config.screen_width, config.screen_height):
|
||||
OVERLAY = pygame.Surface((config.screen_width, config.screen_height), pygame.SRCALPHA)
|
||||
@@ -902,34 +907,68 @@ def draw_confirm_dialog(screen):
|
||||
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."""
|
||||
#draw_redownload_game_cache_dialog
|
||||
def draw_redownload_game_cache_dialog(screen):
|
||||
"""Affiche la boîte de dialogue de confirmation pour retélécharger le cache des jeux."""
|
||||
global OVERLAY
|
||||
#logger.debug("Rendu de draw_redownload_game_cache_dialog")
|
||||
# Vérifier si OVERLAY est valide, sinon le recréer
|
||||
if OVERLAY is None or OVERLAY.get_size() != (config.screen_width, config.screen_height):
|
||||
OVERLAY = pygame.Surface((config.screen_width, config.screen_height), pygame.SRCALPHA)
|
||||
OVERLAY.fill((0, 0, 0, 128))
|
||||
logger.debug("OVERLAY recréé dans draw_redownload_game_cache_dialog")
|
||||
|
||||
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
|
||||
message = "Retélécharger le cache des jeux ?"
|
||||
wrapped_message = wrap_text(message, config.small_font, config.screen_width - 80)
|
||||
line_height = config.small_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)
|
||||
max_text_width = max([config.small_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 = config.small_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_text = config.small_font.render("Oui", True, (0, 150, 255) if config.redownload_confirm_selection == 1 else (255, 255, 255))
|
||||
no_text = config.small_font.render("Non", True, (0, 150, 255) if config.redownload_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)
|
||||
screen.blit(no_text, no_rect)
|
||||
|
||||
def draw_popup(screen):
|
||||
"""Dessine un popup avec un message et un compte à rebours."""
|
||||
screen.blit(OVERLAY, (0, 0))
|
||||
|
||||
popup_width = int(config.screen_width * 0.8) # ~400px pour 1920p
|
||||
line_height = config.small_font.get_height() + 10
|
||||
text_lines = config.popup_message.split('\n')
|
||||
text_height = len(text_lines) * line_height
|
||||
margin_top_bottom = 20
|
||||
popup_height = text_height + 2 * margin_top_bottom + line_height # Espace pour le compte à rebours
|
||||
popup_x = (config.screen_width - popup_width) // 2
|
||||
popup_y = (config.screen_height - popup_height) // 2
|
||||
|
||||
pygame.draw.rect(screen, (50, 50, 50, 200), (popup_x, popup_y, popup_width, popup_height), border_radius=10)
|
||||
pygame.draw.rect(screen, (255, 255, 255), (popup_x, popup_y, popup_width, popup_height), 2, border_radius=10)
|
||||
|
||||
for i, line in enumerate(text_lines):
|
||||
text_surface = config.small_font.render(line, True, (255, 255, 255))
|
||||
text_rect = text_surface.get_rect(center=(config.screen_width // 2, popup_y + margin_top_bottom + i * line_height + line_height // 2))
|
||||
screen.blit(text_surface, text_rect)
|
||||
|
||||
# Afficher le compte à rebours
|
||||
remaining_time = max(0, config.popup_timer // 1000) # Convertir ms en secondes
|
||||
countdown_text = f"Ce message se fermera dans {remaining_time} seconde{'s' if remaining_time != 1 else ''}"
|
||||
countdown_surface = config.small_font.render(countdown_text, True, (255, 255, 255))
|
||||
countdown_rect = countdown_surface.get_rect(center=(config.screen_width // 2, popup_y + margin_top_bottom + len(text_lines) * line_height + line_height // 2))
|
||||
screen.blit(countdown_surface, countdown_rect)
|
||||
202
network.py
202
network.py
@@ -3,7 +3,7 @@ import subprocess
|
||||
import re
|
||||
import os
|
||||
import threading
|
||||
import pygame
|
||||
import pygame # type: ignore
|
||||
import zipfile
|
||||
import json
|
||||
import time
|
||||
@@ -313,7 +313,7 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
||||
config.download_progress[url]["status"] = "Téléchargement"
|
||||
config.download_progress[url]["progress_percent"] = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
logger.debug(f"Progression: {downloaded}/{total_size} octets, {config.download_progress[url]['progress_percent']:.1f}%")
|
||||
#logger.debug(f"Progression: {downloaded}/{total_size} octets, {config.download_progress[url]['progress_percent']:.1f}%")
|
||||
|
||||
if is_zip_non_supported:
|
||||
with lock:
|
||||
@@ -356,7 +356,7 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False):
|
||||
config.menu_state = "download_result"
|
||||
config.needs_redraw = True # Forcer le redraw
|
||||
# Enregistrement dans l'historique
|
||||
add_to_history(platform, game_name, "Download_OK" if result[0] else "Erreur")
|
||||
add_to_history(platform, game_name, "OK" if result[0] else "Error")
|
||||
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'}")
|
||||
|
||||
@@ -387,12 +387,196 @@ def check_extension_before_download(game_name, platform, url):
|
||||
if is_supported:
|
||||
logger.debug(f"L'extension de {sanitized_name} est supportée pour {platform}")
|
||||
return (url, platform, game_name, False)
|
||||
elif is_archive:
|
||||
logger.debug(f"Archive {extension.upper()} détectée pour {sanitized_name}, extraction automatique prévue")
|
||||
return (url, platform, game_name, True)
|
||||
else:
|
||||
if is_archive:
|
||||
logger.debug(f"Fichier {extension.upper()} détecté pour {sanitized_name}, extraction automatique prévue")
|
||||
return (url, platform, game_name, True)
|
||||
logger.debug(f"L'extension de {sanitized_name} n'est pas supportée pour {platform}")
|
||||
return None
|
||||
logger.debug(f"Extension non supportée ({extension}) pour {sanitized_name}, avertissement affiché")
|
||||
return (url, platform, game_name, False)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur vérification extension {url}: {str(e)}")
|
||||
return None
|
||||
return None
|
||||
|
||||
def is_1fichier_url(url):
|
||||
"""Détecte si l'URL est un lien 1fichier."""
|
||||
return "1fichier.com" in url
|
||||
|
||||
|
||||
def download_from_1fichier(url, platform, game_name, is_zip_non_supported=False):
|
||||
"""Télécharge un fichier depuis 1fichier en utilisant l'API officielle."""
|
||||
logger.debug(f"Début téléchargement 1fichier: {game_name} depuis {url}, is_zip_non_supported={is_zip_non_supported}")
|
||||
result = [None, None]
|
||||
|
||||
def download_thread():
|
||||
logger.debug(f"Thread téléchargement 1fichier démarré pour {url}")
|
||||
try:
|
||||
# Nettoyer l'URL
|
||||
link = url.split('&af=')[0]
|
||||
|
||||
# Déterminer le répertoire de destination
|
||||
dest_dir = None
|
||||
for platform_dict in config.platform_dicts:
|
||||
if platform_dict["platform"] == platform:
|
||||
dest_dir = platform_dict.get("folder")
|
||||
break
|
||||
if not dest_dir:
|
||||
logger.warning(f"Aucun dossier 'folder' trouvé pour la plateforme {platform}")
|
||||
dest_dir = os.path.join("/userdata/roms", platform)
|
||||
|
||||
logger.debug(f"Vérification répertoire destination: {dest_dir}")
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
if not os.access(dest_dir, os.W_OK):
|
||||
raise PermissionError(f"Pas de permission d'écriture dans {dest_dir}")
|
||||
|
||||
# Préparer les en-têtes et le payload
|
||||
headers = {
|
||||
"Authorization": f"Bearer {config.API_KEY_1FICHIER}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
payload = {
|
||||
"url": link,
|
||||
"pretty": 1
|
||||
}
|
||||
|
||||
# Étape 1 : Obtenir les informations du fichier
|
||||
logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/file/info.cgi pour {url}")
|
||||
response = requests.post("https://api.1fichier.com/v1/file/info.cgi", headers=headers, json=payload, timeout=30)
|
||||
logger.debug(f"Réponse reçue, status: {response.status_code}")
|
||||
response.raise_for_status()
|
||||
file_info = response.json()
|
||||
|
||||
if "error" in file_info and file_info["error"] == "Resource not found":
|
||||
logger.error(f"Le fichier {game_name} n'existe pas sur 1fichier")
|
||||
result[0] = False
|
||||
result[1] = f"Le fichier {game_name} n'existe pas"
|
||||
return
|
||||
|
||||
filename = file_info.get("filename", "").strip()
|
||||
if not filename:
|
||||
logger.error("Impossible de récupérer le nom du fichier")
|
||||
result[0] = False
|
||||
result[1] = "Impossible de récupérer le nom du fichier"
|
||||
return
|
||||
|
||||
sanitized_filename = sanitize_filename(filename)
|
||||
dest_path = os.path.join(dest_dir, sanitized_filename)
|
||||
logger.debug(f"Chemin destination: {dest_path}")
|
||||
|
||||
# Étape 2 : Obtenir le jeton de téléchargement
|
||||
logger.debug(f"Envoi requête POST à https://api.1fichier.com/v1/download/get_token.cgi pour {url}")
|
||||
response = requests.post("https://api.1fichier.com/v1/download/get_token.cgi", headers=headers, json=payload, timeout=30)
|
||||
logger.debug(f"Réponse reçue, status: {response.status_code}")
|
||||
response.raise_for_status()
|
||||
download_info = response.json()
|
||||
|
||||
final_url = download_info.get("url")
|
||||
if not final_url:
|
||||
logger.error("Impossible de récupérer l'URL de téléchargement")
|
||||
result[0] = False
|
||||
result[1] = "Impossible de récupérer l'URL de téléchargement"
|
||||
return
|
||||
|
||||
# Étape 3 : Initialiser la progression
|
||||
lock = threading.Lock()
|
||||
with lock:
|
||||
config.download_progress[url] = {
|
||||
"downloaded_size": 0,
|
||||
"total_size": 0,
|
||||
"status": "Téléchargement",
|
||||
"progress_percent": 0,
|
||||
"game_name": game_name
|
||||
}
|
||||
config.needs_redraw = True
|
||||
logger.debug(f"Progression initialisée pour {url}")
|
||||
|
||||
# Étape 4 : Télécharger le fichier
|
||||
retries = 10
|
||||
retry_delay = 10
|
||||
for attempt in range(retries):
|
||||
try:
|
||||
logger.debug(f"Tentative {attempt + 1} : Envoi requête GET à {final_url}")
|
||||
with requests.get(final_url, stream=True, headers={'User-Agent': 'Mozilla/5.0'}, timeout=30) as response:
|
||||
logger.debug(f"Réponse reçue, status: {response.status_code}")
|
||||
response.raise_for_status()
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
logger.debug(f"Taille totale: {total_size} octets")
|
||||
with lock:
|
||||
config.download_progress[url]["total_size"] = total_size
|
||||
config.needs_redraw = True
|
||||
|
||||
downloaded = 0
|
||||
with open(dest_path, 'wb') as f:
|
||||
logger.debug(f"Ouverture fichier: {dest_path}")
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = downloaded
|
||||
config.download_progress[url]["status"] = "Téléchargement"
|
||||
config.download_progress[url]["progress_percent"] = (downloaded / total_size * 100) if total_size > 0 else 0
|
||||
config.needs_redraw = True
|
||||
#logger.debug(f"Progression: {downloaded}/{total_size} octets, {config.download_progress[url]['progress_percent']:.1f}%")
|
||||
|
||||
# Étape 5 : Extraire si nécessaire
|
||||
if is_zip_non_supported:
|
||||
with lock:
|
||||
config.download_progress[url]["downloaded_size"] = 0
|
||||
config.download_progress[url]["total_size"] = 0
|
||||
config.download_progress[url]["status"] = "Extracting"
|
||||
config.download_progress[url]["progress_percent"] = 0
|
||||
config.needs_redraw = True
|
||||
extension = os.path.splitext(dest_path)[1].lower()
|
||||
if extension == ".zip":
|
||||
success, msg = extract_zip(dest_path, dest_dir, url)
|
||||
elif extension == ".rar":
|
||||
success, msg = extract_rar(dest_path, dest_dir, url)
|
||||
else:
|
||||
raise Exception(f"Type d'archive non supporté: {extension}")
|
||||
if not success:
|
||||
raise Exception(f"Échec de l'extraction de l'archive: {msg}")
|
||||
result[0] = True
|
||||
result[1] = f"Downloaded / extracted : {game_name}"
|
||||
else:
|
||||
os.chmod(dest_path, 0o644)
|
||||
logger.debug(f"Téléchargement terminé: {dest_path}")
|
||||
result[0] = True
|
||||
result[1] = f"Download_OK : {game_name}"
|
||||
return
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Tentative {attempt + 1} échouée : {e}")
|
||||
if attempt < retries - 1:
|
||||
import time
|
||||
time.sleep(retry_delay)
|
||||
else:
|
||||
logger.error("Nombre maximum de tentatives atteint")
|
||||
result[0] = False
|
||||
result[1] = f"Échec du téléchargement après {retries} tentatives"
|
||||
return
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Erreur API 1fichier : {e}")
|
||||
result[0] = False
|
||||
result[1] = f"Erreur lors de la requête API, la clé est peut etre incorrecte: {str(e)}"
|
||||
|
||||
finally:
|
||||
logger.debug(f"Thread téléchargement 1fichier terminé pour {url}")
|
||||
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
|
||||
# Enregistrement dans l'historique
|
||||
add_to_history(platform, game_name, "Download_OK" if result[0] else "Erreur")
|
||||
config.history = load_history()
|
||||
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)
|
||||
logger.debug(f"Démarrage thread pour {url}")
|
||||
thread.start()
|
||||
thread.join()
|
||||
logger.debug(f"Thread rejoint pour {url}")
|
||||
|
||||
return result[0], result[1]
|
||||
3997
output.json
Normal file
3997
output.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2146,7 +2146,15 @@
|
||||
".elf",
|
||||
".dol",
|
||||
".m3u",
|
||||
".json"
|
||||
".xci"
|
||||
]
|
||||
},
|
||||
{
|
||||
"system": "switch",
|
||||
"folder": "/userdata/roms/switch",
|
||||
"extensions": [
|
||||
".nsp",
|
||||
".xci"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
51
utils.py
51
utils.py
@@ -1,4 +1,4 @@
|
||||
import pygame
|
||||
import pygame # type: ignore
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
@@ -25,18 +25,52 @@ def create_placeholder(width=400):
|
||||
return placeholder
|
||||
|
||||
def truncate_text_middle(text, font, max_width):
|
||||
"""Tronque le texte en insérant '...' au milieu."""
|
||||
"""Tronque le texte en insérant '...' au milieu, en préservant le début et la fin, sans extension de fichier."""
|
||||
# Supprimer l'extension de fichier
|
||||
text = text.rsplit('.', 1)[0] if '.' in text else text
|
||||
text_width = font.size(text)[0]
|
||||
if text_width <= max_width:
|
||||
return text
|
||||
ellipsis = "..."
|
||||
ellipsis_width = font.size(ellipsis)[0]
|
||||
max_text_width = max_width - ellipsis_width
|
||||
while text_width > max_text_width and len(text) > 0:
|
||||
text = text[:-1]
|
||||
text_width = font.size(text)[0]
|
||||
mid = len(text) // 2
|
||||
return text[:mid] + ellipsis + text[mid:]
|
||||
if max_text_width <= 0:
|
||||
return ellipsis
|
||||
|
||||
# Diviser la largeur disponible entre début et fin
|
||||
chars = list(text)
|
||||
left = []
|
||||
right = []
|
||||
left_width = 0
|
||||
right_width = 0
|
||||
left_idx = 0
|
||||
right_idx = len(chars) - 1
|
||||
|
||||
while left_idx <= right_idx and (left_width + right_width) < max_text_width:
|
||||
if left_idx < right_idx:
|
||||
left.append(chars[left_idx])
|
||||
left_width = font.size(''.join(left))[0]
|
||||
if left_width + right_width > max_text_width:
|
||||
left.pop()
|
||||
break
|
||||
left_idx += 1
|
||||
if left_idx <= right_idx:
|
||||
right.insert(0, chars[right_idx])
|
||||
right_width = font.size(''.join(right))[0]
|
||||
if left_width + right_width > max_text_width:
|
||||
right.pop(0)
|
||||
break
|
||||
right_idx -= 1
|
||||
|
||||
# Reculer jusqu'à un espace pour éviter de couper un mot
|
||||
while left and left[-1] != ' ' and left_width + right_width > max_text_width:
|
||||
left.pop()
|
||||
left_width = font.size(''.join(left))[0] if left else 0
|
||||
while right and right[0] != ' ' and left_width + right_width > max_text_width:
|
||||
right.pop(0)
|
||||
right_width = font.size(''.join(right))[0] if right else 0
|
||||
|
||||
return ''.join(left).rstrip() + ellipsis + ''.join(right).lstrip()
|
||||
|
||||
def truncate_text_end(text, font, max_width):
|
||||
"""Tronque le texte à la fin pour qu'il tienne dans max_width avec la police donnée."""
|
||||
@@ -65,6 +99,9 @@ def sanitize_filename(name):
|
||||
|
||||
def wrap_text(text, font, max_width):
|
||||
"""Divise le texte en lignes pour respecter la largeur maximale."""
|
||||
if not isinstance(text, str):
|
||||
text = str(text) if text is not None else ""
|
||||
|
||||
words = text.split(' ')
|
||||
lines = []
|
||||
current_line = ''
|
||||
|
||||
Reference in New Issue
Block a user