[Ненормальное программирование, Assembler, Игры и игровые приставки] Перепрограммирование GameBoy за счёт бага в Pokemon Yellow (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Pokemon Yellow - это карманная вселенная со своими правилами. В ней можно покупать и продавать предметы, тренировать покемонов, побеждать других тренеров — но нельзя менять правила самой игры. Нельзя построить себе дом, поменять музыку или даже переодеться. Точнее, так было задумано. На самом деле есть последовательность валидных команд (типа перемещения из одного места в другое и манипуляций с предметами), которая позволяет превратить игру в Pacman, тетрис, Pong, MIDI-проигрыватель и что угодно ещё.Существует спидран Felipe Lopes de Freitas (p4wn3r), в котором Pokemon Yellow проходится за 1 минуту 36 секунд. Этот спидран основан на следующем хаке: в норме инвентарь игрока ограничен 20 предметами. Но есть баг, который позволяет игнорировать это ограничение и обращаться с памятью, расположенной сразу после инвентаря, как будто бы это был список предметов. Соответственно, стандартные манипуляции с предметами позволяют эту память переписывать. Спидраннер использует эту возможность, чтобы заставить дверь из стартовой комнаты переносить его на финальную локацию, в которой ему остаётся только выслушать поздравления.Когда я впервые увидел этот спидран и понял, что памятью Gameboy можно манипулировать с помощью одного только списка предметов, безо всяких внешних инструментов, я решил посмотреть, получится ли у меня усовершенствовать приёмы p4wn3r. Вот что получилось:Извините, данный ресурс не поддреживается. :( Gameboy — это восьмибитный компьютер. Соответственно, всё происходящее в игре — результат того, что исполняется некая последовательность однобайтовых команд. Например, последовательность[62 16 37 224 47 240 37 230 15 55] в местных машинных кодах означает, что надо выяснить, какие кнопки нажаты в данный момент, и записать результат в регистр A. Можно создать программу, которая будет читать ввод с кнопок и записывать куда-нибудь в память исполняемый код, а потом передавать ему управление. Если удастся каким-то образом засунуть такую программу (назовём её программой ввода) в память и заставить Gameboy её исполнить — то я выиграл, потому что теперь я могу исполнять произвольный код (скажем, тетрис) вместо Pokemon Yellow. Во-первых, как написать такую программу? Восьмибитными числами представлены не только правила, но и состояние игры (инвентарь, список покемонов, имя главного героя и т.п.) Инвентарь хранится вот так:
item-one-id (0-255)
item-one-quantity (0-255)
item-two-id (0-255)
item-two-quantity (0-255)
.
.
.
Упомянутая выше подпрограмма для чтения кнопок хранится в той же последовательности битов, что и следующий набор предметов:
lemonade x16
guard spec. x224
leaf stone x240
guard spec. x230
parlyz heal x55
Так что если нам удастся собрать нужные предметы в нужном количестве и порядке — то получится код программы ввода. Правда, его с самого начала нужно писать так, чтобы он состоял только из валидных ID и количеств предметов. Это не так-то просто, потому что предметов в игре немного, а многие машинные инструкции состоят из 2-3 битов. Та версия программы, на которой я остановился, состоит из 92 бит; примерно половина из них не делает ничего полезного и вставлена только для того, чтоб код был ещё и корректным списком предметов.Записать код в память — это только полдела, потому что надо ещё заставить игру его исполнить. К счастью для нас, недалеко от инвентаря есть указатель на функцию, которую игра регулярно вызывает, чтобы обсчитывать эффект отравления и прочие штуки, срабатывающие по таймеру. Без него ничего бы не получилось, а с ним мы можем исполнить код по произвольному адресу, положив нужные предметы в соответствующую ячейку инвентаря.Поехали!Я начинаю с того, что называю своего противника Lp/k. Эти символы в своё время превратятся в предметы и будут перемещены на указатель функции, чтобы я мог исполнить программу ввода. Но сперва её надо записать, так что я начинаю забег так же, как и p4wn3r: перезапускаю игру при сохранении, чтобы сломать список покемонов. После этого я меняю местами 8 и 10 покемона, что ломает список предметов и позволяет мне манипулировать предметами после 20-го (то есть соответствующей областью памяти). С помощью этих манипуляций я выставляю скорость текста на максимум и переключаю свою дверь на магазин Celadon Dept. store. p4wn3r в этот момент переключил её на “Зал славы” и выиграл, но мне неинтересен спидран как таковой.Поэтому я не останавливаюсь, а выкладываю из своего поломанного инвентаря в сундук множество глитчевых предметов 0x00, которые мне ещё пригодятся. Потом я забираю из сундука зелье, что вызывает переполнение счётчика предметов с 0xFF на 0x00 и восстанавливает мой инвентарь. Зелье при этом, правда, исчезает. После этого я забираю 255 0x00-предметов и отправляюсь в магазин. Там я продаю 2 0x00 за 414925 монет каждый, что позволяет мне купить следующие предметы:
+-------------------+----------+
|##| Item | Quantity |
+--+----------------+----------+
|1 | TM02 | 98 |
|2 | TM37 | 71 |
|3 | TM05 | 1 |
|4 | TM09 | 1 |
|5 | burn-heal | 12 |
|6 | ice-heal | 55 |
|7 | parlyz-heal | 99 |
|8 | parlyz-heal | 55 |
|9 | TM18 | 1 |
|10| fire-stone | 23 |
|11| water-stone | 29 |
|12| x-accuracy | 58 |
|13| guard-spec | 99 |
|14| guard-spec | 24 |
|15| lemonade | 16 |
|16| TM13 | 1 |
+--+----------------+----------+
Эти предметы я раскладываю в инвентаре в таком порядке, чтобы получилась моя первая программа ввода. Первая — потому что написать окончательную версию программы ввода внутри игры у меня не получилось. Та, которую я собираю из предметов, умеет читать только кнопки A, B, start и select. Каждый фрейм игры она пишет 4 бита в определённую область памяти; собрав 200 с лишним байт, она исполняет то, что написала. С помощью этой программы я пишу следующую, которая читает уже все 8 кнопок и пишет произвольное число байтов в произвольную область памяти, а потом исполняет код по произвольному адресу. Эта программа, в свою очередь, используется для загрузки третьей, которая делает всё то же самое, но ещё и выводит записываемый код на экран.Закончив с кодом первой программы, я отправляюсь в Celadon mansion (это не критично, мне просто нравится локация). Там я снова меняю местами сломанных покемонов и ломаю инвентарь. Проскроллив до имени своего противника, я выкидываю предметы до тех пор, пока имя не окажется на указателе функции. Как только я закрываю меню, программа ввода исполняется, и я могу заливать следующую программу.ИнфраструктураВсё видео было записано ботами, а сам я к Gameboy (точнее, к эмулятору) даже не притрагивался. Ниже — краткое описание инфраструктуры, которую я создал для этого проекта. Исходники доступны в http://hg.bortreb.com/vba-clojureПрежде всего, мне нужен был программный доступ к эмулятору, так что я скачал vba-rerecording. Поверх него я написал низкоуровневый интерфейс на C, к которому я смогу обращаться из Java через JNI. Этот интерфейс позволяет обращаться к базовым функциям эмулятора: прогнать один фрейм игры или один тик часов Gameboy, обратиться к любому адресу памяти или регистру. Кроме того, он позволяет выгрузить состояние эмулятора в объект Java или загрузить его обратно.Поверх JNI я написал обёртку на clojure, так что в итоге у меня получился функциональный интерфейс к vba-rerecording. Этот интерфейс работает с состоянием эмулятора как с иммутабельным объектом и позволяет делать в функциональном стиле всё то, что интерфейс на C позволял делать в императивном. С помощью этого интерфейса я написал функции, которые принимают на вход состояние эмулятора, находят и исполняют последовательность команд, которая приведёт к желаемому эффекту. Из этих функций я собрал высокоуровневые функции, которые выполняют в игре задачи типа перемещения и покупки предметов. Вот, например, код для перемещения кратчайшим путём из магазина покемонов в Viridian City в лабораторию Оака:
(defn-memo viridian-store->oaks-lab
([] (viridian-store->oaks-lab
(get-oaks-parcel)))
([script]
(->> script
(walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
← ← ← ← ← ← ← ← ←
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
← ←
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
↓ ↓ ↓ ↓ ↓ ↓ ↓
→ → → → → → → →
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
← ← ← ← ←
↓ ↓ ↓ ↓
])
(walk-thru-grass
[↓ ↓ ↓ ↓ ↓ ↓ ↓])
(walk [↓ ↓ ← ↓ ↓ ↓ ←
↓ ↓ ↓ ↓ ↓ ↓
→ → → ↑])
(do-nothing 1))))
А вот функция, которая находит кратчайшую последовательность команд для переноса нужных предметов в сундук:
(defn-memo hacking-10
([] (hacking-10 (hacking-9)))
([script]
(->> script
begin-deposit
(deposit-held-item 17 230)
(deposit-held-item-named :parlyz-heal 55)
(deposit-held-item 14 178)
(deposit-held-item-named :water-stone 29)
(deposit-held-item 14 32)
(deposit-held-item-named :TM18 1)
(deposit-held-item 13 1)
(deposit-held-item 13 191)
(deposit-held-item-named :TM02 98)
(deposit-held-item-named :TM09 1)
close-menu)))
Вооружившись этими инструментами, я составил программу ввода, которая может быть представлена в инвентаре. Это было непросто, потому что многие полезные коды не соответствуют никакому предмету, а количество предметов не может быть больше 99. Итак, вот что у меня получилось:Программа ввода(defn pc-item-writer-program [] (let [;;limit 75 limit 201 ;; (item-hack 201 is the smallest I could make this.) [target-high target-low] (disect-bytes-2 pokemon-list-start)] (flatten [[0x00 ;; (item-hack) no-op (can't buy repel (1E) at celadon) 0x1E ;; load limit into E limit 0x3F ;; (item-hack) set carry flag no-op ;; load 2 into C. 0x0E ;; C == 1 means input-first nybble 0x04 ;; C == 0 means input-second nybble 0x21 ;; load target into HL target-low target-high 0x37 ;; (item-hack) set carry flag no-op 0x00 ;; (item-hack) no-op 0x37 ;; (item-hack) set carry flag no-op 0x00 ;; (item-hack) no-op 0xF3 ;; disable interrupts ;; Input Section 0x3E ;; load 0x20 into A, to measure buttons 0x10 0x00 ;; (item-hack) no-op 0xE0 ;; load A into [FF00] 0x00 0xF0 ;; load 0xFF00 into A to get 0x00 ;; button presses 0xE6 0x0F ;; select bottom four bits of A 0x37 ;; (item-hack) set carry flag no-op 0x00 ;; (item-hack) no-op 0xB8 ;; see if input is different (CP A B) 0x00 ;; (item-hack) (INC SP) 0x28 ;; repeat above steps if input is not different ;; (jump relative backwards if B != A) 0xED ;; (literal -19) (item-hack) -19 == egg bomb (TM37) 0x47 ;; load A into B 0x0D ;; dec C 0x37 ;; (item-hack) set-carry flag ;; branch based on C: 0x20 ;; JR NZ 23 ;; skip "input second nybble" and "jump to target" below ;; input second nybble 0x0C ;; inc C 0x0C ;; inc C 0x00 ;; (item-hack) no-op 0xE6 ;; select bottom bits 0x0F 0x37 ;; (item-hack) set-carry flag no-op 0x00 ;; (item-hack) no-op 0xB2 ;; (OR A D) -> A 0x22 ;; (do (A -> (HL)) (INC HL)) 0x1D ;; (DEC E) 0x00 ;; (item-hack) 0x20 ;; jump back to input section if not done 0xDA ;; literal -36 == TM 18 (counter) 0x01 ;; (item-hack) set BC to literal (no-op) ;; jump to target 0x00 ;; (item-hack) these two bytes can be anything. 0x01 0x00 ;; (item-hack) no-op 0xBF ;; (CP A A) ensures Z 0xCA ;; (item-hack) jump if Z target-low target-high 0x01 ;; (item-hack) will never be reached. ;; input first nybble 0x00 0xCB 0x37 ;; swap nybbles on A 0x57 ;; A -> D 0x37 ;; (item-hack) set carry flag no-op 0x18 ;; relative jump backwards 0xCD ;; literal -51 == TM05; go back to input section 0x01 ;; (item-hack) will never reach this instruction ] (repeat 8 [0x00 0x01]);; these can be anything [;; jump to actual program 0x00 0x37 ;; (item-hack) set carry flag no-op 0x2E ;; 0x3A -> L 0x3A 0x00 ;; (item-hack) no-op 0x26 ;; 0xD5 -> L 0xD5 0x01 ;; (item-hack) set-carry BC 0x00 ;; (item-hack) these can be anything 0x01 0x00 0xE9 ;; jump to (HL)Мне особенно пригодились глитч-предметы 0x00 и 0xFF. 0x00 стоит почти половину максимально возможного числа денег, и всего двух хватило на то, чтоб купить все необходимые предметы. К тому же 0x00 — это NO-OP, который я могу вставлять куда угодно, чтобы подогнать программу под требования инвентаря. 0xFF имеет другое полезное свойство. В норме игра объединяет стеки предметов: если купить, например, покеболл, а потом ещё один покеболл, то инвентарь будет выглядеть вот так:
pokeball x2
Но если где-то перед тем в списке предметов есть 0xFF, то эта функция отключается, что позволяет мне получить вот такой инвентарь:
pokeball x1
pokeball x1
pokeball x1
Так что я вставляю в самое начало инвентаря 0xFF и больше об этом не беспокоюсь. Полезная нагрузка, которую я залил в Gameboy, — это тоже последовательность программ. Я создал упрощённый формат MIDI, имплементировал его в машинных кодах GameBoy, и перевёл в свой формат мелодию с http://www.everyponysings.com/. В полезной нагрузке последней программы ввода содержится как сам MIDI-файл, так и интерпретатор. Картинка устроена так же — я перевёл PNG-файл в Gameboy-совместимый формат и добавил код, который её показывает. И картинку, и мелодию загружает последняя программа ввода, так что исходники можно увидеть на экране эмулятора (или скачать с http://hg.bortreb.com/vba-clojure).
===========
Источник:
habr.com
===========
===========
Автор оригинала: Robert McIntyre
===========Похожие новости:
- [Программирование микроконтроллеров, Гаджеты, Интернет вещей, DIY или Сделай сам, Игры и игровые приставки] Рождение Гуся — как создаётся умный стрелковый тренажёр
- [Лайфхаки для гиков, Здоровье] Что такое компьютерный зрительный синдром и как от него защититься?
- [Законодательство в IT, Копирайт] Насколько реально противостоять шантажу копирастов?
- [Управление e-commerce, Компьютерное железо, Лайфхаки для гиков, Криптовалюты] Chia-шиза добралась до России
- [Программирование микроконтроллеров, Производство и разработка электроники, Компьютерное железо] Новые цены на микроконтроллеры STM32 из свежих поставок
- [Программирование, Читальный зал, Лайфхаки для гиков, Научная фантастика] Программирование и писательство (перевод)
- [Научно-популярное, Инженерные системы] Доктора в синем небе
- [GTD, Читальный зал, Лайфхаки для гиков] Аарон Шварц: как стать продуктивнее (перевод)
- [Читальный зал, Дизайн, Научно-популярное, Лайфхаки для гиков] Как делают стул, который служит 150 лет и выдерживает попадание торпеды (перевод)
- [JavaScript, Космонавтика, Лайфхаки для гиков] Отслеживание и визуализация положения МКС с помощью 30 строк JavaScript-кода (перевод)
Теги для поиска: #_nenormalnoe_programmirovanie (Ненормальное программирование), #_assembler, #_igry_i_igrovye_pristavki (Игры и игровые приставки), #_gameboy, #_speedrun, #_pokemon, #_haki (хаки), #_blog_kompanii_timeweb (
Блог компании Timeweb
), #_nenormalnoe_programmirovanie (
Ненормальное программирование
), #_assembler, #_igry_i_igrovye_pristavki (
Игры и игровые приставки
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:44
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Pokemon Yellow - это карманная вселенная со своими правилами. В ней можно покупать и продавать предметы, тренировать покемонов, побеждать других тренеров — но нельзя менять правила самой игры. Нельзя построить себе дом, поменять музыку или даже переодеться. Точнее, так было задумано. На самом деле есть последовательность валидных команд (типа перемещения из одного места в другое и манипуляций с предметами), которая позволяет превратить игру в Pacman, тетрис, Pong, MIDI-проигрыватель и что угодно ещё.Существует спидран Felipe Lopes de Freitas (p4wn3r), в котором Pokemon Yellow проходится за 1 минуту 36 секунд. Этот спидран основан на следующем хаке: в норме инвентарь игрока ограничен 20 предметами. Но есть баг, который позволяет игнорировать это ограничение и обращаться с памятью, расположенной сразу после инвентаря, как будто бы это был список предметов. Соответственно, стандартные манипуляции с предметами позволяют эту память переписывать. Спидраннер использует эту возможность, чтобы заставить дверь из стартовой комнаты переносить его на финальную локацию, в которой ему остаётся только выслушать поздравления.Когда я впервые увидел этот спидран и понял, что памятью Gameboy можно манипулировать с помощью одного только списка предметов, безо всяких внешних инструментов, я решил посмотреть, получится ли у меня усовершенствовать приёмы p4wn3r. Вот что получилось:Извините, данный ресурс не поддреживается. :( Gameboy — это восьмибитный компьютер. Соответственно, всё происходящее в игре — результат того, что исполняется некая последовательность однобайтовых команд. Например, последовательность[62 16 37 224 47 240 37 230 15 55] в местных машинных кодах означает, что надо выяснить, какие кнопки нажаты в данный момент, и записать результат в регистр A. Можно создать программу, которая будет читать ввод с кнопок и записывать куда-нибудь в память исполняемый код, а потом передавать ему управление. Если удастся каким-то образом засунуть такую программу (назовём её программой ввода) в память и заставить Gameboy её исполнить — то я выиграл, потому что теперь я могу исполнять произвольный код (скажем, тетрис) вместо Pokemon Yellow. Во-первых, как написать такую программу? Восьмибитными числами представлены не только правила, но и состояние игры (инвентарь, список покемонов, имя главного героя и т.п.) Инвентарь хранится вот так: item-one-id (0-255)
item-one-quantity (0-255) item-two-id (0-255) item-two-quantity (0-255) . . . lemonade x16
guard spec. x224 leaf stone x240 guard spec. x230 parlyz heal x55 +-------------------+----------+
|##| Item | Quantity | +--+----------------+----------+ |1 | TM02 | 98 | |2 | TM37 | 71 | |3 | TM05 | 1 | |4 | TM09 | 1 | |5 | burn-heal | 12 | |6 | ice-heal | 55 | |7 | parlyz-heal | 99 | |8 | parlyz-heal | 55 | |9 | TM18 | 1 | |10| fire-stone | 23 | |11| water-stone | 29 | |12| x-accuracy | 58 | |13| guard-spec | 99 | |14| guard-spec | 24 | |15| lemonade | 16 | |16| TM13 | 1 | +--+----------------+----------+ (defn-memo viridian-store->oaks-lab
([] (viridian-store->oaks-lab (get-oaks-parcel))) ([script] (->> script (walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ← ← ← ← ← ← ← ← ← ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ← ← ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ → → → → → → → → ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ← ← ← ← ← ↓ ↓ ↓ ↓ ]) (walk-thru-grass [↓ ↓ ↓ ↓ ↓ ↓ ↓]) (walk [↓ ↓ ← ↓ ↓ ↓ ← ↓ ↓ ↓ ↓ ↓ ↓ → → → ↑]) (do-nothing 1)))) (defn-memo hacking-10
([] (hacking-10 (hacking-9))) ([script] (->> script begin-deposit (deposit-held-item 17 230) (deposit-held-item-named :parlyz-heal 55) (deposit-held-item 14 178) (deposit-held-item-named :water-stone 29) (deposit-held-item 14 32) (deposit-held-item-named :TM18 1) (deposit-held-item 13 1) (deposit-held-item 13 191) (deposit-held-item-named :TM02 98) (deposit-held-item-named :TM09 1) close-menu))) pokeball x2
pokeball x1
pokeball x1 pokeball x1 =========== Источник: habr.com =========== =========== Автор оригинала: Robert McIntyre ===========Похожие новости:
Блог компании Timeweb ), #_nenormalnoe_programmirovanie ( Ненормальное программирование ), #_assembler, #_igry_i_igrovye_pristavki ( Игры и игровые приставки ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:44
Часовой пояс: UTC + 5