software-development
Today

Кровавый Blood и сочный C++

Поскольку автор опять забыл выпить свои таблетки, вас ждет еще одна увлекательная статья на тему аморальных инженерных развлечений.

На этот раз мы соберем и запустим форк знаменитой игры «Blood». Целиком из исходников и под FreeBSD.

Вся красота в действии. Включая новую роль Анечки.

Анамнез

Долгий, многомесячный кранч — дни и ночи за компьютером, в погоне за убегающими сроками могут доконать кого угодно. Во время этой крайне выматывающей гонки с психикой программиста, чьи мозги работают на предельной мощности могут происходить самые удивительные метаморфозы:

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

И хорошо если кранч заканчивается лишь на стадии наблюдения, без перехода в.. ээ.. интерактив.

Хотя в моем случае всегда обходилось без подобного экстрима (сказываются годы тренировок), однажды по итогам затянувшегося летнего кранча, плавно перетекшего в осенний, автор обнаружил себя ранним утром посреди девственного леса, в известном питерском парке «Сосновка».

Вокруг пели птицы, по стволам сосен скакали жирные, откромленные местными жителями белки, а на коленях лежал медленно остывающий ноутбук с FreeBSD и открытой средой разработки.

С только что собранным проектом, вот этим:

Нет, я не знаю как оно тут появилось. Я просто куда-то не туда нажал в осеннем лесу.

I live... again

Ладно, закончим наконец стебать почтенную публику моими литературными талантами и начнем разговор по делу.

Blood это еще одна замечательная игра из моего далекого и прекрасного детства, оставившая заметный след на хрупкой детской психике в истории видеоигр своей запредельной (даже по меркам тех сказочных лет) жестокостью.

Вышедшая в 1997 году игра сразу же «навела шороху» среди игровой общественности и разнообразных моралистов тех лет, на что впрочем были определенные веские причины.

Саундтрек к статье.

В этой прекрасной игре главный герой — восставший мертвец в ковбойском прикиде начинал уровень выпрыгивая из собственной могилы с вилами наперевес, а простой пистолет стрелял зажигательными патронами, позволяя с пары выстрелов сжигать врагов заживо..

Тут можно было (и всячески поощрялось) взрывать зомби динамитом под раскатистый хохот главного героя и его чернушные шутки:

Суть "Blood" в одной картинке.

А единственными живыми представителями человеческой расы были поехавшие сектанты в рясах, с томми-ганами, кидающиеся динамитом. И бегающие в панике по уровню полуголые NPC.

Которых можно было заколоть вилами, сжечь и взорвать, да.

Еще тут давали сыграть на церковном органе и пустить под откос целый поезд, самолично взорвав паровоз. А одним из самых страшных монстров была.. отрубленная человеческая рука, очень быстро бегающая за вами по карте.

Напрыгнув, она медленно душила главного героя, с эффектным затемнением экрана и дикими хрипами в фоне.

А еще в этой замечательной игре был зверский обрез, который Калеб — тот самый главный герой и оживший мертвец умел быстро перезаряжать.

И оживающие мертвецы, которым этим обрезом можно было отстреливать лысые бошки. А если взять специальный бонус, обрезов на какое-то время становилось два.. И начиналось настоящее веселье!

Короче это одна из тех самых игр, ради которых придумали возрастные рейтинги и игровую цензуру. Поскольку весь контент «Blood», все игровые механики и даже главное меню ныне подпадают под те или иные ограничения и запреты в самых разных странах и на всех континентах.

Еще в «Blood» было много отсылок к мировой классике жанра «хоррор», вроде заледеневшего Джека из х\ф «Сияние»:

Кстати в игре было аж три вида динамита! С управляемой дальностью броска - все для изысканных гурманов экстремального цифрового насилия.

Теперь, после столь красочного описания всех этих «компьютерных ужасов», хотелось бы добавить классическое оправдание:

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

К сожалению истина куда мрачнее, поскольку в эту и все остальные интересные игры автор в детстве не просто играл, как обычный молодой человек тех и этих лет, он эти интересные игры.. ээ.. разбирал.

И немного препарировал.

Исходный код Blood

Хотя с исходным кодом оригинального «Blood» происходили весьма интересные пертрубации, официально он так никогда и не был открыт. Несмотря на дружный вой фанатов и разнообразные петиции.

Поэтому интересный проект NBlood, который мы сегодня будем собирать и запускать, на самом деле является результатом реверс-инжиниринга оригинальной игры, цитируя пост автора:

NBlood, an open-source Blood port based on EDuke32 and developed by NukeYKT, has been released. NBlood is a user-friendly and highly accurate port featuring multiplayer, software & OpenGL (Polymost) renderers, support for Redbook CD audio & accurate FM OPL3 (SB/AdLib) emulation, and DEF/HRP support.

Хотя проекту сильно помогло состояться недавнее раскрытие исходников игрового движка Build, на котором была создана оригинальная игра, не стоит забывать, что «Blood» в техническом плане был сильно продвинутей более известного «Duke Nukem 3D».

Например в Blood была поддержка воксельных 3D-моделей для разбросанных по уровню предметов интерьера, оружия и патронов:

Надгробие справа на самом деле разрушаемый трехмерный объект, а не просто очередной убогий спрайт.

Так что несмотря на обилие самых разных портов, в фанатской тусовке «Blood» имеет место быть длительный срач дискурс на тему наиболее точного и последовательного восстановления оригинальных механик игры.

И проект «NBlood» на данный момент позиционируется как наиболее точный.

Причем без всяких нейросетей и переписывания на Rust.

KDevelop и кровавая сборка

Собирать игровой проект из исходников — само по себе занятие непростое, поскольку в игровой индустрии редко соблюдаются правила совместной разработки и мягко говоря не котируются такие вещи как читаемость кода, документирование или соблюдение стандартов, что уж говорить о фанатских поделках проектах.

Поэтому большинство инструкций по сборке чего-нибудь игрового заканчиваются на выполнении цепочки консольных команд, ради получения заветного запускаемого файла.

Который хоть как-то работает.

Но поскольку автор держит марку по упоротости было принято волевое решение показать дорогим читателям не просто сборку, а полноценную настройку окружения разработчика C++:

чтобы было можно отлаживать NBlood из среды разработки, работали подсказки и проверки исходного кода — все как у приличных людей в инвестбанках.

С поправкой на KDevelop и FreeBSD, разумеется.

Среда разработки KDevelop с открытым проектом "Blood".

Хотя кого я обманываю, конечно же KDevelop это весьма нишевый инструмент для задротов и в общем случае коммерческой разработки использовалась бы скучная и банальная Microsoft Visual Studio или продукты Jetbrains.

Но поскольку для создания этой статьи и укрепления стойкости духа использовался винтажный HP Compaq nx7400 двадцатилетней выдержки, все эти замечательные продукты на нем безбожно тормозят.

Так что будем обходиться KDevelop, что конечно не так хардкорно как Emacs или (прости Господи) vi, но тоже весьма и весьма неплохо.

Кровавая подготовка

Как обычно, первым делом забираем исходники проекта:

git clone --depth 1 https://github.com/NBlood/NBlood.git

Скрипты сборки основаны на GNU Make, у которого во FreeBSD есть отдельный котел бинарник gmake .

Команда для сборки релизной версии выглядит так:

gmake RELEASE=1

Поскольку проект NBlood основан на кодовой базе EDuke32, набор требуемых библиотек полностью совпадает, как и инструкции по сборке.

Перед сборкой необходимо установить в систему следующие пакеты:

pkg install sdl2 nasm gtk2 flac libvpx

Стоит также заметить, для сборки использовался штатный GСС14, который также должен быть заранее установлен в системе.

К сожалению сборка на FreeBSD ломается при попытке использования Link-Time Optimization (LTO):

lto-wrapper: fatal error: make returned 2 exit status

Ее необходимо отключить, для чего в файле Common.mak переопределяем специальную переменную LTO.

В районе сток 439 — 442 происходит установка начальных значений:

..
    LTO := 0
else
    OPTLEVEL := 2
    LTO := 1
endif
..

Однако чуть ниже находится еще один блок, в котором происходит переопределение этой переменной, в зависимости от типа и версии компилятора:

..
ifeq (0,$(CLANG))
    ifeq (0,$(GCC_PREREQ_4))
        override LTO := 0
    endif
..    

Чтобы гарантированно обойти всю эту дичь, сразу после блока выше (строка 461) было вставлено радикальное:

override LTO := 0

Что отключает использование LTO во всех случаях, вне зависимости от платформы и компилятора.

Теперь надо сделать очистку проекта:

gmake clean

Затем перезапустить сборку:

gmake RELEASE=1

Теперь сборка успешно завершается и в корне появляются финальные бинарники, в том числе требуемый NBlood.

Но только при попытке запустить вас будет ждать облом:

./nblood 
ld-elf.so.1: /usr/local/lib/gcc10/libstdc++.so.6: version GLIBCXX_3.4.32 required by /opt/work/tmp/NBlood/nblood not found

Эта происходит из-за особенностей сборки под FreeBSD, точнее из-за неправильной подстановки при определении значения rpath, строка 547 в файле Common.mak:

 LINKERFLAGS += -Wl,-rpath,'$ORIGIN' -Wl,-z,origin

Хитрая подстановка $ORIGIN нужна для раскрытия переменной непосредственно в cамом линковщике, что к сожалению не отрабатывает правильно на FreeBSD.

Поэтому был добавлен отдельный блок (строка 545), с указанием rpath уже безо всяких подстановок:

..
else ifeq ($(HOSTPLATFORM),BSD)
    # Locate .so files
    LINKERFLAGS += -Wl,-rpath,/usr/local/lib/gcc14 -Wl,-z,origin
    $(info    FIX FOR BSD $(LINKERFLAGS))
..

За такой хак автор разумеется будет гореть в особенном аду для программистов, но мы тут пилим все же развлекательный контент, а не руководство по балету продуктовой сборке серьезных проектов.

После этой финальной правки необходимо повторно запустить очистку и полную сборку:

gmake clean
gmake RELEASE=1

Теперь наконец можно узреть собранную программу в действии:

Стартовое меню. Как видите NBlood поддерживает OpenGL и высокие разрешения, оригинальная игра была создана для MS-DOS.

Теперь переходим к весьма важному вопросу доставания игровых ресурсов.

Оригинальная обложка CD-ROM с игрой. Сразу понятна суть игры, цель и смысл ;)

Кровавый образ кровавого диска

Поскольку оригинальный «Blood» был и остается коммерческой игрой, из-за юридических заморочек до сих пор нет возможности выложить игровые ресурсы (текстуры, карты, модели, звуки и музыку) в публичный репозиторий Github, рядом с исходным кодом.

При этом игра давным давно является abandonware, так что как оригинальные образы дисков, так и различные фанатские сборки можно легко и без страшных юридических последствий скачать в интернете, например тут.

Стоит добавить важный нюанс:

для NBlood нужны ресурсы из последней официальной версии за номером 1.21, более старые не подойдут.

Так что указанный выше образ на самом деле от более поздней коллекционной версии игры, уже со всеми обновлениями и двумя официальными дополнениями, выпущенный в 1998 году.

Образ немного нестандартный по меркам современного пользователя, поскольку вместо одного ISO-файла внутри аж 9 непонятных файлов с расширением bin.

Игровые ресурсы находятся в файле BLOOD121 (Track 1).bin, все остальные это музыкальные треки в высоком качестве, которые чуть ниже мы также прочитаем, конвертируем и используем в игре.

Тем более что оригинальный саундрек «Blood» — оочень атмосферный и запоминающийся.

Прежде чем монтировать ISO-образ, в данном случае его еще придется выковырять из bin-файла, для чего используется утилита bchunk:

bchunk BLOOD121\ \(Track\ 1\).bin track.cue out

К сожалению bchunk не умеет работать с мультитрековыми RAW-образами, физически разнесенными по разным файлам, поэтому не получится использовать оригинальный .cue файл из же подборки.

Вместо него создаем новый файл track.cue с таким содержанием:

