Gamedev Maltsev Denis
4 961

Пишем прокси-dll для исправления Silent Storm

Решил я на выходных поностальгировать в Silent Storm. Установил, запустил и как-то не пошло… Разрешение, то ладно, можно и в 4:3 поиграть. Но, отсутствие теней, эффектов и дикий FPS мешали погрузиться в игру.

В закладки
Аудио

В сообществе стима уже были варианты исправлений, но использовать чужую dll-ину не хотелось. Да и не все мои хотелки она правила. Автор патча Novik65 в одном из обсуждений рассказал, что именно надо поправить и, даже, дал ссылку на исходники. Которые, к сожалению, за давностью лет потерялись.

Вот и появился повод прокачать скилл программинга 😊 Итак, порядок действий был таков:

  • Написать "чистый" прокси для библиотеки d3d9.dll и проверить работоспособность экспортируемых функций.
  • Реализовать свои интерфейсы IDirect3D9 и IDirect3DDevice9, которые будут вызывать методы настоящих.
  • Поправить функцию Direct3DCreate9, так чтобы она создавала фальшивый IDirect3D9.
  • Поправить метод IDirect3D9::CreateDevice, так чтобы он создавал фальшивый IDirect3DDevice9. Здесь же принудительно включается VSync.
  • Поправить метод Direct3DDevice9::GetAvailableTextureMem. Дело в том, что игра кушает результат в int и не переваривает размер видеопамяти современных видюх. Надо урезать результат до 2-х гигов, чтобы все работало правильно.

Пишем чистый прокси.

Я в этом вопросе плохо разбираюсь, поэтому нашел статью грамотного человека:

Она рассказывает про прокси для ddraw.dll, но принцип тот же: получаем полный перечень всех экспортируемых функций, загружаем оригинальную библиотеку и получаем их адреса. А дальше работает такой код для всех "чистых" функций:

extern "C" __declspec(naked) void Fake_D3DPERF_BeginEvent() { _asm { jmp[d3d9dll.D3DPERF_BeginEvent] } }

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

На этом все! Собрал и подкинул фейковую d3d9.dll в папку с игрой. Игра запустилась и заработала без проблем. Значит можно продолжать.

Реализовываем свои IDirect3D9 и IDirect3DDevice9.

Открываем заголовочный файл d3d9.h и охреневаем 🤔

Интерфейс IDirect3DDevice9. И каждую строчку нужно реализовать.

В этот момент, мне расхотелось играть в Silent Storm 😊 Ладно, копаем дальше, должны же быть извращенцы помимо меня. И я нашел аналогичный прокси для Pac-Man Battle Royale.

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

void FakeDirect3DDevice9::SetCursorPosition(int X, int Y, DWORD Flags) { return(m_pIDirect3DDevice9->SetCursorPosition(X, Y, Flags)); }

Подмена интерфейсов и правка багов.

Вот тут начинается самое интересное. Мне нужно было переписать некоторые функции, чтобы они возвращали мои липовые интерфейсы. И, конечно, исправляли недочеты игры. Первым делом переписал Direct3DCreate9, возвращающую интерфейс на объект Direct3D9.

extern "C" IDirect3D9* WINAPI Fake_Direct3DCreate9(UINT SDKVersion) { typedef IDirect3D9* (WINAPI * DIRECT3DCREATEPROC)(UINT SDKVersion); DIRECT3DCREATEPROC Direct3DCreate9Proc = (DIRECT3DCREATEPROC)d3d9dll.Direct3DCreate9; IDirect3D9* pFakeDirect3D9 = Direct3DCreate9Proc(SDKVersion); return new FakeDirect3D9(pFakeDirect3D9); }

Идея всех подобных функций проста: сначала я создаю настоящий Direct3D9 объект (первые три строчки в теле функции), а потом создаю свой прокси-объект, которому скармливаю настоящий (последняя строка). В дальнейшем все методы прокси либо будут передавать управление настоящему объекту, либо вносить правки и уже потом передавать управление. Наружу из такой функции, естественно возвращается интерфейс на поддельный объект.

Теперь очередь за IDirect3D9::CreateDevice.

