Wing Commander для DOS получил кооператив после 11 лет реверса

Автор под ником danielrh рассказывает об 11-летней истории: вместе с братом Патриком он хотел играть в космосимулятор Wing Commander не вдвоём в одном корабле, как в детстве (один у управления, другой на лазерах и форсаже, «двухголовая гидра»), а как два напарника в разных кораблях. Всё началось 4 октября 2015 года: играя в DOSBox, автор понял, что вся память игры, вероятно, умещается в 640 КБ. Если найти координаты X, Y, Z игрока и ведомого, сетевые пакеты смогут управлять ведомым.

Первая задача, найти координаты среди менее чем миллиона байт. Автор очистил игру от лишних объектов: вылетел на миссию в пустом космосе без ведомого Паладина (предыдущую миссию пришлось «слить»), снял дампы памяти командой DOSBox MEMDUMPBIN в покое и после рывка вперёд и сравнил их. В сегменте данных изменились 35 отдельных кластеров байтов; визуальный осмотр сократил кандидатов до дюжины, а вид с камеры преследования помог выйти на адрес 13D3:A9C2. Прибавление чисел по этому адресу командой SM отбросило корабль вперёд. Затем, в сентябре 2016 года, автор написал утилиту для автоматического сравнения снимков памяти DOSBox. С направлением вышло сложнее: векторы состоят из 4-байтовых скаляров, где 256 равно 1,0, поэтому малые повороты меняют один байт. Помогло то, что в игре 64 слота свойств объектов (0x00-0x3F), временные значения лежат в 0x3F, а камера в 0x3D, рядом. Так нашлись векторы вверх и вперёд; запись в forward[0] заметно сдвинула звёздное поле. После этого автор поставил точки останова и проследил цепочку вызовов до главного цикла.

В декабре 2016 года стало ясно, что одной записью в память игрой не управлять: лазерный огонь и самонаводящиеся ракеты слишком сложны. Нужно было вызывать оригинальные функции игры, а они лежат в оверлеях Borland, загружаемых в разные места. Братья построили «трамплин», код, размещённый в области строк игры (сначала по адресу строки «Loading WING COMMANDER» 13D3:0187, затем в строке об ошибке 13D3:0395): он сохраняет регистры, кладёт аргументы, вызывает функцию через заглушку и возвращается назад. Первый вызов включил лазерные пушки. Позже выяснилось, что загрузчик оверлеев Borland при подкачке кода обходит цепочку сохранённых кадров BP и правит адреса возврата; у самодельной заглушки кадра не было, и игра рандомно падала, с октября по декабрь. Решение, сделать трамплин похожим на функцию Borland (push bp, mov bp,sp, nop, pop bp, retf) и перехватывать выполнение на nop в DOSBox, подставляя вызов из очереди заданий.

В марте 2017 года нашли предсказуемое место в главном цикле (0560:20E3) для перехвата, в июле, функцию do_damage (её блокировали для второго игрока). В августе 2017-го написали сетевой протокол: простая клиент-серверная схема на TCP с сообщениями protobuf. Каждый кадр ведущий отправляет позиции всех кораблей плюс события стрельбы, исчезновения и повреждений; клиент отвечает своей позицией, выстрелами и «катастрофическими» событиями (катапультирование, посадка, гибель). У каждого игрока свой корабль, сущность 0, партнёр, сущность 1, поэтому при приёме пакетов их меняют местами. В октябре 2017-го обнаружилось, что временные объекты попадают в разные слоты у разных игроков; их ограничили слотами 12-15 из 61 слота сущностей. В декабре клиент стал хранить соответствие идентификаторов корабля на сервере и локальных, и тогда заработали астероиды. В 2018-м занимались взрывами, ракетами и автопилотом. В 2019-м победные очки считались нестабильно: по сети гоняли весь 64-килобайтный сегмент данных и делили пополам, нашли 80 байт дерева миссий (13D3:D248), а в октябре, общий счётчик победных очков (13D3:C280) и номера миссий (13D3:C255 и C256), что позже позволило выбирать миссию из выпадающего списка. В начале 2020-го нашли функцию вывода реплик: чат пошёл по сети и проигрывается на экране ведомого (VDU).