FILE "BLOOD121 (Track 1).bin" BINARY
  TRACK 01 MODE1/2352
    INDEX 01 00:00:00

Технически это просто первый блок метаданных, скопированный из оригинального cue-файла.

После запуска bchunk с указанием этого файла (см. команду выше), в текущем каталоге должен появиться out01.iso, содержащий нужные данные игры уже в формате ISO 9660.

Для проверки корректности конвертации, можно запустить утилиту file, которая покажет тип получившегося образа:

file ./Downloads/out01.iso 
./Downloads/out01.iso: ISO 9660 CD-ROM filesystem data 'BLOOD121'

Теперь наконец монтируем образ стандартным для FreeBSD способом — через жопу mdconfig:

mdconfig -a -t vnode -f /path/to/out01.iso
mkdir -p /mnt/iso
mount -t cd9660 -o -g /dev/md0 /mnt/iso

Но и это еще не все, поскольку на диске находится не сама игра, а лишь ее инсталлятор и искомые файлы с ресурсами запакованы внутрь.

Чтобы винтажный инсталлятор из 1998 года правильно отработал в реалиях 2026 и под FreeBSD необходим Wine и небольшая индейская хитрость:

в настройках Wine необходимо смонтировать каталог, в который был смонтирован ISO-образ диска, который мы выдрали из первого трека оригинального диска.. как виртуальный CD-ROM.

И пусть горят на этом месте все, кто изучает русский язык.

Теперь запускаем winecfg, переходим во вкладку «Drives», жмем кнопку «Add» и указываем путь к каталогу /mnt/iso.

И.. нет, это тоже еще не все :)

Осталось внезапно самое главное, без чего весь этот скотский цирк на конной тяге никуда не поедет — в разделе «Advanced», в поле «Type» выбираем значение CD-ROM:

Так выглядит дважды виртуальный CD-ROM.

Запуск также немного отличается, поскольку необходимо не просто запустить приложение инсталлятора, но запустить его c дважды виртуального CD-ROM.

Для этого запускаем аналог cmd.exe — wineconsole, переходим на диск D: и наконец запускаем сам инсталлятор:

Оригинальный инсталлятор игры Blood из 1998 года, запущенный в 2026 году под FreeBSD. Как тебе такое Илон Маск?

Получаем начальную форму с большой кнопкой «Install» и весьма злобным звуковым эффектом на фоне. Инсталлятор оформлен в фирменном стиле игры, поэтому отлично вписывается в мое оформление Xfce под FreeBSD.

Основной цвет тут кроваво-красный:

Надпись "Parental advisory" когда-то была знаком качества.

В инсталляторе просто жмем несколько раз «Next», поскольку ничего дополнительно выбирать не надо. По-умолчанию игра устанавливается в каталог C:\BLOOD, который на хосте выглядит так:

~/.wine/drive_c/blood

Из этого каталога копируем в корень проекта «NBlood» следующие файлы, обязательно в верхнем регистре (иначе при запуске игра вылетит с ошибкой):

  • BLOOD.INI
  • BLOOD.RFF
  • BLOOD000.DEM, ..., BLOOD003.DEM (optional)
  • CP01.MAP, ..., CP09.MAP (optional, Cryptic Passage)
  • CPART07.AR_ (optional, Cryptic Passage)
  • CPART15.AR_ (optional, Cryptic Passage)
  • CPBB01.MAP, ..., CPBB04.MAP (optional, Cryptic Passage)
  • CPSL.MAP (optional, Cryptic Passage)
  • CRYPTIC.INI (optional, Cryptic Passage)
  • CRYPTIC.SMK (optional, Cryptic Passage)
  • CRYPTIC.WAV (optional, Cryptic Passage)
  • GUI.RFF
  • SOUNDS.RFF
  • SURFACE.DAT
  • TILES000.ART, ..., TILES017.ART
  • VOXEL.DAT

Файлы помеченные как «optional» тоже стоит скопировать, поскольку они нужны для дополнения «Cryptic Passage» — весьма своеобразного и также поддерживаемого в проекте NBlood.

Скан оригинального бокса с игрой. Да, когда-то давно писать "splatter your friends with blood" было можно..

