Mercurial > vba-clojure
view org/total-control.org @ 609:65b7c5b47de1
first draft of explination.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Thu, 22 Nov 2012 10:54:59 -0600 |
parents | 2c348cc68bac |
children | 4dd5ebf224cd |
line wrap: on
line source
1 #+title: Pokemon Yellow Total Control Hack2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: Taking over Pokemon Yellow from the inside.5 #+keywords: pokemon, pokemon yellow, rom, gameboy, assembly, hex, pointers, clojure6 #+SETUPFILE: ../../aurellem/org/setup.org7 #+INCLUDE: ../../aurellem/org/level-0.org8 #+BABEL: :exports both :noweb yes :cache no :mkdirp yes9 #+OPTIONS: num:212 Full Source : http://hg.bortreb.com/vba-clojure14 Youtube Video w/ Visual Keypresses: http://www.youtube.com/watch?v=p5T81yHkHtI16 Special Thanks to:18 - http://tasvideos.org/2913S.html for the save corruption hack which19 is used at the start of this run.20 - http://www.everyponysings.com/ for providing the midi file I used21 to create the song at the end.22 - http://www.zophar.net/ for the terminal font.25 * Introduction27 Think of pokemon yellow as creating a little universe with certain28 rules. Inside that universe, you can buy items, defeat rival trainers,29 and raise your pokemon. But within that universe, you are bound by the30 rules of pokemon. You can't build new buildings, or change the music,31 or change your clothes.. There are some games (like chess), where it32 is not possible to alter the rules of the game from within the33 game. No matter what moves you make in chess, you can never change the34 rules of the game so that it becomes checkers or basketball. The point35 of this run is to show that you CAN change the rules in pokemon36 yellow. There is a certain sequence of valid actions like walking from37 one place to another or buying items that will allow you to transform38 pokemon yellow into Pacman, or Tetris, or Pong, or a MIDI player, or39 anything else you can imagine.41 * Background43 [[http://tasvideos.org/2913S.html][This speedrun]] by Felipe Lopes de Freitas (p4wn3r), beats pokemon44 yellow in only 1 minute and 36 seconds. It does it by corrupting the45 in-game item list so that he can advance the list past its normal46 limit of 20 items. The memory immediately after the item list includes47 the warp points for the current map, and by treating that data as48 items and switching and dropping them, he can make the door from his49 house take him directly to the end of the game.51 When I first saw that speedrun, I was amazed at how fast pokemon52 yellow could be beaten, and that it was possible to manipulate the53 game from the inside, using only the item list. I wondeered how far I54 could extend the techniques found in p4wn3r's run.56 The gameboy is an 8 bit computer. That means that ultimately, anything57 that happens in pokemon is a result of the gameboy's CPU reading a58 stream of 8 bit numbers and doing whatever those numbers mean. For59 example, in the gameboy, the numbers:61 [62 16 37 224 47 240 37 230 15 55]63 mean to check which buttons are currently pressed and copy that result64 into the "A" register. With enough numbers, you can spell out an65 interactive program that reads input from the buttons and allows you66 to write any program you want to the gameboy. Once you have assembled67 such a program and forced the game to run it, you have won, since you68 can use that program to write any other program (like tetirs or69 pacman) over pokemon yellow's code. I call a program that allows you70 to write any other program a "bootstrapping program". So, the goal is71 to somehow get a bootstrapping program into pokemon yellow and then72 force yellow to run that program instead of its own.74 How can we spell out such a program? Everything in the game is75 ultimately nunbers, including all items, pokemon, levels, etc. In76 particular, the item list looks like:78 item-one-id (0-255)79 item-one-quantity (0-255)80 item-two-id (0-255)81 item-two-quantity (0-255)82 .83 .84 .86 Let's consider the button measuring program [37 62 16 37 224 37 24087 37 230 15 55] from before. Interpreted as items and item quantities, it is89 lemonade x1690 guard spec. x22491 leaf stone x24092 guard spec. x23093 parlyz heal x5595 So, if we can get the right items in the right quantities, we can96 spell out a bootstrapping program. Likewise, when writing the97 bootstrapping program, we must be careful to only use numbers that are98 also valid items and quantities. This is hard because there aren't99 many different items to work with, and many machine instructions100 actually take 2 or even 3 numbers in a row, which severely restricts101 the types of items you can use. I ended up needing about 92 numbers to102 implement a bootstrap program. Half of those numbers were elaborate103 ways of doing nothing and were just there so that the entire program104 was also a valid item list.106 The final part of the hack is getting pokemon yellow to execute the107 new program after it has been assembled with items. Fortunately,108 pokemon keeps a number called a function pointer within easy reach of109 the corrupted item list. This function pointer is the starting point110 (address) of a program which the game runs every so often to check for111 poison and do general maintaiance. By shifting an item over this112 function pointer, I can rewrite that address to point to the113 bootstrapping program, and make the game execute it. Without this114 function pointer, it would not be possible to take over the game.116 * The Run118 I start off and name my rival Lpk. These characters will eventually be119 treated as items and shifted over the function pointer, causing it to120 execute the bootstrapping program that will soon be constructed. I121 start the run the same as p4wn3r's and restart the game while saving,122 so that the pokemon list is corrupted. By switching the 8th and 10th123 pokemon, I corrupt the item list and can now scroll down past the 20th124 item. I shift items around to increase the text speed to maximum and125 rewrite the warp point of my house to Celadon Dept. Store. (p4wn3r126 used this to go directly to the hall of fame and win the game in his127 run.) I deposit many 0x00 glitch items into the PC from my corrupted128 inventory for later use. Then, I widthdraw the potion from the129 PC. This repairs my item list by overflowing the item counter from130 0xFF back to 0x00, though the potion is obliterated in the process. I131 then take 255 glitch items with ID 0x00 from the computer into my132 personal items.134 Leaving my house takes me directly to Celadon Dept. store, where I135 sell two 0x00 items for 414925 each, giving myself essentially max136 money. I hit every floor of the department store, gathering the137 following items:139 #+begin_example140 +-------------------+----------+141 |##| Item | Quantity |142 +--+----------------+----------+143 |1 | TM02 | 98 |144 |2 | TM37 | 71 |145 |3 | TM05 | 1 |146 |4 | TM09 | 1 |147 |5 | burn-heal | 12 |148 |6 | ice-heal | 55 |149 |7 | parlyz-heal | 99 |150 |8 | parlyz-heal | 55 |151 |9 | TM18 | 1 |152 |10| fire-stone | 23 |153 |11| water-stone | 29 |154 |12| x-accuracy | 58 |155 |13| guard-spec | 99 |156 |14| guard-spec | 24 |157 |15| lemonade | 16 |158 |16| TM13 | 1 |159 +--+----------------+----------+160 #+end_example162 After gathering these items, I deposit them in the appropriate order163 into the item PC to spell out my bootstrapping program. Writing a full164 bootstrap program in one go using only items turned out to be too165 hard, so I split the process up into three parts. The program that I166 actually construct using items is very limited. It reads only from the167 A, B, start, and select buttons, and writes 4 bits each frame to a168 fixed point in memory. After it writes 200 or so bytes, it jumps169 directly to what it just wrote. In my run, I use this program to write170 another bootstrapping program that can write to any number of bytes to171 any location in memory, and then jump to any location in memory. This172 new program also can write 8 bits per frame by using all the173 buttons. Using this new bootstrap program, I write a final174 bootstrapping program that does everything the provious bootstrapping175 program does except it also displays the bytes it is writing to memory176 on the screen.178 After completing this bootstrapping program, I go to the celadon179 mansion, because I find the metaness of that building to be180 sufficiently high to serve as an exit point for the pokemon181 universe. I corrupt my item list again by switching corrupted pokemon,182 scroll down to my rival's name and discard untill it is equal to the183 address of my bootstrapping program, and then swap it with the184 function pointer. Once the menu is closed, the boostrapping program185 takes over, and I write the payload....187 * Infrastructure189 The entire video was completely produced by bots --- I didn't manually190 play the game at all to produce this speedrun. Here is a brief account191 of the infrastructure I build to make the video. The entire source of192 the project is available at http://hg.bortreb.com/vba-clojure194 The first step was to build a programatic interface to pokemon195 yellow. So, I downloaded vba-rerecording from196 http://code.google.com/p/vba-rerecording/. After repairing their197 broken auto-tools scripts so that it would compile on GNU/Linux, I198 added a low level C interface that I could call from Java via199 JNI. This C interface gives me basic control over the emulator: I can200 step the emulator either one clock cycle or one frame, and I can get201 the contents of any memory location or register. The interface also202 allows me to freeze the state of the emulator, save it to a Java203 object, and reload that state again at any time.205 I built a layer of [[http://clojure.org/][clojure]] code on top of the JNI bindings to get an206 entirely functional interface to vba-rerecording. This interface207 treats state of the emulator as an immutable object, and allows me to208 do everything I could do with the lower level C interface in a209 functional manner. Using this functional code, I wrote search programs210 that take a particular game-state and try out different combinations211 of button prosses to get any desired effect. By combining different212 styles of search with different initial conditions, I created high213 level functions that could each accomplish a certain general task,214 like walking and buying items. For example, here is some actual code:216 #+begin_src clojure217 (defn-memo viridian-store->oaks-lab218 ([] (viridian-store->oaks-lab219 (get-oaks-parcel)))220 ([script]221 (->> script222 (walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓223 ← ← ← ← ← ← ← ← ←224 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓225 ← ←226 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓227 ↓ ↓ ↓ ↓ ↓ ↓ ↓228 → → → → → → → →229 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓230 ← ← ← ← ←231 ↓ ↓ ↓ ↓232 ])233 (walk-thru-grass234 [↓ ↓ ↓ ↓ ↓ ↓ ↓])235 (walk [↓ ↓ ← ↓ ↓ ↓ ←236 ↓ ↓ ↓ ↓ ↓ ↓237 → → → ↑])239 (do-nothing 1))))240 #+end_src242 This script walks from the Viridian City pokemon store to Oak's243 Lab in the most efficient way possible. The walk-thru-grass function244 gaurantees that no wild battles will happen by manipulating the game's245 random number generator.247 #+begin_src clojure248 (defn-memo hacking-10249 ([] (hacking-10 (hacking-9)))250 ([script]251 (->> script252 begin-deposit253 (deposit-held-item 17 230)254 (deposit-held-item-named :parlyz-heal 55)255 (deposit-held-item 14 178)256 (deposit-held-item-named :water-stone 29)257 (deposit-held-item 14 32)258 (deposit-held-item-named :TM18 1)259 (deposit-held-item 13 1)260 (deposit-held-item 13 191)261 (deposit-held-item-named :TM02 98)262 (deposit-held-item-named :TM09 1)263 close-menu)))264 #+end_src266 This script calculates the fastest sequence of key presses to deposit267 the requested items into a pc, assuming that the character starts out268 in front of a computer.270 I also wrote functions that coudl grovel through the game's memory and271 present the internal data structures in useable ways. For example, the272 function =print-inventory= returns the current inventory in a human273 readable format.275 #+begin_src clojure :results output276 (com.aurellem.gb.items/print-inventory)277 #+end_src279 #+results:280 #+begin_example281 +-------------------+----------+282 |##| Item | Quantity |283 +--+----------------+----------+284 |0 | poke-ball | 14 |285 |1 | TM28 | 1 |286 |2 | TM11 | 1 |287 |3 | TM45 | 1 |288 |4 | nugget | 1 |289 |5 | s.s.ticket | 1 |290 |6 | helix-fossil | 1 |291 |7 | moon-stone | 1 |292 +--+----------------+----------+294 #+end_example297 Armed with these functions, I constructed a bootstrapping program that298 could be expressed as items. This is particurally hard, since many299 useful opcodes do not correspond any item, and the item quantities300 must all be less than 99.302 Here is the first bootstrapping program in all its glory.304 #+begin_src clojure305 (defn pc-item-writer-program306 []307 (let [;;limit 75308 limit 201 ;; (item-hack 201 is the smallest I could make this.)309 [target-high target-low] (disect-bytes-2 pokemon-list-start)]310 (flatten311 [[0x00 ;; (item-hack) no-op (can't buy repel (1E) at celadon)312 0x1E ;; load limit into E313 limit314 0x3F ;; (item-hack) set carry flag no-op316 ;; load 2 into C.317 0x0E ;; C == 1 means input-first nybble318 0x04 ;; C == 0 means input-second nybble320 0x21 ;; load target into HL321 target-low322 target-high323 0x37 ;; (item-hack) set carry flag no-op325 0x00 ;; (item-hack) no-op326 0x37 ;; (item-hack) set carry flag no-op328 0x00 ;; (item-hack) no-op329 0xF3 ;; disable interrupts330 ;; Input Section332 0x3E ;; load 0x20 into A, to measure buttons333 0x10335 0x00 ;; (item-hack) no-op336 0xE0 ;; load A into [FF00]337 0x00339 0xF0 ;; load 0xFF00 into A to get340 0x00 ;; button presses342 0xE6343 0x0F ;; select bottom four bits of A344 0x37 ;; (item-hack) set carry flag no-op346 0x00 ;; (item-hack) no-op347 0xB8 ;; see if input is different (CP A B)349 0x00 ;; (item-hack) (INC SP)350 0x28 ;; repeat above steps if input is not different351 ;; (jump relative backwards if B != A)352 0xED ;; (literal -19) (item-hack) -19 == egg bomb (TM37)354 0x47 ;; load A into B356 0x0D ;; dec C357 0x37 ;; (item-hack) set-carry flag358 ;; branch based on C:359 0x20 ;; JR NZ360 23 ;; skip "input second nybble" and "jump to target" below362 ;; input second nybble364 0x0C ;; inc C365 0x0C ;; inc C367 0x00 ;; (item-hack) no-op368 0xE6 ;; select bottom bits369 0x0F370 0x37 ;; (item-hack) set-carry flag no-op372 0x00 ;; (item-hack) no-op373 0xB2 ;; (OR A D) -> A375 0x22 ;; (do (A -> (HL)) (INC HL))377 0x1D ;; (DEC E)379 0x00 ;; (item-hack)380 0x20 ;; jump back to input section if not done381 0xDA ;; literal -36 == TM 18 (counter)382 0x01 ;; (item-hack) set BC to literal (no-op)384 ;; jump to target385 0x00 ;; (item-hack) these two bytes can be anything.386 0x01388 0x00 ;; (item-hack) no-op389 0xBF ;; (CP A A) ensures Z391 0xCA ;; (item-hack) jump if Z392 target-low393 target-high394 0x01 ;; (item-hack) will never be reached.396 ;; input first nybble397 0x00398 0xCB399 0x37 ;; swap nybbles on A401 0x57 ;; A -> D403 0x37 ;; (item-hack) set carry flag no-op404 0x18 ;; relative jump backwards405 0xCD ;; literal -51 == TM05; go back to input section406 0x01 ;; (item-hack) will never reach this instruction408 ]409 (repeat 8 [0x00 0x01]);; these can be anything411 [;; jump to actual program412 0x00413 0x37 ;; (item-hack) set carry flag no-op415 0x2E ;; 0x3A -> L416 0x3A419 0x00 ;; (item-hack) no-op420 0x26 ;; 0xD5 -> L421 0xD5422 0x01 ;; (item-hack) set-carry BC424 0x00 ;; (item-hack) these can be anything425 0x01427 0x00428 0xE9 ;; jump to (HL)429 ]])))431 #+end_src433 I use the glitch items 0x00 and 0xFF to great effect in my run. 0x00434 sells for almost half of max_money, and I use just 3 of them to435 finance the purchace of all the other items I need. 0x00 is also a436 NO-OP in the gameboy's machine language, which means that I can stick437 them anywhere where I need to break up an other wise illegal pair of438 opcodes. 0xFF is also extremely useful because it is the end-of-list439 sentinel. Normally, the game will "compact" your items whenever you440 make a purchase or deposit. For example, if you deposit a pokeball,441 then deposit another pokeball, the item list looks like:443 pokeball x2445 instead of:447 pokeball x1448 pokeball x1450 However, the compaction stops after the first 0xFF item, so if there451 is an 0xFF item at the beginning of the list, it will "shield" all the452 items below it from compaction. It the beginning of the run, I stick453 an 0xFF item at the top of the PC item list, allowing me to put items454 in with impunity. At the end, I toss the 0xFF away to reveal the455 completed bootstrap program.457 The final payload program is actually multiple programs. I created a458 reduced form of MIDI and implemented it in gameboy machine459 language. Then I translated a midi file from460 http://www.everyponysings.com/ into this reduced MIDI language. The461 payload program contains both the music data and the MIDI interpreter462 to play that data. The picture works in a similiar way. There is code463 to translate a png file into a form that can be displayed on a464 gameboy, and other code to actually display that image. Both the image465 and the display code are also written by the final bootstrapping466 program. Even though my final payload is rather simple, you can write467 any program at all as the payload. The source for the sound and image468 displaying code is at http://hg.bortreb.com/vba-clojure.