이 게임은 Windows, Linux, 브라우저에서 실행되는 단일 13KiB 파일임
(iczelia.net)- 13KiB 크기의 단일 파일로 Windows, Linux, 브라우저에서 모두 실행되는 Snake 게임으로, 세 플랫폼을 하나의 소스에서 지원
- 뱀을 조작해 먹이를 먹고 벽에 부딪히지 않도록 하는 고전적인 Snake 규칙을 따르며, 점수·레벨·미로 구조가 포함됨
- 각 플랫폼별로 C(WinAPI/X11) 및 JavaScript(HTML5 Canvas) 로 구현되었으며, 각각 3~5KiB 수준으로 압축·병합됨
- Windows는 비정상적인 PE 헤더와 apphelp 메커니즘, Linux는 lzma 압축 및 셸 드로퍼, 브라우저는 HTML/CSS 트릭을 이용해 실행
- 세 구현을 결합한 결과 13,312바이트의 단일 실행 파일이 완성되어, 멀티플랫폼 실행 파일 구조의 실험적 가능성을 보여줌
Cosmopolitan libc에서 착안한 멀티플랫폼 시도
-
Cosmopolitan libc는 C 소스 코드를 여러 운영체제에서 실행 가능한 단일 바이너리로 컴파일할 수 있는 툴킷
- Windows, Linux, BSD 등 다양한 OS에서 네이티브 실행 지원
- GUI 미지원과 큰 바이너리 크기 한계로 인해, 저자는 16KiB 이하의 비디오 게임을 직접 제작하는 도전을 선택
- 목표는 Windows, Linux, 브라우저에서 모두 실행되는 단일 소스 기반 게임 제작
게임 구조와 규칙
- 게임은 표준 Snake 게임으로, 방향키 또는 WASD로 조작
-
ESC로 종료,R로 리셋,P로 일시정지, 스페이스바로 시작
-
- 먹이를 먹을 때마다 점수가 증가하며, 일반 먹이는 10점, 노란색 먹이(15% 확률) 는 20점
- 먹이는 일정 시간 후 사라지며, 사라지는 시간은 뱀의 속도(길이에 비례)에 따라 결정
-
10개의 먹이를 먹으면 다음 레벨로 이동, 벽 배치가 무작위로 변경
- 항상 먹이까지의 경로가 존재하도록 미로 생성
- 뱀의 초기 위치도 무작위지만, 최소 5칸의 여유 공간이 확보된 방향으로 배치
- 완성된 게임 파일 크기는 13,772바이트
세 플랫폼별 구현 방식
-
Windows 버전은 WinAPI 기반 C 코드로 작성되어, 압축 스크립트와 디컴프레서 스텁을 포함
- PE 헤더의
MZ시그니처 이후 자유롭게 제어 가능한 바이트를 활용해 셸 스크립트를 삽입 - 이로 인해 파일이 Windows 실행 파일이면서 동시에 Linux 셸 스크립트로도 유효
- 첫 실행 시 “The application was unable to start correctly (0xc0000005)” 오류가 발생할 수 있으나, 재실행 시 정상 동작
- PE 헤더의
-
Linux 버전은
lzma압축을 사용하고, 작은 셸 드로퍼가 압축된 ELF64 바이너리를 추출·실행- 파일의 앞뒤 일부를 건너뛰며 실행되도록 구성
-
HTML 버전은 브라우저가 파일의 불필요한 앞부분을 무시하고 HTML을 처리하는 특성을 이용
- CSS를 통해 불필요한 데이터가 화면에 보이지 않도록 처리
단일 파일 결합 및 구조
- 세 플랫폼용 파일을 특정 순서로 연결(concatenate) 하여, 각 환경이 자신에게 맞는 부분만 실행하도록 구성
- Windows는 PE 섹션, Linux는 ELF 섹션, 브라우저는 HTML 섹션을 인식
- 최종 파일 크기는 13,312바이트로, 세 환경에서 모두 실행 가능한 폴리글롯 바이너리 완성
- 파일 내부에는 Windows PE 헤더, Linux용 LZMA 압축 코드, HTML/CSS/JavaScript 코드가 순차적으로 포함됨
- 예시 코드 블록에는
MZ시그니처,ks마커,<html>태그,<script>블록 등이 순서대로 존재
- 예시 코드 블록에는
기술적 의의
- 단일 파일로 Windows, Linux, 브라우저를 모두 지원하는 실행 구조를 구현
- PE, ELF, HTML 포맷의 중첩 활용을 통해 플랫폼별 실행 경로를 분기
- 13KiB라는 극도로 작은 크기에서 압축, 포맷 해킹, 멀티플랫폼 실행을 결합한 실험적 성취
Hacker News 의견들
-
리눅스 실행 파일을 추출했는데, readelf와 objdump가 제대로 읽지 못해서 놀랐음
조사해보니 PT_DYNAMIC 헤더의 비사용 필드에 동적 링커 이름을 억지로 넣어 공간을 절약한 구조였음- 이건 수동으로 한 건지, 아니면 이런 식으로 처리하는 툴이 있는지 궁금함
- 이런 비정상적인 바이너리를 분석할 수 있는 툴이 있는지도 알고 싶음
-
ndisasm이나 헥스 에디터로 읽을 수 있음
하지만 이런 포맷을 일부러 망가뜨리는 건 무의미한 절약이라 생각함
안티바이러스나 DEP 같은 보안 기능과 충돌할 수도 있어서 이런 실행 파일은 건드리고 싶지 않음 - “choke”가 무슨 의미인지 모르겠음, 내 환경에서는 두 툴 모두 정상 작동했음
-
원본 젤다(Zelda) 게임의 파일 크기가 너무 작아서 놀라움
그렇게 적은 코드와 데이터로 얼마나 큰 감동과 몰입감을 만들어냈는지 경이로움
The Legend of Zelda (Wikipedia)- 진짜 마법 같음. Commodore 64의 Eindeloos라는 게임도 64KB 안에 거대한 맵을 담았음
40년 만에 맵을 추출했는데 PNG로만 800KB가 넘음
게임 맵 보기 - Zelda 1은 128KB였고, 압축도 없었음. 속편은 그 두 배 정도였음
- 던전과 동굴이 사실 하나의 직사각형 맵 안에 모두 들어 있었음
디자이너들이 공간 제약 안에서 퍼즐처럼 구성했음
Zelda 맵 구조 참고
- 진짜 마법 같음. Commodore 64의 Eindeloos라는 게임도 64KB 안에 거대한 맵을 담았음
-
내 테스트 결과
-
브라우저: 확장자를
.html로 바꾸면 실행됨 -
리눅스:
lzma명령이 없다는 오류가 나서xz패키지를 설치하니 작동함 -
윈도우:
.com이나.exe로 실행 시 오류(0xc0000005)가 났는데, DEP 설정을 해제하니 실행됨 - Windows 11에서도 정상 작동함
- 단순히
chmod +x snake.com후 실행하면 Mono가 시도되며 실패함
대신bash snake.com으로 실행하면 잘 됨. Debian 13 기준임
-
브라우저: 확장자를
-
흥미로운 점은 이 파일이 세 개의 실행 파일이 하나로 합쳐진 형태라는 것임
그래서 각 플랫폼에서 완전히 다른 프로그램을 담을 수도 있음 -
kkrieger라는 96KB짜리 FPS 게임이 떠오름
당시 그래픽 수준이 놀라웠음
kkrieger (archive) -
맥에서 브라우저 외에 실행할 방법이 있는지 궁금함
cannot execute binary file오류가 발생함 -
이런 메커니즘을 발전시켜 진짜 범용 실행 파일(universal binary) 을 만들 수 있을지 궁금함
예를 들어 Haxe로 코드를 작성해 C++로 컴파일하면 Win32와 Linux용을 만들고,
JavaScript로 변환해 HTML에서도 실행 가능하게 한 뒤,
세 결과물을 하나의 파일로 합치는 식으로 구현할 수 있을 것 같음 -
하나의 파일로 어디서든 실행 가능한 단일 실행 앱 개념이 마음에 듦
나도 서버리스 플랫폼을 만들며 이런 방향을 연구 중임
단 하나의.html파일로 데이터 기반 앱을 만들고, web-components를 원격에서 불러오는 구조임
file://프로토콜로 HTML을 직접 여는 기능이 강력한데, 종종 간과됨- 원문 작성자는 실제로 세 번 구현했음 — WinAPI 기반 C, X11 기반 C, 그리고 HTML5 Canvas 기반 JS 버전
즉, 단순히 브라우저 인스턴스를 띄운 게 아니라 완전한 네이티브 앱 두 개를 만든 셈임 - 아쉽게도 HTTPS가 아닌 환경에서는 브라우저 기능 제약이 많음
- 단일 HTML 앱 개발 방식을 더 알고 싶음. 혹시 공개된 예제가 있는지 궁금함
- 원문 작성자는 실제로 세 번 구현했음 — WinAPI 기반 C, X11 기반 C, 그리고 HTML5 Canvas 기반 JS 버전
-
Polyglot 파일이 이렇게 늦게 등장한 게 신기함
기술적으로는 10~20년 전에도 가능했을 것 같음- 최초의 polyglot 파일이 언제 등장했는지 궁금함
EICAR.COM이 텍스트와 실행 파일을 겸한 초창기 예시로 떠오름
- 최초의 polyglot 파일이 언제 등장했는지 궁금함
-
js13kGames 같은 초소형 웹게임 대회가 생각남