HRESULT FakeDirect3D9::CreateDevice(UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) { pPresentationParameters->PresentationInterval = D3DPRESENT_INTERVAL_ONE; // force VSYNC HRESULT hres = m_pIDirect3D9->CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); *ppReturnedDeviceInterface = new FakeDirect3DDevice9(*ppReturnedDeviceInterface); return(hres); }

Тут, тоже самое. За исключением первой строчки в теле метода. Именно она отвечает за включение вертикальной синхронизации. Никаких дополнительных проверок я делать не стал - ребята из Nival дичи не творили, все параметры передают хорошие.

Ну и последний метод, исправляющий баг с тенями и эффектами:

UINT FakeDirect3DDevice9::GetAvailableTextureMem(void) { UINT ret = m_pIDirect3DDevice9->GetAvailableTextureMem(); if (ret > (UINT)INT_MAX) ret = (UINT)INT_MAX; return(ret); }

Исправляем то, о чем я писал выше. Игра внутри себя хранит результат в int (максимум 2'147'483'647), а надо хранить в unsigned int (максимум 4'294'967'296). Ну т.е. на видюхе с более чем 2 гига оперативки, int захлебывается и уходит в отрицательные значения, что и приводит к неверному авто-определению настроек. Данный метод, просто обрубает настоящий результат по границе в 2 гига.

Итог.

Update: Добавил фикс для разрешения 1920х1080. Мне не нравится результат - многие надписи обрезаются по вертикали. В комментариях есть скриншоты с примерами. Чтобы перейти в FullHD, в игре нужно установить разрешение 800х600.

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

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

Написать
{ "author_name": "Maltsev Denis", "author_type": "self", "tags": [], "comments": 77, "likes": 270, "favorites": 149, "is_advertisement": false, "subsite_label": "gamedev", "id": 50127, "is_wide": false, "is_ugc": true, "date": "Mon, 13 May 2019 23:40:59 +0300" }
{ "id": 50127, "author_id": 92857, "diff_limit": 1000, "urls": {"diff":"\/comments\/50127\/get","add":"\/comments\/50127\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/50127"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64954, "last_count_and_date": null }

77 комментариев 77 комм.

Популярные

По порядку

Написать комментарий...
73

Вот это тот самый контент, ради которого я пришёл на ДТФ! Больше!

Ответить
4

Это контент раздела реверс инженеринг на хабре. И если на хабре такой контент норм заходит, то тут это на любителя.
Это, все таки, не гейм дев.

Ответить
16

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

Ответить
0

Заглядывая за текстуры можно посмотреть как оптимизировать ресурсы и своё время.
А тут лишь правка чужих программ.

Ответить
0

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

Жаль что современный геймдев вырастил уже то поколение делателей игр, которые мало понимают как устроен вывод изображения на экран + вообще какое-то понимание о конечности машинных ресурсов и как использовать эти знания для того, чтобы не делать дурной работы там, где ее можно не делать. Собственно на этом незнании и строятся статьи о заглядывании за текстуры + банальное непонимание работы железа (это про статью где сворачивали дракончика в рулетик под полом по вполне очевидным причинам минимизации издержек по ресурсам этого самого железа.

Автор и не скрывал что мопед не его, но тут хоть какая-то реализация... А именно реализация модофикса без исходных текстов игры и пруфы того, как какая-то часть игры "действительно" работает под капотом (реальная реализация с реальной пользой). Надеюсь я до вас донес смысл моих претензий к статьям по типу "посмотрим что за текстуркой? Ого тут нет глазниц (подобный контент растянутый на лонгрид)" и подобных им в этом разделе.

Ответить

Комментарий удален

4

Ну это совсем другого уровня работа. Я просто собрал в кучку то, что уже делали до меня.

Ответить
2

немножко другого уровня работа. Тут пара ифов и подстановка значений, дело в основном в технике DLL-Вызовов. Что не умаляет усилий автора. Но по циве работа более скрупулезная, не стоит сравнивать.

Ответить
0

Денис - охуенный, вроде он пилил оптимизацию графики в Factorio

Ответить
0

Т.е. опять правил проблемы игры вместо разрабов?

Ответить
13

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

Ответить
3

А ведь еще недавно топовый графоний там был.

Ответить
22

Херасе недавно,я успел школу закончить,универ, проработать 10 лет женится и стать отцом:)

Ответить
3

Всего-то? А я сегодня скачал Rage 2. И оторвал зад от дивана. Но Rage 2 важнее.
Мдааа.

Ответить
2

Время летит неумолимо и когда играешь в свою любимую игру в порыве ностальгии, в которую играл 15-20лет назад, то чувствуешь себя по настоящему старым.

Ответить
0

Есть такое...

Ответить
–3

А разговариваете всё так-же, как восемнадцатилетний балбес. Любопытно.

Ответить
1

Потому что это комменты сайта про игры,а не профессорский чат.
Решил подъебать,умник?

Ответить
6

Буквально вчера при просмотре стрима по dark earth 1997 года задумывался, можно ли с помощью какой-то сторонней программы запарсить текстуры и подменить их при рендере игры, так например прогнав через какой-нить esrgan улучшить текстурки, а может быть и модельки, адаптировав игру более-менее под современность (встроенные текстурки в формате .CIF, ничем раскрыть их не удалось). Для новых игр что-то подобное есть (uMod), а для старых (хз чем они там рендерятся, drect draw?) что-то не нашел. Хз в тему написал или нет, я не разбираюсь в этих вопросах, но такая вещь была бы очень полезной для реинкарнации старых игр.

Ответить
0

Проще всего заменить ресурсы игры. Но если совсем тяжелый случай, то можно перехватывать методы CreateTexture, CreateVolumeTexture и CreateCubeTexture. Просто в них передаются уже raw-data. Никаких имен файлов и других идентификаторов - просто массивы пикселей.

Ответить
0

лучше не смотреть вилавгеймса вообще ;)

Ответить
0

А много у игры поклонников?

Ответить
4

Как раз недавно вспоминал ее. Ах как бы хотелось ремейк SS, SS2, Серп и Молот. Ведь игра даже для нашего времени на голову выше X-COM в плане реализации тактик. И да спасибо автору.

Ответить
4

Я вот месяц назад в поисках похожего на x-com сел в нее поиграть, и как человек без сильной дозы ностальгии, могу сказать, что не показалась она мне выше x-com совсем. Да, есть интересные механики с возможностью выбора части тела для выстрела, стрельбы на шум или разные варианты выстрела, которые влияют на точность и расход очков действия, это понравилось. Но в целом я поиграл часов 5 в игру и понял, что я скорее борюсь с тем, какая игра неудобная, чем занимаюсь собственно тактикой. А неудобно буквально все: перемещение персонажей, камера и сами перестрелки и даже использование взрывчатки(сравнить с тем же икскомом).
Для меня удивительно, что только недавно разработчики данного жанра поняли, что укрытия должны использоваться при выборе позиции персонажа, к примеру. Перестрелки здесь выглядят нелепо: ты выходишь на линию огня стреляешь, если у тебя остались очки действия, то пытаешься отойти, но обычно их не остается. АИ ведет себя еще хуже, просто оставаясь на линии огня. В итоге самая простая тактика была: выдать всем дальнобойное оружие и отстреливать максимально точным выстрелом компьютерных болванчиков. В какой-то момент я понял, что это скучно и удалил игру. Играл я на максимальном уровне сложности, если что.
Возможно я упустил что-то, готов послушать поклонников игры, чем она вам так нравится.

Ответить
0

Игра крутая, но с определенного момента ты становишься имбой, да.

Ответить
0

SS на другой части спектра от современного XCOM (в старый не играл). Где в XCOM все сглажено в угоду консольщикам, тут все неудобно и медленно даже на ПК.
Просто нужен баланс, а не убийство одного аутиста за 5 ходов с расчетом хода врага по 30 секунд.
При этом механики тут правда круче и симуляторнее - чего только разрушения стоят.

Ответить
0

Тут вся соль в разрушаемом окружении. Из-за нее генерируются такие ситуации и способы прохождения, которые невозможны в других играх. Классический пример - подорвать несущие стены здания, в котором засели враги. Здание рухнет и похоронит всех.

Ответить
1

Партизанов же пилят

Ответить
2

Судя по демо видео, это будет аля коммандос. Да и масштаб не тот.

Ответить
1

ага, многие хотят SS, но Орловский говорит мол дорого и не окупится.

Ответить
0

Условно-бесплатные МОО Аллоды приносят до хера денег.

Ответить
0

Здрасьте, Орловский давно уже просрал Аллоды свои.

Ответить
0

Тогда ладно, читал выходили всякие обновы на игру и большие дополнения.

Ответить
0

Ну они там с мэйлсру как-то не поделили деньги, мэйлсру создала отделение Аллоды Онлайн, куда некоторые сотрудники ушли.

Ответить
0

Теперь все понятно, игра живёт, без Орловского и Нивала.

Ответить
0

Спасибо за пояснение ситуации.

Ответить
4

можно не париться с прокси и хукнуть только необходимые методы (например https://github.com/Rebzzel/Universal-D3D11-Hook/tree/master)

Ответить
1

филиал хабра?

Ответить
1

Ох Жиза. Меня тут попросили запустить пару старых казуальных игр 2004 и 2007 года под.
Все эти режимы совместимости в windows 10 , подкидывания патчей не помогли.
И самое смешное что эти игры без плясок и бубнов запустились в 1 клик в Wine под Ubuntu.

Ответить
0

На GoG или old-games их нет?

Ответить
1

пользуясь случаем - Новик65 крутой!
правда, я не знаю, где он сейчас обитает из форумов - да и не играю активно.

если кто вдруг не в курсе - это автор "Серп и Молот", также его код и ценная помощь есть дофига где - и нам помогал как-то, и "Апейрону".

автор статьи тоже молодец, конечно - не только для себя сделал, но и поделился.

Ответить
1

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

Ответить
–3

Понастольгировать*

Ответить
3

Проверочное слово «ностальгия» а не «настолка»

Ответить
0

Мой косяк

Ответить
0

Очень интересно) А скажи, таким макаром можно настроить мёртвую зону на джойстике для старой игры?

Ответить
1

Хз. Если она использует direct input, то, наверное, можно.

Ответить
1

Есть программы типа Joy2Key которые должны бы уметь такое.

Ответить
0

Спасибо! Не знал про неё, обязательно опробую.

Ответить
0

а можешь выложить отдельно версию с поддержкой широкоформатных экранов?

Ответить
1

Нет, и заморачиваться не хочется. Тут ведь и с управлением придется шаманить. Если очень сильно хочется попробовать, то в FakeDirect3D9::CreateDevice можно дальше модифицировать pPresentationParameters. А именно установить поля BackBufferWidth и BackBufferHeight в нужные значения, ну и выключить Windowed. Возможно прокатит...

Ответить
1

Если устроит такое изображение, то могу сделать сборку.

Ответить
0

Почему бы и нет? Можно и выложить, если разработка этой версии много времени не займет.

А вообще работа замечательная. Я сам хотел пройти SS, но что на моем ноутбуке с GTX940M, что на компьютере с GTX650Ti игра выдавала весьма маленький FPS. Надеюсь, теперь смогу :)

