Zin in een klassieke game? Je bent niet de enige. Retrogaming is de laatste jaren in opmars. Dat is te zien aan de wildgroei aan retroconsoles, maar ook aan het groeiende aantal decompilaties, recompilaties en ports van klassieke games. Daardoor is het mogelijk om verschillende retrogames native op de pc te draaien. Vaak ondersteunen de ports ook native 4k-resolutie, hoge refreshrates, ultrawideaspectratio's en mods. Hoe krijgen de hobbyisten achter de projecten dat voor elkaar?
Het is fans van verschillende grote retrogames de afgelopen jaren op verschillende manieren gelukt om de gecompileerde code van games om te zetten in door mensen leesbare code. Daardoor hebben onder meer Mario Kart 64, Super Mario 64, de Jak and Daxter-trilogie en The Legend of Dragoon al native ports voor de pc.
Die ports kunnen ontwikkelaars maken aan de hand van een decompilatie of een recompilatie. Bij een decompilatie reverse-engineeren programmeurs de gehele game. Uiteindelijk is door mensen leesbare broncode van het spel het resultaat.
Ontwikkelaars gebruiken vaak reverse-engineeringtools als Ghidra om de gecompileerde code te decompileren en het resultaat ervan te analyseren. Vervolgens pluizen zij het resultaat functie voor functie uit om te kijken wat elke functie doet. Veel functies hebben namelijk geen logische, maar generieke namen. Daardoor moeten de ontwikkelaars stukje bij beetje uitzoeken wat elke functie doet.
Dit proces is natuurlijk zeer arbeidsintensief en tijdrovend. Ontwikkelaars zijn vaak jaren bezig om de code helemaal om te zetten. Het grote voordeel is dat de broncode hierdoor schoon en begrijpelijk is. Dat maakt het mogelijk om spellen tot in de kern aan te passen. Denk bijvoorbeeld aan diepgaande mods, multiplayer toevoegen of de game overzetten naar een nieuwe engine.
Recompilaties: minder tijdrovend en minder aanpasbaar
Een recompilatie kost een stuk minder tijd. Bij dit proces gebruiken programmeurs speciale tools, zogenoemde recompilers, om de programmeertaal van een oude console om te zetten in een moderne taal als C of C++. Zulke tools bestaan al voor Nintendo 64-games (N64Recomp) en XBOX 360-spellen (XenonRecomp en ReXGlue).
Het voordeel van een recompilatie is dat die veel sneller en eenvoudiger is dan een decompilatie. Wanneer een recompiler beschikbaar is, kunnen gebruikers snel broncode van een oud spel omzetten naar een taal voor moderne hardware. Het nadeel is dat die code voor mensen moeilijk leesbaar is. Daardoor is de game lastig diepgaand aan te passen. Het is vaak wel mogelijk om ondersteuning voor hoge framerates en een hogere resolutie toe te voegen, maar het spel blijft in de basis functioneren zoals het altijd deed.
Ports: uitvoerbare versies van re- en decompilaties
Een port is een speelbaar eindresultaat van decompilaties en recompilaties. Het is een uitvoerbaar programma dat native draait op moderne hardware. Een voorbeeld van zo'n port is BattleShip, dat het Nintendo 64-spel Super Smash Bros. native op pc speelbaar maakt. De maker van die port is Jack Rickey, die met hulp van Claude de gedecompileerde code van Super Smash Bros. omzette in een werkende port voor Windows, macOS en Linux. Mede doordat de port is gebaseerd op een decompilatie, heeft hij ook verschillende zaken aan het spel kunnen toevoegen, zoals een co-opmodus en drie extra levels.
Hij vertelde eerder aan Linux Gaming Central dat hij verschillende maatregelen nam om ervoor te zorgen dat de port geen typische AI-slop zou worden. Zo liet hij Claude elke bugoplossing uitgebreid documenteren. Uiteindelijk zag hij dat elk soort data, zoals textures, personageanimaties en de aanvallen van de verschillende personages, op een andere manier moest worden verwerkt. Hij ontwierp voor elk gegevenstype een hulpsysteem om de gegevens correct te verwerken.
Buganalyses
Tegenover Tweakers geeft Rickey meer uitleg over hoe hij die systemen heeft vormgegeven: "Telkens wanneer ik een bug onderzocht, liet ik de agent de eerdere bugs en probleemoplossingen opnieuw doorlezen. Als die tot dezelfde 'bugfamilie' behoorde, was het makkelijk om dezelfde oplossing te volgen of de construct die ik gebruikte aan te scherpen. Dit helpt ook om een werkgeheugen op te bouwen. Ik had bijvoorbeeld een bug waarbij een struct uit de decompilatie, indien gecompileerd met Clang, een deel van de personagedata afkapte vanwege padding. Door zo'n buganalyse op te slaan leren agents om structs niet blindelings te vertrouwen."
van Battleship
Dat betekent niet dat het debuggingproces meteen perfect verloopt. Rickey: "Er waren genoeg momenten waarop ik lastige bugs tegenkwam. Visuele bugs zijn lastig, omdat veel van wat je beschrijft moeilijk over te brengen is op een large language model (llm). Daarnaast zijn de ingebouwde visuele modellen in Claude of ChatGPT eerlijk gezegd niet zo geschikt om kleine problemen met de visuele kwaliteit te debuggen. De manier om dit op te lossen is volhouden en zoveel mogelijk informatie omzetten in iets dat het model kan verwerken. Dat betekent veel logs toevoegen.
"Het helpt ook om te vergelijken met andere dingen. Je zou bijvoorbeeld het spel op een emulator kunnen draaien en displaylists (de N64-tekeninstructiecode die naar de rsp wordt gestuurd) kunnen dumpen en vergelijken met je eigen resultaten. Leren van dit soort technieken is erg belangrijk. Als je vastloopt en niet weet wat je moet doen, heeft het geen zin om tegen je model te zeggen: 'Het is weer kapot en werkt niet, repareer het' en te verwachten dat het dan werkt. Je kunt vragen stellen als: 'Wat zou een professional in deze situatie doen?', 'Welke andere informatie heb je nodig?' en 'Welke referenties of betrouwbare bronnen zouden hier nuttig zijn om dit te bevestigen?'"
Audioproblemen zorgen voor de meeste kopzorgen
Uiteindelijk bleken de audiobugs het hardnekkigst: "Stabiliteitsproblemen zijn frustrerend, maar met de juiste tools eenvoudig te diagnosticeren en op te lossen. De kern van vrijwel elk stabiliteitsprobleem is een fout in het geheugen. Meestal gaat het om een poging om geheugen te gebruiken dat al was vrijgegeven, of een pointer die verwijst naar geheugen dat buiten het voor het programma toegestane bereik valt.
"Deze fouten zijn in de meeste gevallen deterministisch en reproduceerbaar, dus spoor je ze op met speciale tools zoals AddressSanitizer (ASan)", legt Rickey uit. "Met deze tools kun je de build compileren met zogeheten red zones tussen geheugenallocaties. Simpel gezegd doen deze dienst als bufferzones, die het programma nauwlettend in de gaten houdt. Als een teller verder oploopt dan de bedoeling is, komt deze in zo'n red zone terecht. ASan legt het programma dan direct stil en geeft aan welke functie het probleem veroorzaakte.
"Ik haal dit allemaal aan om uit te leggen wat opsporen van het ene type probleem inhoudt in vergelijking met een ander type probleem. In dit voorbeeld heb je een deterministische compilerhulp die je de exacte locatie van de bug geeft. Zodra je weet waar de fout zit, is het vrij eenvoudig om de bug in de code te zien en te repareren."
"Audio daarentegen is veel moeilijker. Mixers moeten audiostreams verwerken, ze synchroon houden met het verloop van de game en netjes mixen en faden", legt Rickey uit. "Wanneer audio uit de pas loopt met de gamelogica, krijg je tikken en ruis. Wanneer de mixer een bug bevat, kan de audio ontaarden in hoge pieptonen op hoog volume, of hoor je het geluid nog maar via één kanaal. Aan het geluid kun je horen dat er iets mis is, maar het is lastig vast te stellen wat precies."
Ook gedumpte WAV-bestanden helpen daarbij niet: die tonen aan dat er een probleem met de audio is, maar laten niet zien welke fout in de software dat probleem veroorzaakt.
Audio op de verkeerde plek
Rickey noemt als voorbeeld een bug waarbij het 'centrum' van de audio naar links was verschoven. "Dit leidde ertoe dat audio die vanuit het midden van het scherm kwam, alleen op het linkerkanaal werd afgespeeld. Audio die vanaf de linkerkant van het scherm kwam, veroorzaakte een integer overflow. Dat resulteerde in een hard, krakend geluid."
Die bug was hardnekkig, maar de oplossing bleek kinderlijk eenvoudig. Rickey: "Het was zo simpel dat het grappig is. In de decompilatie stond balance = 64 - balance. Dit keerde de x-positie om. Een personage aan de rechterkant werd naar links geprojecteerd. De code behandelde 0 dus als links en 127 als rechts. Als het personage helemaal links op het scherm stond, veroorzaakte dat een integer overflow in de audiomixer. De oplossing was om het minteken in de decompilatiecode te veranderen in een plusteken: balance = 64 + balance."
Om goed te debuggen is uiteindelijk 'gewoon' kennis nodig, ook met hulp van llm's, zegt Rickey: "Jij en het model moeten uitstekend begrijpen hoe deze systemen werken om effectief te kunnen debuggen. Gelukkig is er veel zeer goede openbare documentatie over de hardwarespecificaties van de Nintendo 64, evenals van alle andere consoles."
Hij noemt als voorbeeld onder meer de compressie van textures op de Nintendo 64. Omdat het werkgeheugen op de console zo beperkt was, comprimeerde het systeem alle textures in een soort kronkelig patroon.
"Dat is een goed voorbeeld, omdat bijna niemand dit ooit in het openbaar te zien krijgt. Dit is hoe de ruwe sprite eruitziet als je hem van de cartridge dumpt. Als het model niet begrijpt dat er op hardwareniveau iets hoort te gebeuren om dit er normaal uit te laten zien, denkt het gewoon dat dit is hoe de sprite eruit hoort te zien."
Geweldige leerervaring
Mensen die nu zelf zin krijgen om een port te maken, doen er volgens Rickey goed aan om een sprong in het diepe te wagen: "Ik zou andere ontwikkelaars absoluut aanraden om een game te porten. Je leert veel over N64-hardware, functioneel programmeren op laag niveau en de manier waarop games daadwerkelijk op een computer werken."
"Ik wil er wel bij zeggen dat de game testen een groot deel van het werk uitmaakt", waarschuwt de ontwikkelaar. "Dat vormt waarschijnlijk het grootste knelpunt. Super Smash Bros. was relatief makkelijk te testen, omdat het een erg korte game is, waarbij bijna alle speltoestanden binnen 15 minuten spelen toegankelijk zijn. Voor langere singleplayergames is dat misschien niet het geval."
"Mensen die dit soort werk willen doen, zou ik aanmoedigen om er open over te zijn en anderen erbij te betrekken. Je wilt dat je port een goede basis vormt voor anderen om aan te werken, bijvoorbeeld voor mods en online spelen. Feedback van mensen met meer ervaring in de scene is altijd nuttig."
"Ik heb er enorm veel plezier aan beleefd", onderstreept Rickey. "Het is een zeer bevredigende ervaring om een hoop gedecompileerde gamecode om te zetten in iets dat mensen daadwerkelijk kunnen spelen. Zodra de game stabiel is, is het fantastisch om nieuwe functies toe te voegen, zoals 16:9-breedbeeld, om de game vanuit een compleet nieuw perspectief te bekijken. Ik ben erg trots op wat ik en vooral de andere ontwikkelaars voor het project hebben gedaan."
Redactie: Imre Himmelbauer • Eindredactie: Marger Verschuur
:strip_exif()/i/2008370662.jpeg?f=imagenormal)
:strip_exif()/i/2008368766.jpeg?f=imagenormal)
:strip_exif()/i/2008368806.jpeg?f=imagenormal)

