Más de 11 años en Internet
->No hagas click en esta frase.

23 noviembre, 2011

Las entrañas del "Sebas Game Station"

Hace unos días os mostré unos vídeos sobre mi "Sebas Game Station". Hoy os voy a explicar su funcionamiento por si os interesa realizar un experimento similar en casa.

En este caso mi estación de juego es un sistema GNU/Linux con escritorio LXDE. El motivo de esta elección es porque LXDE es un gestor de ventanas ligero con las opciones básicas de un entorno de escritorio. Esto se traduce en una combinación decente del manejo de las ventanas con una muy buena optimización de los recursos del sistema. En mi caso he optado por coger Lubuntu, una distribución precocinada de Ubuntu con LXDE, pero podríais instalar perféctamente una Debian o una Gentoo y seguir los mismos pasos. Podría haber seleccionado algún entorno más ligero, como IceWM light, pero he preferido la opción de LXDE para facilitar las tareas de depuración.

Requisitos:
  • Conocimientos básicos de shell y shell script para instalar y depurar el sistema.
  • Sistema Linux con escritorio LXDE instalado. Puede ser otro escritorio, pero deberás de ser tu quién adapte los scripts de arranque.
  • Unidad lectora de tarjetas SD para la carga de juegos. Este punto no es realmente necesario, se puede adaptar el sistema para que las tarjetas sean perféctamente reemplazables por lápices usb, cd roms o cualquier otro tipo de unidad extraíble.
  • Ratón y teclado para el proceso de instalación del sistema.
  • Conexión a Internet, también para el proceso de instalación del sistema (vamos a hacer un par de apt-gets).


Introducción:

Lo primero es lo primero, ¿en qué consiste este sistema? Pues consta de dos procesos que he instalado en /usr/sbin: el load_SD y el SD_ejected. Ambos procesos son scripts de bash que arrancan al iniciarse el entorno LXDE y se quedan en segundo plano de forma permanente.
  1. El load_SD a groso modo es el proceso encargado de vigilar si se ha insertado una tarjeta SD y en ese caso invoca un ejecutable que se encuentra en dicha tarjeta de memoria.
  2. Por su parte, el SD_ejected se encarga de averiguar si la tarjeta SD ha sido expulsada. Este proceso símplemente cuando detecta la expulsión, mata el juego que esté en ejecución.
Ejemplo del esquema del sistema:



Primer paso, definir el tipo de juegos que vamos a usar:

En mi caso tengo claro que mi máquina sólo va a emplear tres tipos de juegos: juegos de Super Nintendo, de Nintendo 64 y de PlayStation One. No obstante cada persona tiene sus propios gustos y puede instalar los emuladores que desee.

En mi caso he instalado los siguientes emuladores haciendo uso de apt-get:
  • zsnes: Emulador de Super Nintendo.
  • Mupen64: Emulador de Nitendo 64.
  • PCSX: Emulador de PlayStation One.

Estos tres emuladores son proyectos de software libre, que no requieren la instalación de ningún bios o firmware de consola y además traen soporte para aceleración por OpenGL. Este último dato garantiza una amplia compatibilidad con gran parte de las tarjetas gráficas del mercado.

Uno de tus primeros pasos debe de ser reconfigurarlos para que los juegos sean controlados con los gamepads, puesto que piensa que tarde o temprano "sentenciaremos" el ratón y el teclado.


Segundo paso, formato de las tarjetas SD:

Todo eso está muy bien, ¿pero cómo "le metemos" un juego a una tarjeta SD? Conforme he elaborado mi sistema todo juego debe de contener tres archivos: /run, /src/rom y /src/info.txt.

El /src/info.txt sólo debe de contener una línea: El título del juego, por ejemplo: "Super Sebas Kart - Super Nintendo". Este literal será empleado para definir el nombre de la carpeta del disco duro donde se guardará el juego (en este caso sería $HOME/.games/Super_Sebas_Kart_-_Super_Nintendo) y como información adicional para el log $HOME/arcade.log. Este log sirve para que el usuario tenga un registro de los juegos que ha utilizado y a que hora los ha jugado.