Ответить
0

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

Ответить
0

Если устроит такое изображение

а что с ним не так? соотношение сторон вроде соблюдено, искажённог FOV-а тоже не наблюдаю

Ответить
0

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

Ответить
0

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

Ответить
1

Добавил поддержку, статью тоже обновил. Релиз 1.1. Чтобы перейти в 1920х1080 нужно в игре сменить разрешение на 800х600.

Ответить
0

спасибо! вечером протестирую

Ответить
0

Блин, жаль что фулки не будет. для стримов не удобно. А так рукожопный я. Но файлик скачал) Спасибо большущее. С очень полезной штукой заморочился.

Ответить
0

Зачем асм? Это на х64 не сработает

Ответить
0

Когда выйдет 64-битный Silent Storm эта дллка будет не нужна :)

Ответить
0

Даже в режиме совместимости?

Ответить
0

Исходники? я когда лет 15 назад пытался добавить в аллоды 2 разрешение выше чем 800х600, я добавлял секцию со своим кодом, и туда все переходы патчил, ух было время.

Ответить
0

Ну кому лень писать прокси есть dgVoodoo2 с настройками.

Ответить
0

Спасибо за обновление к этой классной игре.

Ответить
0

Аааа.!!! Омгэ, код на дтф. Никогда такого не видел. А ты смельчак)

За статью спасибо, всегда хотел понять как это делают.

Ответить
0

Более того, были на дтф стенки кода без специального поля для него...

Ответить
0

Наконец-то хорошая статья

Ответить
0

Ооо это реально круто, бро..спасибо

Ответить
0

А правда, а че ты с этим постом не пойдешь на хабр? Там аудитория должна оценить это по достоинству

Ответить
0

По Хабровским меркам это слабенько, но давненько такого не видел - должны бы порадоваться и такому.

Ответить
0

Таак, падажжи ебана. Эту статью нужно читать под чаек вдумчиво.

Ответить
0

Валхаки научишь писать?)0))))

Ответить
0

Спасибо, поностальгировал.

Ответить
0

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } } ]
Узнавайте новости о мостах
Санкт-Петербурга первыми
Подписаться на push-уведомления