:strip_icc():strip_exif()/u/4634/ratbuddy_ORIGINAL.jpg?f=community)
:strip_icc():strip_exif()/u/147791/crop56e423ed8d794_cropped.jpeg?f=community)
:strip_icc():strip_exif()/u/19747/557px-Coat_of_Arms_of_Indonesia_Garuda_Pancasila_svg2.jpg?f=community)
:strip_icc():strip_exif()/u/18149/catfish60.jpg?f=community)
:strip_exif()/u/33165/crop660ea38befdf0.gif?f=community)
/u/451681/crop636a63ad7726a_cropped.png?f=community)
:strip_exif()/u/285020/crop6085b8acc4f0d_cropped.gif?f=community)
/u/37695/crop59072b57d660b_cropped.png?f=community)
:strip_exif()/u/174875/crop5a47567761dca_cropped.gif?f=community)
/u/127261/crop5dcd39ec2f45d_cropped.png?f=community)
/u/94596/crop643fb12fd4e6d.png?f=community)
/u/296054/crop68eceac396f4d_cropped.png?f=community)
/u/38782/crop6146c7a29805d_cropped.png?f=community)
/u/1906/crop5dfd46928e003.png?f=community)
/u/381607/have%2520a%2520nice%2520day%2520-%2520small.png?f=community)
:strip_icc():strip_exif()/u/47442/hacktic.jpg?f=community)
:strip_exif()/u/574353/crop5807cdc0eece4_cropped.gif?f=community)
:strip_icc():strip_exif()/u/734655/crop69fb2d19b907a_cropped.jpg?f=community)