[Patch] Alone In The Dark - The New Nightmare

В конце девяностых и до середины нулевых разработчики частенько выпускали демоверсии своих игр, дабы привлечь внимание потенциального покупателя. 
Демки могли представлять из себя обрезанную версию или же версию, которая находится в стадии разработки и просто отдана "как есть", дабы была возможность оценить игровой процесс.
Это все прекрасно и замечательно, но не все так гладко.
Порой случается так, что демоверсия прекрасно работает на любой ОС, а полная версия отказывается запускаться или же содержит в своем коде ряд ошибок, которые приводят к ее частичной неработоспособности.
Об этом и пойдет речь.

Итак, встречаем: 
Alone In The Dark - The New Nightmare (Она же AITD4, Alone In The Dark 4).

Запустим демоверсию:



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



   С меню, внутриигровым рендером и инвентарем все замечательно:


А если мы запустим полную версию ? 
А вот, что будет:







Вполне очевидно, что беда с рендерингом. 
Я битый час ломал голову, как вообще такое возможно, чтобы демка работала корректно, а полная версия нет. 
Игра использует функции OpenGL для отрисовки меню, игрового окружения, инвентаря. 
Я использовал апи мониторы, проверял, какие результаты дают те или иные графические функции, но самое смешное в том, что они давали идентичные результаты в обоих случаях. 
Я решил, что не стоит копаться в этом, и пойти другим путем - исправить демку. Первым своим планом я хотел сделать так, чтобы демка стала разблокированной.  Моей главной функцией в данном случае стала небезызвестная CreateFileA
Я использовал ее для условного логирования файлов, к которым обращается игра при запуске, при отрисовке меню, при открытии инвентаря, при переходе на локацию, во время ведения заскриптованных диалогов и, конечно же, при взятии каких-то особых предметов 
(ключи, таблички и т.д.). 
Пройдя небольшую часть игры, я столкнулся с тем, что она завершается 
(происходит вызов ExitProcess), после того, как я беру определенный ключ. 
Загрузил сейв в полной версии - все нормально и ключ добавляется в инвентарь.
Поставил бряки на все вызовы ExitProcess в коде, поднялся выше и попал вот в такое место:


 Все банально и просто - проверка на константу.
Пропускаем сие действо.


Хорошо. 
С этим разобрались и игра дальше нормально работает. 
А как я получил идентификатор уровня ? 
Да очень просто.
В игре есть большое кол-во строчных констант, которые помогают в навигации по коду.




Момент поиска адреса, содержащего идентификатор локации, я опущу, так как он ничем не отличается от поиска в том же CheatEngine.
Да, динамическая память, да указатели.
Я поставил бряк на доступ по нужному мне адресу в памяти и набрел на такой код:




И опять константы. 
Первое условие проверяет то, какой мы локации достигли, и встречает нас нелестным сообщением, но мы его не увидем, так как CALL 00486575 в конечном итоге приведет нас к очередному вызову ExitProcess
Предназначение второй проверки я так и не понял, так как не встречал ничего с таким идентификатором (99999), но учитывая то, что результат ее работы приводит к аналогичному результату, то делаем безусловный переход.

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

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




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




Идентификатор локации = 21(h) = 33(dec)
Идентификатор же локации, в которой Эдвард приземляется, = 1e(h) = 30(dec).
Замена 21 на 1e позволит нам начать новую игру с нужного места.



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

В корне демоверсии игры можно заметить файлы aweap.csv/cweap.csv и папки MAPA/MAPC, что явно указывает на возможность выбора персонажа.
  

Так же это подтверждает наличие файла select2 в папке menu.




 И в коде игры.




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





На вид два схожих блока кода. 
Предположим, что первый вызывает основное меню, а второй - меню выбора персонажа.
Меня интересует CALL 0047f308 по адресу 00480ae5:




Обратим внимание на проверку по адресу 0047f323.
Дело все в том, что даже во время нахождения в главном меню, по адресу 00502a6c считываются и записываются данные. 
Он определяет состояние меню, которое было вызвано, поэтому переход должен быть выполнен. 
Если он выполнен, то мы попадаем сюда:



CALL 0047f523 приводит нас к тому, что нам и нужно:



А как обстоят дела с демоверсией ?
Видем очень схожий код:




 Пролистываем вниз:




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



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




Нет кода - нет вызова. Будем это исправлять.
В качестве места для патча я использую конец секции кода. 
Я просто скопирую нужный мне блок кода из полной версии и скорректирую его для демо. Выглядят патчи следующим образом:


 
Теперь решим воспрос с начальной локацией.
Снова обратимся к коду полной версии:




Сделаем так, чтобы она выбиралась в зависимости от персонажа, а не была жестко задана в коде по идентификатору:




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


Теперь мы можем спокойно играть за Алину.




Комментарии

Популярные сообщения из этого блога

Steam и фриварная японская говногама [Часть 2].

Mafia 3 Savegame Editor