Por su parte el /src/rom no es ni más ni menos que la rom del juego renombrado. Es decir, son los famosos archivos .smc (Super Nintendo), .v64 (Nintendo 64) o .iso (PSX) renombrados como "rom" y sin extensión de fichero.

Por último tenemos el /run, un simple script de bash. Este script tiene un único parámetro de entrada y es obligatorio: el directorio donde se encuentra actualmente el ejecutable. Esto hace posible copiar el script al disco duro y ejecutarlo con rutas absolutas con la total seguridad de que funcionará sin modificar su interior.


Ejemplo de script "run" para un juego de Super Nintendo:

#!/bin/bash
datetime=`date "+%D - %H:%M"`
if [ $# -lt 1 ]
then
echo "${datetime} > Missing arguments. Game can't start"
exit
fi
game_title=`head -1 ${1}/src/info.txt`
echo "${datetime} > Game session started: ${game_title}"
zsnes ${1}/src/rom > /dev/null 2> /dev/null
La captura de la fecha que hacemos y sus posteriores "echo" sólo se utilizan para escribir en el arcade.log del usuario (este log será la salida estándar del script). El script para un juego de Nintendo 64 es exáctamente idéntico, símplemente cambiamos el comando zsnes por el de mupen64plus:
#!/bin/bash
datetime=`date "+%D - %H:%M"`
if [ $# -lt 1 ]
then
echo "${datetime} > Missing arguments. Game can't start"
exit
fi
game_title=`head -1 ${1}/src/info.txt`
echo "${datetime} > Game session started: ${game_title}"
mupen64plus ${1}/src/rom > /dev/null 2> /dev/null

Por su parte los juegos de PlayStation requieren de un par de parámetros extra para arrancar una imagen ISO de forma automática:

#!/bin/bash
datetime=`date "+%D - %H:%M"`
if [ $# -lt 1 ]
then
echo "${datetime} > Missing arguments. Game can't start"
exit
fi
game_title=`head -1 ${1}/src/info.txt`
echo "${datetime} > Game session started: ${game_title}"
(unclutter -idle 0) &
pcsx -nogui -cdfile ${1}/src/rom > /dev/null 2> /dev/null
pkill unclutter
Si nos fijamos, antes de ejecutar el emulador pcsx invocamos el comando unclutter y lo ejecutamos en segundo plano. Esto es debido a que este emulador te muestra en todo momento el cursor de ratón, lo cual resulta bastante molesto. Unclutter es una aplicación cuya única utilidad es ocultar el cursor del ratón y basta hacer un sencillo "sudo apt-get install unclutter" para instalarlo en Ubuntu o Debian. Una vez el usuario sale del juego, matamos el unclutter con el comando pkill, para poder volver a visualizar el cursor del ratón.




El proceso SD_ejected:

Antes de explicar cómo cargar un juego en el ordenador, vamos a explicar cómo detectar la expulsión de un dispositivo extraíble.


Abre una terminal y teclea el comando:
df -h > lista_sin_SD
Ahora inserta una tarjeta SD cualquiera y haz
df -h > lista_con_SD 
diff lista_sin_SD lista_con_SD
Te saldrá una línea nueva, en ella puedes identificar cual es el dispositivo que se encarga de gestionar las tarjetas SD. En mi caso es /dev/sdb1, pero en el tuyo podría ser otro, como /dev/mmcblk1. En la mayoría de distribuciones Debian la forma de saber si un dispositvo está presente o no, independientemente de si volúmen está desmontado, es hacer un simple:
ls -l /dev/sdb1
echo $?
Si tienes una tarjeta SD presente el resultado del "echo" es 0 y si no está presente devolverá un 2.

Bueno, el funcionamiento del script es el siguiente. Cómo véis es bastante inteligible y tampoco voy a demorarme mucho en explicarlo... Símplemente es un bucle que revisa de forma constante si se ha expulsado una tarjeta. En caso de estar expulsada, se revisa la existencia de procesos zsnes, mupen64plus y pcsx y se matan:


[ Descargar script en Google Docs ]

Veréis que hago un "-eq 2" en los if en vez de un "-gt 1". Esto es porque el sistema no lo he ideado para ser multi usuario, de forma que en el peor de los casos sólo puede haber una sola instancia de cada emulador... pero podría ser perféctamente reemplazable por un "-gt 1". ¿Por qué no es "-eq 1" o "-gt 0"? Sencillo, al hacer el "ps -ef | grep pcsx" salen dos resultados y no uno: el emulador y el grep.



El proceso load_SD:

Ahora que ya queda claro que al expulsar una tarjeta del lector vamos a "matar" un juego, ya podemos pasar a cargarlos a través de la tarjeta SD. Para ello he definido el proceso load_SD. Este ya es más complejo que su predecesor, por lo que he definido este esquema para que pueda resultar más sencillo de comprender:


Atentos que este script tiene chicha:


Descargar script en Google Docs ]

Vamos a fijarnos en un par de puntos: Al principio se hace una carga de parámetros a través del /etc/arcade.conf. El formato de este arcade.conf es el siguiente:

log=arcade.log
installed_games_folder=.games

Donde arcade.log sería el log del sistema que se guardará en el $HOME y .games el directorio donde vamos a copiar todos los juegos que pongamos en las tarjetas SD.

También veréis referencias a los comandos: xinput set-int-prop ${fake_mouse_device} "Device Enabled" 8 0xinput set-int-prop ${fake_mouse_device} "Device Enabled" 8 1. Esto es debido a que por razones de debug estoy empleando el gamepad como ratón de escritorio. Esto se puede hacer cómodamente mediante un "sudo apt-get install xserver-xorg-input-joystick" en cualquier sistema Debian o Ubuntu. Para activar o desactivar el gamepad como ratón tenemos que usar el comando xinput, tal y como utilizo en el script, siendo "8 0" la opción de desactivar y "8 1" la de habilitarlo.

Por último veréis que hago uso del shutdown para apagar el ordenador a los 60 segundos. Para poder invocar este comando con un usuario normal he tenido que activar su set_uid, de forma que no haga falta ser root para apagar el equipo:
sudo chmod u+s /sbin/shutdown

Configurando el auto arranque de load_SD y SD_ejected:


Para finalizar vamos a hacer que arranquen de inicio el load_SD y el SD_ejected. Recordad guardar los dos ejecutables en lugares accesibles desde el path de GNU/Linux, como pudiera ser /usr/sbin/, además de darle permisos de ejecución ("chmod +x load_SD SD_ejected").

Para hacer que una aplicación arranque al iniciarse el entorno LXDE debemos de crear un archivo ".desktop" dentro de la carpeta $HOME/.config/autostart/. En este caso vamos a crear en esta carpeta dos archivos:

load_SD.desktop

[Desktop Entry]
Encoding=UTF-8
Name=load_SD
Comment=Check for available games on SD cards
Exec=load_SD
Type=Application

SD_ejected.desktop

[Desktop Entry]
Encoding=UTF-8
Name=SD_ejected
Comment=Check if the SD card is ejected
Exec=SD_ejected
Type=Application

6 comentarios:

  1. Ahora mismo lo paso a un pdf y lo imprimo para llevar acabo el experimento en 1 semana que inicen mis vacaciones :D

    ResponderEliminar
  2. Jajaja, que honor =). Ya tengo pensado un par de mejoras y va a haber Java de por medio ;)

    ResponderEliminar
  3. una pregunta sebas, se me ocurrio una fantabulosa idea.


    Un amigo tiene configurado un wiimote para usarlo como control en conferencias, lo configuro con gentoo.

    crees que podemos hacer lo mismo para configurar el wiimote para jugar con el

    ResponderEliminar
  4. Dale un vistazo a Dolphin, un emulador de Wii compatible con Linux, Windows y Mac. Ahí te explican cómo hacer funcionar el Wiimote en cada sistema operativo.

    ResponderEliminar
  5. ¿Por qué no cargas los emuladores como un demonio en init.d al arrancar?

    ¿No detectan la carga de la SD?

    ResponderEliminar
  6. Se podría haber puesto los arranques de load_SD y SD_ejected en el init.d, pero quería que empezara a contar los 60 segundos del apagado a partir de la pantalla de "Insert SD card" y no desde el arranque del demonio.

    De todas formas, esto es "beta" ;)

    ResponderEliminar