Need for Speed: Underground 2 corre en el navegador a hasta 120 fps en pantalla panorámica, con carreras online y soporte para mando o volante. El proyecto Underground 2 Revived no imita la consola original: tradujo el juego entero con recompilación estática, la técnica que convierte un binario de otra arquitectura en código fuente que cualquier compilador moderno entiende.
La misma idea mueve a toda una familia de proyectos que reconstruyen juegos de Nintendo 64 y Xbox 360 en C y los llevan hasta WebAssembly. Ninguno busca que el jugador se divierta más: buscan que el software sobreviva cuando el hardware original ya no exista.
TL;DR
- La recompilación estática traduce un binario x86 o PowerPC completo a código C, sin emular la CPU en tiempo real.- NFSU2Recomp convierte Need for Speed: Underground 2 (Xbox, 2004) en una app que corre en Chrome y Firefox.- XenonRecomp ataca binarios de Xbox 360 (CPU Xenon); N64: Recompiled hace lo mismo con cartuchos de N64.- El compilador final optimiza el código resultante, por eso estos ports corren más rápido que un emulador clásico.- Detectar los límites de cada función y los saltos indirectos es más difícil que traducir línea por línea.
¿Qué es la recompilación estática?
La recompilación estática es una técnica de ingeniería inversa que traduce instrucción por instrucción el binario compilado de un programa (una ROM, un .exe) a código fuente en C, sin ejecutarlo nunca. Un compilador normal procesa después ese C para generar un programa nativo en otra arquitectura, incluida WebAssembly.
La diferencia con la emulación es la que importa. Un emulador reinterpreta cada instrucción en cada partida, ciclo por ciclo, mientras el juego corre. La recompilación estática hace ese trabajo una sola vez, antes de que el jugador toque un botón, y entrega un ejecutable que el procesador corre de forma nativa.
El resultado, cuando el lenguaje de destino es C, resulta portable. Ese mismo C se puede compilar con GCC para Windows, con Clang para macOS o con Emscripten para generar WebAssembly, que es justo lo que hacen los tres proyectos de este artículo.
Por qué importa la preservación de software
Una consola deja de fabricarse, pero el software que corrió en ella no tiene por qué desaparecer con el hardware. Los discos ópticos se degradan, las baterías de las memory cards fallan, y encontrar un Xbox 360 o un cartucho de Nintendo 64 en buen estado es cada año más difícil. Reescribir el binario original como código fuente legible convierte un objeto físico frágil en un archivo de texto que cualquier compilador futuro puede volver a construir.
La recompilación de binarios también resuelve un problema que la emulación tradicional no puede. Cuando un emulador tiene un error de temporización, hay que parchear el núcleo entero, y ese parche puede afectar a otros cien juegos que corren sobre el mismo emulador. Si el error vive en el C generado de un solo juego recompilado, se corrige ahí, sin tocar nada más.
Underground 2 Revived, por ejemplo, no cobra por el juego ni vende mejoras. Se financia con donaciones voluntarias y reserva un rol de Discord para quien aporta. El código recompilado es lo que permite agregar funciones que el Xbox original nunca tuvo, como partidas en línea entre amigos.
Need for Speed: Underground 2 salió originalmente en 2004 para PlayStation 2, Xbox y GameCube.
Cómo funciona el proceso, paso a paso
Así ocurre, en términos generales, la recompilación estática en estos tres proyectos.
Encontrar las funciones dentro del binario
El primer obstáculo no es traducir instrucciones; es saber dónde empieza y termina cada función dentro de un binario que no trae esa información. Un ejecutable compilado pierde los nombres de funciones y buena parte de los símbolos de depuración. Por eso el recompilador tiene que inferir los límites analizando instrucciones de salto y llamada. Para eso construye un grafo con todas las rutas posibles de ejecución.
La traducción binaria clásica resuelve esto en caliente, instrucción por instrucción, mientras el programa corre. Un recompilador estático hace el análisis completo antes de generar una sola línea de C, por eso puede tardar minutos u horas en procesar un binario grande la primera vez, aunque el resultado final corra sin ese costo.
De instrucciones de CPU a líneas de C
Cada instrucción de la CPU original se convierte en una o varias líneas de C que leen y escriben los mismos registros que usaría el procesador real. El recompilador no entiende el juego: traduce mecánicamente, preservando el comportamiento exacto de cada instrucción.
Una suma en PowerPC como add r3, r4, r5 se convierte en algo equivalente a esto:
// r3 = r4 + r5 en el PowerPC original void fn_08A3F120(PPCContext* ctx, uint8_t* base) { ctx->r3.u64 = ctx->r4.u64 + ctx->r5.u64; } Esta función representa el efecto de una sola instrucción de suma, usando una estructura PPCContext que modela el banco de registros del PowerPC; base apunta a la memoria del juego mapeada como un arreglo de C. La salida esperada no es texto en una terminal, sino el mismo valor que habría puesto el chip original en el registro: si r4 vale 10 y r5 vale 32, la función deja ctx->r3.u64 en 42.
Cuando el binario completo tiene miles de funciones, nadie escribe esto a mano. El recompilador genera automáticamente miles de funciones como esta, una por cada dirección de memoria donde el análisis detectó el inicio de una función nueva.
De C a WebAssembly
El C generado no sirve de nada por sí solo: necesita una capa adicional, normalmente llamada runtime o shim (capa de compatibilidad), que reemplace el acceso a disco, el controlador gráfico, el sonido y el mando de la consola original.
Esa capa traduce las llamadas que el juego original hacía a la GPU de Xbox 360 o al microcódigo de sonido del Nintendo 64 hacia APIs modernas como WebGL o Web Audio.
Para que el resultado corra en un navegador, el C (runtime incluido) se compila con Emscripten, la cadena de herramientas basada en LLVM que convierte C y C++ en WebAssembly. El mismo C, sin cambios, también se puede compilar de forma nativa con GCC o Clang para generar un ejecutable de Windows, macOS o Linux. La recompilación de binarios separa así la lógica del juego, fija en el C, de la plataforma de destino, que decide el compilador.
flowchart LR A["Binario original (x86 / PowerPC)"] --> B["Análisis de funciones y saltos"] B --> C["Código C generado"] C --> D["Runtime: GPU, audio, entrada"] D --> E["Emscripten + LLVM"] E --> F["WebAssembly en el navegador"] Ejemplos prácticos
Los saltos condicionales simples muestran bien la mecánica de traducción. Un salto de x86 como este:
cmp eax, 0 jne distinto ; bloque A distinto: ; bloque B se convierte en C así:
if (eax != 0) { goto distinto; } // bloque A distinto: // bloque B Qué hace: reproduce la comparación cmp y el salto jne ('salta si no es igual') con un if y un goto. La salida esperada es idéntica a la del binario original. Si eax vale 0, el programa ejecuta el bloque A y sigue al bloque B; con cualquier otro valor, salta directo al bloque B.
Los saltos directos como este son el caso fácil. El verdadero desafío de estos recompiladores son los saltos indirectos, es decir, tablas de salto que se resuelven en memoria durante la ejecución original; eso se explica más abajo, en la sección de errores comunes.
Casos de uso reales: la familia de proyectos
Ninguno de estos tres proyectos nació para que alguien jugara mejor. Nacieron para demostrar que la recompilación de binarios funciona a escala real, con binarios de decenas de megabytes y miles de funciones.
N64: Recompiled
N64: Recompiled es el nombre con el que se conoce a la ola de proyectos construidos sobre una cadena de herramientas que traduce ROMs de Nintendo 64, compiladas para el procesador MIPS R4300i, a código C. Su caso más conocido reconstruyó The Legend of Zelda: Ocarina of Time como un port nativo para PC, con resolución panorámica y tasa de cuadros libre, algo que la consola original de 1998 nunca ofreció.
XenonRecomp
XenonRecomp apunta a binarios de Xbox 360, compilados para la CPU Xenon, un chip tricore basado en PowerPC. Su caso insignia recompiló Sonic Unleashed en un port nativo de PC, construido por la comunidad que mantiene el proyecto hedge-dev.
La CPU Xenon de Xbox 360 fue desarrollada por IBM como un chip PowerPC de tres núcleos.
NFSU2Recomp
NFSU2Recomp, creado por el desarrollador Talha Müderrisoğlu, es el recompilador estático original de x86 a C para Need for Speed: Underground 2. Es, dentro de esta familia, el primero en atacar un binario x86 en vez de una CPU RISC como MIPS o PowerPC, y el x86 tiene instrucciones de longitud variable que complican mucho más el análisis automático.
Underground 2 Revived se forkeó de la versión de NFSU2Recomp licenciada bajo MIT del 16 de octubre de 2026 y le agregó el runtime del navegador, el código de red para las carreras online y la compilación a WebAssembly.
ProyectoConsola / CPU originalArquitectura fuenteCaso insigniaN64: RecompiledNintendo 64 (MIPS R4300i)MIPS (RISC)Port nativo de Ocarina of Time a PCXenonRecompXbox 360 (CPU Xenon)PowerPC (RISC)Port nativo de Sonic Unleashed a PCNFSU2RecompXbox original / PC (x86)x86 (CISC)Underground 2 Revived, con build para WebAssembly
flowchart TD A["N64: Recompiled (MIPS a C)"] --> C["Inspira el enfoque"] B["XenonRecomp (PowerPC a C)"] --> C C --> D["NFSU2Recomp (x86 a C)"] D --> E["Underground 2 Revived (fork + runtime WebAssembly)"] Errores comunes y buenas prácticas
Tres problemas se repiten en cualquier recompilador estático serio, más uno que no es técnico sino legal.
- Saltos indirectos: las tablas de salto que generan los switch de C o las llamadas a función virtual apuntan a una dirección calculada en tiempo de ejecución, no fija en el binario. El recompilador tiene que reconocer el patrón que genera cada compilador o, si no lo reconoce, caer en un pequeño intérprete solo para ese bloque.- Heurísticas atadas al compilador original: el análisis de límites de función depende de reconocer patrones típicos de un compilador concreto, como viejas versiones de GCC o MSVC. Un binario compilado con una herramienta distinta puede romper esas heurísticas y exigir ajustes manuales.- Determinismo de punto flotante: x86, PowerPC y ARM pueden redondear una misma operación en punto flotante de forma distinta. En un juego de carreras con física simulada, esa diferencia mínima se acumula cuadro a cuadro y puede desincronizar una partida en línea entre dos máquinas distintas.- Assets con copyright separado del código: la recompilación traduce el ejecutable, no los archivos de arte, audio o video del disco original, que siguen protegidos.
⚠️ Ojo: estos proyectos traducen código, no assets. Texturas, modelos, audio y video del disco original siguen protegidos por derechos de autor, así que exigen que el usuario aporte su propia copia legal del juego; no redistribuyen esos archivos.
Comparativa con alternativas
La recompilación estática no es la única forma de preservar un juego viejo, y no siempre es la mejor opción.
EnfoqueCuándo usarloVentajaLimitaciónEmulación dinámica (JIT)Cubrir muchos juegos con un solo núcleoUn solo emulador soporta toda una bibliotecaReinterpreta instrucciones en cada partida: más CPU, timing menos fielHLE (reemplazo de funciones del sistema)Cuando la API del sistema original es conocida y estableEvita simular hardware enteroUn error en la reimplementación afecta a todos los juegos que la usanRecompilación estáticaPreservar un binario puntual con máximo rendimientoCorre nativo; el código queda legible y parcheableTrabajo de análisis enorme por cada binario distintoPort manual con el código fuente originalCuando el estudio conserva y libera el código fuenteCalidad y mantenimiento óptimosCasi nunca disponible: la mayoría de estudios no publica su código
flowchart TD subgraph EM["Emulación dinámica"] EA["Leer instrucción"] --> EB["Interpretar o compilar al vuelo"] EB --> EC["Ejecutar"] EC --> EA end subgraph RE["Recompilación estática"] RA["Analizar el binario completo una vez"] --> RB["Generar código C"] RB --> RC["Compilar a nativo o a WebAssembly"] RC --> RD["Ejecutar código ya compilado"] end Profundizando: qué hace falta por dentro
Resolver una tabla de salto en PowerPC o MIPS normalmente implica reconocer el patrón exacto de instrucciones que el compilador original usó para implementar un switch de C: una carga de una tabla de direcciones seguida de un salto indirecto. El recompilador compara ese patrón contra una librería de firmas conocidas y, si coincide, genera un switch de C equivalente con todos los casos posibles ya enumerados.
La otra mitad del trabajo no está en la CPU sino en el sistema operativo que rodeaba al juego. Xbox 360 exponía APIs propias, la familia XAM, para memoria, hilos y entrada; Nintendo 64 usaba las librerías de bajo nivel de Nintendo conocidas como libultra. Ningún recompilador traduce esas llamadas instrucción por instrucción. Cada proyecto reimplementa a mano un runtime que ofrece el mismo contrato, apoyado en hilos, memoria y controladores modernos.
El determinismo también importa para el online. Dos jugadores de Underground 2 Revived en navegadores distintos corren el mismo C compilado a WebAssembly, así que la física de sus autos coincide instrucción por instrucción; eso es mucho más difícil de garantizar si cada plataforma tuviera su propio port escrito a mano.
La licencia también habilita esta ola de proyectos. NFSU2Recomp y Underground 2 Revived se distribuyen bajo la licencia MIT, que permite forkear, modificar y redistribuir el código con solo mantener el aviso de copyright original; es lo que le permitió a Underground 2 Revived construirse directamente sobre el trabajo de Müderrisoğlu en vez de empezar desde cero.
📖 Resumen en Telegram: Ver resumen
Tu próximo paso: cloná cualquiera de estos recompiladores estáticos open source y abrí la carpeta donde guarda las funciones generadas para comparar, línea por línea, una función C contra el manual de instrucciones de la CPU que tradujo.
Preguntas frecuentes
¿Qué necesita la recompilación estática para funcionar?
Solo el binario compilado del programa original. No necesita ni usa el código fuente del estudio que lo creó, porque nunca tuvo acceso a él.
¿Es legal usar un traductor binario estático como NFSU2Recomp?
El código del recompilador y del runtime puede ser legal si se distribuye bajo una licencia abierta como MIT, pero los assets del juego (arte, audio, video) siguen protegidos. Por eso estos proyectos exigen que cada usuario aporte su propia copia legal del juego original.
¿En qué se diferencia un recompilador de consolas de un emulador?
El emulador reinterpreta instrucciones en cada partida, cuadro por cuadro. El recompilador hace ese análisis una sola vez, antes de jugar, y entrega un programa que la CPU corre de forma nativa, sin ese costo repetido.
¿Por qué la transpilación de x86 a C fue más difícil que la de MIPS o PowerPC?
Porque x86 es un conjunto de instrucciones de longitud variable, con muchos modos de direccionamiento distintos, mientras que MIPS y PowerPC son RISC con instrucciones de ancho fijo, mucho más fáciles de desensamblar automáticamente.
¿Cualquier binario es candidato a recompilación de binarios?
No. El código que se modifica a sí mismo en tiempo de ejecución, o que usa fuertemente saltos calculados dinámicamente, resiste el análisis estático y suele necesitar un intérprete parcial para esos fragmentos puntuales.
Referencias
- Underground 2 Revived: sitio oficial del proyecto, con la historia del port, el fork de NFSU2Recomp y el modelo de financiamiento por donaciones.- Wikipedia: Binary translation: contexto general sobre traducción binaria estática y dinámica.- WebAssembly.org: especificación oficial del formato binario portable que usan estos recompiladores como destino final.- Emscripten: documentación oficial de la cadena de herramientas que compila C y C++ a WebAssembly.- Open Source Initiative: Licencia MIT: texto oficial de la licencia bajo la que se distribuyen NFSU2Recomp y Underground 2 Revived.
📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.