diff --git a/ports/RGSX/assets/xdvdfs.exe b/ports/RGSX/assets/xdvdfs.exe new file mode 100644 index 0000000..f46a676 Binary files /dev/null and b/ports/RGSX/assets/xdvdfs.exe differ diff --git a/ports/RGSX/config.py b/ports/RGSX/config.py index f64813c..b444252 100644 --- a/ports/RGSX/config.py +++ b/ports/RGSX/config.py @@ -4,7 +4,7 @@ import sys import logging # Version actuelle de l'application -app_version = "1.9.8.0" +app_version = "1.9.8.1" def get_application_root(): """Détermine le dossier de l'application de manière portable.""" @@ -64,7 +64,9 @@ HISTORY_PATH = os.path.join(SAVE_FOLDER, "history.json") LANGUAGE_CONFIG_PATH = os.path.join(SAVE_FOLDER, "language.json") JSON_EXTENSIONS = os.path.join(APP_FOLDER, "rom_extensions.json") MUSIC_CONFIG_PATH = os.path.join(SAVE_FOLDER, "music_config.json") -UNRAR_EXE = os.path.join(SAVE_FOLDER, "unrar.exe") +UNRAR_EXE = os.path.join(APP_FOLDER,"assets", "unrar.exe") +XDVDFS_EXE = os.path.join(APP_FOLDER,"assets", "xdvdfs.exe") +XDVDFS_LINUX = os.path.join(APP_FOLDER,"assets", "xdvdfs") # URL @@ -73,6 +75,8 @@ OTA_VERSION_ENDPOINT = os.path.join(OTA_SERVER_URL, "version.json") OTA_UPDATE_ZIP = os.path.join(OTA_SERVER_URL, "RGSX.zip") OTA_data_ZIP = os.path.join(OTA_SERVER_URL, "rgsx-data.zip") unrar_download_exe = os.path.join(OTA_SERVER_URL, "unrar.exe") +xdvdfs_download_exe = os.path.join(OTA_SERVER_URL, "xdvdfs.exe") +xdvdfs_download_linux = os.path.join(OTA_SERVER_URL, "xdvdfs") # Constantes pour la répétition automatique dans pause_menu REPEAT_DELAY = 350 # Délai initial avant répétition (ms) - augmenté pour éviter les doubles actions diff --git a/ports/RGSX/display.py b/ports/RGSX/display.py index c5a039f..8c4a647 100644 --- a/ports/RGSX/display.py +++ b/ports/RGSX/display.py @@ -658,6 +658,18 @@ def draw_game_scrollbar(screen, scroll_offset, total_items, visible_items, x, y, scrollbar_y = y + (game_area_height - scrollbar_height) * (scroll_offset / max(1, total_items - visible_items)) pygame.draw.rect(screen, THEME_COLORS["fond_lignes"], (x, scrollbar_y, 15, scrollbar_height), border_radius=4) +def format_size(size): + """Convertit une taille en octets en format lisible.""" + if not isinstance(size, (int, float)) or size == 0: + return "N/A" + + for unit in ['o', 'Ko', 'Mo', 'Go', 'To']: + if size < 1024.0: + return f"{size:.1f} {unit}" + size /= 1024.0 + return f"{size:.1f} Po" + + def draw_history_list(screen): # logger.debug(f"Dessin historique, history={config.history}, needs_redraw={config.needs_redraw}") history = config.history if hasattr(config, 'history') else load_history() @@ -669,7 +681,7 @@ def draw_history_list(screen): if entry.get("status") in ["Téléchargement", "downloading"]: speed = entry.get("speed", 0.0) if speed and speed > 0: - speed_str = f" - Téléchargement : {speed:.2f} Mo/s" + speed_str = f" - {speed:.2f} Mo/s" break screen.blit(OVERLAY, (0, 0)) @@ -684,13 +696,15 @@ def draw_history_list(screen): # Define column widths as percentages of available space column_width_percentages = { - "platform": 0.25, # platform column + "platform": 0.20, # platform column "game_name": 0.50, # game name column - "status": 0.25 # status column + "size": 0.10, # size column + "status": 0.20 # status column } available_width = int(0.95 * config.screen_width - 60) # Total available width for columns col_platform_width = int(available_width * column_width_percentages["platform"]) col_game_width = int(available_width * column_width_percentages["game_name"]) + col_size_width = int(available_width * column_width_percentages["size"]) col_status_width = int(available_width * column_width_percentages["status"]) rect_width = int(0.95 * config.screen_width) @@ -706,7 +720,7 @@ def draw_history_list(screen): speed = history[config.current_history_item].get("speed", 0.0) if speed > 0: speed_str = f"{speed:.2f} Mo/s" - title_text = _("history_title").format(history_count) + f" - Téléchargement : {speed_str}" + title_text = _("history_title").format(history_count) + f" {speed_str}" else: title_text = _("history_title").format(history_count) title_surface = config.title_font.render(title_text, True, THEME_COLORS["text"]) @@ -751,12 +765,13 @@ def draw_history_list(screen): pygame.draw.rect(screen, THEME_COLORS["button_idle"], (rect_x, rect_y, rect_width, rect_height), border_radius=12) pygame.draw.rect(screen, THEME_COLORS["border"], (rect_x, rect_y, rect_width, rect_height), 2, border_radius=12) - headers = [_("history_column_system"), _("history_column_game"), _("history_column_status")] + headers = [_("history_column_system"), _("history_column_game"), _("history_column_size"), _("history_column_status")] 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, - rect_x + 20 + col_platform_width + col_game_width + col_status_width // 2 + rect_x + 20 + col_platform_width + col_game_width + col_size_width // 2, + rect_x + 20 + col_platform_width + col_game_width + col_size_width + col_status_width // 2 ] for header, x_pos in zip(headers, header_x_positions): text_surface = config.small_font.render(header, True, THEME_COLORS["text"]) @@ -770,8 +785,16 @@ def draw_history_list(screen): entry = history[i] platform = entry.get("platform", "Inconnu") game_name = entry.get("game_name", "Inconnu") + + # Correction du calcul de la taille + size = entry.get("total_size", 0) + color = THEME_COLORS["fond_lignes"] if i == config.current_history_item else THEME_COLORS["text"] + size_text = format_size(size) + status = entry.get("status", "Inconnu") progress = entry.get("progress", 0) + + # Personnaliser l'affichage du statut if status in ["Téléchargement", "downloading"]: status_text = _("history_status_downloading").format(progress) @@ -801,19 +824,21 @@ def draw_history_list(screen): status_text = status #logger.debug(f"Affichage statut inconnu: {game_name}, status={status_text}") - color = THEME_COLORS["fond_lignes"] if i == config.current_history_item else THEME_COLORS["text"] 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) + size_text = truncate_text_end(size_text, config.small_font, col_size_width - 10) status_text = truncate_text_middle(status_text, config.small_font, col_status_width - 10, is_filename=False) 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) + size_surface = config.small_font.render(size_text, True, color) # Correction ici status_surface = config.small_font.render(status_text, True, color) platform_rect = platform_surface.get_rect(center=(header_x_positions[0], y_pos)) game_rect = game_surface.get_rect(center=(header_x_positions[1], y_pos)) - status_rect = status_surface.get_rect(center=(header_x_positions[2], y_pos)) + size_rect = size_surface.get_rect(center=(header_x_positions[2], y_pos)) + status_rect = status_surface.get_rect(center=(header_x_positions[3], y_pos)) if i == config.current_history_item: glow_surface = pygame.Surface((rect_width - 40, line_height), pygame.SRCALPHA) @@ -822,6 +847,7 @@ def draw_history_list(screen): screen.blit(platform_surface, platform_rect) screen.blit(game_surface, game_rect) + screen.blit(size_surface, size_rect) screen.blit(status_surface, status_rect) if len(history) > items_per_page: diff --git a/ports/RGSX/languages/de.json b/ports/RGSX/languages/de.json index 45baa62..535af51 100644 --- a/ports/RGSX/languages/de.json +++ b/ports/RGSX/languages/de.json @@ -37,6 +37,7 @@ "history_empty": "Keine Downloads im Verlauf", "history_column_system": "System", "history_column_game": "Spielname", + "history_column_size": "Größe", "history_column_status": "Status", "history_status_downloading": "Download: {0}%", "history_status_extracting": "Extrahieren: {0}%", diff --git a/ports/RGSX/languages/en.json b/ports/RGSX/languages/en.json index 699a111..f6c833a 100644 --- a/ports/RGSX/languages/en.json +++ b/ports/RGSX/languages/en.json @@ -37,6 +37,7 @@ "history_empty": "No downloads in history", "history_column_system": "System", "history_column_game": "Game name", + "history_column_size": "Size", "history_column_status": "Status", "history_status_downloading": "Downloading: {0}%", "history_status_extracting": "Extracting: {0}%", diff --git a/ports/RGSX/languages/es.json b/ports/RGSX/languages/es.json index 7ea631f..769b6d7 100644 --- a/ports/RGSX/languages/es.json +++ b/ports/RGSX/languages/es.json @@ -37,6 +37,7 @@ "history_empty": "No hay descargas en el historial", "history_column_system": "Sistema", "history_column_game": "Nombre del juego", + "history_column_size": "Tamaño", "history_column_status": "Estado", "history_status_downloading": "Descargando: {0}%", "history_status_extracting": "Extrayendo: {0}%", diff --git a/ports/RGSX/languages/fr.json b/ports/RGSX/languages/fr.json index e4d9897..062117f 100644 --- a/ports/RGSX/languages/fr.json +++ b/ports/RGSX/languages/fr.json @@ -37,6 +37,7 @@ "history_empty": "Aucun téléchargement dans l'historique", "history_column_system": "Système", "history_column_game": "Nom du jeu", + "history_column_size": "Taille", "history_column_status": "État", "history_status_downloading": "Téléchargement : {0}%", "history_status_extracting": "Extraction : {0}%", diff --git a/ports/RGSX/network.py b/ports/RGSX/network.py index b802c2f..6af19e0 100644 --- a/ports/RGSX/network.py +++ b/ports/RGSX/network.py @@ -215,6 +215,12 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas total_size = int(response.headers.get('content-length', 0)) logger.debug(f"Taille totale: {total_size} octets") + if isinstance(config.history, list): + for entry in config.history: + if "url" in entry and entry["url"] == url: + entry["total_size"] = total_size # Ajouter la taille totale + save_history(config.history) + break # Initialiser la progression avec task_id progress_queues[task_id].put((task_id, 0, total_size)) @@ -445,6 +451,12 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported= response.raise_for_status() total_size = int(response.headers.get('content-length', 0)) logger.debug(f"Taille totale: {total_size} octets") + if isinstance(config.history, list): + for entry in config.history: + if "url" in entry and entry["url"] == url: + entry["total_size"] = total_size # Ajouter la taille totale + save_history(config.history) + break with lock: if isinstance(config.history, list): for entry in config.history: diff --git a/ports/RGSX/utils.py b/ports/RGSX/utils.py index 6aa5827..fe17614 100644 --- a/ports/RGSX/utils.py +++ b/ports/RGSX/utils.py @@ -421,7 +421,13 @@ def extract_zip(zip_path, dest_dir, url): config.needs_redraw = True break os.chmod(file_path, 0o644) - + # Vérifier si c'est un dossier xbox et le traiter si nécessaire + xbox_dir = os.path.join(os.path.dirname(os.path.dirname(config.APP_FOLDER)), "xbox") + if dest_dir == xbox_dir: + success, error_msg = handle_xbox(dest_dir) + if not success: + return False, error_msg + for root, dirs, files in os.walk(dest_dir): for dir_name in dirs: os.chmod(os.path.join(root, dir_name), 0o755) @@ -669,6 +675,88 @@ def handle_ps3(dest_dir): return True, None +def handle_xbox(dest_dir): + """Gère la conversion des fichiers Xbox extraits.""" + logger.debug(f"Traitement spécifique Xbox dans: {dest_dir}") + + # Attendre un peu que tous les processus d'extraction se terminent + time.sleep(2) + system_type = platform.system() + if system_type == "Windows": + # Sur Windows; telecharger le fichier exe + XDVDFS_EXE = config.XDVDFS_EXE + if not os.path.exists(XDVDFS_EXE): + logger.warning("xdvdfs.exe absent, téléchargement en cours...") + try: + import urllib.request + os.makedirs(os.path.dirname(XDVDFS_EXE), exist_ok=True) + urllib.request.urlretrieve(config.xdvdfs_download_exe, XDVDFS_EXE) + logger.info(f"xdvdfs.exe téléchargé dans {XDVDFS_EXE}") + except Exception as e: + logger.error(f"Impossible de télécharger xdvdfs.exe: {str(e)}") + return False, _("utils_xdvdfs_unavailable") + xdvdfs_cmd = [XDVDFS_EXE] + ["pack"] + + else: + # Linux/Batocera : télécharger le fichier xdvdfs + # Vérifier si xdvdfs est disponible + XDVDFS_LINUX = config.XDVDFS_LINUX + if not os.path.exists(XDVDFS_LINUX): + logger.warning("xdvdfs non trouvé, téléchargement en cours...") + try: + import urllib.request + os.makedirs(os.path.dirname(XDVDFS_LINUX), exist_ok=True) + urllib.request.urlretrieve(config.xdvdfs_download_linux, XDVDFS_LINUX) + logger.info(f"xdvdfs téléchargé dans {XDVDFS_LINUX}") + except Exception as e: + logger.error(f"Impossible de télécharger xdvdfs: {str(e)}") + return False, _("utils_xdvdfs_unavailable") + xdvdfs_cmd = XDVDFS_LINUX + ["pack"] + try: + # Chercher les fichiers ISO à convertir + iso_files = [] + for root, dirs, files in os.walk(dest_dir): + for file in files: + if file.lower().endswith('.iso'): + iso_files.append(os.path.join(root, file)) + + if not iso_files: + logger.warning("Aucun fichier ISO xbox trouvé") + return True, None + + for iso_xbox_source in iso_files: + logger.debug(f"Traitement de l'ISO Xbox: {iso_xbox_source}") + xiso_dest = os.path.splitext(iso_xbox_source)[0] + "_xbox.iso" + + # Convertir l'ISO avec xdvdfs + logger.debug(f"Conversion de l'ISO xbox : {iso_xbox_source} -> {xiso_dest}") + process = subprocess.run( + xdvdfs_cmd + [iso_xbox_source, xiso_dest], + capture_output=True, + text=True + ) + + if process.returncode != 0: + logger.error(f"Erreur lors de la conversion de l'ISO: {process.stderr}") + return False, f"Erreur lors de la conversion de l'ISO: {process.stderr}" + + # Vérifier que l'ISO converti a été créé + if os.path.exists(xiso_dest): + logger.info(f"ISO converti avec succès: {xiso_dest}") + # Remplacer l'ISO original par l'ISO converti + os.remove(iso_xbox_source) + os.rename(xiso_dest, iso_xbox_source) + logger.debug(f"ISO original remplacé par la version convertie") + else: + logger.error(f"L'ISO converti n'a pas été créé: {xiso_dest}") + return False, "Échec de la conversion de l'ISO" + + return True, "Conversion Xbox terminée avec succès" + + except Exception as e: + logger.error(f"Erreur lors de la conversion Xbox: {str(e)}") + return False, f"Erreur lors de la conversion: {str(e)}" + def play_random_music(music_files, music_folder, current_music=None):