Навигация по сайту
Случайная игра

Вступай!!!
Облако тегов
Поиск читов


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

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

Подразумевается, что человек, читающий этот документ, уже знает команды ассемблера M6502, понимает, как работают программы вообще и Famicom в частности. Для работы потребуются всего две основные программы: собственно эмулятор — обычный FCEU с родным дебаггером и просмотрщиком памяти, а также IDA pro с модулем дизассемблера M6502. Хочу заметить, что первая необходима при слепом поиске, а вторая — при поиске от известных данных. Хотя одна очень хорошо дополняет другую и чаще всего используются вместе.

Итак, начнем, пожалуй, не с самого элементарного, но с самого распространенного вида секретов: с кодов, набираемых на кнопках джойстика. Срабатывают эти секреты при нажатии на определенные комбинации кнопок джойстика или их последовательности. Предположим, что нам ничего не известно о существовании данных читов в игре и в каком месте программы они срабатывают. Будем действовать универсальными методами, которые могут пригодиться не только при поиске подобных читов, но и в ромхаке вообще. Задача предельно ясна — найти точку программы, где происходит проверка на предмет нажатия секретных комбинаций кнопок и последующая обработка, для этого нужно сначала найти главную процедуру опроса джойстика.

Видов подобных процедур опроса джойстика бывает множество, но большая их часть работает по одному принципу: считанное из порта число бит за битом записывается во временный буфер, после окончания восьми циклов чтения настает время обработки полученного результата. Чаще всего новое число сравнивается со старым значением, откуда делается вывод о том, нажата ли текущая кнопка только что или же удерживается с прошлого опроса джойстика. Отсюда чаще всего образуются пары байтов для каждого джойстика: первый некоторое время (как правило, до следующего опроса) содержит данные о последней нажатой кнопке, второй — данные о всех нажатых или удерживаемых к данному времени кнопках. Для срабатывания чита использоваться могут как первые, так и вторые для обоих джойстиков.

Начнем с места в карьер: запускаем эмулятор, грузим игру, входим в отладчик, и для начала ставим точку останова на адрес $4016 (порт первого джойстика). Сразу попадаем в процедуру опроса — что и требовалось, точку останова можно убрать, она свое отработала. Сработала она в той части процедуры опроса, где производится непосредственное считывание байта данных из порта. Это цикл на 8 итераций. Как таковой он нам не нужен, потому что переменные, которые он использует, чаще всего являются просто временными. Всё, что нам нужно сделать, это дождаться выхода из этого цикла в основную процедуру опроса. Нам нужен самый ее конец, непосредственно перед командой RET. Именно там происходит окончательное присвоение переменным значений. По этим командам находятся необходимые нам ячейки памяти для хранения информации о нажатых кнопках. Теперь нужно просто поставить точки останова на чтение этих ячеек и найти ветку кода, который исполняется при совпадении нажатых комбинаций с заданными. Чтобы не попадать каждый раз в процедуру опроса, можно поставить точку останова на самой последней ее команде и только после ее срабатывания ставить останов на искомые ячейки. Если после нескольких трассировок точка останова так и не сработала где-либо вне процедуры опроса, следует проверить, не является ли процедура опроса одновременно функцией, возвращающей значения нажатых кнопок не только в заданных ячейках, но и в регистрах. Сравнение может происходить и непосредственно с регистровыми значениями, так что все наши точки останова на чтения памяти будут бесполезны. Некоторые программы могут производить проверку на наличие нажатий вообще, и лишь потом делать какие-то иные сравнения, либо проверять нажатые комбинации кнопок только после нажатия кнопки Start, так что это не даст нам сразу куска кода, где происходит проверка. В таком случае точка останова ставится на команду за подобной проверкой и после ее срабатывания от любого нажатия можно добавить точки останова на искомые ячейки, если это еще будет требоваться.

Для наглядности возьмем игрушку “Castlevania III — Dracula’s Curse”, а точнее ее японский вариант “Akumajou Densetsu”. Забегая вперед скажу, что в обеих играх чит работает одинаково, с той лишь разницей, что в английской он выглядит частично инородным телом, так как при выводе портит титульный экран, чего, в силу расположения текста, в японской версии не происходит. Но для этой игры чит уже известен! — скажете вы. Да, известен, но информация о нем несколько отличается от источника к источнику, что говорит о его неточности. Попробуем найти, как же на самом деле должен включаться звуковой тест в этой игре. Запускаем, идем в отладчик, ставим точку останова на 0х4016 и нажимаем кнопку сброса, чтобы начать проверку с самого начала работы программы, с процедуры инициализации. Очень часто тот или иной чит включается именно в это время, именно для этого и нужно, зажав некую комбинацию кнопок, сбросить приставку. Подобные читы просто-напросто не срабатывают нигде больше в коде программы. Сбросив, запускаем отладку и вываливаемся в недра процедуры опроса джойстиков.


  E29B: A0 01     LDY     #$01; сброс джойсика
  E29D: 8C 16 40  STY     $4016
  E2A0: 88        DEY
  E2A1: 8C 16 40  STY     $4016
  E2A4: A0 08     LDY     #$08; цикл на 8 итераций