К концу 2020 года игра была играбельной, но делиться ею было слишком трудно: нужно найти копию старой игры, собрать или скачать необычную сборку DOSBox, обменяться публичными IP и пробивать файрволы. В семье поиграли несколько раз, проект почти простаивал на GitHub. В 2026 году, с распространением WebRTC и зрелой поддержкой emscripten, автор вернулся к нему. Он написал lobbylink, простую лобби-службу на Go, зависящую только от библиотеки websocket, которая организует прямые WebRTC-соединения между игроками по общему коду комнаты; у сервера статический список разрешённых источников (origins), а coturn выдаёт TURN-учётные данные тем, кто сидит за строгими файрволами. Транспорт перенесли на lobbylink, а LLM попросили настроить сценарии сборки под emscripten и использовать распакованный установщик GOG (Inno Setup) как файлы данных. В итоге игру можно скачать с GOG, перетащить в хранилище браузера на стороне клиента, ввести код комнаты и играть. Раздел «The game» в доступном тексте обрывается.

Ключевые факты

  • С 4 октября 2015 года автор danielrh с братом Патриком реверсили память Wing Commander в DOSBox: из 35 изменившихся кластеров байтов нашли координаты корабля (адрес 13D3:A9C2) и затем векторы ориентации.
  • Записи в память не хватило: для лазеров и ракет построили «трамплин» для вызова оригинальных функций игры, а случайные падения вылечили, сделав его похожим на функцию Borland (проблема с загрузчиком оверлеев).
  • Сетевой протокол 2017 года: TCP клиент-сервер с protobuf; ведущий каждый кадр шлёт позиции всех кораблей и события, клиент отвечает своей позицией и выстрелами; временные объекты ограничили слотами 12-15.
  • К 2020 году игра работала, включая чат через экран ведомого, но запускать её было слишком сложно (DOSBox, IP, файрволы).
  • В 2026 году добавили лобби lobbylink на Go с WebRTC и TURN и сборку emscripten: игрок берёт копию игры с GOG, загружает в браузер, вводит код комнаты и играет.

Почему это важно

Это пример того, как через отладчик и сравнение дампов памяти можно превратить одиночную игру 1990-х в кооперативную без исходного кода. Текст подробно показывает методы: поиск изменившихся байтов между снимками, использование соседства слотов камеры, точки останова, перехват главного цикла, вызов функций игры через трамплин с учётом оверлеев Borland. Отдельный сюжет, как WebRTC, emscripten и лобби по коду комнаты убирают барьер доступа для друзей.

Кому это важно

Энтузиастам реверс-инжиниринга и моддинга старых игр, тем, кто работает с DOSBox, IDA и эмуляцией, и всем, кто вспоминает Wing Commander. Также любопытно разработчикам, которым нужна простая схема пиринговых WebRTC-соединений с лобби на Go и TURN.

Как это применить

Игрок в версии из рассказа берёт Wing Commander с GOG, перетаскивает его в браузерное хранилище на стороне клиента, вводит код комнаты и играет. Лобби lobbylink опубликовано автором на GitHub (danielrh/lobbylink); оно работает на небольшом сервере, а coturn выдаёт TURN-данные для игроков за строгими файрволами. Данные игры в проект не входят: игрок приносит свою копию с GOG.

Можно ли доверять

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

Риски и подводные камни

Игру нужно купить и скачать с GOG самостоятельно, это условие запуска. Проект строится на хрупких адресах памяти и перехватах внутри эмулятора, поэтому вряд ли подойдёт для других версий игры без доработки. Автор отмечает, что в 2026 году LLM использовали для настройки сборки под emscripten и распаковки установщика GOG; какая именно нейросеть и какую долю работы она выполнила, не сказано.

«Должен сказать, что в 1981 году, принимая эти решения, я чувствовал, что даю достаточно свободы на 10 лет. То есть переход с 64 К на 640 К казался чем-то, что продержится очень долго. Что ж, не продержался.»

— Билл Гейтс; настоящая цитата, как её приводит автор статьи в противовес апокрифическому «640 К хватит всем»