Dado que no voy a tener vacaciones (por estar en California trabajando con Bernardo y Silvia), decidí tomarme esta semana de ídem. Siendo como soy, eso involucró ir al cine, leer un par de novelas, y concentrarme en un proyecto de cómputo que no tuviera nada que ver con mi doctorado.
El proyecto sonaba sencillo; hacer que mi Media Center (que es una computadora razonablemente poderosa) sirviera también para jugar juegos de Nintendo, Super Nintendo y Sega Genesis. En principio es trivial; los emuladores para esos sistemas existen desde hace años y permiten jugar casi todos los juegos que se hayan escrito para esas consolas… e incluso varios que de hecho no se escribieron para esas consolas.
En la práctica no fue tan simple. Tenía varios requerimientos en mente, pero el más importante de ellos era sin duda que pudiera controlar el correr los emuladores y el salir de ellos utilizando únicamente mi gamepad USB que compré como por cien pesos hace años y que la verdad casi nunca uso (aunque parece que funciona en mi PlayStation 3). Mi Media Center está en la sala de mi casa, y lo controlo únicamente con un control remoto; no quería tener que usar un teclado para nada. Eso implicaba varios problemas; primero, dado que el programa que emula a cada consola es distinto, significaba que tendría que escribir un programa que lanzara a cada emulador dependiendo del juego que quisiera jugar. Segundo, dicho programa tendría que ser controlable a través del gamepad.
Y de hecho ahí cometí una estupidez monumental; mi primera versión la escribí con soporte para control remoto, no para gamepad. Y de hecho funcionaba bastante bien; sólo que sí es estupidísimo que, si voy a tener que usar el gamepad para jugar los juegos, que tenga que utilizar el control remoto para inciarlos. Por suerte mi diseño estaba bastante bien, si se me permite decirlo, y fue sólo cambiar la lógica del control remoto por la del gamepad. Y aprender a usar una pequeña biblioteca para manejar joysticks en general; resulta que nunca había hecho eso en mi vida. Lo cual es raro; siempre he tenido ganas de programar algún tipo de juego, pero nunca lo he hecho.
Como sea; el lanzador de juegos quedó relativamente rápido. Está escrito en Python y utiliza PyGame, que por debajo (en Linux) utiliza SDL. SDL tiene años; yo recuerdo básicamente cuando comenzó. A finales de los noventas e inicios de este siglo, una compañía llamada Loki Software se le ocurrió la genial idea de escribir y/o portar juegos de Windows a Linux. Su programador principal, un chavo con experiencia trabajando con juegos en distintos sistemas operativos, se le ocurrió hacer una biblioteca pequeña que encapsulara las operaciones más utilizadas en juegos de computadoras, y que (siempre que fuera posible) sólo llamara a la biblioteca nativa en el sistema operativo donde estuviera corriendo el programa; en Windows eso significaba DirectX. En Linux creo que de hecho sí terminaron escribiendo un montón de código.
Loki terminó tronando como ejote, por supuesto; pero SDL (siendo Open Source) no sólo sobrevivió a ello, sino que se convirtió en el estándar de facto en Linux (y Unix en general) para escribir juegos. SDL funciona con “módulos”, que en terminología moderna llamaríamos más bien “plugins”; tiene un módulo para sonido, otro para video, otro para joysticks (gamepads incluidos), y encima de todo tiene uno para OpenGL. Entonces en general en Linux uno tiene en SDL una biblioteca con todo lo necesario para escribir un juego, ya sea en 2D o 3D. PyGame se cuelga de SDL, y tengo entendido que varias otras bibliotecas también; además de que SDL tiene bindings en varios lenguajes (está escrito en C). Son pocos los juegos en Linux que no utilizan SDL, directa o indirectamente; y de hecho ahorita no se me viene ninguno a la cabeza.
Y de hecho los tres emuladores que utilizo corren sobre SDL; FCEUX, Zsnes y Gens. Que esa fue la otra bronca que tuve; hacer que los pinches emuladores compilaran y corrieran en mi Media Center, y que además pudiera salirme de ellos utilizando el gamepad. La bronca comienza por el hecho de que estos emuladores en general comenzaron a escribirse a finales de los noventas; aunque las computadoras en ese entonces tenían ya la capacidad de emular el hardware de las consolas de 8 y 16 bits, la verdad es que la tenían por un pelo de rana calva.
Como el hardware a duras penas podía con la emulación, los programadores de entonces llegaron a la conclusión de que tenían que hacer el código lo más rápido posible, y hace diez años eso básicamente se traducía a “vamos a escribirlo en ensamblador”. Entonces un montón de código en los emuladores se escribió en ensamblador; y como (en ese entonces) código en ensamblador cuidadosamente escrito le ganaba pero por mucho al código de máquina generado por un compilador (especialmente GCC), dicho código en ensamblador se fue preservando en distintos proyectos y nuevas generaciones de los emuladores. No me queda claro que la rapidez que ofrece justifique el increíble dolor de cabeza que implica estar lidiando con ensamblador; pero además dudo que dicho código le gane por mucho al código máquina generado por GCC (ha avanzado mucho el compilador en estos diez años), y encima creo que las máquinas modernas son lo suficientemente poderosas como para poder descartar la necesidad de utilizar ensamblador escrito a mano.
Como sea, los tres emuladores que uso siguen teniendo generosas porciones escritas en ensamblador, lo cual no sería ningún problema si a) mi Media Center no fuera x86-64, y b) si no necesitara compilar los emuladores desde código fuente. Como utilizo x86-64, los emuladores no son trivialmente compilables; de hecho todo lo contrario, es un desmadre compilarlos en x86-64. Se puede, pero da dolores de cabeza. Y x86-64 puede ejecutar código de x86-32 sin ningún problema (por eso es que es difícil compilarlos; porque hay que hacer crosscompiling: compilar en x86-64 a código nativo de x86-32… por suerte Gentoo ayuda con eso), pero yo necesitaba el poder compilarlos porque además de todo, excepto por Zsnes, ningún maldito emulador era completamente controlable desde el gamepad.
Entonces me tuve que meter al código de Gens y de FCEUX (después de romperme la cabeza viendo cómo hacer el crosscompiling), y modificar los programas para que me permitieran salirme del emulador utilizando un botón del gamepad. Ya entrados en negocios, y como mi gamepad tiene doce botones (seis más que el de SNES o el del Genesis), también asigné un botón para guardar el estado de la consola, y otro para cargar dicho estado. En otras palabras, para poder hacer trampa salvando justo antes de alguna parte difícil del juego, y cargándola de nuevo si me matan. Que es lo común, por cierto.
No por presumir, pero quedó poca madre. Cuando cierro el programa de mi Media Center (con un botón del control remoto), el script que lo ejecuta determina si está o no conectado mi gamepad. Si no está conectado la máquina se apaga; si sí está conectado, ejecuta mi programa para elegir algún juego. El programa es bien sencillo; presenta en pantalla una imagen (la carátula original del juego), y con el gamepad puedo ir navegando las carátulas hasta que llegue al juego que quiero jugar.
Al presionar un botón, el programa escupe a la salida estándar el nombre del ROM, y el script que lo lanzó ve qué tipo de ROM es; dependiendo del tipo lanza el emulador correspondiente, y entonces ya puedo jugar (y hacer trampa guardando y cargando estados de memoria del emulador), y cuando me aburro presiono otro botón y el emulador acaba. El script recupera el control, y vuelve a ejecutar el programa que elige juegos.
El programita detecta un botón en especial, que si lo presiono quiere decir que ya no quiero seguir jugando, y entonces termina sin escupir nada a la salida estándar. El script detecta que el programa no escupió nada, y entonces termina el ciclo y la máquina se apaga.
Lo maravilloso del asunto es que es trivial agregar juegos (sólo es aventar el ROM y la imagen de la carátula a dos directorios especiales), y casi igual de trivial agregar emuladores (sólo tengo que asegurarme de que pueda modificarlos para que sean completamente controlables a través del gamepad).
Sé que es medio ridículo que, teniendo un PlayStation 3 (probablemente la consola más poderosa que jamás haya existido), me ponga a jugar Super Mario Bros., un juego que cumplirá 25 años el año que viene. Pero ese juego, al igual que Contra que mi hermano y yo jugamos hasta la ignominia; Golden Axe que sólo pude jugar en maquinitas (no recuerdo a nadie que tuviera un Sega Genesis aquí en México), o Flashback que de hecho jugué en la computadora, están irremediablemente ligados a mi niñez. No es sólo ser “retro”; de verdad hay una conexión emocional con esos juegos.
(Además de que, para ser sincero, a veces me siguen pareciendo más divertidos que los juegos modernos).
Fue un proyecto divertido, y la verdad quedó bastante bien. Y, sorprendentemente, se ven muy bien estos seniles juegos en mi televisión de 46″.