Кровавые треки

Как уже упоминалось выше:

саундтрек «Blood» — отдельное, совершенно особенное произведение, точно достойное выдирания и прослушивания.

Хотя без оригинального саундтрека музыка в игре все равно будет присутствовать, но в куда более низком качестве — в виде MIDI, что сильно снизит атмосферность.

В упомянутом образе игры треки выложены в виде отдельных bin-файлов, еще есть один общий cue-файл с метаданными.

Для выдирания музыки из RAW-образа будет использоваться вся та же утилита bchunk, которой мы получали ISO-образ абзацем выше.

Создаем файл audio.cue следующего содержания:

FILE "BLOOD121 (Track 2).bin" BINARY
  TRACK 01 AUDIO
    INDEX 00 00:00:00
    INDEX 01 00:02:00

Конвертируем в WAV следующей командой, где 2 это внезапно префикс для названия выходного файла:

bchunk -w BLOOD121\ \(Track\ 2\).bin audio.cue 2

Затем конвертируем получившийся WAV-файл в формат FLAC, поддерживаемый NBLood:

flac 201.wav

Повторяем со всеми остальными треками, в сумме должно получиться 8 файлов. Копируем их в каталог с игрой (да, прямо в корень), переименовав согласно формату: bloodXX.flac

Где XX это номер трека от 01 до 08.

После копирования треков, будет необходимо включить опцию «Redbook audio» в меню «Sound» непосредственно в игре.

Ну и наслаждаться отличным саундтреком.

Теперь, закончив наконец с игровыми ресурсами и пройдя начальную сборку проекта, переходим к настоящему веселью — к работе с NBlood из среды разработки, как у настоящих C++ программистов.

Среда разработки KDevelop смотрит на нового пользователя.

Кровавый KDevelop

Первая публичная версия среды разработки KDevelop вышла еще в далеком 1998 году, с тех пор проект активно развивается и улучшается, давно завоевав определенную популярность и известность среди разработчиков.

По характерному названию, которое начинается с заглавной «K» нетрудно догадаться, что KDevelop является частью проекта KDE (названия на «k» — их фирменный прикол), так что разработка и сопровождение ведутся на технической базе KDE, в их рассылках и репозиториях.

Несмотря на определенную известность и популярность, KDevelop имеет ряд врожденных ээ.. особенностей, главная из которых — эпическая падучесть.

Все классические шутки линуксоидов про KDE, из серии «плазма не падает» полностью применимы и к KDevelop.

Но есть и хорошие новости:

KDevelop очень широко распространен, представлен практически во всех в открытых операционных системах, он есть во всех *BSD/Linux и некоторых UNIX (например Solaris), везде где есть KDE и Qt.

Разумеется давно существуют версии KDevelop для Windows и MacOS.

Во FreeBSD KDevelop давно присутствует в виде готового пакета и устанавливается командой:

pkg install kdevelop

Но прежде чем погружать дорогого читателя в кровавый ад разработки особенности сборки именно «NBlood» — немаленького и непростого проекта, стоит проверить работу и правильность настройки KDevelop на чем-то более простом.

Hello, world

Поможет в задаче проверки среды «нестареющая классика» в виде простой консольной программы на C++, печатающей «Hello, world».

Выбираем в меню «Project→New from Template», затем выбираем (слева направо) «Category → Standard» и «Project Type → Terminal».

В качестве последнего выбора актуален «CMake C», либо «CMake C++»:

Тут "Акелла промахнулся" и выбрал шаблон на С, вместо C++.

Также будет необходимо ввести название проекта и желательно обойтись без особых изысков, хотя-бы на начальном этапе.

После нажатия кнопки «Next» появится диалог выбора системы контроля версий, которая не очень актуальна для первого и тестового проекта, поэтому просто оставляем выбор по-умолчанию None и едем дальше.

После завершения визарда, будет автоматически запущен CMake-скрипт для сборки проекта:

Классический "Hello, world" на С++ в KDevelop.

Теперь нажимаем клавишу F8, либо большую кнопку «Build» на панели инструментов — запустится непосредственно сборка и линковка:

Полученный с таким невероятным трудом бинарник hello можно сразу запускать из каталога build, но стоит попробовать запустить и из среды разработки — как делают настоящие профессионалы, которым за программирование на C++ еще и платят.

Для этого нажимаем Shift-F9, либо большую кнопку «Execute» на панели инструментов — появится диалог настройки запуска:

Так выглядит диалог настройки запуска

После начальной настройки, нажатие Shift-F9 или кнопки «Execute» будет запускать уже непосредственно собираемое приложение, если будет необходимо вернуться в диалог настройки запуска — это можно сделать через меню «Run→Configure Launches».

Так выглядит результат запуска только что собранного приложения:

Добро пожаловать в C++ программисты!

Кровавый проект в KDevelop

Ну что ж, коль вам хватило терпения дочитать до этого места и не свихнуться, то действительно имеется определенный интерес к теме.

Так что теперь честно рассказываю, как стать программистом работать с проектом NBlood из KDevelop.

Для начала запускаем новую сессию из меню «Session →Start New Session», после чего будет запущен новый сеанс среды разработки, с настройками по-умолчанию.

Теперь выбираем меню «Project→Open/Import Project» и указываем каталог с NBlood, появится диалог с информацией о проекте:

Обратите внимание на поле "Project manager" - тут обязательно должен быть GNUmakefile!

Нажимаем «Finish», после чего произойдет загрузка проекта в среде разработки:

Так выглядит загруженный проект KDevelop.

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

Однако все еще есть ряд проблем, мешающих нормальной работе с проектом, которые мы сейчас сейчас будем последовательно решать.

Для начала настроим запуск собранного приложения NBlood из среды разработки.

Открываем меню «Run→Configure Launches», жмем кнопку «+Add» и выбираем «Compiled Binary», в поле «Executable» указываем полный путь до запускаемого бинарника NBlood:

Настойка запуска.

Теперь необходимо настроить систему сборки, поскольку по-умолчанию в FreeBSD используется свой make, а GNU-версия называется gmake.

Вызываем меню «Project→Open Configuration.», откроется уже известный вам диалог настройки проекта.

Переходим в раздел «Make» и в поле «Make executable» вводим значение gmake:

После этой настройки, можно наконец запускать сборку из среды разработки, с надеждой на успешное завершение. Для чего нажимаем большую кнопку «Build» на панели инструментов, либо клавишу F8.

Так выглядит результат успешной сборки и линковки, после которой появляется запускабельное приложение NBlood:

Так выглядит успешная сборка из KDevelop. Что делать с ошибками, подчеркнутыми красным - см. ниже.

Для запуска только что собранного бинарника из среды разработки нажимаем «Shift-F9», либо большую кнопку «Execute».

Так выглядит запущенное приложение, обратите внимание на сообщения в консоли при запуске:

Теперь вы умеете собирать и запускать приложение из KDevelop — базовый минимум, необходимый для разработки «NBlood».

Самое время позаботиться об удобстве.

Кровавые заголовки

Начав бороздить бескрайние просторы исходных кодов NBlood, вы немедленно обнаружите, что KDevelop почему-то ругается на отсутствующие заголовки.

Хотя сам проект успешно собирается и запускается.

Характерный пример:

Хотя автору, закаленному разработкой в «Блокноте» и vim это не сильно мешает, более нежных читателей, избалованных современными средами разработки такое поведение быстро начнет бесить.

Для того чтобы помочь Дарье KDevelop найти пропавшие заголовки, открываем меню «Project→Open Configuration..», затем переходим на вкладку «Language Support».

На вкладке «Includes/Imports» нажимаем кнопку «Open file dialog» и указываем каталоги:

source/build/include
/usr/local/include
/usr/include

Так выглядит финальная настройка:

После изменения этой настройки, запустится повторное фоновое сканирование проекта:

Результатом работы будет список обнаруженных проблем в проекте, доступный из вкладки «Problems» внизу рабочего окна KDevelop:

Не все найденные проблемы будут актуальны, о чем следует помнить прежде чем исправлять.

Clazy

Cppcheck

Clang-Tidy