rlm@145: (ns com.aurellem.exp.pokemon rlm@145: "Here I find out how pokemon are stored in memory." rlm@145: (:use (com.aurellem.gb gb-driver items assembly util rlm@145: characters)) rlm@145: (:import [com.aurellem.gb.gb_driver SaveState])) rlm@145: rlm@145: rlm@145: (def pidgeot-lvl-36 (mid-game)) rlm@145: rlm@145: rlm@145: (def pidgeot-lvl-37 (read-state "pidgeot-lvl-37")) rlm@145: rlm@145: rlm@145: (def pidgeot-lvl-38 (read-state "pidgeot-lvl-38")) rlm@145: rlm@145: rlm@145: (def pidgeot-lvl-39 (read-state "pidgeot-lvl-39")) rlm@145: rlm@145: rlm@145: (def pidgeot-lvl-40 (read-state "pidgeot-lvl-40")) rlm@145: rlm@145: rlm@145: (defn level-analysis [] rlm@145: (apply common-differences rlm@145: (map (comp vec memory) rlm@145: [pidgeot-lvl-36 rlm@145: pidgeot-lvl-37 rlm@145: pidgeot-lvl-38 rlm@145: pidgeot-lvl-39 rlm@145: pidgeot-lvl-40]))) rlm@145: rlm@145: ;; inconclusive -- implies that level is calculated from rlm@145: ;; some other values. rlm@145: rlm@145: rlm@145: (def name-pidgeotto (read-state "name-pidgeotto")) rlm@145: (def named-A (read-state "named-A")) rlm@145: (def named-B (read-state "named-B")) rlm@145: (def named-C (read-state "named-C")) rlm@145: (def named-D (read-state "named-D")) rlm@145: (def named-E (read-state "named-E")) rlm@145: (def named-F (read-state "named-F")) rlm@145: rlm@145: (defn name-analysis [] rlm@145: (apply common-differences rlm@145: (map (comp vec memory) rlm@145: [named-A rlm@145: named-B rlm@145: named-C rlm@145: named-D rlm@145: named-E rlm@145: named-F]))) rlm@145: rlm@145: ;; resluted in 3 separate locations that could rlm@145: ;; possibly hold the first letter of the pokemon's name rlm@145: rlm@145: 0xCF4A rlm@145: 0xD2EB rlm@145: 0xCEED rlm@145: rlm@145: ;; try changing each of them rlm@145: rlm@145: rlm@145: (defn test-cf4a [] rlm@145: (continue! rlm@145: (set-memory named-A 0xCF4A (character->character-code "Z")))) rlm@145: ;; result -- pidgeotto named "A" rlm@145: rlm@145: (defn test-d2eb [] rlm@145: (continue! rlm@145: (set-memory named-A 0xD2EB (character->character-code "Z")))) rlm@145: ;; result -- pidgeotto named "Z" rlm@145: rlm@145: (defn test-ceed [] rlm@145: (continue! rlm@145: (set-memory named-A 0xCEED (character->character-code "Z")))) rlm@145: ;; result -- pidgeotto named "A" rlm@145: rlm@145: (def sixth-pokemon-name-start 0xD2EB) rlm@145: rlm@145: rlm@145: (defn set-sixth-pokemon-name-first-character rlm@145: ([state character] rlm@145: (set-memory state sixth-pokemon-name-start rlm@145: (character->character-code character))) rlm@145: ([character] rlm@145: (set-sixth-pokemon-name-first-character @current-state rlm@145: character))) rlm@145: rlm@145: rlm@146: (def end-of-name-marker 0x50) rlm@147: (def max-name-length 10) rlm@147: (def name-width 11) rlm@147: rlm@148: (defn read-name [codes] rlm@147: (character-codes->str rlm@147: (take-while rlm@147: (partial not= end-of-name-marker) codes))) rlm@147: rlm@145: rlm@146: (defn sixth-pokemon-name [^SaveState state] rlm@148: (read-name rlm@146: (subvec (vec (memory state)) rlm@146: sixth-pokemon-name-start rlm@146: (+ (inc max-name-length) rlm@147: sixth-pokemon-name-start)))) rlm@145: rlm@147: (defn rename-sixth-pokemon rlm@147: ([^SaveState state new-name] rlm@147: (assert (< (count new-name) max-name-length)) rlm@147: (set-memory-range state sixth-pokemon-name-start rlm@147: (concat (str->character-codes new-name) rlm@147: [end-of-name-marker]))) rlm@147: ([new-name] rlm@147: (rename-sixth-pokemon @current-state new-name))) rlm@147: rlm@147: (defn print-text rlm@147: ([^SaveState state begin end] rlm@147: (dorun rlm@147: (map (fn [character-code line] rlm@147: (println rlm@147: (format "0x%04X: " line) rlm@147: (str (character-code->character character-code)))) rlm@147: (subvec (vec (memory state)) begin end) rlm@147: (range begin end))) rlm@147: state) rlm@147: ([begin end] rlm@147: (print-text @current-state begin end))) rlm@147: rlm@147: (defn examine-name-memory [] rlm@147: (print-text rlm@147: named-A rlm@147: (- sixth-pokemon-name-start 100) rlm@147: (+ sixth-pokemon-name-start 100))) rlm@147: rlm@147: ;; results: rlm@147: ;; 0xD287: end-of-name-sentinel rlm@147: ;; 0xD288: R rlm@147: ;; 0xD289: L rlm@147: ;; 0xD28A: M rlm@147: ;; 0xD28B: end-of-pokemon-name-sentinel rlm@147: ;; 0xD28C: end-of-name-sentinel rlm@147: ;; 0xD28D: end-of-name-sentinel rlm@147: ;; 0xD28E: end-of-name-sentinel rlm@147: ;; 0xD28F: end-of-name-sentinel rlm@147: ;; 0xD290: end-of-name-sentinel rlm@147: ;; 0xD291: end-of-name-sentinel rlm@147: ;; 0xD292: end-of-name-sentinel rlm@147: ;; 0xD293: R rlm@147: ;; 0xD294: L rlm@147: ;; 0xD295: M rlm@147: ;; 0xD296: end-of-pokemon-name-sentinel rlm@147: ;; 0xD297: end-of-name-sentinel rlm@147: ;; 0xD298: end-of-name-sentinel rlm@147: ;; 0xD299: end-of-name-sentinel rlm@147: ;; 0xD29A: end-of-name-sentinel rlm@147: ;; 0xD29B: end-of-name-sentinel rlm@147: ;; 0xD29C: end-of-name-sentinel rlm@147: ;; 0xD29D: end-of-name-sentinel rlm@147: ;; 0xD29E: R rlm@147: ;; 0xD29F: L rlm@147: ;; 0xD2A0: M rlm@147: ;; 0xD2A1: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2A2: end-of-name-sentinel rlm@147: ;; 0xD2A3: end-of-name-sentinel rlm@147: ;; 0xD2A4: end-of-name-sentinel rlm@147: ;; 0xD2A5: end-of-name-sentinel rlm@147: ;; 0xD2A6: end-of-name-sentinel rlm@147: ;; 0xD2A7: end-of-name-sentinel rlm@147: ;; 0xD2A8: end-of-name-sentinel rlm@147: ;; 0xD2A9: R rlm@147: ;; 0xD2AA: L rlm@147: ;; 0xD2AB: M rlm@147: ;; 0xD2AC: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2AD: end-of-name-sentinel rlm@147: ;; 0xD2AE: end-of-name-sentinel rlm@147: ;; 0xD2AF: end-of-name-sentinel rlm@147: ;; 0xD2B0: end-of-name-sentinel rlm@147: ;; 0xD2B1: end-of-name-sentinel rlm@147: ;; 0xD2B2: end-of-name-sentinel rlm@147: ;; 0xD2B3: end-of-name-sentinel rlm@147: ;; 0xD2B4: P rlm@147: ;; 0xD2B5: I rlm@147: ;; 0xD2B6: D rlm@147: ;; 0xD2B7: G rlm@147: ;; 0xD2B8: E rlm@147: ;; 0xD2B9: O rlm@147: ;; 0xD2BA: T rlm@147: ;; 0xD2BB: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2BC: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2BD: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2BE: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2BF: P rlm@147: ;; 0xD2C0: I rlm@147: ;; 0xD2C1: K rlm@147: ;; 0xD2C2: A rlm@147: ;; 0xD2C3: C rlm@147: ;; 0xD2C4: H rlm@147: ;; 0xD2C5: U rlm@147: ;; 0xD2C6: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2C7: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2C8: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2C9: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2CA: C rlm@147: ;; 0xD2CB: H rlm@147: ;; 0xD2CC: A rlm@147: ;; 0xD2CD: R rlm@147: ;; 0xD2CE: I rlm@147: ;; 0xD2CF: Z rlm@147: ;; 0xD2D0: A rlm@147: ;; 0xD2D1: R rlm@147: ;; 0xD2D2: D rlm@147: ;; 0xD2D3: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2D4: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2D5: V rlm@147: ;; 0xD2D6: E rlm@147: ;; 0xD2D7: N rlm@147: ;; 0xD2D8: U rlm@147: ;; 0xD2D9: S rlm@147: ;; 0xD2DA: A rlm@147: ;; 0xD2DB: U rlm@147: ;; 0xD2DC: R rlm@147: ;; 0xD2DD: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2DE: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2DF: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2E0: P rlm@147: ;; 0xD2E1: R rlm@147: ;; 0xD2E2: I rlm@147: ;; 0xD2E3: M rlm@147: ;; 0xD2E4: E rlm@147: ;; 0xD2E5: A rlm@147: ;; 0xD2E6: P rlm@147: ;; 0xD2E7: E rlm@147: ;; 0xD2E8: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2E9: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2EA: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2EB: A rlm@147: ;; 0xD2EC: end-of-pokemon-name-sentinel rlm@147: ;; 0xD2ED: S rlm@147: ;; 0xD2EE: T rlm@147: ;; 0xD2EF: E rlm@147: ;; 0xD2F0: R rlm@147: ;; 0xD2F1: rlm@147: ;; 0xD2F2: B rlm@147: ;; 0xD2F3: A rlm@147: ;; 0xD2F4: L rlm@147: ;; 0xD2F5: L rlm@147: ;; 0xD2F6: rlm@147: ;; 0xD2F7: A rlm@147: ;; 0xD2F8: rlm@147: ;; 0xD2F9: rlm@147: ;; 0xD2FA: end-of-name-sentinel rlm@147: ;; 0xD2FB: end-of-name-sentinel rlm@147: ;; 0xD2FC: A rlm@147: ;; 0xD2FD: rlm@147: ;; 0xD2FE: end-of-name-sentinel rlm@147: ;; 0xD2FF: end-of-name-sentinel rlm@147: ;; 0xD300: end-of-name-sentinel rlm@147: ;; 0xD301: end-of-name-sentinel rlm@147: ;; 0xD302: end-of-name-sentinel rlm@147: ;; 0xD303: end-of-name-sentinel rlm@147: ;; 0xD304: end-of-name-sentinel rlm@147: ;; 0xD305: end-of-name-sentinel rlm@147: ;; 0xD306: end-of-name-sentinel rlm@147: ;; 0xD307: end-of-name-sentinel rlm@147: ;; 0xD308: end-of-name-sentinel rlm@147: ;; 0xD309: rlm@147: ;; 0xD30A: w rlm@147: ;; 0xD30B: rlm@147: ;; 0xD30C: V rlm@147: ;; 0xD30D: rlm@147: ;; 0xD30E: rlm@147: ;; 0xD30F: K rlm@147: ;; 0xD310: rlm@147: ;; 0xD311: rlm@147: ;; 0xD312: rlm@147: ;; 0xD313: A rlm@147: ;; 0xD314: rlm@147: ;; 0xD315: rlm@147: ;; 0xD316: rlm@147: ;; 0xD317: i rlm@147: ;; 0xD318: rlm@147: ;; 0xD319: rlm@147: ;; 0xD31A: end-of-name-sentinel rlm@147: ;; 0xD31B: end-of-name-sentinel rlm@147: ;; 0xD31C: rlm@147: ;; 0xD31D: rlm@147: ;; 0xD31E: rlm@147: ;; 0xD31F: rlm@147: ;; 0xD320: rlm@147: ;; 0xD321: rlm@147: ;; 0xD322: rlm@147: ;; 0xD323: rlm@147: ;; 0xD324: rlm@147: ;; 0xD325: rlm@147: ;; 0xD326: rlm@147: ;; 0xD327: rlm@147: ;; 0xD328: rlm@147: ;; 0xD329: rlm@147: ;; 0xD32A: rlm@147: ;; 0xD32B: rlm@147: ;; 0xD32C: rlm@147: ;; 0xD32D: rlm@147: ;; 0xD32E: rlm@147: ;; 0xD32F: rlm@147: ;; 0xD330: rlm@147: ;; 0xD331: 9 rlm@147: ;; 0xD332: end-of-name-sentinel rlm@147: ;; 0xD333: 9 rlm@147: ;; 0xD334: rlm@147: ;; 0xD335: 9 rlm@147: ;; 0xD336: rlm@147: ;; 0xD337: 9 rlm@147: ;; 0xD338: end-of-name-sentinel rlm@147: ;; 0xD339: end-of-name-sentinel rlm@147: ;; 0xD33A: end-of-name-sentinel rlm@147: ;; 0xD33B: end-of-name-sentinel rlm@147: ;; 0xD33C: end-of-name-sentinel rlm@147: ;; 0xD33D: end-of-name-sentinel rlm@147: ;; 0xD33E: end-of-name-sentinel rlm@147: ;; 0xD33F: end-of-name-sentinel rlm@147: ;; 0xD340: end-of-name-sentinel rlm@147: ;; 0xD341: end-of-name-sentinel rlm@147: ;; 0xD342: end-of-name-sentinel rlm@147: ;; 0xD343: end-of-name-sentinel rlm@147: ;; 0xD344: end-of-name-sentinel rlm@147: ;; 0xD345: end-of-name-sentinel rlm@147: ;; 0xD346: rlm@147: ;; 0xD347: rlm@147: ;; 0xD348: rlm@147: ;; 0xD349: G rlm@147: ;; 0xD34A: A rlm@147: ;; 0xD34B: R rlm@147: ;; 0xD34C: Y rlm@147: ;; 0xD34D: end-of-pokemon-name-sentinel rlm@147: ;; 0xD34E: J rlm@147: rlm@147: rlm@147: ;; from this, it looks like the pokemon names are stored all rlm@147: ;; together in one location that begins at 0xD2B4 and rlm@147: ;; extends until 0xD2F5, with each name taking up 11 bytes. rlm@147: ;; rlm@147: ;; rival's name again clearly starts at 0xD349. rlm@147: rlm@147: rlm@147: (def pokemon-names-start 0xD2B4) rlm@147: rlm@147: rlm@147: ;; determine whether "number of pokemon in party" rlm@147: ;; might be kept in RAM and if so, where? rlm@147: rlm@147: (def six-pokemon (read-state "6-pokemon")) rlm@147: (def five-pokemon (read-state "5-pokemon")) rlm@147: (def four-pokemon (read-state "4-pokemon")) rlm@147: (def three-pokemon (read-state "3-pokemon")) rlm@147: (def two-pokemon (read-state "2-pokemon")) rlm@147: (def one-pokemon (read-state "1-pokemon")) rlm@147: rlm@147: rlm@147: (defn analyze-num-pokemon [] rlm@147: (apply common-differences rlm@147: (map (comp vec memory) rlm@147: [one-pokemon rlm@147: two-pokemon rlm@147: three-pokemon rlm@147: four-pokemon rlm@147: five-pokemon rlm@147: six-pokemon]))) rlm@147: rlm@147: ;; ;; results rlm@147: ;; ([53602 (1 2 3 4 5 6)] rlm@147: ;; [65314 (105 61 93 60 92 34)] rlm@147: ;; [55875 (34 36 43 52 7 0)] rlm@147: ;; [55876 (18 0 33 52 54 30)] rlm@147: ;; [49158 (197 194 77 117 174 134)] rlm@147: ;; [49160 (29 26 57 239 15 243)] rlm@147: ;; [49736 (74 93 34 89 91 59)] rlm@147: ;; [49162 (165 162 182 179 197 109)] rlm@147: ;; [49227 (187 105 204 5 90 238)] rlm@147: ;; [53067 (128 136 132 145 135 11)] rlm@147: ;; [53068 (147 131 141 136 128 7)] rlm@147: ;; [53069 (136 134 148 140 145 2)] rlm@147: ;; [49904 (2 11 10 3 27 12)] rlm@147: ;; [49172 (100 109 213 195 68 104)] rlm@147: ;; [65492 (11 103 128 160 19 56)] rlm@147: ;; [49173 (80 77 72 75 76 67)] rlm@147: ;; [49334 (8 10 11 5 3 1)] rlm@147: ;; [49335 (49 10 11 19 17 15)] rlm@147: ;; [49336 (8 10 11 5 3 1)] rlm@147: ;; [49720 (106 14 118 0 38 11)] rlm@147: ;; [65304 (32 88 19 114 106 33)] rlm@147: ;; [53561 (59 229 48 17 155 103)] rlm@147: ;; [55935 (6 5 4 3 2 1)]) rlm@147: rlm@147: rlm@147: ;; two canidates : 0xD162 or 0xDA7F rlm@147: ;; they seem to always sum to 6... rlm@147: rlm@147: rlm@147: rlm@147: ;; try to set both of them when having only one pokemon. rlm@147: rlm@147: (defn change-party-number [^SaveState state new-num] rlm@147: (set-memory state 0xD162 new-num)) rlm@147: rlm@147: ;; (continue! (change-party-number one-pokemon 3)) rlm@147: ;; result -- can scroll down beyone first pokemon, finding rlm@147: ;; glitched pokemon in places where there were previously no rlm@147: ;; pokemon. rlm@147: rlm@147: rlm@147: (defn change-party-number* [^SaveState state new-num] rlm@147: (set-memory state 0xDA7F new-num)) rlm@147: rlm@147: rlm@147: ;; (continue! (change-party-number* one-pokemon 3)) rlm@147: ;; cannot widthdraw any pokemon from box 1 past the third rlm@147: ;; pokemon. rlm@147: rlm@147: (def party-number-address 0xD162) rlm@147: rlm@147: (defn party-number rlm@147: ([^SaveState state] rlm@147: (aget (memory state) party-number-address)) rlm@147: ([] (party-number @current-state))) rlm@147: rlm@147: (def pokemon-in-box-1-address 0xDA7F) rlm@147: rlm@147: (defn party-names rlm@147: ([^SaveState state] rlm@147: (let [raw-names rlm@147: (subvec (vec (memory state)) rlm@147: pokemon-names-start rlm@147: (+ pokemon-names-start rlm@148: (* name-width 6)))] rlm@147: (map rlm@148: read-name rlm@147: (take rlm@147: (party-number state) rlm@147: (partition name-width rlm@147: raw-names))))) rlm@147: ([] (party-names @current-state))) rlm@147: rlm@148: rlm@148: (defn rename-pokemon rlm@148: ([^SaveState state n new-name] rlm@148: (assert (<= 0 n (dec (party-number state)))) rlm@148: (assert (<= (count new-name) max-name-length)) rlm@148: (set-memory-range rlm@148: state rlm@148: (+ (* n name-width) pokemon-names-start) rlm@148: (concat (str->character-codes new-name) [end-of-name-marker]))) rlm@148: ([n new-name] rlm@148: (rename-pokemon @current-state n new-name))) rlm@148: rlm@148: rlm@148: ;; on further analysis, it appears that the original rlm@148: ;; trainer info for each pokemon is also stored together, rlm@148: ;; starting at 0xD272 and continuing to 0xD2B3, with rlm@148: ;; 11 bytes reserved for each OT name. rlm@148: rlm@148: (def OT-start 0xD272) rlm@148: rlm@148: (defn original-trainers rlm@148: ([^SaveState state] rlm@148: (let [raw-names rlm@148: (subvec (vec (memory state)) rlm@148: OT-start rlm@148: (+ OT-start rlm@148: (* name-width 6)))] rlm@148: (map read-name rlm@148: (take (party-number state) rlm@148: (partition name-width raw-names))))) rlm@148: ([] (original-trainers @current-state))) rlm@148: rlm@148: (defn set-original-trainer rlm@148: "Set the OT name for a pokemon. rlm@148: Note that a pokemon is still considered 'yours' if rlm@148: the OT ID is the same as your own." rlm@148: ([^SaveState state n new-name] rlm@148: (assert (<= 0 n (dec (party-number state)))) rlm@148: (assert (<= (count new-name) max-name-length)) rlm@148: (set-memory-range rlm@148: state rlm@148: (+ (* n name-width) OT-start) rlm@148: (concat (str->character-codes new-name) [end-of-name-marker]))) rlm@148: ([n new-name] rlm@148: (set-original-trainer @current-state n new-name))) rlm@148: rlm@148: ;; PIKACHU stops following if you set it's OT to another name rlm@148: ;; and then back to you own. rlm@148: ;; But not if you set it to your own name, obviously. rlm@148: