Гайд Изменение качества шейдеров в игре Age of Empires III

Арбитр
Продавец
Статус
Оффлайн
Регистрация
13 Июл 2018
Сообщения
1,528
Реакции[?]
1,637
Поинты[?]
280K
Перевод статьи: Cracking Age of Empires III over shader quality settings
Автор статьи: Lancelot de Ferrière
(кликабельно)
1 мая 2020 года — если вы похожи на меня, то карантин вызывает у вас желание переиграть в игры, к которым вы не прикасались годами. Если вы еще больше похожи на меня, возможно, у вас где-то хранится копия Age of Empires 3. Возможно, вы играете на Mac, возможно, вы еще не перешли на Catalina, и, возможно, вы жаждете немного того самого Моргана Блэка.

Примечание:
Catalina — операционная система для персональных компьютеров и серверов, разработанная Apple.
Морган Блэк — вымышленный рыцарь, родившийся в шотландской семье Блэков, который позже присоединился к рыцарям Святого Иоанна. Он является главным героем игры Act I: Blood в Age of Empires III.
1670424423473.png

Итак, вы загружаете игру, попадаете в главное меню и сразу же замечаете, что что-то не так...
Главное меню выглядит ужасно.
Если вам интересно, что именно выглядит ужасно, то посмотрите на воду. Все остальное тоже выглядит ужасно, но не так сильно бросается в глаза.
Итак, вы переходите к настройкам графики, увеличиваете настройки их до предела...
Но ничего ничего толком не стало лучше, игра по прежнему выглядит ужасно.

Вы можете увидеть, что настройки "Качество Шейдеров" заблокированы на "Низкое качество"

Попытка № 1 - Взлом/Изменение настроек графики
На этом этапе вы будете искать папку, которую игра создала где-то в Документах, потому что в 2005 году многие разработчики игр помещали файлы именно в эту папку.

Что вы ищете, спросите вы? Конечно же, папку, в которой хранятся настройки. Интерфейс игры может не давать нам изменить настройки, но мы можем изменить настройки иначе, ведь мы умнее системы(нет) :roflanEbalo: .

Итак, вы находите файл формата XML, который ищете, потому что на дворе 2005 год, и натыкаетесь на опцию "optiongrfxshaderquality", установленную на 0. Это выглядит правильно, думаете вы, и поэтому вы увеличиваете значение до 100, потому что много графики никогда не бывает. Я с вами полностью согласен.


Вы также можете заметить, что это ужасное использование XML.
К счастью, хорошего применения ему нет.

Вы запускаете игру. Однако, PATATRAS (это французский, посмотрите), ничего не изменилось. И когда вы смотрите на старый XML- файл, настройки снова вернулись к 0. Ensemble Studios (Земля ей пухом) посмотрела на ваши манипуляции с файлами и посмеялась. Всё оказалось не так просто, как с майнкрафт, да? :roflanEbalo:

На этом этапе вы можете сдаться. Это всё же macOS, и вы вряд ли найдете пользовательский патч для неё (Я немного покопался в интернете. Нашёл пару сомнительных патчей, они вполне могут сработать, но я не стал этого проверять). Мы хотим лишь изменить настройки графики, но на пути встаёт столько преград...

Но вы не будете этого делать. Потому что вы помните, как должна выглядеть игра Age 3. Вы помните, как смотрели на эту красивую волнистую воду. На эти эффекты частиц. На эти огромные корабли, которые просто не помещались в чертовом, мать его, кадре.

Попытка № 2 - Взлом/Изменение данных
В верхней части этой папки в ваших документах находится файл с логами. Игра отображает вашу графическую карту и сообщает, что она выбирает настройку “generic dx7”. Она говорит, что у вас Intel GMA 950, которая действительно была на вашем предыдущем компьютере. Но на этом компьютере стоит Intel Iris Graphics 6100. Вы находитесь в замешательстве, что-то тут явно не так. Вы делаете смелое предположение, что игра определяет графические возможности вашего компьютера, сопоставляя его с базой данных видеокарт, вместо того, чтобы проверить возможности вашей видеокарты. Потому что так поступают ААА-разработчики, возможно, вы работали над игрой RTS с открытым исходным кодом и знаете, как это бывает.


Содержимое файла с логами. Если вы видите смешным, что Intel подписан как 0x8086, то вы пришли по адресу.
Случайно, вы нашли старые патч-ноты. Они подтверждают ваши опасения. Вы просматриваете некоторые файлы в папке GameData, но это все файлы имеют расширения .bar и .xmb, а они в основном нечитаемы. Вы вводите в поиск "Intel", "dx7". Ничего не происходит. Вы удаляете некоторые файлы... Ничего не происходит. Ну, вы знаете, что AAA-игры могут захотеть разместить некоторые ресурсы в странных местах, а эта игра портирована с Windows на macOs, так что здесь всё может быть странным.

Тем не менее, в итоге вы находите файл "render.bar", и перемещение этого файла приводит к сбою при запуске игры. Это смутно хорошо. Вы открываете этот файл в программе Hex Fiend, потому что она обычно может показать скрытую информацию о двоичных файлах. И что ж, так оно и есть. Некоторые из этих данных - читаемый текст. Часть из них - шейдеры. Но большинство из них странным образом разделены пустыми пробелами...

Интерфейс Hex Fiend. Является минималистичным, но вполне справляется с работой
Первые байты — это буквы «ESPN», которые являются заголовком .bar.

"Очевидно, это UTF-16", - воскликнете вы! Это изначально игра для Windows, поэтому вполне логично, что они хранили текст в UTF-16, тратя практически половину пространства 30 Мб файла данных. В конце концов, Windows любит UTF-16 (хотя и не должна), и почему бы не использовать его и ASCII шейдеры в одном файле. Отличная идея! // (Не показано: мне понадобился день, чтобы понять это)

В любом случае. Hex Fiend на самом деле имеет опцию для разбора байтов как UTF-16, так что вы снова ищете строчку dx7. Это дает вам несколько совпадений. Вы заменяете все это на dx9, который также иногда появляется в этих данных, сохраняете и загружаете игру. Что ж. В логах даже не написано "dx9". Возможно, это объявлено где-то в другом месте.

Давайте попробуем по-другому. Вы предполагаете, что игра определяет графические карты, и у вас есть шестнадцатеричные идентификаторы в логах (в моем случае 0x8086 - Intel, и 0x2773). Поиск "8086" в Hex Fiend дает результат, и часть текста выглядит обнадеживающе. Intel 9 Series - это серия GMA 950, и она, вероятно, не очень хорошо запускала Age 3.


Некоторые числа там похожи на идентификаторы карт (2582, 2592), так что, возможно, их изменение что-то даст. Вы в любом случае сделали копию файла, так что попытка не повредит.

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

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


Я не уверен, почему этот "generic dx7" не такой же, как тот, что указан выше.
Попытка № 3 - Взлом/Crack
Итак, если все остальное не помогает, нам остается последнее: изменить сам исполняемый файл (в 2005 году это называлось "cracking"). Сегодня, наверное, мы бы назвали это просто взломом, но слово прижилось в сфере игрового пиратства (не то чтобы я когда-либо был близок к этому, конечно).

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

Hopper Disassembler
Нашим инструментом для этого шага будет дизассемблер: приложение, которое берет двоичный код исполняемого файла и превращает его в читаемый :roflanEbalo: ассемблерный код. Самый известный - IDA Pro, но он безумно дорогой, и я не уверен, что он работает на Mac, поэтому я выберу Hopper. Он не бесплатный (на самом деле он стоит 90$), но вы можете свободно использовать его в течение 30 минут за раз с почти всеми необходимыми функциями.
Я не буду вдаваться в подробности о том, как выглядит интерфейс, так как в учебнике по Hopper это хорошо описано, но я объясню, что я делаю, и предположительно здесь достаточно скриншотов, чтобы вы могли чему-то научиться.

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

И еще одно замечание: "взлом" обычно незаконен и/или делается в незаконных целях. Это редкий пример довольно законного использования, которое также является достаточно "белой шляпой", так что это хорошо. Я действительно считаю, что технически это незаконно / противоречит EULA, но это, очевидно, форс-мажорный случай. В любом случае, пожалуйста, платите за вещи, которые вам нравятся, и только за них, потому что антипотребительство - это круто.


Перетащите приложение Age 3, чтобы открыть его.
Вам просто нужно выбрать “x86” вместо PowerPC, потому что да, это 2005 год.

Шаг 1 — Поиск соответствующего бита
Это, как ни удивительно, может оказаться самой сложной частью. У вас есть весь разобранный код, и вы не знаете, где его искать. Правда, некоторые игры поставляются с некоторым уровнем отладочных символов, что делает имена функций читаемыми, но в этой игре этого нет. Hopper выдает "sub_a8cbb6", и вам придется довольствоваться этим.

К счастью, у нас есть преимущество: наш файл с логами. Вполне вероятно, что для записи в него используются строковые литералы, что в основном означает, что это ASCII, жестко закодированный в исполняемом файле. Это то, что вы можете найти в поисковой строке, потому что никакая компиляция не удалит это (обфускация кода может скрыть это. К счастью, в данном случае это не так). Поиск строковых литералов обычно является первым, что вы делаете при разборке чего-либо. Итак, давайте наберём "found vendor" в поиске Hopper, и вы увидите, что он что-то нашёл:

Ура, строковые литералы, люблю их.

Само по себе это не слишком продвинет вас вперед. Но поскольку адрес этой "литеральной строки" жестко закодирован, вы можете найти ссылки на нее. Hopper делает это для вас в зеленых битах справа: DATA XREF=sub_1d5908+1252. Двойной щелчок на этой строке приведет вас к процедуре sub_1d5908, нашему герою дня.

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

По умолчанию Hopper использует "синтаксис Intel", что означает, что первый операнд является конечным, а второй - исходным. На выделенной строке вы видите mov dword [esp+0x1DC8+var_1DC4], aXmlRenderConfi Давайте разберем это.

Наши первые строки на ассемблере
mov - это инструкция MOV, знаменитая тем, что на x86 она не имеет сложности по Тьюрингу. Она перемещает (ну, копирует) данные из A в B, что в основном означает, что она читает A и записывает его в B. Учитывая то, что я рассказал вам выше, aXmlRenderConfi - это A, а dword [esp+0x1DC8+var_1DC4] - это B, место назначения. Давайте разберем это дальше.

aXmlRenderConfi - это на самом деле Хоппер помогает нам. Дело в том, что это псевдоним адреса памяти строкового литерала, на который мы щелкнули несколько секунд назад. Если вы разделите окно и посмотрите на вещи в Hex-режиме (предпочтительный режим хакеров в любом случае), вы увидите 88 18 83 00 справа.


Hopper удобно выделяет одни и те же выбранные кусочки с обеих сторон.

Если "перевернуть" его правильным образом, то получится 0x00831888, адрес памяти строкового литерала (он даже выделен желтым цветом на одном из приведенных выше скриншотов). Итак, одна загадка решена: код выполняет MOV, то есть записывает адрес 0x00831888.

Почему он пишется 88 18 83 00, а не 00 83 18 88? Ну, это из-за эндианальности(порядка байтов), довольно заумной темы, которая обычно не имеет большого влияния. Это одна из тех вещей, которые заставляют людей говорить: "Знаете, компьютеры не работают" (я искал ссылку на это и не смог, но я знаю, что она где-то есть). Для нас это означает, что все числа, которые мы сегодня рассмотрим, имеют эту проблему.

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

А что тогда с dword [esp+0x1DC8+var_1DC4]? Здесь сложнее.
Технически dword означает, что мы работаем с "двойными словами" (double-words) , то есть 16*2 бита. Это означает, что мы копируем 32 бита. Вспомните: адрес нашей памяти - 0x00831888, что составляет 8 шестнадцатеричных символов, а каждый шестнадцатеричный символ может принимать 16 значений, что составляет 4 бита данных. Таким образом, это 8*4 = 32. Кстати, именно отсюда происходит слово "32 бита" в компьютерных системах. Если бы мы использовали 64 бита, то адрес нашей памяти записывался бы 0x001122334455667788, был бы вдвое длиннее и в 2^32 раза больше, и нам пришлось бы копировать 16 байт. Это дало бы гораздо больше памяти для игр, но это также вдвое больше работы по копированию указателя, поэтому 64 бита на самом деле не вдвое быстрее 32 бит (это для вас, мальчики и девочки 1994 года).
Кстати, более полное объяснение того, что такое "слово", можно найти здесь. Потому что, конечно, все гораздо сложнее.

Итак, что это за фрагмент в квадратных скобках? Скобки означают, что мы вычислим то, что находится внутри, и будем обращаться с ним как с указателем. Таким образом, наш MOV будет записывать по тому адресу памяти, в который это оценивается.
Снова давайте разберем это (и не волнуйтесь, как только это будет сделано, вы будете знать, как читать ассемблерный код на Intel Syntax).

esp - это регистр, который является ближайшим эквивалентом переменной в ассемблере. В архитектуре x86 их много, они несколько отличаются в своем использовании, но нас это, в общем-то, не сильно волнует. Подробности можно найти здесь. Просто знайте, что есть специальные регистры для плавающей точки, а также есть "флаги", которые используются для сравнения и тому подобное.
Остальное - это просто числа, записанные как шестнадцатеричные, и Хоппер немного переработал их, чтобы помочь. var_1DC4 - это -0x1DC4, вы можете увидеть это в верхней части процедуры или на правой панели.
Это означает, что [esp+0x1DC8+var_1DC4] равняется [esp + 0x1DC8 - 0x1DC4], то есть просто [esp + 4], что означает "взять 32-битное значение регистра ESP, добавить 4 и интерпретировать это как адрес памяти".

Итак, напомним, что мы записываем адрес нашего строкового литерала в "esp+4".
Это правильная информация, но крайне бесполезная. Что это значит?

Это самый сложный момент при чтении кода на Ассемблере: выяснить, что это должно делать. Здесь у нас есть преимущество: мы знаем, что это, вероятно, регистрирует что-то. Поэтому мы можем ожидать вызова функции, которая регистрирует что-то. Давайте посмотрим вокруг.



Выше приведено несколько подобных вызовов, а также инструкция a call, которая вызывает процедуру. Hopper обычно говорит об этом (не уверен, почему он не говорит об этом здесь), но в основном происходит то, что мы устанавливаем аргументы для вызова нашей функции. Если вы щелкните по различным подвызовам, то увидите элемент с именем imp___jump_table___Z22StringVPrintfExWorkerAPcmmPS_PmmPKcS_. Это имя функции, и "Printf" достаточно, чтобы понять, что она действительно печатает что-то. Неважно, как. Мы в чистом поле.

Шаг назад
В этот момент вы уже совершенно перестали понимать, что делаете: пытаетесь заставить игру признать, что ваша видеокарта 2015 года выпуска действительно достаточно мощная для запуска игры 2005 года. Давайте подведем итоги. Мы нашли место, где происходит запись логов. Если нам повезет, то здесь же находится код, который проверяет наши настройки и возможности видеокарты. И, к счастью, нам повезло (есть большая вероятность, что иначе я не смог бы написать эту статью).

Чтобы получить общую картину, мы воспользуемся функцией Hopper's Control Flow Graph, которая разбивает код на маленькие блоки и рисует стрелки для представления различных переходов. Это гораздо легче понять, чем фактическую сборку, которая часто бывает ужасно неупорядоченной.


Наш вызов логирования находится в середине ужасно длинной функции, потому что, опять же, так делают ААА-игры.
Это тот самый момент, когда можно было бы поискать другие строковые литералы и "подсказки" Хоппера, надеясь найти что-нибудь. Я дам вам несколько подсказок (эх).
В верхней части процедуры вы увидите остальной лог, а внизу - несколько интересных кусочков о "принудительных настройках dx7" или "Very High" и проблеме с версией пиксельного шейдера... Что мы и сделали.

В логе наших "взломанных" данных говорится, что у нас пиксельный шейдер версии 0.0, который несовместим с настройками "High". Этот код находится в левом нижнем углу нашей функции, и если вы прищуритесь, то увидите кое-что особенное (выделено в ужасном стиле вашим истинным автором): это просто 4 раза один и тот же код, для каждого из "Очень высокий", "Высокий", "Средний" и "Низкий". И да, мне понадобилось несколько часов, чтобы увидеть это.



Я выделил синим блок, в котором регистрируется лог «pixel shader version 0.0 High».
Похожие части я выделил другими красивыми цветами.

Единственное отличие заключается в том, что мы пишем "0x0", "0x1", "0x2" и "0x3" в куче мест. Это подозрительно похоже на наш файл Settings из предыдущего раздела и настройку optiongrfxshaderquality (ну, возможно, вы этого еще не знаете. Но это так, поверьте мне).

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