> E2A6: AD 16 40  LDA     $4016
  E2A9: 85 04     STA     $04
  E2AB: 4A        LSR     A
  E2AC: 05 04     ORA     $04
  E2AE: 4A        LSR     A    ; выделение младшего бита
  E2AF: 36 00     ROL     $00,X; запись полученного бита со сдвигом влево, что
                              ; означает, что кнопке A будет соответствовать
                              ; 0x80, а кнопке Right — 0x01.
  E2B1: AD 17 40  LDA     $4017; то же самое для второго джойстика
  E2B4: 85 05     STA     $05
  E2B6: 4A        LSR     A
  E2B7: 05 05     ORA     $05
  E2B9: 4A        LSR     A
  E2BA: 36 01     ROL     $01,X
  E2BC: 88        DEY
  E2BD: D0 E7     BNE     $E2A6
  E2BF: 60        RTS


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

E264: A2 00     LDX     #$0
  E266: 20 9B E2  JSR     $E29B
> E269: A2 02     LDX     #$2
  E26B: 20 9B E2  JSR     $E29B
  E26E: A5 00     LDA     $00
  E270: C5 02     CMP     $02
  E272: D0 1C     BNE     $E290
  E274: A5 01     LDA     $01
  E276: C5 03     CMP     $03
  E278: D0 16     BNE     $E290
  E27A: A2 00     LDX     #$0
  E27C: 20 80 E2  JSR     $E280; штука, чтобы избежать циклов и счетчиков, если
                              ; нужны всего две итерации. Вместо зацикливания
                              ; куска кода он вызывается как подпрограмма, а
                              ; потом сам получает управление еще раз и теперь
                              ; RET возвращает управление в основную программу.
  E27F: E8        INX
  E280: B5 00     LDA     $00,X
  E282: A8        TAY
  E283: 55 FA     EOR     $FA,X
  E285: 35 00     AND     $00,X
  E287: 95 28     STA     $28,X
  E289: 95 F8     STA     $F8,X
  E28B: 94 2A     STY     $2A,X
  E28D: 94 FA     STY     $FA,X
  E28F: 60        RTS
  E290: A9 00     LDA     #$00
  E292: 85 28     STA     $28
  E294: 85 F8     STA     $F8
  E296: 85 29     STA     $29
  E298: 85 F9     STA     $F9
  E29A: 60        RTS


Сразу видно, что опрос джойстика производится дважды, а полученные данные затем сравниваются между собой. Дальнейшая обработка происходит только в том случае, когда значения двух поочередных опросов равны. Делается это для того, чтобы определить момент отпускания ранее нажатой кнопки джойстика или нажатия новой и очистить соответствующую ячейку памяти. В нашем случае адреса таких ячеек для первого джойстика 0х28 и 0хF8, а для второго — 0x29 и 0xF9. Кроме того, оставшийся код очищает те биты в указанных ячейках, которые присутствовали там после предыдущего вызова данной функции. Всё вместе это дает переменную, которая хранит информацию о кратковременном нажатии одной кнопки или их комбинации. Она устанавливается в какое-либо значение и очищается уже при следующем опросе джойстика. Наряду с вышеописанными переменными, хранению подлежит информация и о постоянно зажатых на настоящий момент кнопках. Хранится она соответственно в ячейках 0x2A, 0xFA для первого и 0x2B, 0xFB для второго джойстиков.

Вот и всё, мы знаем 8 ячеек памяти, где хранится вся информация о нажатых кнопках. Она же, соответственно, и проверяется на наличие читовых комбинаций. Ставим точки останова на эти ячейки и прогоняем программу до тех пор, пока она не остановится где-нибудь вне процедуры опроса.

E397: A9 01     LDA     #$01
  E399: 20 D0 E2  JSR     $E2D0
  E39C: 20 D5 A9  JSR     $A9D5
> E39F: A5 F8     LDA     $F8
  E3A1: 29 30     AND     #$30
  E3A3: F0 02     BEQ     $E3A7
  E3A5: E6 19     INC     $19
  E3A7: 60        RTS


Это единственное место в процедуре инициализации, где сработала точка останова. Это проверка на нажатие любой из кнопок Start или Select, которое приводит к пропуску заставочной анимации и переходу к титульному экрану. Значит здесь нам ловить нечего. Идем на титул, делаем точно такие же операции по отлову нажатий.

E3DD: A9 0F     LDA     #$0F
  E3DF: 4C 58 E5  JMP     $E558; запуск звукового теста
  ...
  E3EA: A9 00     LDA     #$00
  E3EC: 20 D0 E2  JSR     $E2D0
  E3EF: 20 8A 8A  JSR     $8A8A
> E3F2: A5 F8     LDA     $F8
  E3F4: 29 20     AND     #$20; нажата ли кнопка Select?
  E3F6: F0 0B     BEQ     $E403; нет
  E3F8: A9 74     LDA     #$74
  E3FA: 20 49 E2  JSR     $E249
  E3FD: A5 68     LDA     $68
  E3FF: 49 01     EOR     #$01
  E401: 85 68     STA     $68
  E403: A5 F8     LDA     $F8
  E405: 29 10     AND     #$10; нажата ли кнопка Start?
  E407: F0 0C     BEQ     $E415; нет
  E409: A5 2A     LDA     $2A
  E40B: 29 C0     AND     #$C0; зажаты ли к моменту нажатия Start кнопки A и B
                              ; вместе или по отдельности?
  E40D: D0 CE     BNE     $E3DD; да
  E40F: A9 80     LDA     #$80
  E411: 85 32     STA     $32
  E413: E6 19     INC     $19
  E415: 60        RTS


Ну вот и все, код найден с точностью до операции. Для включения режима теста звука необходимо на титульном экране зажать на первом джойстике A или B и нажать Start.

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

Автор статьи: CAH4E3
Журнал: Game Bit



Вернуться
  • Комментарий: 0
  • Просмотров: 4252

Комментарии:

Оставить комментарий