forked from Mirrors/RGSX
v2.1.0.1 -
- correct BIOS download folder and extraction - Implement old unused file deletion at startup - Silence ALSA warnings and filter stderr for better logging
This commit is contained in:
@@ -26,7 +26,7 @@ from controls import handle_controls, validate_menu_state, process_key_repeats,
|
||||
from controls_mapper import load_controls_config, map_controls, draw_controls_mapping, get_actions
|
||||
from utils import (
|
||||
detect_non_pc, load_sources, check_extension_before_download, extract_zip_data,
|
||||
play_random_music, load_music_config
|
||||
play_random_music, load_music_config, silence_alsa_warnings, enable_alsa_stderr_filter
|
||||
)
|
||||
from history import load_history, save_history
|
||||
from config import OTA_data_ZIP
|
||||
@@ -77,6 +77,7 @@ def _run_windows_gamelist_update():
|
||||
|
||||
_run_windows_gamelist_update()
|
||||
|
||||
|
||||
# Initialisation de Pygame
|
||||
pygame.init()
|
||||
pygame.joystick.init()
|
||||
@@ -84,6 +85,15 @@ logger.debug("------------------------------------------------------------------
|
||||
logger.debug("---------------------------DEBUT LOG--------------------------------")
|
||||
logger.debug("--------------------------------------------------------------------")
|
||||
|
||||
# Nettoyage des anciens fichiers de paramètres au démarrage
|
||||
try:
|
||||
from rgsx_settings import delete_old_files
|
||||
delete_old_files()
|
||||
logger.info("Nettoyage des anciens fichiers effectué au démarrage")
|
||||
except Exception as e:
|
||||
logger.exception(f"Échec du nettoyage des anciens fichiers: {e}")
|
||||
|
||||
|
||||
#Récupération des noms des joysticks si pas de joystick connecté, verifier si clavier connecté
|
||||
joystick_names = [pygame.joystick.Joystick(i).get_name() for i in range(pygame.joystick.get_count())]
|
||||
if not joystick_names:
|
||||
@@ -186,6 +196,13 @@ config.repeat_last_action = 0
|
||||
# Initialisation des variables pour la popup de musique
|
||||
|
||||
|
||||
# Réduction du bruit ALSA (VM Batocera/alsa)
|
||||
try:
|
||||
silence_alsa_warnings()
|
||||
enable_alsa_stderr_filter()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Initialisation du mixer Pygame
|
||||
pygame.mixer.pre_init(44100, -16, 2, 4096)
|
||||
pygame.mixer.init()
|
||||
|
||||
@@ -2,10 +2,9 @@ import pygame # type: ignore
|
||||
import os
|
||||
import logging
|
||||
import platform
|
||||
from rgsx_settings import load_rgsx_settings, save_rgsx_settings
|
||||
|
||||
# Version actuelle de l'application
|
||||
app_version = "2.1.0.0"
|
||||
app_version = "2.1.0.1"
|
||||
|
||||
def get_operating_system():
|
||||
"""Renvoie le nom du système d'exploitation."""
|
||||
|
||||
@@ -275,7 +275,7 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
|
||||
dest_dir = apply_symlink_path(config.ROMS_FOLDER, platform_folder)
|
||||
|
||||
# Spécifique: si le système est "BIOS" on force le dossier BIOS
|
||||
if platform == "- BIOS by TMCTV":
|
||||
if platform_folder == "bios" or platform == "BIOS" or platform == "- BIOS by TMCTV -":
|
||||
dest_dir = config.RETROBAT_DATA_FOLDER
|
||||
logger.debug(f"Plateforme 'BIOS' détectée, destination forcée vers RETROBAT_DATA_FOLDER: {dest_dir}")
|
||||
|
||||
@@ -417,7 +417,18 @@ async def download_rom(url, platform, game_name, is_zip_non_supported=False, tas
|
||||
os.chmod(dest_path, 0o644)
|
||||
logger.debug(f"Téléchargement terminé: {dest_path}")
|
||||
|
||||
if is_zip_non_supported:
|
||||
# Forcer extraction si plateforme BIOS même si le pré-check ne l'avait pas marqué
|
||||
force_extract = is_zip_non_supported
|
||||
if not force_extract:
|
||||
try:
|
||||
bios_like = {"BIOS", "- BIOS by TMCTV -", "- BIOS"}
|
||||
if platform_folder == "bios" or platform in bios_like:
|
||||
force_extract = True
|
||||
logger.debug("Extraction forcée activée pour BIOS")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if force_extract:
|
||||
logger.debug(f"Extraction automatique nécessaire pour {dest_path}")
|
||||
extension = os.path.splitext(dest_path)[1].lower()
|
||||
if extension == ".zip":
|
||||
@@ -559,10 +570,10 @@ async def download_from_1fichier(url, platform, game_name, is_zip_non_supported=
|
||||
dest_dir = apply_symlink_path(config.ROMS_FOLDER, platform_folder)
|
||||
logger.debug(f"Répertoire destination déterminé: {dest_dir}")
|
||||
|
||||
# Spécifique: si le système est "00 BIOS" on force le dossier BIOS
|
||||
if platform == "00 BIOS":
|
||||
# Spécifique: si le système est "- BIOS by TMCTV -" on force le dossier BIOS
|
||||
if platform_folder == "bios" or platform == "BIOS" or platform == "- BIOS by TMCTV -":
|
||||
dest_dir = config.RETROBAT_DATA_FOLDER
|
||||
logger.debug(f"Plateforme '00 BIOS' détectée, destination forcée vers RETROBAT_DATA_FOLDER: {dest_dir}")
|
||||
logger.debug(f"Plateforme '- BIOS by TMCTV -' détectée, destination forcée vers RETROBAT_DATA_FOLDER: {dest_dir}")
|
||||
|
||||
logger.debug(f"Vérification répertoire destination: {dest_dir}")
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
|
||||
@@ -1,20 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Module de gestion des paramètres RGSX
|
||||
Gère le fichier unifié rgsx_settings.json qui remplace les anciens fichiers :
|
||||
- accessibility.json
|
||||
- language.json
|
||||
- music_config.json
|
||||
- symlink_settings.json
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
#Fonction pour supprimer les anciens fichiers de paramètres non utilisés
|
||||
def delete_old_files():
|
||||
old_files_saves = [
|
||||
"accessibility.json",
|
||||
"language.json",
|
||||
"music_config.json",
|
||||
"symlink_settings.json",
|
||||
"sources.json"
|
||||
]
|
||||
old_files_app = [
|
||||
"rom_extensions.json",
|
||||
"es_input_parser.py",
|
||||
"sources.json"
|
||||
]
|
||||
|
||||
|
||||
for filename in old_files_saves:
|
||||
file_path = os.path.join(config.SAVE_FOLDER, filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"Ancien fichier supprimé : {file_path}")
|
||||
logger.info(f"Ancien fichier supprimé : {file_path}")
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de la suppression de {file_path} : {str(e)}")
|
||||
logger.error(f"Erreur lors de la suppression de {file_path} : {str(e)}")
|
||||
for filename in old_files_app:
|
||||
file_path = os.path.join(config.APP_FOLDER, filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"Ancien fichier supprimé : {file_path}")
|
||||
logger.info(f"Ancien fichier supprimé : {file_path}")
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de la suppression de {file_path} : {str(e)}")
|
||||
logger.error(f"Erreur lors de la suppression de {file_path} : {str(e)}")
|
||||
|
||||
def load_rgsx_settings():
|
||||
"""Charge tous les paramètres depuis rgsx_settings.json."""
|
||||
from config import RGSX_SETTINGS_PATH
|
||||
|
||||
@@ -31,6 +31,114 @@ unavailable_systems = []
|
||||
# Cache/process flags for extensions generation/loading
|
||||
|
||||
|
||||
def silence_alsa_warnings():
|
||||
"""Silence ALSA stderr spam (e.g., 'underrun occurred') on Linux by overriding the error handler.
|
||||
|
||||
Safe no-op on non-Linux or if libasound is unavailable.
|
||||
"""
|
||||
try:
|
||||
if platform.system() == "Linux":
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
lib = ctypes.util.find_library('asound')
|
||||
if not lib:
|
||||
return
|
||||
asound = ctypes.CDLL(lib)
|
||||
CErrorHandler = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p)
|
||||
|
||||
def py_error_handler(filename, line, function, err, fmt):
|
||||
return
|
||||
|
||||
handler = CErrorHandler(py_error_handler)
|
||||
try:
|
||||
asound.snd_lib_error_set_handler(handler)
|
||||
logger.info("ALSA warnings silenced via snd_lib_error_set_handler")
|
||||
except Exception as inner:
|
||||
logger.debug(f"snd_lib_error_set_handler not available: {inner}")
|
||||
except Exception as e:
|
||||
logger.debug(f"Unable to silence ALSA warnings: {e}")
|
||||
|
||||
|
||||
def enable_alsa_stderr_filter():
|
||||
"""Filter ALSA 'underrun occurred' spam from stderr by intercepting FD 2.
|
||||
|
||||
Works on Linux by routing stderr through a pipe and dropping matching lines.
|
||||
No-op on non-Linux systems. Safe to call multiple times; installs once.
|
||||
"""
|
||||
try:
|
||||
if platform.system() != "Linux":
|
||||
return
|
||||
# Avoid double-install
|
||||
if getattr(config, "_alsa_filter_installed", False):
|
||||
return
|
||||
|
||||
import os as _os
|
||||
import threading as _threading
|
||||
|
||||
patterns = [
|
||||
"ALSA lib pcm.c:",
|
||||
"snd_pcm_recover) underrun occurred",
|
||||
]
|
||||
|
||||
# Save original stderr fd and create pipe
|
||||
save_fd = _os.dup(2)
|
||||
rfd, wfd = _os.pipe()
|
||||
_os.dup2(wfd, 2) # redirect current process stderr to pipe writer
|
||||
_os.close(wfd)
|
||||
|
||||
stop_event = _threading.Event()
|
||||
|
||||
def _reader():
|
||||
try:
|
||||
with _os.fdopen(rfd, 'rb', buffering=0) as r, _os.fdopen(save_fd, 'wb', buffering=0) as orig:
|
||||
buf = b''
|
||||
while not stop_event.is_set():
|
||||
chunk = r.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
buf += chunk
|
||||
while b"\n" in buf:
|
||||
line, buf = buf.split(b"\n", 1)
|
||||
try:
|
||||
s = line.decode('utf-8', errors='ignore')
|
||||
if not any(p in s for p in patterns):
|
||||
orig.write(line + b"\n")
|
||||
orig.flush()
|
||||
except Exception:
|
||||
# Swallow any decode/write errors; keep filtering
|
||||
pass
|
||||
if buf:
|
||||
try:
|
||||
s = buf.decode('utf-8', errors='ignore')
|
||||
if not any(p in s for p in patterns):
|
||||
orig.write(buf)
|
||||
orig.flush()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
try:
|
||||
# Best-effort: restore original stderr on failure
|
||||
_os.dup2(save_fd, 2)
|
||||
except Exception:
|
||||
pass
|
||||
logger.debug(f"ALSA stderr filter reader error: {e}")
|
||||
|
||||
t = _threading.Thread(target=_reader, daemon=True)
|
||||
t.start()
|
||||
|
||||
def _restore():
|
||||
try:
|
||||
_os.dup2(save_fd, 2)
|
||||
except Exception:
|
||||
pass
|
||||
stop_event.set()
|
||||
|
||||
# Expose restore in config for future use if needed
|
||||
config._alsa_filter_installed = True
|
||||
config._alsa_filter_restore = _restore
|
||||
logger.info("ALSA underrun stderr filter installed")
|
||||
except Exception as e:
|
||||
logger.debug(f"Unable to install ALSA stderr filter: {e}")
|
||||
def restart_application(delay_ms: int = 2000):
|
||||
"""Schedule a restart with a visible popup; actual restart happens in the main loop.
|
||||
|
||||
@@ -268,6 +376,15 @@ def check_extension_before_download(url, platform, game_name):
|
||||
dest_folder_name = _get_dest_folder_name(platform)
|
||||
system_known = any(s.get("folder") == dest_folder_name for s in extensions_data)
|
||||
|
||||
# Traitement spécifique BIOS: forcer extraction des archives même si le système n'est pas connu
|
||||
try:
|
||||
bios_like = {"BIOS", "- BIOS by TMCTV -", "- BIOS"}
|
||||
if (dest_folder_name == "bios" or platform in bios_like) and is_archive:
|
||||
logger.debug(f"Plateforme BIOS détectée pour {sanitized_name}, extraction auto forcée pour {extension}")
|
||||
return (url, platform, game_name, True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if is_supported:
|
||||
logger.debug(f"L'extension de {sanitized_name} est supportée pour {platform}")
|
||||
return (url, platform, game_name, False)
|
||||
|
||||
Reference in New Issue
Block a user