Можно ли уместить игру в QR-код?

Сжатый перевод видео, которое закреплено ниже

Технически, это возможно, ведь QR-код такой же носитель информации, как, например, дискета, CD или жёсткий диск.

Такие коды обычно хранят в себе ASCII-текст, но их можно приспособить и для двоичных данных — главное не забывать про ограничения размера, ведь самый большой QR версии 40 может хранить в себе целых 2956…

БАЙТ

То есть, примерно 2.88 килобайта.

Чтоб вы понимали, насколько это мало, посмотрите на этот звуковой файл из Windows — он весит 11 килобайт.

На самом деле, DTF по какой-то причине не позволил мне прикрепить файл WAV, поэтому я пережал его в MP3. Оригинал вы всё равно можете найти по адресу C:\Windows\Media под названием «Windows — начало обзора.wav» или «Windows Navigation Start.wav» (если у вас стоит английский язык).

Вот ещё пример — обычная дискета ёмкостью 1.44 МБ может хранить примерно в 500 раз больше данных.

Тем не менее, хорошо оптимизированный код может творить чудеса — люди даже умещали игры в загрузочный сектор дискеты (а это 512 байт). Можете также посмотреть видео о создании игры Micro Mages для NES.

Сначала автор хотел создать «Тетрис», но, учитывая, что Tetris Company любит подавать в суд за нарушение авторских прав, решил написать не менее известную «Змейку».

Логично также было бы сделать версию игры для смартфонов, ведь обычно именно с них и считывают QR-коды, однако особенности этих платформ, а также их закрытость вряд ли позволили бы запустить исполняемый файл прямо из QR. Таким образом, было принято решение написать программу для Windows на языке С с расчётом на то, что код можно будет распознать веб-камерой.

Можно было пойти лёгким путём и написать всё на HTML и JavaScript, что автор и сделал для демонстрации. Однако для этого понадобился бы браузер, который, по сути, делал бы множество вещей за нас.
Можно было пойти лёгким путём и написать всё на HTML и JavaScript, что автор и сделал для демонстрации. Однако для этого понадобился бы браузер, который, по сути, делал бы множество вещей за нас.

Поначалу задача казалась невозможной, ведь даже простейший exe-файл с hello world уже занимает 100 КБ.

Можно ли уместить игру в QR-код?

Но не все 100 КБ этого файла — код. Большую часть занимают runtime-библиотеки, которые можно отключить специальной командой (после этого, правда, придётся вручную подключать нужные библиотеки).

Путь к файлу\hworld>cl /c hworld.c && link /NODEFAULTLIB /ENTRY:main /SUBSYSTEM:WINDOWS hworld.obj ucrt.lib //Где hworld - название конечной папки, а hworld.c - имя файла

Таким образом, файл уменьшается в размере до 3 КБ.

Можно ли уместить игру в QR-код?

Можно было бы сократить размер ещё больше, используя различные оптимизации, однако автор решил писать программу на языке, ближе всего к машинному — языке ассемблера х86.

Без какой-либо помощи со стороны MattKC сделал простое окно на ассемблере, и оно уже заняло 2.5 КБ. Он стёр несколько лишних строк кода, и размер файла уменьшился ровно до 2 КБ, что странно, ведь удалено было гораздо меньше кода.

Выяснилось, что формат Win32 PE, который используется почти во всех современных exe-файлах имеет минимальный размер секции кода в 512 байт (то есть, если удалить секцию, которая на самом деле весит, к примеру, 30 байт, то всё равно размер файла уменьшится на 512 байт; по крайней мере, я так понял).

Использование такого формата означает, что нужно будет сделать приложение не только меньше 2.88 КБ (напомню, что это — вместимость QR 40), но и меньше 2.5 КБ, ведь добавление лишней секции кода «округлит» этот размер до 3 КБ.

Однако, и эту проблему можно решить — нужно лишь переопределить размер секции в компоновщике, а Windows умеет принимать любые значения. В качестве инструмента был выбран MSVC. Автору удалось поставить минимальное значение секции в 16 байт, так как именно столько использовали библиотеки Win32. Но даже так полный код «Змейки» весил 2.92 КБ.

А что если просто переписать код на С и надеяться, что компилятор создаст более эффективный ассемблерный код, чем человек?…

Без комментариев
Без комментариев

Но и эта идея в дальнейшем провалилась, ведь полное приложение вышло размером 3.1 КБ, а все дальнейшие сокращения кода приводили к неиграбельному состоянию «Змейки».

Оставалось только полагаться на сжатие данных. При этом, MattKC не хотел использовать архиваторы типа 7zip, ведь это нарушило бы принцип «просканировал код и играешь». К счастью, существует принцип упаковки исполняемых файлов, когда к сжатому exe прикрепляется программа-декомпрессор, которая выполняет распаковку данных прямо при запуске.

UPX в этом случае не подошёл, ведь он не поддерживал размер секции в 16 байт, а при возвращении к стандартному размеру сжатый exe занимал 4 КБ. Поэтому автор решил использовать Crinkler, который применяется в том числе для создания демосцен (например, вот таких). Новая программа смогла сжать файл аж до 1.4 КБ.

Оставалось только создать сам QR-код (естественно, с помощью команды терминала).

qrencode -r out.exe -8 -o code.png

Для того, чтобы считать код была выбрана программа zbar (а конкретно zbarimg для чтения цифровых изображений). Выполняется команда ниже всё в том же терминале.

zbarimg --oneshot -Sbinary --raw code.png > ReadFromCode.exe //Sbinary используется для того, чтобы сообщить программе, что QR содержит двоичный код, а не текст

И на первый взгляд, всё сработало отлично — код считался и приложение запустилось.

Можно ли уместить игру в QR-код?

При «реальных» испытаниях с распечатанным QR-кодом exe-файл по какой-то причине не запустился.

Если попробовать запустить приложение из QR на терминале, то возникает ошибка сегментации
Если попробовать запустить приложение из QR на терминале, то возникает ошибка сегментации

При побайтовом сравнении exe-файла цифрового изображения QR и его отсканированной версии стала ясна причина ошибки — байт 0А стал двумя байтами 0D 0A. Всё дело в том, что UNIX-системы используют в качестве символа переноса строки байт 0x0А (символ \n), а в Windows исторически использовались два байта — возвращения каретки и переноса строки 0x0D0A (символы \r \n).

Zbarcam, который использовался для сканирования кода с помощью камеры не давал Windows никакого сигнала о том, что данные в QR являются двоичным кодом, и поэтому система автоматически конвертировала данные в текст, попутно заменяя байты.

Небольшое изменение кода решило проблему — теперь приложение запускается. Можно даже написать небольшой скрипт, который автоматически будет открывать exe-файл после сканирования.

zbarcam -1 -Sbinary --raw > snake.exe && snake.exe
Рабочая версия QR-кода
Рабочая версия QR-кода

Для того, чтобы самому запустить «Змейку», нужно перейти на сайт MattKC и скачать изменённый дистрибутив zbarcam.

Можете зайти в описание к видео, там много всяких ссылок

А ещё на Хабре есть интересная статья о том, как человеку прочитать QR-код.

5252
4 комментария

Пиздато, еще бы можно было с телефона опробовать вообще была бы пушка

6
Ответить

 А что если просто переписать код на С и надеяться, что компилятор создаст более эффективный ассемблерный код, чем человек?..Почти всегда :)
Современные компиляторы используют такие хаки, что аж руки опускаются :(

2
Ответить

В случае с C ещё много чего от флагов компилятора зависит.

3
Ответить

Статья крутая, но прокомментировать даже не могу, специфичная тема)

2
Ответить