- Ein Snake-Spiel, das als einzelne 13-KiB-Datei unter Windows, Linux und im Browser läuft und alle drei Plattformen mit einer einzigen Codebasis unterstützt
- Es folgt den klassischen Snake-Regeln: Die Schlange wird gesteuert, frisst Futter und darf nicht gegen Wände stoßen; enthalten sind Punktestand, Level und Labyrinth-Strukturen
- Die Implementierungen für die einzelnen Plattformen wurden in C (WinAPI/X11) bzw. JavaScript (HTML5 Canvas) erstellt und jeweils auf etwa 3–5 KiB komprimiert und zusammengeführt
- Unter Windows kommen ein ungewöhnlicher PE-Header und der apphelp-Mechanismus zum Einsatz, unter Linux lzma-Komprimierung und ein Shell-Dropper, im Browser HTML/CSS-Tricks
- Durch die Kombination der drei Implementierungen entstand eine einzelne ausführbare Datei mit 13.312 Byte, die die experimentellen Möglichkeiten einer Multiplattform-Executable-Struktur zeigt
Ein von Cosmopolitan libc inspirierter Multiplattform-Versuch
- Cosmopolitan libc ist ein Toolkit, mit dem sich C-Quellcode zu einem einzelnen Binärprogramm kompilieren lässt, das auf mehreren Betriebssystemen ausgeführt werden kann
- Unterstützt native Ausführung auf verschiedenen OS wie Windows, Linux und BSD
- Wegen fehlender GUI-Unterstützung und der Begrenzung durch große Binärdateien entschied sich der Autor für die Herausforderung, selbst ein Videospiel mit weniger als 16 KiB zu bauen
- Das Ziel war ein Spiel auf Basis einer einzigen Codequelle, das unter Windows, Linux und im Browser läuft
Spielaufbau und Regeln
- Das Spiel ist ein standardmäßiges Snake-Spiel, gesteuert mit den Pfeiltasten oder WASD
ESCzum Beenden,Rzum Zurücksetzen,Pzum Pausieren, Leertaste zum Starten
- Mit jedem gefressenen Futter steigt der Punktestand; normales Futter bringt 10 Punkte, gelbes Futter (15 % Wahrscheinlichkeit) 20 Punkte
- Futter verschwindet nach einer bestimmten Zeit, die von der Geschwindigkeit der Schlange abhängt (proportional zu ihrer Länge)
- Nach 10 gefressenen Futterstücken geht es ins nächste Level, wobei die Wandanordnung zufällig verändert wird
- Das Labyrinth wird so erzeugt, dass immer ein Pfad zum Futter existiert
- Auch die Startposition der Schlange ist zufällig, wird aber in eine Richtung mit mindestens 5 freien Feldern gesetzt
- Die fertige Spieldatei hat eine Größe von 13.772 Byte
Implementierung auf den drei Plattformen
- Die Windows-Version wurde als C-Code auf Basis der WinAPI geschrieben und enthält ein Kompressionsskript sowie einen Dekompressor-Stub
- Die frei kontrollierbaren Bytes nach der
MZ-Signatur im PE-Header werden genutzt, um ein Shell-Skript einzubetten - Dadurch ist die Datei zugleich eine Windows-Executable und ein gültiges Linux-Shell-Skript
- Beim ersten Start kann der Fehler „The application was unable to start correctly (0xc0000005)“ auftreten, beim erneuten Start funktioniert sie jedoch normal
- Die frei kontrollierbaren Bytes nach der
- Die Linux-Version verwendet
lzma-Komprimierung, und ein kleiner Shell-Dropper extrahiert und startet das komprimierte ELF64-Binärprogramm- Sie ist so aufgebaut, dass beim Ausführen bestimmte Teile am Anfang und Ende der Datei übersprungen werden
- Die HTML-Version nutzt aus, dass Browser unnötige Daten am Anfang der Datei ignorieren und anschließend das HTML verarbeiten
- Über CSS wird sichergestellt, dass unnötige Daten nicht auf dem Bildschirm erscheinen
Zusammenführung und Aufbau der Einzeldatei
- Die Dateien für die drei Plattformen werden in einer bestimmten Reihenfolge aneinandergehängt (concatenate), sodass jede Umgebung nur den für sie passenden Teil ausführt
- Windows erkennt den PE-Abschnitt, Linux den ELF-Abschnitt, der Browser den HTML-Abschnitt
- Die endgültige Datei ist 13.312 Byte groß und bildet damit ein Polyglot-Binärprogramm, das in allen drei Umgebungen lauffähig ist
- Im Inneren der Datei sind ein Windows-PE-Header, für Linux LZMA-komprimierter Code sowie HTML/CSS/JavaScript-Code nacheinander enthalten
- Im Beispiel-Codeblock erscheinen der
MZ-Header, derks-Marker, das<html>-Tag und der<script>-Block in dieser Reihenfolge
- Im Beispiel-Codeblock erscheinen der
Technische Bedeutung
- Implementiert eine Ausführungsstruktur, die mit einer einzigen Datei Windows, Linux und Browser zugleich unterstützt
- Nutzt die verschachtelte Verwendung der Formate PE, ELF und HTML, um plattformspezifische Ausführungspfade zu verzweigen
- Eine experimentelle Leistung, die auf extrem kleinen 13 KiB Komprimierung, Format-Hacking und Multiplattform-Ausführung kombiniert
1 Kommentare
Hacker-News-Kommentare
Ich habe die Linux-Executable extrahiert und war überrascht, dass readelf und objdump sie nicht richtig lesen konnten.
Bei näherer Untersuchung stellte sich heraus, dass der ungenutzte Bereich des PT_DYNAMIC-Headers zweckentfremdet wurde, um dort den Namen des dynamischen Linkers unterzubringen und so Platz zu sparen.
Ich halte das absichtliche Beschädigen solcher Formate aber für eine sinnlose Einsparung.
Es könnte außerdem mit Sicherheitsfunktionen wie Antivirenprogrammen oder DEP kollidieren, daher würde ich solche Executables lieber nicht anfassen.
Ich bin erstaunt, wie klein die Dateigröße des ursprünglichen Zelda-Spiels war.
Es ist beeindruckend, wie viel Emotion und Immersion mit so wenig Code und Daten erzeugt werden konnte.
The Legend of Zelda (Wikipedia)
40 Jahre später wurde die Karte extrahiert, und allein als PNG ist sie über 800 KB groß.
Spielkarte ansehen
Die Designer haben sie innerhalb der Platzbeschränkungen wie ein Puzzle angeordnet.
Referenz zur Zelda-Kartenstruktur
Meine Testergebnisse:
.htmländert.lzmafehlt; nachdem ich das Paketxzinstalliert hatte, funktionierte es..comoder.exetrat der Fehler 0xc0000005 auf, aber nachdem ich die DEP-Einstellung deaktiviert hatte, lief es.chmod +x snake.commacht und es dann ausführt, versucht Mono es zu starten und scheitert.Mit
bash snake.comfunktioniert es dagegen. Getestet auf Debian 13.Interessant ist, dass diese Datei im Grunde drei Executables in einer vereint.
Deshalb könnte sie auf jeder Plattform auch ein völlig anderes Programm enthalten.
Das erinnert mich an das 96-KB-FPS-Spiel kkrieger.
Das Grafikniveau war damals erstaunlich.
kkrieger (Archiv)
Ich frage mich, ob es auf dem Mac außer im Browser eine Möglichkeit gibt, das auszuführen.
Ich bekomme den Fehler
cannot execute binary file.Ich frage mich, ob man diesen Mechanismus weiterentwickeln könnte, um eine echte universelle Binärdatei (universal binary) zu erstellen.
Man könnte zum Beispiel Code in Haxe schreiben, ihn nach C++ kompilieren und daraus Versionen für Win32 und Linux bauen,
ihn zusätzlich nach JavaScript übersetzen, damit er auch als HTML läuft,
und dann alle drei Ergebnisse in einer einzigen Datei zusammenführen.
Mir gefällt die Idee einer Single-Executable-App, die als eine Datei überall läuft.
Ich erforsche selbst eine ähnliche Richtung beim Bau einer serverlosen Plattform.
Dabei werden datengetriebene Apps in nur einer
.html-Datei erstellt, während web-components aus der Ferne geladen werden.Die Möglichkeit, HTML direkt über das
file://-Protokoll zu öffnen, ist mächtig, wird aber oft übersehen.Es geht also nicht einfach nur darum, eine Browserinstanz zu starten, sondern es wurden zwei vollständig native Apps gebaut.
Erstaunlich, dass Polyglot-Dateien erst so spät auftauchen.
Technisch gesehen wäre das vermutlich schon vor 10 bis 20 Jahren möglich gewesen.
EICAR.COM fällt mir als frühes Beispiel ein, das zugleich Textdatei und Executable ist.
Das erinnert an ultrakleine Webgame-Wettbewerbe wie js13kGames.