Поклонники SEMVER, обратите внимание более совершенный формат версий: числа с плавающей запятой.

Действительно, прямо над ним находится инструкция jb, инструкция перехода (вспомните "goto"). Это "условный переход", и условием является "если ниже". Ассемблерные "если" работают через регистровые флаги, о которых я кратко упоминал ранее. Просто знайте, что обычно достаточно посмотреть на инструкцию выше: она, вероятно, устанавливает флаг.
Часто эта инструкция - cmp или test, но здесь это ucomiss.
Это варвар. Это также легко найти в Google: ucomiss. Это инструкция сравнения между плавающими точками, что объясняет, почему мы видим xmm0 : это регистр с плавающей точкой. Это имеет смысл: в логах говорится, что версия нашего пиксельного шейдера "0.0", что является значением с плавающей точкой.
Так что же тогда находится по адресу памяти 0x89034c?
Шестнадцатеричные данные - 00 00 00 40, что может оставить вас в недоумении. Но мы знаем, что нужно ожидать плавающей точки, поэтому давайте интерпретируем это как плавающую точку: это означает 2.0. Что является разумным значением с плавающей точкой для пиксельного шейдера, поскольку Microsoft действительно использовала его для своего языка High-Level Shading Language, на котором написаны шейдеры DirectX, а Age 3 - это игра на DirectX, даже если порт для Mac использует OpenGL. В 2005 году также имело смысл установить значение "High", которое требует пиксельный шейдер версии 2.0, так что все в порядке.

Как же нам теперь это исправить? Ну, эта инструкция compare сравнивает со значением в xmm0, которое установлено в строке выше: movss xmm0, dword [eax+0xA2A8] . MOVSS - это специальная инструкция "перемещения" для регистров с плавающей точкой, и мы записываем 32 бита из того значения, в которое оценивается eax+0xA2A8.

Предположительно, это значение не 2.0, а скорее 0.0.
Давайте исправим это.

Время настоящего Хакинга
Наконец-то мы готовы пройти путь до настроек High в игре. Мы хотим пройти по «красному» пути и пропустить всю логику с «неверной версией пиксельных шейдеров». Это можно сделать, успешно пройдя сравнение, то есть обратив условие перехода, но у нас есть ещё один трюк: код составлен таким образом, что при удалении перехода мы пойдём «красным» путём. Давайте просто заменим переход nop — операцией, которая ничего не делает (невозможно просто удалять или добавлять данные в исполняемый файл, потому что произойдёт смещение, и всё поломается).

Я рекомендую выйти для этого из CFG, потому что в противном случае Hopper начинает сходить с ума. Если сделать всё правильно, то в окне Hex мы увидим красные строки.


Эти красные кусочки - изменения, которые мы внесли. На этом этапе, если вы заплатили за Hopper, вы можете просто сохранить файл. Но я этого не сделал, поэтому я открою исполняемый файл в Hex Fiend, найду нужную область (скопировав секцию из Hopper на вкладке Find в Hex Fiend) и изменю ее вручную. Будьте осторожны, вы можете все сломать, поэтому я бы рекомендовал скопировать оригинальный исполняемый файл куда-нибудь.

После этого запустите игру, зайдите в настройки... И ура, вы можете выбрать "High". Но нельзя выбрать "средний". И вы не можете использовать высокие настройки теней. Но все равно, прогресс!


Посмотрите на эти великолепные отражения в воде. Просто посмотрите на них. ПОСМОТРИ НА НИХ.
 
Арбитр
Продавец
Статус
Оффлайн
Регистрация
13 Июл 2018
Сообщения
1,528
Реакции[?]
1,637
Поинты[?]
280K
Легенда форума
Статус
Оффлайн
Регистрация
10 Дек 2018
Сообщения
4,380
Реакции[?]
2,284
Поинты[?]
189K
Название заинтриговало, подумал, будет описан какой-то способ проапгрейдить графику старых игр, но вышло совсем не это)
хорошая работа энивей
 
Эксперт
Статус
Оффлайн
Регистрация
10 Фев 2021
Сообщения
1,740
Реакции[?]
559
Поинты[?]
2K
Зачем я потратил уйму времени на прочтение этого всего?
- хз по приколу
А так узнал для себя много чего нового, спасибо
 
Сверху Снизу