Cave Story (или в оригинале — Dōkutsu Monogatari), является одной из самых известных бесплатных японских Indie-игр. Весь игровой контент, включая музыкальные композиции и код, был создан лишь одним человеком — Daisuke «Pixel» Amaya. Pixel в течении пяти лет трудился над своей игрой, уделяя ей как можно больше свободного времени. Именно благодаря знакомству с Cave Story многие начинающие Game Developer’ы вдохновились на создание собственных Indie-проектов. После своего первого релиза для PC, который состоялся в 2004 году, игра медленно завоёвывала популярность геймеров в японском интернете, а после её перевода на английский язык — стремительным темпом распространилась по всему миру. В целом, Cave Story получила достаточно положительные отзывы критиков за свой увлекательный геймплей и интересный сюжет. Несколько лет спустя, в ноябре 2011-го года, в сервисе цифровой дистрибуции Steam вышло переиздание игры, названное Cave Story+, содержащее незначительные отличия от оригинальной версии.
Скриншоты из русской версии Cave Story, запущенной с помощью NXEngine на устройстве Ritmix RZX-50; локации «Хранилище Яиц» и «Грасстаун»
Cave Story представляет собой 2D-платформер с элементами RPG, выполненный в антураже старых классических игр, в частности, разработчик вдохновлялся таким проектом, как Metroid. После проявления интереса к игре со стороны публики Cave Story была портирована на наиболее популярные платформы. Поскольку исходный код игры закрыт, Cave Story удалось перенести лишь на ограниченное количество игровых устройств. К счастью, нашлась энтузиастка, которая полностью переписала движок игры на C++ (изначально он был написан на C) и выпустила код под лицензией GNU GPL Version 3. Свой проект Caitlin «rogueeve» Shaw назвала NXEngine. Благодаря этому движку, практически полностью совместимому с оригинальным, в Cave Story можно поиграть на самых разных платформах и устройствах.
Но, к сожалению, не всё так гладко и NXEngine всё ещё нуждается в «доработке напильником». Например, на Dingoo A320 невозможно играть со включенной музыкой из-за возникающих «подтормаживаний» в игре. Caitlin Shaw хотела полностью сохранить совместимость с оригинальными DATA-файлами Cave Story, поэтому музыка в нём генерируется процедурно, создавая большую нагрузку на CPU. Центральный процессор Dingoo A320 не справляется с такой нагрузкой, отсюда и возникают «подтормаживания», сильно мешающие игровому процессу. Одна из моих модификаций исходного кода NXEngine позволяет использовать библиотеку SDL_mixer, добавляя в игру возможность воспроизведения трекерной музыки из внешних файлов, вместо ресурсоёмкой процедуры генерации. Кроме того, NXEngine не мог отобразить русские буквы из русифицированных DATA-файлов, но с моим небольшим исправлением это стало возможным. Теперь геймеры, не слишком хорошо знающие английский язык, смогут всецело насладиться сюжетными диалогами. Помимо этого, в движок была добавлена возможность работы с широкоформатными дисплеями с разрешением 480×272, что позволило запускать Cave Story на Ritmix RZX-50 в полноэкранном режиме. Для удобства внесения изменений в код NXEngine была произведена некоторая адаптация исходного кода к интегрированной среде разработки — Qt Creator.
Содержание:
1. Обзор ключевых элементов игры
2. Основные клавиши управления
3. Информация для разработчиков
3.1. Windows
3.2. Linux
3.3. Dingux/OpenDingux/MotoMAGX/EZX
3.4. Ресурсы
4. Центр загрузки
5. Обратная связь
1. Обзор ключевых элементов игры
Запустив Cave Story, вы можете посмотреть небольшую кат-сцену, с которой и начинается завязка сюжетной линии. Подобные заставки ещё не раз будут встречаться вам на протяжении всей игры. Прокрутить диалог можно нажав на клавишу прыжка. Итак, главный герой игры появляется в первой локации, которая называется «Начальная точка» и где можно попробовать повзаимодействовать с внутриигровыми предметами — аптечкой и дискетой. Клавиша «Вниз» отвечает за использование. Таким образом, подойдя к аптечке и нажав «Вниз» можно пополнить здоровье, а подойдя к вращающейся дискете — сохраниться. Поднявшись вверх к двери вы попадёте в первую пещеру, наполненную различными врагами и кроваво-красными шипами, к которым нельзя прикасаться. С помощью баночек с сердечком (одна из которых лежит в этой локации) можно «прокачивать» здоровье героя, тем самым повышая шанс на выживание в трудных схватках с противниками. Такие баночки очень хорошо запрятаны в труднодоступных уголках уровней, придётся напрячься, чтобы достать их оттуда. Не стоит пренебрегать ими, так как Cave Story достаточно хардкорная игра и её прохождение без должных бонусов может сильно затянуться.
NXEngine в дистрибутиве Linux Mint 15 KDE; локация «Первая пещера»
Как только вы найдёте в сундучке ваше первое оружие — пистолет «Полярная Звезда», возвращайтесь назад, к начальной точке. Кстати, в таких сундучках можно найти очень много полезных вещей и пополнить свой инвентарь. Вы можете заметить, что после убийства врагов из них выпадают различные бонусы, например, оранжевые кристаллики или сердечки. С помощью кристалликов можно «прокачать» оружие, увеличив скорострельность и сделав сильнее наносимый врагам урон. Однако, следует помнить, что если враги нанесут урон вам, уровень оружия может понизиться и оно вновь станет слабым.
Cave Story, запущенный с помощью NXEngine на устройстве Motorola ZN5, под управлением операционной системы MotoMAGX; локации «Руины», «Лабиринт М» и «Внешняя стена»
Поднявшись обратно наверх, разбейте выстрелом блоки и дверь, а затем выходите из пещеру наружу, в локацию «Деревня Мимига», в которой вас ждёт увлекательная квестовая составляющая этой игры. Вообще в Cave Story смешивается несколько игровых жанров, что и делает эту игру интересной для большой аудитории геймеров. Каждый находит в ней что-то приятное и наиболее ценное для себя. Мне, к примеру, понравился уровень «Лабиринт М», который нужно проходить вместе с одним из персонажей игры.
Cave Story в операционной системе MS Windows 8.1; локация «Деревня Мимига»
На этом я заканчиваю ваш процесс освоения в игровом мире. Вы уже достаточно самостоятельны и сможете сами разобраться в управлении и механике игры. Кстати, в Cave Story имеется целых три различных концовки — две «хороших» и одна «плохая». Решения, которые вы принимали на протяжении всего игрового процесса, могут оказать сильное влияние на то, как вы завершите Cave Story. Поэтому играйте очень внимательно! Если всё же у вас возникли непреодолимые трудности, вы можете почитать большой гайд по прохождению Cave Story здесь. Но первый раз лучше всего пройти эту замечательную игру без подглядывания в различные мануалы, чтобы получить наиболее полные впечатления от Cave Story.
2. Основные клавиши управления
Windows/Linux
- Движение — ←↑↓→;
- Прыжок — Z;
- Огонь — X;
- Предыдущее оружие — A;
- Следующее оружие — S;
- Инвентарь — Q;
- Карта — W;
- Выход — ESC;
- Опции — F3.
Dingux/OpenDingux
- Движение — D-Pad;
- Прыжок — A;
- Огонь — B;
- Предыдущее оружие — X;
- Следующее оружие — Y;
- Инвентарь — R;
- Карта — START;
- Выход — SELECT;
- Опции — L.
MotoMAGX
- Движение — D-Pad;
- Прыжок — Громкость ‘+’;
- Огонь — Центр джойстика;
- Предыдущее оружие — 0;
- Следующее оружие — 8;
- Инвентарь — 2;
- Карта — 5;
- Выход — Кнопка камеры;
- Опции — #.
EZX
- Движение — D-Pad;
- Прыжок — Громкость ‘+’;
- Огонь — Громкость ‘-‘;
- Предыдущее оружие — ModKey + Громкость ‘-‘;
- Следующее оружие — ModKey + Громкость ‘+’;
- Инвентарь — Центр джойстика;
- Карта — ModKey + Центр джойстика;
- Выход — Кнопка камеры;
- Опции — ModKey + Кнопка камеры.
Примечание: Клавиша ModKey на Motorola A1200 находится между кнопками регулировки громкости, а на Motorola ROKR E6 под красной трубкой.
3. Информация для разработчиков
Если вы хотите собрать NXEngine из исходников, то немного информации об кросс-платформенной компиляции этого движка, вы можете найти здесь. Существует несколько вариантов сборки:
- Через Makefile;
- С помощью Qt Creator.
Опишу некоторые нюансы, касающиеся построения проекта из исходного кода для различных операционных систем.
3.1. Windows
Для сборки необходим MinGW и установленные библиотеки SDL, SDL_ttf и SDL_mixer (опционально). Посмотреть подробнее про сборку SDL-библиотек для MinGW можно тут. После компиляции необходимых библиотек и их установки в тулчейн можно приступать к построению NXEngine.
Для получения исходного кода рекомендуется воспользоваться утилитой Git. Ссылку на GitHub-репозиторий, в котором доступен исходный код движка, можно найти ниже, в этом разделе. После получения исходников переходим в директорию с ними и выполняем команду:
mingw32-make -f Makefile.mingw32
После выполнения которой в директории с исходным кодом появится исполняемый файл «nx.exe».
Для сборки проекта с помощью Qt Creator необходимо установить в тулчейн MinGW SDL-библиотеки, затем открыть файл «nx.pro» и начать построение проекта.
3.2. Linux
Перед компиляцией проекта устанавливаем следующие пакеты, содержащие библиотеки и заголовочные файлы:
sudo apt-get install libsdl1.2-dev libsdl-mixer1.2-dev libsdl-image1.2-dev libsdl-ttf2.0-dev
После установки необходимых библиотек в систему, получаем исходники и начинаем построение NXEngine с помощью файла «Makefile.linux»:
make -f Makefile.linux
Или с помощью Qt Creator. Затем собранный исполняемый файл «nx» необходимо поместить в директорию с DATA-файлами игры.
3.2.1. Разрешение зависимостей
Чтобы исполняемый бинарник «nx» не зависел от некоторых системных библиотек, которые могут отсутствовать в одних дистрибутивах GNU/Linux и присутствовать в других, его необходимо собрать в так называемой LSB-системе (Linux Standard Base), следуя инструкциям из этого поста.
Лично я советую собирать NXEngine и другие проекты, в основе которых лежит SDL, в GNU/Linux дистрибутиве CentOS 5.10:
Собранный NXEngine в операционной системе CentOS 5.10, 32-bit
Следует отметить, что так как библиотеки SDL и SDL_ttf не входят в состав LSB, их необходимо собрать отдельно и установить вручную в дистрибутив. Процедура компиляции и установки довольно тривиальна: следует загрузить исходники с официального сайта www.libsdl.org и выполнить их построение с помощью команды:
./configure && make -j4 && sudo make install
После выполнения которой необходимые библиотеки и заголовочные файлы будут установлены в каталог «/usr/local/*». Так как мультимедийные SDL-библиотеки отсутствуют во многих дистрибутивах, я рекомендую использовать статическую линковку с ними. После сборки в LSB-совместимой системе и статической линковки, мы получаем исполняемый файл с минимальным набором зависимостей, который, теоретически, должен работать практически в любом популярном дистрибутиве:
1 2 3 4 5 6 7 8 9 10 11 |
exl@exl-virtual-machine:~/Games/NXEngine-RUS > ldd nx linux-vdso.so.1 => (0x00007fff75ddb000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7bffa02000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f7bff7e5000) libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f7bff52b000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f7bff228000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7bfef23000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f7bfed0c000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7bfe944000) /lib64/ld-linux-x86-64.so.2 (0x00007f7bffc25000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f7bfe72d000) |
Помимо этого, при компиляции приложений в LSB-системе, будет отсутствовать ошибка связанная с GLIBC/GLIBCXX, что позволит приложению запуститься на любых дистрибутивах, выпущенных позже 2006-го года:
1 2 3 4 5 6 7 |
exl@exl-virtual-machine:~/Games/NXEngine-RUS > strings nx | grep GLIBC GLIBC_2.1 GLIBC_2.0 GLIBCXX_3.4 GLIBC_2.2 GLIBC_2.3 GLIBC_2.1.3 |
3.3. Dingux/OpenDingux/MotoMAGX/EZX
При установленных и настроенных тулчейнах для этих платформ сборка NXEngine отличается лишь переключением тулкитов в Qt Creator’е по способу, описанному здесь. Также NXEngine можно скомпилировать с помощью файлов «Makefile.dingux», «Makefile.ezx» или «Makefile.motomagx», если у вас не установлен Qt Creator.
Пакет для Dingux/OpenDingux представляет собой обычный ZIP-архив, содержащий DATA-файлы игры и исполняемый файл «nx.dge». MGX-пакет для MotoMAGX по своей сути тоже является обычным ZIP или 7Z-архивом, в котором содержится каталог с необходимыми для запуска программы файлами. Файл «NXEngine.cfg» содержит необходимую установочную информацию, а файл «run.sh» является скриптом, определяющим некоторые переменные и пути до необходимых для запуска приложения библиотек. PKG-пакет для платформы EZX представляет собой TAR.GZ-архив со специальным DESKTOP-файлом, описывающим установку. Подробнее изучить структуру пакетов для этих платформ можно скачав из этого раздела необходимые файлы и открыв их в любом архиваторе, к примеру, в 7-zip.
3.4. Ресурсы
3.4.1. Предопределённые макросы
Некоторые из предопределённых макросов могут использовать уникальные DATA-файлы, не входящие в обычную поставку Cave Story или NXEngine. Так, например, если вы будете использовать макрос _480X272, то в каталог «data/» следует поместить файлы «bkFog480fix.pbm» и «bkMoon480fix.pbm», которые являются фиксами фонов для широкоформатных дисплеев. Найти эти файлы и их исходники в виде «*.psd»-файлов можно в репозитории на GitHub’е. При использовании макроса _SDL_MIXER музыка не генерируется приложением, а просто проигрывается из трекерных файлов. Поэтому в корневой каталог NXEngine следует поместить папку «xm/», содержащую трекерную музыку в формате «*.xm». Скачать архив с директорией «xm/» можно здесь. Благодаря использованию этого макроса снижается нагрузка на процессор, позволяя NXEngine работать на слабых устройствах. Используя макрос _L10N_CP1251 вместе с _320X240 или _480X272, не забудьте поместить файл «DroidSansMono.ttf» в корневой каталог с игрой. Благодаря шрифту Droid Sans Mono русские буквы при разрешении 320×240 или 480×272 не выглядят слишком маленькими и отлично читаются.
Макрос |
Платформа |
|||||||||||
Таблица предопределённых макросов и их использование в проекте NXEngine
Помимо этого, будет полезно прочитать файл «nx.pro», в котором тоже описывается назначение каждого макроса.
3.4.2. Локализация
Для включения русской локализации нужно расскоментировать в Makefile строчку, содержащую «-D_L10N_CP1251» или изменить строку #20 в файле «nx.pro»:
1 |
CONFIG -= l10n_rus |
на
1 |
CONFIG += l10n_rus |
После чего необходимо полностью пересобрать NXEngine. Следует заметить, что для русской версии игры используются русифицированные DATA-файлы, выдернуть которые можно из русскоязычных пакетов в этом разделе.
3.4.3. DATA-файлы
После компиляции NXEngine, создастся исполняемый файл, которому нужны DATA-файлы для корректной работы. Их можно взять, «распотрошив» готовые к запуску архивы с движком и игрой, которые можно скачать ниже. В соответствии с описанием, данном в этом разделе, поместите необходимые для работы приложения файлы в корневой каталог NXEngine.
4. Центр загрузки
Загрузить последние версии NXEngine, собранные из исходного кода для различных платформ и операционных систем, можно в таблице размещённой ниже. Архивы представляют собой готовые Bundle-версии движка с уже установленной игрой Cave Story, которые следует лишь распаковать или установить, а затем запустить.
Готовые пакеты, предназначенные для запуска на конечной системе, спасибо за хостинг www.cavestory.org, зеркало файлов на Yandex.Disk
5. Обратная связь
Если вы собрали NXEngine из исходного кода, расположенного в моём репозитории на GitHub’е, добавив, например, поддержку какого-нибудь Handheld-устройства, или просто форкнули NXEngine, исправив ошибки или нарастив функционал — обязательно напишите мне, я обновлю эту страницу или добавлю ссылки для загрузки в таблицу.
Update 20-DEC-2016: В центр загрузки добавлены пакеты для смартфонов Motorola на платформе EZX, которые я собрал ещё в феврале прошлого года, специально для подаренного мне ROKR E6. Поскольку для разработки под платформу EZX используется старый кросс-компилятор GCC 3.3.6, возникли некоторые осложнения с выравниванием размеров структур. К сожалению, протестировать полностью Cave Story на этом устройстве мне не удалось из-за нехватки свободного времени, но до локации «Грасстаун» я смог дойти.
Запущенная русская версия игры Cave Story на движке NXEngine на смартфоне Motorola ROKR E6; платформа EZX
Все изменения для платформы EZX опубликованы в репозитории на GitHub, ссылку на который можно увидеть в предыдущем разделе. В раздел, посвящённый управлению в этой статье, добавлена соответствующая информация.
Update 22-DEC-2016: Администрация фанатского сайта www.cavestory.org заметила мой проект переноса NXEngine на различные платформы и любезно разместила установочные пакеты на своих серверах. Этот сайт содержит множество информации о Cave Story и обязателен к посещению каждым поклонником этой игры. Огромное спасибо andwhyisit и SkyeWelse за ваше внимание!
Установочные пакеты на всякий случай залиты ещё и на Yandex.Disk, играйте с удовольствием!
Update 22-FEB-2017: Со мной связался Ola Andersson и попросил скомпилировать ванильный NXEngine версии 1.0.0.6 для MS Windows 32-bit, я выполнил его просьбу и пакет был добавлен как на сайт Cave Story, так и в папку NXEngine на Yandex.Disk. По сути это просто обновление английской версии NXEngine до актуального состояния.
Кроме того, хочу посоветовать к использованию на современных системах форк NXEngine-evo от iSage, поскольку он имеет ряд исправленных ошибок и поддерживает популярные широкоформатные разрешения. Скачать готовые к запуску пакеты для основных настольных операционных систем можно в его репозитории.
Update 14-DEC-2018: Мной был подготовлен порт движка NXEngine для операционной системы Haiku. Подробнее почитать о процессе портирования можно в этой статье.
Запущенная русская версия игры Cave Story на движке NXEngine в операционной системе Haiku
Создатели сайта www.cavestory.org любезно добавили готовые HPKG-пакеты для Haiku OS к себе на сайт, в раздел загрузок.
Update 19-MAR-2021: В комментарии этой статьи заглянула Caitlin «rogueeve» Shaw, автор движка NXEngine. В своём объёмном повествовании на английском языке, которое расположено чуть ниже на этой странице, она поведала историю создания проекта NXEngine и рассказала про интересные моменты жизненного пути игры Cave Story.
Update 30-MAR-2021: Caitlin «rogueeve» Shaw выложила некоторые интересные файлы: обновлённую программу «SIFEdit» и реализацию более ранней игры Pixel’я под названием Ikachan, её движок называется «Ika».
Встреча с шипами просто потрясающая. Кстати, рекомендую — Hotline Miami и Droid Assault, потрясающие игрушки в 8 битном стиле.
С шипами на первом уровне?
Спасибо за рекомендации, обязательно посмотрю эти игры.
Пара поправок:
1) Pixel сам никогда не портировал свой движок ни на какие платформы. Портировали энтузиасты на условиях: а) написать прототип самостоятельно б) получить после этого исходники и никому их не давать.
2) Кейтлин (автор nxengine) — она.
Большое спасибо. Поправил статью.
Доброго !
Спасибо за ваш труд, я вышел на эту статью пытаясь установить порт Cave Story на RetroPie.
Как я понял там используется NxEngine из основной ветки, которая русские ресурсы не поддерживает.
Вопрос — почему вы держите отдельный репозиторий, не получится добавить ваши патчи в основную ветку ?
Доброго дня!
Я не задавался целью протолкнуть свои патчи в основную ветку NXEngine, некоторые из них сделаны в спешке и имеют весьма посредственное качество, так как делал я их в основном для себя. Для того, чтобы протолкнуть что-то в апстрим, требуется провести работу по их причёсыванию, у меня на это нет времени и желания. Тем более у моего знакомого вышел проект под названием NXEngine-evo, который осовременивает эту игру, добавляет привычное меню и нормальные переводы на другие языки. Попробуйте этот проект. Удачи!
Здравствуйте. Павел, подскажите пожалуйста как вы установили русскую версию Cave Story на RetroPie?
Hello, this is rogueeve — the original author of the NXEngine rewrite, as you mentioned in your blog post. I realize this is an old port/post, but I only just found this particular one. I have known about nxengine-evo for some time (which is all the better for those that enjoy it, but it’s not for me), but not this one, which has some unique improvements while not breaking some other things I didn’t like about the changes in nxengine-evo.
I just want to say how glad I am that you and the community have picked up this code and translated it to other devices like this. This was exactly what I wanted to give the world in the 9+ months I worked on NXEngine nearly 12 hours a day so that this great game could live on in immortality. Originally NXEngine was going to be just a demo of the Sand Zone, then there would be a menu to select a few more zones, then it just kept going.
I hope in addition to being able to port an almost-identical game to any platform now without the stickiness of getting «permission» like it was at the time I published NXEngine — and there have been about a zillion ports since release — that I can remind the community of my other hope I wanted to give when creating this project — that the community will also see the incredible modding potential this opens up for new mods to the game to keep it going and enjoyable again and again. I haven’t seen/come across any mods based on NXEngine so far, but hope to someday — if I were a modder, it would be just too tempting. I would _love_ to play one and would thank the author _forever_. A whole new game could even be made, similar to the famous «Jenka’s Nightmare» — but without the technical limitations of that game — totally new enemy AI and types of entities, new object behaviors for blocks & elevators etc could be made, new weapons or inventory items, gravity modifications (go to space?), levels where you walk on the ceiling…unique new background types…new TSC script codes…nearly anything could be modded in by a half-decent C++ programmer. The «sifedit» program that was used to set entity frames and add spritesheets and set object hitboxes etc has been updated and improved and compiles again on modern wxWidgets. I am lazy and haven’t posted it, but will happily send the patches to anyone who asks.
I also am very happy that you have fixed the music on low-end devices, as this is something I have been wanting to do for a long time but never got to and you are the first porter I’ve seen who has taken the time to do this. The main problem with the music in the original — I believe — is that NXEngine is entirely single-threaded. Every few «beats» of music, a trigger fires at the end of the game step to generate the next few beats. The generated music, transformed into 16-bit PCM audio (the original game used 8-bit audio), is stored into two alternating buffers, so that as soon as one finishes the next is ready to send to sound card without interruption. When one buffer finishes playback, the other is swapped in, and the trigger then fires to refill the other buffer with the next part of the song while the other one is playing. On PC’s, the buffer can be filled before the frametime is up (at 50fps, you have 20ms to complete each game step) and causes no problem. But on low-end devices, this explains the «stutter»/freeze every few seconds if music is enabled. The fact that I used floating-point math in many places in the music generator, which may need to be software-emulated on low-end ARM-powered devices, can’t help. My planned solution was always that I was going to try to generate the music in a 2nd thread, although this might not be sufficient, especially on single-core devices. Another solution I considered was to use the procedurally-generated music code as is, but cache each song in it’s entirety before playback when entering a new area that music hasn’t been cached for yet (+ probably pre-cache some of the boss music songs that can start in the middle of a level), or just cache the whole OST on initial game startup, either way; it would be saving each whole song to a PCM file so that it does not have to be generated in real time. In any case, I never considered your solution since I’m a purist, but I hear it works, so I am very happy you have fixed it for the end-user.
Doing the music generator was a whole subproject in itself btw. I was so excited when I first heard the melody of «Plant» awkwardly beep out on the PC speaker, then finally «Final Battle» playing with original instruments, then lastly I added the drums and finally heard «Final Battle»‘s start as intended. It was extremely difficult to reverse engineer the format and correctly play it back, since Pixel went all out with his own wavetable-synthesis system and everything. That’s what I love about him. Keep in mind all of the sound effects are also procedurally-generated «.pxt» files — there is btw a program floating around called SeaTone, written by me but anonymously, that can edit and create these files. I could probably not have done all this work without the partial reverse-engineering already of the .org and .pxt formats I found in text files on the internets, and having access to Pixel’s original tools to see what changes what in the output file. So kudos to the unknown-to-me authors who started that initial work. I believe the sound effects and drumbeats ARE partially or completed pre-cached btw, and this is what the sndcache.pcm and drum.pcm files are when you look at the NXEngine directory after having run it for the first time.
And not only that, but I was well aware that my script text parser could only handle ASCII characters and thus would only work on the English Aeon Genesis translation, not on the original Japanese version or on translations into other languages. This was something that was missing from being a perfect recreation of the original, and I am a bit of a perfectionist so although I only cared to play the English version, it did bother me. I worked hard to try to make all the physics and enemy behavior work EXACTLY as it does in the real game. I even took screen-recordings of Quote’s jump, and wrote a program to find the corner of his hat in each frame and graphed the jumps on a parabola, to determine the exact thrust speed and how it works (the secret is that gravity is disabled during a jump, and then re-enables afterwards — this is what makes Cave Story feel so «floaty» — incidentially, it’s also what lets you fly up in the streams and fans such as found in Grasstown). Hurrah to you for fixing this as well!
I much prefer the original game to Cave Story +, which is why I have never supported it. I think they ruined the feel of the graphics, and just generally do not like it or anything that Nicolas did to the original Cave Story, although I do miss out on being able to play the Wind Fortress level for myself. In my personal humble opinion, Pixel was scammed by Nicolas as the first company that came along, which was a tiny mostly-flopping company at the time, and they had no business or sufficient skill to become the holder and future of such a legendary classic.
Trivia: Supposedly at one point Nicolas was planning to release an HTML5 web-based version of the original game based on NXEngine while replacing the music-generation code with a Javascript translation from the original source. I heard this through a 3rd-party developer who claimed to have been contracted to create this game off of a partial demo port he had made in exchange for $8000. I even at one point spoke through email with the head of Nicolas briefly to lend this rumor some credence, although he was quite unresponsive and did not write back very well or succinctly. I don’t know what became of the project, it dragged on and on and I suppose eventually was scrapped. At one point I may had a beta of this game hanging around on my HD somewhere, but if so, I have not been able to find it for a long time, unfortunately.
Trivia 2: Why «NXEngine»? Yes, its kind of a poor name. You see I had worked on an experimental indy game previously code-named just Nextgame, which had overly ambitious features such as multiple layers of map tiles in one square, a whole scripting system based on various types of invisible named markers rather than Cave Story’s simpler «send NPC X to coord Y», anyway it just never panned out, as I am not so good at game design, just game programming, and it just didn’t feel «fun». So immediately following up on the end of about 6 months of working on that, the «NX» stands for or is a homage to «Next» I suppose. It just popped into my head. It makes no sense I know, but it was the best I’ve ever come up with, and I suppose it’s too late to change it now.
Trivia 4: I have a «secret» port of Pixel’s earlier game Ikachan as well that I have never released. If anybody wants to do something with it, write me. I’ll send you the GPL3 code.
If anybody is interested in old alpha and beta versions of NXEngine, just for amusement, I have those as well.
Lastly I must apologize for what I’d now consider some instances of poor coding style and for requiring SDL1.2 even as SDL2 was recommended for new projects at that time (but I was already familiar with SDL1.2, and didn’t want to bother to learn). It is old code and I am much better programmer now — you learn every day, right? I think the code is pretty clean for the most part, but nowadays I think I would have done a better job in some areas.
There was also a long-time bug that crashed some platforms, where two arrays having to do with map background types, if I remember correctly, were not of the same length and caused an invalid memory access in some circumstances — but I believe that’s fixed if you take the latest source from the original nxengine.sourceforge.net page. There are also a few minor extant bugs still known about. One nice fellow a few years ago sent me an exhaustive list of testing he did between NXEngine and the original, which I have yet to address. Will be happy to forward to anyone who wants to attempt to repair these minor differences. For example, after Chaco walks over to tell you about the Jellyfish Juice, if you sleep in the bed, she is not sleeping when you wake up, as in the original. Also apparently question mark over head is not supposed to disappear when new one is created (although the NXEngine behavior makes sense to me). That kind of minor details.
Enough nostalgia. I hope some of you in the community read this and find interesting pieces of info in it. I occasionally get mail through the NXEngine sourceforge site — all of you are always welcome to write to me. I try to help everybody I notice through the wall of spam, no matter what their problem or question.
Thank you very much for such a large and voluminous comment that perfectly complements this blog post and reveals the details of the NXEngine creation. Many people have been able to play this masterpiece game on a bunch of different devices thanks to your engine!
I have similar feelings when someone takes the source code of this NXEngine fork and ports it to some interesting platforms like Amiga OS or another one exotic portable handheld. Sometimes, during the building process on a rare platforms, interesting things arise that you did not know about before, like this one on PPC64.
I heard a long time ago that the GP2X handheld game console received a port of the original Cave Story game engine which was written in pure C. I found a notes about porting to these consoles on the OpenHandhelds File Archive site: GP2X and GP2X WIZ. The man who made this port is Simon Parzer, and I also heard interesting story about gaining access to the sources: Pixel asked to implement one of the Cave Story levels and when the porting man coped with it, he sent him the original Cave Story source code. I don’t know if this is true story or not, but your NXEngine under GPLv3 was free of such requirements and permits, which helped it become very popular for porting to the various devices and operating systems, for which thank you again!
On the contrary, I think that using SDL 1.2 expanded the possibilities for porting your engine to the various platforms in those days. Most of the devices in this article had an SDL 1.2 port, but never got SDL2.
I would like to ask you about using the makegen utility and its «*.fdh» files. Was using this utility justified? Are you using it now for any your projects? Once upon a time, I used the progen and tmake utilities for similar purposes but then I switched to using qmake -project command which generates a quite digestible Makefiles and do not create additional files.
Wow. Hello, and thanks for this rather interesting read.
If you don’t mind me asking, what’s your opinion on nxengine-evo? You said that it isn’t for you, but it would be nice if you can elaborate on that.
nxengine-evo: I’m very glad they picked it up and did something with it, fixed bugs, added features, etc. If people like it, great. Mainly, I didn’t care for the introduction of the Cave Story + features, because I don’t like Cave Story +, and last time I tried running it, I think I remember they had removed my multiple save slots and put it back to the original single-save slot, which I rather liked my multiple save slots addition. I’m not sure but they may have taken out the F3 debug menu too, I’m not sure.
I looked again after we discussed nxengine-evo here, and my opinion softened somewhat, and I did see in the code they did provide some nice fixes. Some of the stuff they did with the renderer is a little too much long-winded C++-ification for me, and I personally wouldn’t write code like that, but I suppose if it helps port to multiple platforms, then it’s does the job — and they’re the ones maintaining it, not me, so my personal feelings on coding style are really pretty irrelevant to their project.
Another factor — I may be a bit biased, because I am also the author of the CloneKeen Commander Keen clone, and when it was picked up and now is mostly seen — available on Play Store and such etc — as Commander Genius — in early renditions at least, and every time I’ve tried it — My impression was that they’ve completely messed up the physics of the game. Commander Genius feels way too «fast» for me, as if was re-designed for modern gamers with less patience, and not true to the original. Even the «life» popup on first appearing in the map seems to play at double-speed — everything feels like it’s on fast-forward — I applause their addition of the Keen 4-6 games, and understand that very little code of the original CloneKeen remains in their project at this point, but I did not approve of the wonky «sped-up» feel at all of that project, when I go to great lengths when cloning a game to get the physics as close as possible to the original — so this may have unfairly knee-jerked biased me against my review of nxengine-evo when they started also adding features I didn’t approve of for my mainline version.
But…both of these games are GPLv3 for a reason. People can change them however they want, whether I personally approve of it or not, and if they or their audience like that better than my mainline version, hurrah for them. I may or may not be merging my changes back into my version, but this is open source and this is how open-source works, and diversity is a good thing in the open-source world.
I, personally, don’t really like cs+ too. That’s why there’s still no 60fps or hi-res assets support. But there’s high demand for some features. Then again, i always implement them as opt-in, with the ability to disable (except main menu). Btw, multiple save slots and all debug features (in debug build) are still in.
Извините за стену текста, это сообщение появилось в виде абзацев в поле для комментариев. Я также прошу прощения за то, что написал на английском языке — мой автоматический переводчик был включен, и мне показалось, что другие комментаторы писали на английском языке — моя ошибка. Я сделаю все возможное, чтобы повторно отправить сообщение на русском языке или в нескольких сообщениях, если лучше.
Please use English language on my blog freely. Those people who are interested in this topic will still be able to read you.
Thank you for fixing up my post into it’s paragraphs the way I intended it 🙂 and for your nice response. As to your question about the makegen utility. Is it justified? Well, I guess it depends on your point of view. I wrote it way, way, back, I don’t know how long ago; the earliest date still in the source mentions 2010, but it is likely several years older than that. It is a utility thrown together many years ago by a much younger me for a need, to generate makefiles out of a file in a simpler format, a format that I would be able to remember and write from scratch, or at least copy from the last project into the next, since I found the format of Makefiles extremely obtuse, and I usually have pretty generic needs for a Makefile, basically regular make and make clean, and I needed something that would scan the source code and find all the proper dependencies, updating them as needed, so that the dependencies in the Makefile were always correct, the whitespace was always correct without me having to think about it, and I would never have any of those annoying bugs where your program crashes because one part of it didn’t compile due to forgetting to update a dependency. While I was at it (scanning the source), I came up with the idea of the .fdh files to eliminate the need for listing function prototypes in .h files. fdh is supposed to stand for «function definition header».
But back to is it justified. I have noticed a lot of people seem to not like it, and the first thing they do once they get one of my projects into their git repo is to rip out the makelist.ml and all the .fdh files and port it to some newer system such as cmake or qmake or whatever they prefer. So in that sense, for sharing, maybe it is not, so much. I know there are fancier and more standard systems nowadays. But for me, it still does the job, it does it very quickly (and I am very very picky about my compiles (and program startups) being as quick as possible, as I develop in a style where I run the program very frequently, writing a little code, running, then writing a little more, so that I can know for sure that X part works and «forget about it») — and yes, I do still use it, for just about every one of my projects, although with the increased use of C++ classes, I do not always use the .fdh feature in all of my modules so much anymore. Still, personally I do the .fdh files handy in some instances when writing less formal code, so as to not have to worry about forward-declaring regular functions or functions found in another file, as makegen will automatically find and add any function I reference to the .fdh file, so I only need to have each module include the .fdh file of the same name after it’s .h file, and it’s all taken care of. Understand these are for simple projects, that are not libraries, and that tend to have some sort of «main.h» that every file includes and where main.h includes the .h files of all modules — in other words, most everything besides static functions/variables, and private methods of classes, is visible to everything else.
Makegen does have a few more features nowadays than it originally did, or than it did in the times of NXEngine development, that keep it still useful to me besides it’s basic operation. It is now capable of an «ARCH_AWARE» mode; where it generates a bin/ directory and subdirectories beneath bin/, thus allowing for a shared source tree that can be compiled on both x86 and ARM platforms, with the output directory of the object files being chosen at compile-time, so that when develop switches back to the other platform, there are not indecipherable .o files littering the project that requires a «make clean» to get rid of so that the project will link again. It also allows me to automatically insert various switches to GCC. For example, some of my projects require -Wno-psabi on ARM, but this is an invalid flag on x86. Makegen inserts or removes such things as appropriate for the current build environment just before build. It also allows me to have a common set of -W warning enable and disable flags. As I discover new ones I want on or off, I can change a single config file, and all my projects will build with the new setting. This works because my projects all tend to be of similar design — obviously this would not be appropriate for some cases.
That said, makegen is written in pure C — really really embarrassingly bad pure C since it is so old and has only been patched up through the years, never really seriously refactored or rewritten since it works despite itself, but does have it’s failings — it was written a long time ago, for a time when I wrote only in C, never in C++, so it has only bits & pieces of C++ understanding patched in over the years. It is the oldest program I have written that is still in general use. It is not uncommon for it to make mistakes and includes functions in the .fdh files which are harmless to the compile but aren’t actually necessary, since it has no proper lexer or anything like that to really understand what it is scanning, as I did not know of such things back then, and thus it occasionally also makes more serious mistakes which cause the program not to compile, such as for example mistaking a lambda function for something that needs to go in the .fdh file; requiring you to slightly adjust the phrasing of the code or as last resort use the // MG:IGNORE comment on the line it doesn’t understand. Or it’s complete lack of knowledge about namespaces, a feature I don’t use often, but one of my libraries (an embedabble scripting language similar to a cross between C and Javascript that I have been working on for a while) which does make use of them, must make sure to include a .fdh file, if it wants to use one, from *within* the namespace, lest havoc ensue.
I code mostly in isolation, with a large codebase of projects, the majority of which I never get around to sharing, and thus makegen is something I sort of see as one of my personal extension to C — an extension which is by now quite familiar to me, so much so that I don’t even generally see it as non-standard, but which makes others go…WTF? 🙂 Nowadays, I also have a library of common routines, called libkaty, which nearly all my projects include, and I am so used to, that these functions also, are seen by me as an extension to C++ to the point where I almost think of them as if they were part of C++, since I am so used to having them at hand. Functions like strbegin() (does string start with), fgetline() (like fgets(), but strips the terminating \n), fgeti()/ fgetl() (to go with standard fgetc()), maxcpy() (a version of strncpy() that is not broken — not wasting time padding the unused buffer to maximum length, and always including the terminating NUL char) — or hexdump(), sysbeep(), the stprintf() function, which is like sprintf() but returns it’s result in a rotating static buffer of sufficient size that you needn’t worry about deallocating the result nor it being overwritten by subsequent calls within a reasonable range — or classes like DString or List<T> (I now have learned more of the STL than I used to bother with and use std::vector when appropriate, but I still prefer DString to std::string). I wrote in C for a long time, and when I switched to C++, it was — and for the most part still is — a sort of «soft» style of C++, similar to what you see in the NXEngine code, where I used classes etc, but not use C++isms such as static_cast, or great deals of indirection, and only use objects like shared_ptr when it is best appropriate, as I feel I am sufficiently used to manual memory management to not make mistakes with raw pointers in all but the most complex of cases…slowly, I have become less stubborn and added more C++ features to my repertoire, such as lambda functions, templated classes, and some parts of the STL, and now use them when appropriate, but do not go out of my way to do so.
I guess to sum up all this rambling I got into, I would say that for code that is to be shared, or that I was working on with others, I would try to avoid using makegen and libkaty, or at least, if I could not stand not having them, copy a few routines from libkaty into the source tree of the project, rather than assuming for the user to install the library. But since most of my code is «for fun» and starts out as something small not intended to be shared, by the time the rare project does get shared, it is probably already using quite a bit of both. NXEngine managed to skip libkaty, as back then it was not a library but simply a directory called «common» — the directory in NXEngine called «common» is in fact, is a very old ancestor of what became what I now call libkaty.
</rambling_about_makegen>
Since it seems your blog is not dead and you are still around, and you asked about it, although you may not need it I have posted a few of my «essential» programs & libraries as well as a few things which may interest you. Included in this package is the current version of the (in?)famous makegen, which I know I have posted on a git repo somewhere at least once, but could not locate it as it seems other people have used the same name for their own tools since then.
Also the new fixed sifedit2 I mentioned in my previous post, which is necessary to add to or edit the sprites.sif file which controls all of the hitboxes, spritesheet locations, animations, and much else regarding sprites and entities for NXEngine, rather than simply treating the original version I distributed as a binary blob. It has been several months since I made this update, but I do know this version of SIFEdit has a different in-memory format than the original (or that of NXEngine), and a new extended on-disk format with additional features and sections, so that I would have the possibility of using it for other games as well, although since NXEngine was the #1 biggest program that ever used the .sif format, I was very careful, so it should still be backwards-compatible, both read & write, with the old format & loader code used by NXEngine.
sifedit2 is now dependent on a library called siflib, which I have split out the .sif reading & writing functions into, for the purpose of it’s aforementioned potential for use in other games. siflib is in a subdirectory inside the sifedit2 source tree, and can be compiled/installed on most Linux systems simply by running the ./go script, which will put it into, I believe, /usr/local/lib and /usr/include/siflib; you may then run make clean; make in the main directory to compile sifedit2. (It looks like the ./go script for siflib assumes that makegen is installed, but since the generated Makefile is already present, it would be fine to just comment out the line calling makegen).
Or, if you don’t want to install siflib globally onto your system, you can likely simply modify the makelist.ml to add all the .cpp files from the siflib subdirectory into the compile list, remove the -lsiflib from the linker line in makelist.ml also, and change the #include statements in sifedit.h referencing the siflib .h files from #include <sifedit/blah.h> to #include "sifedit/blah.h", thus setting it up to statically compile siflib into the sifedit binary instead. Then use the makegen program to recreate the makefile and just run make clean; make in the main directory to compile both sifedit and siflib into a single executable.
At some point, it must have been convenient for me to make sifedit2 become dependent on libkaty as well, as «ldd» against the executable shows it as being linked to libkaty. So libkaty is in the code dump too. I don’t recall how much of libkaty is used by sifedit2 — probably only a small portion, and only in the new pieces of code, since sifedit did not originally depend on libkaty — It’s probably easiest to just install libkaty globally, but you could try a similar trick as the siflib suggestion above and include a few files from it and compile them in statically as well and then change the #include <katy/katy.h> to #include’s for each of the .h files. From a brief grep, it looks like it is using DString, stat() (an unfortunately-named function due to the POSIX function of the same name, but basically it’s like printf()), and likely some stuff from misc. So DString.cpp, misc.cpp, stat.cpp, and their corresponding .h files would be what I’d start with copying over from libkaty and adding to the makelist.ml and sifedit.h, if I wanted to compile sifedit2 without installing libkaty. On the other hand if you don’t mind installing libkaty to save the trouble, it will also compile and install automatically on every Linux system I’ve ever attempted, again into /usr/local/lib and /usr/include, by running the ./go script in it’s source tree. If you run ./go without having first installed makegen, it will look around for it, including in the parent directory above the libkaty source tree, and try to install it first for you.
And lastly, and probably most interestingly, I have included «ika» — the Ika-chan clone I mentioned in my previous post. I don’t remember much about this program, it’s been so long, but this one should give you the least trouble, being that it is from shortly after the same days as the NXEngine development, and has no strange «me-specific» dependencies. It looks like Ikachan introduces yet another, albeit from a brief look at the player code, likely much simpler than Cave Story’s .org, custom generated-in-realtime music format called .pmd. On the other hand, sound effects are just simple .wav files, so no .pxt stuff to worry about. I have NO idea or recollection of how I managed to reverse engineer the .pmd format, as there is probably even less documentation on this than there was of the .org format.
This source directory may require cleaning up — or in other words creating some sort of tool to extract the data from the original Ikachan — depending upon your interpretation of the license of the original freeware. It will «make» and run the game in it’s current state. In other words, it looks like the full freeware version — or a translated version thereof, absent the .exe, is included under the «data» directory in this proto-dump. Although there are also some assets from the freeware game that seem to have made their way into «sounds» and «music».
This obviously, is also an English translation, I don’t remember which one/what it was called, it is «the good one». As to the «other translation», I loved it, if only because of it’s opening text message on a new game — «SUDDENLY, YOU ARE IN THE WATER.». I liked that translation of that line a lot — it’s become a short of catch phrase I always remember if I get a chance to use it. But the rest of that «other» translation was mostly incomprehensible. Of interest is that it seems to include it’s own copy of sifedit/siflib (don’t worry, this siflib directory is just shared code between Ikachan’s sifedit and the game, and should not be confused with the true .so shared library file I created much much later and described above that is also called siflib). I do not know if the copy of sifedit in this «ika» source tree is identical to the one published with NXEngine, or if there are any differences. It’s been too long, I just don’t remember. This game also appears to be using 2D OpenGL for graphics, which is new, and not necessary as NXEngine demonstrated, and I don’t remember why I chose that, but I’d guess there was some platform I was hoping to port to that I thought might be accelerated by using OpenGL. It also appears to possibly include a few bonuses, such as a level editor. I don’t recall writing or using this level editor at all, but there does appear to be code for it, although I have no idea how to activate said editor — likely someplace in the code is a constant or commented-out line that can be «turned on» to enter editor mode after a recompile. I was able to dig up a bit of (very short) documentation on this editor, as well as an old screenshot of it running in a Fluxbox desktop environment I had at the time, which I’ve thrown in.
All of this code is GPL3 — it may not have the appropriate LICENSE files or comments within the code as I tend to be lazy about that, but I will trust you to treat it as GPL3. If you wish to do the work of adding the appropriate LICENSE file and pushing any of it to a more accessible location such as github, go ahead.
I will not say directly the location of this code so that search engines or even less desirable robots won’t trace it. But you will find it by accessing the path /kt_essentials/ of the domain name starlightstarbright.org, using HTTPS on port 555.
Thank you very much for these comments, which contain so much useful information that its can be considered a whole separate article! I really like your approach to create your own tools, libraries, and environments in order to make development more convenient and fast. Developers gain experience for a wide variety of problems while creating auxiliary programs such as makegen and sifedit. It is doubly wonderful when tools which were created just for personal use, start to be used by other people.
Looks like I found your lost makegen repository 🙂 https://github.com/rogueeve575/makegen
Once upon a time I was familiar with operating systems of the classic Mac OS family and saw the SysBeep() function there. Is your function somehow related to this? I became very interested in how your function works. I bet it’s not just a wrapper over the fprintf(stderr, "\a\n") function 🙂
Update. I found an implementation of this function in the «katy/sound.cpp» source file, great!
Thanks for sharing files, I downloaded them all. Your stories encourages me back to porting games to various weak devices again. It was a very exciting journey, maybe someday I’ll be back to the «just of fun porting» business in my free time 🙂
I hope that your files will be useful not only to me, but also to other people who come across this blog post looking for information about Cave Story and NXEngine. Can I mirror these original files here?
I like your minimalistic and ascetic Fluxbox desktop and I was especially interested in the «Sisong» program. Is it a full-fledged IDE or an advanced text editor? What does it use to render controls like buttons? GTK+? Bare X11-libraries? Or maybe just SDL? Is code highlighting or auto-completion supported? Does it use the Scintilla under the hood like many similar programs? Please tell me about «Sisong», was NXEngine mainly developed in this program?
In addition, I noticed that you are familiar with the Haiku and have used it for a long time and even develop some programs for it. I also got familiar with Haiku a few years ago. It’s a great operating system and I loved it for its simplicity and very rich capabilities of the standard UI, like convenient tabs, stacking and tiling windows, replicants, etc. I’m happy to be able to play Cave Story using port of the NXEngine inside Haiku, this stuff was even added to the Haiku Depot repository. You can read more about my fascinating journey to Haiku in the «The Haiku operating system: porting applications and creating packages» blog post. It is in Russian but you can always use a Google Translator or just look at screenshots with a cute Haiku yellow tabs ^_^
I wrote a long response, but OH NO — towards the end, I accidentally refreshed the page, and lost it ALL. Perhaps it was too wordy anyway.
I did want to write back though for if for no other reason to at least note an errata that was my mistake and I should have caught. I see that many of you have downloaded my source offerings, however after my initial upload & posting, I discovered a problem in libkaty’s ./go script that makes it unsuitable for distribution (the assumption of the existance of a ‘getmakecmd’ script somewhere in $PATH — another me-ism — getmakecmd automatically determines the CPU count etc and also temporarily pauses certain processes running on one of my machines that would otherwise slow down the compile). This is now fixed in the copy on the website, but as many of you may have already figured out, there is a simple workaround. Just change the place where it sets MAKECMD=$(getmakecmd), around line 69, to MAKECMD="make -j4" (or however many CPUs you feel are appropriate for your system). My apologies for this error.
I also have included a makegen-related file called cppflags.txt that I did not include originally. This is a global way of telling makegen a set of standard flags to be passed to the C compiler, along with some intelligence to change them depending on architecture, etc. It is not required, as, if not found, makegen will assume reasonable defaults, but for completeness, this will allow you to compile the projects with the exact same C flags I am using. Comments in it’s contents will tell you more about what it is about and suitable directories you can choose to place it in for makegen to find it (or you can add your own directory to the search list, the list is in header.c in makegen’s source at line 640).
Sisong — yes, it is still is use every day by me too — in significantly evolved form. When I first discovered Haiku, I noticed that everybody seemed to be running in it virtual machines and not as their primary native OS, which I felt would more effectively determine it’s usability and find more bugs, so I took on about a 4-month challenge to do just that. I did find several bugs during that time, most notably one in the screensaver code, which ended up getting merged into mainline, so I have been lucky enough to have made a small contribution to the Haiku OS.
However, one of the first issues I encountered upon my complete switch to Haiku was that I could find no decent text editor or IDE at the time that fit my standards of usability. I was previously using Windows and Notepad++, so I went back to Windows and started bootstrapping a «programmer’s text editor» similar to Notepad++ (trivia: originally in a compiled language I was playing with at the time called «K»; now long-sense abandoned, but a 2nd try, a sort of middle ground, called Ria is very much alive and will be described shortly). Once the text editor was bare-bones enough to be usable, I ported it to C++ and BeOS API and went back to Haiku and used it to develop itself.
I believe the same rogueve575 repo you found (and yes, that is the correct repo, although the version of makegen that I uploaded on this forum is newer than the one found there), has a copy of the original Haiku Sisong also (the original-original, available on a sourceforge site somewhere called «the 575 project», no longer compiles on 64-bit, but the version in that repo — I did enough work to fix it up to compile). I believe this was also accepted as a pull request into another repo dedicated to preserving open-source Haiku code, where I stumbled upon it one day, and finding the owner of the repo having attempting in vain to get it to compile, and then given up — this inspired me as the original author to try to use my knowledge of it’s workings to fix it up enough to at least get it to run again to help with their historical preservation project. Looking at the original 2009 code, I was surprised at how much it had changed in the years since it’s original release for Haiku in ways I had forgotten, and so I got it to compile & run, but did little else, since there have been SO many improvements in the Linux version in the 10 years since, that I didn’t really feel it was worth working on any further.
So Scintilla? Bah humbug! :). I would never use such a thing, that would be too easy :)! Sisong was always an editor written from scratch, using it’s own from-scratch text-editor engine, optimized for Haiku.
When I left the Haiku scene (albeit still with fond memories to this day) around 2010, Haiku had finally inspired me to abandon Windows for good, and so I switched to Arch Linux, where I found the same issue under Linux with available text editors to my liking that I had under Haiku, and so I ported Sisong to Linux, The route I chose was by writing a good-enough emulation layer library of the BeOS API called «bapi», which translates the basics of the BeOS/Haiku API such as BWindow, BView, SetHighColor(), CopyBits() etc into Xlib calls. I did take some code from the MIT-licensed Haiku to implement some of the controls and classes such as BList and BButton — although many required modifying or even completely rewriting (I think the scrollbar, for example, is 100% new non-Haiku code) since they called into too many things such as the theming engine and other components of the Haiku OS that I didn’t care to implement/emulate.
Over the years, I came to care less about the Haiku version of Sisong, and needed features in the Linux version NOW for whatever I was doing at the time, so bapi gained both extensions (such as a greatly-enhanced feature for any BView to have multiple timers, rather than relying on the BWindow’s Pulse() alone), and has also always had some drawbacks, such as being single-threaded, so BWindows don’t all run under their own thread like they do in real Haiku.
There IS a new, improved, better and more accurate rendition which I have put a lot of work into off and on now and again known as bapi2, but as of yet it is not at a sufficient state to do more than run a few simple demos. Bapi2 however, no longer relies on Xlib as it’s backend, since Xlib starts to break down badly when called from a heavily-multithreaded program. Instead, it is based on a fully-multithreaded-safe, C++-native library I wrote called «xintf», which was born from the crazy idea I had one morning that I could talk to X11 by simply opening it’s UNIX socket and sending the properly-formed raw packets. As crazy as this idea was, «xintf» is now capable of nearly all the basic functions Xlib can provide, as well as coming built-in with libfreetype and XRender support for drawing proper decent-looking fonts, rather than those terrible old-style X11 server-side fonts.
Since the transition from Haiku to Linux, and over the 10+ years, Sisong has evolved greatly, as I have all that time continued to use it almost every day for 99% of every project I have developed in the 10 years since, and these new versions are now known as Sisong Melody or simply Melody. I still prefer and have a fondness for the BeOS API though, so I have never seen the benefit in trying to port it to something like gtk, which I don’t find superior to BeOS even with the few glitches here-and-there of the emulation layer — and besides, I have come to be used to the look of Sisong Melody with it’s BeOS-style controls, and GTK would change all that.
Besides support for things like mouse gestures and key combos (Alt+F, then S to save, or Alt+F then X to quit), or Ctrl+B lets you bookmark a line, and — I can brag VS doesn’t have this — has a diff-like intelligence where if the line number slightly changes from edits before you Ctrl+R to return to the bookmark, it will search nearby text and most often still find the same line you bookmarked by content rather than blindly taking you back to the original line number).
…the most notable evolution I would say, is that Melody is now integrated with a scripting language I also wrote (I _really_ like to do things from scratch, as you may have noticed by now and I have a fondness for compiles), called Ria. Ria began to solve a simple problem in Sisong, where asynchronous things that must make decisions that would easier written in a synchronous form began to grow to become unwieldy. For example, when you close a document, a dialog box appears. This isn’t modal, the rest of the editor keeps running, but it asks you if you want to save and close, close and not save, or cancel. Once you have made the selection, you must then go back and take action on the asynchronous event. There may have been other solutions, but mine was to write a very basic scripting language that would handle these functions, and the proper course of action could be written out in a synchronous form of 1) prompt, 2) take action on result of prompt. Sisong would launch the script when a document was asked to be closed, and the prompt() function would suspend the script by simply not returning a return value to the script (which caused the VM to go into a suspended-animation mode called WAITING_ON_CCALL), while saving a «return handle» provided to every C function that is registered by a host-program embedding Ria when the function is called by Ria). When a button was pushed in the dialog box, it would then return the result to the Ria script by calling the function to return a value along with supplying the proper return handle, and the script would resume oblivious to the suspension as if the prompt() function had been synchronous. The C dialog box code also needed to have no knowledge of what it was being called for or in what context, but simply open a box of the specified description, and then return a value to the script when a button was pushed. It worked well.
At this time, Ria had much lack of error handling, and was only just enough features to execute the functions in the single file of source code provided by the editor for it’s own use. After a few years, I thought I might have something here, and, liking compilers anyway, began to improve upon Ria, eventually splitting it out into it’s own library, libria, which when compiled, also compiles and installs a small «ria» binary, which is simply an extremely small one-line file that embeds libria, and acts as both an interactive interpreter like many languages have, as well as a script interpreter allows Ria scripts (by convention, with an .r suffix), to be written and executed «natively» as shell scripts (by using the hashbang #!/bin/ria).
I’ve found Ria to be well-suited for text processing, and have began to write many little tools and such in it, finding that I can do so much quicker than I could in Bash, Perl, or C.
Ria is a hybrid between my 1st attempt K, and a true interpreted language like BASIC. It has a full lexer and recursive-descent compile stage, but compiles to a simple stack-based bytecode interpreted by a VM controlled by a ScriptContext class. It has a flexible type system, all things are of type RiaValue internally, which can hold a value of one of [NUMBER, STRING, HANDLE, FUNCTION, TABLE]. The HANDLE class is used rarely, and only by a few C functions, and is simply a way to return an opaque handle of some type such as an open file, while reference-counting it so that it is automatically cleaned up if the variable goes out of scope in the Ria script.
Tables are simply Dictionary-like maps of «string key» to «RiaValue value» — so the value can be of any type, including a table within a table. From tables, you can build more complex structures, even class-like objects by having some keys of the table be functions, and using a special self keyword that refers to the table that the function is running in. You can «instantiate» one of these «classes» simply by copying the table with table_copy().
There is also syntactic sugar built into the language, for creating a table of contiguous numbered keys starting at 0 and having given values — and some functions to act on such tables, such as a remove function to remove an index from the table while decrementing the index of all higher-numbered keys. Tables of this format are known as «array tables» — and internally, there is optimization for keys which are numbers rather than strings, but fundamentally, these are still just tables/maps with a key->value relationship.
I learned after-the-fact that this is quite a lot like Lua handles it’s type system, even giving the same names to some things, and have been inspired from Lua’s setmetatable feature to possibly implement a similar inheritance scheme in Ria rather than instantiating a class via table_copy(), you could create a table { } and then have it inherit from the original class definition.
Anyways. I do get long-winded. I would be happy to share all of this code if there were sufficient interest, but did not originally offer it as the text editor has some undocumented key shortcuts that I have just gotten used to, and some things which are not presentable — also, I am not 100% certain that it does not have some «TODO» hacks in it that assume the existance of my «/b» shared drive that would require the compiler to possibly do some code splunking to alter to your environment to get it working 100%. And also because it is has a lot more dependencies to compile and I was not sure how much me-ification people would be interested in installing — although still not that difficult to compile. To compile the full stack you will need to install makegen, libkaty, libria, and then Melody. For graphics (the ria-gfx extension library), you also need xintf — although it will compile fine without it, you just won’t be able to use graphics or play the «snake» or «xonix» demos. All 4 have ./go scripts in them that should do the work for you on most distributions (and tested on Debian and Ubuntu). If there is any interest in Ria or Ria+Melody let me know and perhaps I will post code — with the caveat that this is again, GPLv3+please give credit, very much a continuing WiP — Ria especially, doesn’t yet have proper documentation or a wiki, only two directories of several code examples and tests, and the source code itself, and is subject to change syntax or names of standard library functions at my whim, although the basics are fairly stable by now — and has never been fixed up to my usual standard of excellence that I would demand of myself for something I was releasing publically.
BTW, sysbeep() arose as a way to make a beep, ANY beep, once PC speakers became less prominent. It attempts several methods, it will try «beep» command, try to ring the X bell, etc, but the usual method it ends up using is that it has a «beep» sound, created by sox, embedded into the library as a long uint8_t array, and it forks and then calls aplay, telling it to play /stdin, and then pipes the embedded wav file to sysbeep().
Newer code, is the function, I think it is called tone() — but you will find it in the same sound.cpp file anyway — this one is the same basic method but gets a little more complicated — after a while I, and my spouse 🙂 — grew quite tired of that same «BEEP» playing in various contexts such as when quick-searching in Sisong Melody and you type chars that return no results, it uses sysbeep() and can make quite a racket if you keep typing a bit before realizing — anyway tone() actually creates a sine wave in real time of specified frequency, length, and volume into a buffer, and then appends a wave-format header and pipes that to aplay as well.
On my system, there is also a sysbeep *command* for use by bash scripts — basically just a tiny C program that calls sysbeep(), although newer version also supports the tone() function if given certain parameters — this is a replacement for the «beep» command, which used to beep the PC speaker, and no longer works on hardly any modern systems.
Hurrah for you on the Haiku port btw. I hadn’t been to «Download Cave Story page» in some time, and wasn’t even aware they had added any mention of NXEngine. I am glad that they accepted your port and the other ports based on NXEngine which would not be possible otherwise. Some ports surprised me though. AmigaOS? It appears from the description that the AmigaOS port is an offical port of the original game. I had no idea that such an esoteric non-NXEngine-based port had been accomplished. Interesting.
Thanks again for answering questions and also for interesting and voluminous stories about Sisong/Melody text editor and K/Ria scripting language!
I was very surprised that you create a Linux port through a compatibility layer of those parts of the BeOS/Haiku API that you used for the Sisong program. Once upon a time there was the BlueEyedOS (screenshots) project, an attempt to implement the BeOS API upon the Linux kernel. I also think they used pure Xlib calls to be able to run the BeOS applications in the classic X11 (via XFree86 in those days) UNIX-like environments. Unfortunately, the BlueEyedOS was not popular and remained only in history. I agree with you that BeOS/Haiku API very nice and user-friendly for programmers, just like separate BeOS/Haiku «kits» and frameworks.
I downloaded your updates, thanks. Can I mirror these original files here on my blog?
It would be great to see release and actual versions of Ria+Melody someday on the GitHub/GitLab/Bitbucket/SourceForge services. I’m curious to look the sources of the Ria interpreter when it will be polished and released 🙂
I downloaded the *.lha archive for AmigaOS, unpacked it and found some documents from GP2X there, quote from *.nfo text file:
It looks like the port for the Amiga OS is based on the GP2X port by Simon Parzer that I mentioned and described above in this comment, BTW, there were a Sony PSP port by ufo_z, which was also based on the original Pixel source code of Cave Story.
Hello, i wanted to try this port on my Motorola Z6w, and i have trouble launching it — the phone doesn’t recognise the file as an executable file. Is there a way to fix this?
Hello!
The ability to run native games and applications on the Motorola Z6w as well as on any other phones of the MotoMAGX platform is available only on custom firmwares. I know only one custom firmware for Z6w which is SAedition MOD by fill.sa developer. Unfortunately, the Motorola Z6w was a very rare MotoMAGX phone and not many people used or hacked it.
You can try to hack the official firmware to be able to run native software on it if you wish but this is a very non-trivial procedure. Therefore, custom firmwares are used to simplify this process and add other interesting features.
The Cave Story game has already been ported to the MotoMAGX platform which includes Z6w too, you don’t need to port it again but the SDL library used by the Cave Story is platform-dependent, so you need to use the corresponding version for Z6w to run it. You can found SDL library for Z6w in the SDL HW (RC5) thread (see SDL_HW_C4_all.zip archive). All other libraries used by the game must be cross-platform within the MotoMAGX platform. In addition, there is a high probability that the Z6w is compatible with the ZN5, including with many software and libraries for it, see Native Games and Emulators for Motorola ZN5 thread. Perhaps SDL library for ZN5 which available there will work on Z6w too.
Good luck with your experiments!
hey EXL i am planning to add this port to my TAS collection
i want to know if the game will work in haiku os installed native on a pc
i think yes