diff options
Diffstat (limited to 'extras')
-rw-r--r-- | extras/crystal.py | 507 | ||||
-rw-r--r-- | extras/disassemble_map_scripts.py | 151 | ||||
-rw-r--r-- | extras/gbz80disasm.py | 17 | ||||
-rw-r--r-- | extras/map_names.py | 486 | ||||
-rw-r--r-- | extras/pksv.py | 1 | ||||
-rw-r--r-- | extras/vba.py | 841 | ||||
-rw-r--r-- | extras/vba_config.py | 15 |
7 files changed, 1526 insertions, 492 deletions
diff --git a/extras/crystal.py b/extras/crystal.py index 06d54ae22..b3f29a12f 100644 --- a/extras/crystal.py +++ b/extras/crystal.py @@ -2816,16 +2816,18 @@ pksv_crystal_more = { 0x9C: ["specialphonecall", ["call_id", MultiByteParam]], 0x9D: ["checkphonecall"], 0x9E: ["verbosegiveitem", ["item", ItemLabelByte], ["quantity", DecimalParam]], - 0x9F: ["verbosegiveitem2", ["item", ItemLabelByte]], + 0x9F: ["verbosegiveitem2", ["item", ItemLabelByte], ["var", SingleByteParam]], 0xA0: ["loadwilddata", ["map_group", MapGroupParam], ["map_id", MapIdParam]], 0xA1: ["halloffame"], 0xA2: ["credits"], 0xA3: ["warpfacing", ["facing", SingleByteParam], ["map_group", MapGroupParam], ["map_id", MapIdParam], ["x", SingleByteParam], ["y", SingleByteParam]], 0xA4: ["storetext", ["pointer", PointerLabelBeforeBank], ["memory", SingleByteParam]], 0xA5: ["displaylocation", ["id", SingleByteParam]], + 0xA6: ["unknown0xa6"], + 0xA7: ["unknown0xa7"], 0xA8: ["unknown0xa8", ["unknown", SingleByteParam]], - 0xB2: ["unknown0xb2", ["unknown", SingleByteParam]], - 0xCC: ["unknown0xcc"], + 0xA9: ["unknown0xa9"], + 0xAA: ["unknown0xaa"], } def create_command_classes(debug=False): """creates some classes for each command byte""" @@ -6322,489 +6324,9 @@ class PokedexEntry: {4}""".format(self.species, self.weight, self.height, self.page1.to_asm(), self.page2.to_asm()) return output -# map names with no labels will be generated at the end of the structure -map_names = { - 1: { - 0x1: {"name": "Olivine Pokémon Center 1F", - "label": "OlivinePokeCenter1F"}, - 0x2: {"name": "Olivine Gym"}, - 0x3: {"name": "Olivine Voltorb House"}, - 0x4: {"name": "Olivine House Beta"}, - 0x5: {"name": "Olivine Punishment Speech House"}, - 0x6: {"name": "Olivine Good Rod House"}, - 0x7: {"name": "Olivine Cafe"}, - 0x8: {"name": "Olivine Mart"}, - 0x9: {"name": "Route 38 Ecruteak Gate"}, - 0xA: {"name": "Route 39 Barn"}, - 0xB: {"name": "Route 39 Farmhouse"}, - 0xC: {"name": "Route 38"}, - 0xD: {"name": "Route 39"}, - 0xE: {"name": "Olivine City"}, - }, - 2: { - 0x1: {"name": "Mahogany Red Gyarados Speech House"}, - 0x2: {"name": "Mahogany Gym"}, - 0x3: {"name": "Mahogany Pokémon Center 1F", - "label": "MahoganyPokeCenter1F"}, - 0x4: {"name": "Route 42 Ecruteak Gate"}, - 0x5: {"name": "Route 42"}, - 0x6: {"name": "Route 44"}, - 0x7: {"name": "Mahogany Town"}, - }, - 3: { - 0x1: {"name": "Sprout Tower 1F"}, - 0x2: {"name": "Sprout Tower 2F"}, - 0x3: {"name": "Sprout Tower 3F"}, - 0x4: {"name": "Tin Tower 1F"}, - 0x5: {"name": "Tin Tower 2F"}, - 0x6: {"name": "Tin Tower 3F"}, - 0x7: {"name": "Tin Tower 4F"}, - 0x8: {"name": "Tin Tower 5F"}, - 0x9: {"name": "Tin Tower 6F"}, - 0xA: {"name": "Tin Tower 7F"}, - 0xB: {"name": "Tin Tower 8F"}, - 0xC: {"name": "Tin Tower 9F"}, - 0xD: {"name": "Burned Tower 1F"}, - 0xE: {"name": "Burned Tower B1F"}, - 0xF: {"name": "National Park"}, - 0x10: {"name": "National Park Bug Contest"}, - 0x11: {"name": "Radio Tower 1F"}, - 0x12: {"name": "Radio Tower 2F"}, - 0x13: {"name": "Radio Tower 3F"}, - 0x14: {"name": "Radio Tower 4F"}, - 0x15: {"name": "Radio Tower 5F"}, - 0x16: {"name": "Ruins of Alph Outside"}, - 0x17: {"name": "Ruins of Alph Ho-oh Chamber"}, - 0x18: {"name": "Ruins of Alph Kabuto Chamber"}, - 0x19: {"name": "Ruins of Alph Omanyte Chamber"}, - 0x1A: {"name": "Ruins of Alph Aerodactyl Chamber"}, - 0x1B: {"name": "Ruins of Alph Inner Chamber"}, - 0x1C: {"name": "Ruins of Alph Research Center"}, - 0x1D: {"name": "Ruins of Alph Ho-oh Item Room"}, - 0x1E: {"name": "Ruins of Alph Kabuto Item Room"}, - 0x1F: {"name": "Ruins of Alph Omanyte Item Room"}, - 0x20: {"name": "Ruins of Alph Aerodactyl Item Room"}, - 0x21: {"name": "Ruins of Alph Ho-Oh Word Room"}, - 0x22: {"name": "Ruins of Alph Kabuto Word Room"}, - 0x23: {"name": "Ruins of Alph Omanyte Word Room"}, - 0x24: {"name": "Ruins of Alph Aerodactyl Word Room"}, - 0x25: {"name": "Union Cave 1F"}, - 0x26: {"name": "Union Cave B1F"}, - 0x27: {"name": "Union Cave B2F"}, - 0x28: {"name": "Slowpoke Well B1F"}, - 0x29: {"name": "Slowpoke Well B2F"}, - 0x2A: {"name": "Olivine Lighthouse 1F"}, - 0x2B: {"name": "Olivine Lighthouse 2F"}, - 0x2C: {"name": "Olivine Lighthouse 3F"}, - 0x2D: {"name": "Olivine Lighthouse 4F"}, - 0x2E: {"name": "Olivine Lighthouse 5F"}, - 0x2F: {"name": "Olivine Lighthouse 6F"}, - 0x30: {"name": "Mahogany Mart 1F"}, - 0x31: {"name": "Team Rocket Base B1F"}, - 0x32: {"name": "Team Rocket Base B2F"}, - 0x33: {"name": "Team Rocket Base B3F"}, - 0x34: {"name": "Ilex Forest"}, - 0x35: {"name": "Warehouse Entrance"}, - 0x36: {"name": "Underground Path Switch Room Entrances"}, - 0x37: {"name": "Goldenrod Dept Store B1F"}, - 0x38: {"name": "Underground Warehouse"}, - 0x39: {"name": "Mount Mortar 1F Outside"}, - 0x3A: {"name": "Mount Mortar 1F Inside"}, - 0x3B: {"name": "Mount Mortar 2F Inside"}, - 0x3C: {"name": "Mount Mortar B1F"}, - 0x3D: {"name": "Ice Path 1F"}, - 0x3E: {"name": "Ice Path B1F"}, - 0x3F: {"name": "Ice Path B2F Mahogany Side"}, - 0x40: {"name": "Ice Path B2F Blackthorn Side"}, - 0x41: {"name": "Ice Path B3F"}, - 0x42: {"name": "Whirl Island NW"}, - 0x43: {"name": "Whirl Island NE"}, - 0x44: {"name": "Whirl Island SW"}, - 0x45: {"name": "Whirl Island Cave"}, - 0x46: {"name": "Whirl Island SE"}, - 0x47: {"name": "Whirl Island B1F"}, - 0x48: {"name": "Whirl Island B2F"}, - 0x49: {"name": "Whirl Island Lugia Chamber"}, - 0x4A: {"name": "Silver Cave Room 1"}, - 0x4B: {"name": "Silver Cave Room 2"}, - 0x4C: {"name": "Silver Cave Room 3"}, - 0x4D: {"name": "Silver Cave Item Rooms"}, - 0x4E: {"name": "Dark Cave Violet Entrance"}, - 0x4F: {"name": "Dark Cave Blackthorn Entrance"}, - 0x50: {"name": "Dragon's Den 1F"}, - 0x51: {"name": "Dragon's Den B1F"}, - 0x52: {"name": "Dragon Shrine"}, - 0x53: {"name": "Tohjo Falls"}, - 0x54: {"name": "Diglett's Cave"}, - 0x55: {"name": "Mount Moon"}, - 0x56: {"name": "Underground"}, - 0x57: {"name": "Rock Tunnel 1F"}, - 0x58: {"name": "Rock Tunnel B1F"}, - 0x59: {"name": "Safari Zone Fuchsia Gate Beta"}, - 0x5A: {"name": "Safari Zone Beta"}, - 0x5B: {"name": "Victory Road"}, - }, - 4: { - 0x1: {"name": "Ecruteak House"}, # passage to Tin Tower - 0x2: {"name": "Wise Trio's Room"}, - 0x3: {"name": "Ecruteak Pokémon Center 1F", - "label": "EcruteakPokeCenter1F"}, - 0x4: {"name": "Ecruteak Lugia Speech House"}, - 0x5: {"name": "Dance Theatre"}, - 0x6: {"name": "Ecruteak Mart"}, - 0x7: {"name": "Ecruteak Gym"}, - 0x8: {"name": "Ecruteak Itemfinder House"}, - 0x9: {"name": "Ecruteak City"}, - }, - 5: { - 0x1: {"name": "Blackthorn Gym 1F"}, - 0x2: {"name": "Blackthorn Gym 2F"}, - 0x3: {"name": "Blackthorn Dragon Speech House"}, - 0x4: {"name": "Blackthorn Dodrio Trade House"}, - 0x5: {"name": "Blackthorn Mart"}, - 0x6: {"name": "Blackthorn Pokémon Center 1F", - "label": "BlackthornPokeCenter1F"}, - 0x7: {"name": "Move Deleter's House"}, - 0x8: {"name": "Route 45"}, - 0x9: {"name": "Route 46"}, - 0xA: {"name": "Blackthorn City"}, - }, - 6: { - 0x1: {"name": "Cinnabar Pokémon Center 1F", - "label": "CinnabarPokeCenter1F"}, - 0x2: {"name": "Cinnabar Pokémon Center 2F Beta", - "label": "CinnabarPokeCenter2FBeta"}, - 0x3: {"name": "Route 19 - Fuchsia Gate"}, - 0x4: {"name": "Seafoam Gym"}, - 0x5: {"name": "Route 19"}, - 0x6: {"name": "Route 20"}, - 0x7: {"name": "Route 21"}, - 0x8: {"name": "Cinnabar Island"}, - }, - 7: { - 0x1: {"name": "Cerulean Gym Badge Speech House"}, - 0x2: {"name": "Cerulean Police Station"}, - 0x3: {"name": "Cerulean Trade Speech House"}, - 0x4: {"name": "Cerulean Pokémon Center 1F", - "label": "CeruleanPokeCenter1F"}, - 0x5: {"name": "Cerulean Pokémon Center 2F Beta", - "label": "CeruleanPokeCenter2FBeta"}, - 0x6: {"name": "Cerulean Gym"}, - 0x7: {"name": "Cerulean Mart"}, - 0x8: {"name": "Route 10 Pokémon Center 1F", - "label": "Route10PokeCenter1F"}, - 0x9: {"name": "Route 10 Pokémon Center 2F Beta", - "label": "Route10PokeCenter2FBeta"}, - 0xA: {"name": "Power Plant"}, - 0xB: {"name": "Bill's House"}, - 0xC: {"name": "Route 4"}, - 0xD: {"name": "Route 9"}, - 0xE: {"name": "Route 10 North"}, - 0xF: {"name": "Route 24"}, - 0x10: {"name": "Route 25"}, - 0x11: {"name": "Cerulean City"}, - }, - 8: { - 0x1: {"name": "Azalea Pokémon Center 1F", - "label": "AzaleaPokeCenter1F"}, - 0x2: {"name": "Charcoal Kiln"}, - 0x3: {"name": "Azalea Mart"}, - 0x4: {"name": "Kurt's House"}, - 0x5: {"name": "Azalea Gym"}, - 0x6: {"name": "Route 33"}, - 0x7: {"name": "Azalea Town"}, - }, - 9: { - 0x1: {"name": "Lake of Rage Hidden Power House"}, - 0x2: {"name": "Lake of Rage Magikarp House"}, - 0x3: {"name": "Route 43 Mahogany Gate"}, - 0x4: {"name": "Route 43 Gate"}, - 0x5: {"name": "Route 43"}, - 0x6: {"name": "Lake of Rage"}, - }, - 10: { - 0x1: {"name": "Route 32"}, - 0x2: {"name": "Route 35"}, - 0x3: {"name": "Route 36"}, - 0x4: {"name": "Route 37"}, - 0x5: {"name": "Violet City"}, - 0x6: {"name": "Violet Mart"}, - 0x7: {"name": "Violet Gym"}, - 0x8: {"name": "Earl's Pokémon Academy", - "label": "EarlsPokemonAcademy"}, - 0x9: {"name": "Violet Nickname Speech House"}, - 0xA: {"name": "Violet Pokémon Center 1F", - "label": "VioletPokeCenter1F"}, - 0xB: {"name": "Violet Onix Trade House"}, - 0xC: {"name": "Route 32 Ruins of Alph Gate"}, - 0xD: {"name": "Route 32 Pokémon Center 1F", - "label": "Route32PokeCenter1F"}, - 0xE: {"name": "Route 35 Goldenrod gate"}, - 0xF: {"name": "Route 35 National Park gate"}, - 0x10: {"name": "Route 36 Ruins of Alph gate"}, - 0x11: {"name": "Route 36 National Park gate"}, - }, - 11: { - 0x1: {"name": "Route 34"}, - 0x2: {"name": "Goldenrod City"}, - 0x3: {"name": "Goldenrod Gym"}, - 0x4: {"name": "Goldenrod Bike Shop"}, - 0x5: {"name": "Goldenrod Happiness Rater"}, - 0x6: {"name": "Goldenrod Bill's House"}, - 0x7: {"name": "Goldenrod Magnet Train Station"}, - 0x8: {"name": "Goldenrod Flower Shop"}, - 0x9: {"name": "Goldenrod PP Speech House"}, - 0xA: {"name": "Goldenrod Name Rater's House"}, - 0xB: {"name": "Goldenrod Dept Store 1F"}, - 0xC: {"name": "Goldenrod Dept Store 2F"}, - 0xD: {"name": "Goldenrod Dept Store 3F"}, - 0xE: {"name": "Goldenrod Dept Store 4F"}, - 0xF: {"name": "Goldenrod Dept Store 5F"}, - 0x10: {"name": "Goldenrod Dept Store 6F"}, - 0x11: {"name": "Goldenrod Dept Store Elevator"}, - 0x12: {"name": "Goldenrod Dept Store Roof"}, - 0x13: {"name": "Goldenrod Game Corner"}, - 0x14: {"name": "Goldenrod Pokémon Center 1F", - "label": "GoldenrodPokeCenter1F"}, - 0x15: {"name": "Goldenrod PokéCom Center 2F Mobile", - "label": "GoldenrodPokeComCenter2FMobile"}, - 0x16: {"name": "Ilex Forest Azalea Gate"}, - 0x17: {"name": "Route 34 Ilex Forest Gate"}, - 0x18: {"name": "Day Care"}, - }, - 12: { - 0x1: {"name": "Route 6"}, - 0x2: {"name": "Route 11"}, - 0x3: {"name": "Vermilion City"}, - 0x4: {"name": "Vermilion House Fishing Speech House"}, - 0x5: {"name": "Vermilion Pokémon Center 1F", - "label": "VermilionPokeCenter1F"}, - 0x6: {"name": "Vermilion Pokémon Center 2F Beta", - "label": "VermilionPokeCenter2FBeta"}, - 0x7: {"name": "Pokémon Fan Club"}, - 0x8: {"name": "Vermilion Magnet Train Speech House"}, - 0x9: {"name": "Vermilion Mart"}, - 0xA: {"name": "Vermilion House Diglett's Cave Speech House"}, - 0xB: {"name": "Vermilion Gym"}, - 0xC: {"name": "Route 6 Saffron Gate"}, - 0xD: {"name": "Route 6 Underground Entrance"}, - }, - 13: { - 0x1: {"name": "Route 1"}, - 0x2: {"name": "Pallet Town"}, - 0x3: {"name": "Red's House 1F"}, - 0x4: {"name": "Red's House 2F"}, - 0x5: {"name": "Blue's House"}, - 0x6: {"name": "Oak's Lab"}, - }, - 14: { - 0x1: {"name": "Route 3"}, - 0x2: {"name": "Pewter City"}, - 0x3: {"name": "Pewter Nidoran Speech House"}, - 0x4: {"name": "Pewter Gym"}, - 0x5: {"name": "Pewter Mart"}, - 0x6: {"name": "Pewter Pokémon Center 1F", - "label": "PewterPokeCenter1F"}, - 0x7: {"name": "Pewter Pokémon Center 2F Beta", - "label": "PewterPokeCEnter2FBeta"}, - 0x8: {"name": "Pewter Snooze Speech House"}, - }, - 15: { - 0x1: {"name": "Olivine Port"}, - 0x2: {"name": "Vermilion Port"}, - 0x3: {"name": "Fast Ship 1F"}, - 0x4: {"name": "Fast Ship Cabins NNW, NNE, NE", - "label": "FastShipCabins_NNW_NNE_NE"}, - 0x5: {"name": "Fast Ship Cabins SW, SSW, NW", - "label": "FastShipCabins_SW_SSW_NW"}, - 0x6: {"name": "Fast Ship Cabins SE, SSE, Captain's Cabin", - "label": "FastShipCabins_SE_SSE_CaptainsCabin"}, - 0x7: {"name": "Fast Ship B1F"}, - 0x8: {"name": "Olivine Port Passage"}, - 0x9: {"name": "Vermilion Port Passage"}, - 0xA: {"name": "Mount Moon Square"}, - 0xB: {"name": "Mount Moon Gift Shop"}, - 0xC: {"name": "Tin Tower Roof"}, - }, - 16: { - 0x1: {"name": "Route 23"}, - 0x2: {"name": "Indigo Plateau Pokémon Center 1F", - "label": "IndigoPlateauPokeCenter1F"}, - 0x3: {"name": "Will's Room"}, - 0x4: {"name": "Koga's Room"}, - 0x5: {"name": "Bruno's Room"}, - 0x6: {"name": "Karen's Room"}, - 0x7: {"name": "Lance's Room"}, - 0x8: {"name": "Hall of Fame", - "label": "HallOfFame"}, - }, - 17: { - 0x1: {"name": "Route 13"}, - 0x2: {"name": "Route 14"}, - 0x3: {"name": "Route 15"}, - 0x4: {"name": "Route 18"}, - 0x5: {"name": "Fuchsia City"}, - 0x6: {"name": "Fuchsia Mart"}, - 0x7: {"name": "Safari Zone Main Office"}, - 0x8: {"name": "Fuchsia Gym"}, - 0x9: {"name": "Fuchsia Bill Speech House"}, - 0xA: {"name": "Fuchsia Pokémon Center 1F", - "label": "FuchsiaPokeCenter1F"}, - 0xB: {"name": "Fuchsia Pokémon Center 2F Beta", - "label": "FuchsiaPokeCenter2FBeta"}, - 0xC: {"name": "Safari Zone Warden's Home"}, - 0xD: {"name": "Route 15 Fuchsia Gate"}, - }, - 18: { - 0x1: {"name": "Route 8"}, - 0x2: {"name": "Route 12"}, - 0x3: {"name": "Route 10 South"}, - 0x4: {"name": "Lavender Town"}, - 0x5: {"name": "Lavender Pokémon Center 1F", - "label": "LavenderPokeCenter1F"}, - 0x6: {"name": "Lavender Pokémon Center 2F Beta", - "label": "LavenderPokeCenter2FBeta"}, - 0x7: {"name": "Mr. Fuji's House"}, - 0x8: {"name": "Lavender Town Speech House"}, - 0x9: {"name": "Lavender Name Rater"}, - 0xA: {"name": "Lavender Mart"}, - 0xB: {"name": "Soul House"}, - 0xC: {"name": "Lav Radio Tower 1F"}, - 0xD: {"name": "Route 8 Saffron Gate"}, - 0xE: {"name": "Route 12 Super Rod House"}, - }, - 19: { - 0x1: {"name": "Route 28"}, - 0x2: {"name": "Silver Cave Outside"}, - 0x3: {"name": "Silver Cave Pokémon Center 1F", - "label": "SilverCavePokeCenter1F"}, - 0x4: {"name": "Route 28 Famous Speech House"}, - }, - 20: { - 0x1: {"name": "Pokémon Center 2F", - "label": "PokeCenter2F"}, - 0x2: {"name": "Trade Center"}, - 0x3: {"name": "Colosseum"}, - 0x4: {"name": "Time Capsule"}, - 0x5: {"name": "Mobile Trade Room Mobile"}, - 0x6: {"name": "Mobile Battle Room"}, - }, - 21: { - 0x1: {"name": "Route 7"}, - 0x2: {"name": "Route 16"}, - 0x3: {"name": "Route 17"}, - 0x4: {"name": "Celadon City"}, - 0x5: {"name": "Celadon Dept Store 1F"}, - 0x6: {"name": "Celadon Dept Store 2F"}, - 0x7: {"name": "Celadon Dept Store 3F"}, - 0x8: {"name": "Celadon Dept Store 4F"}, - 0x9: {"name": "Celadon Dept Store 5F"}, - 0xA: {"name": "Celadon Dept Store 6F"}, - 0xB: {"name": "Celadon Dept Store Elevator"}, - 0xC: {"name": "Celadon Mansion 1F"}, - 0xD: {"name": "Celadon Mansion 2F"}, - 0xE: {"name": "Celadon Mansion 3F"}, - 0xF: {"name": "Celadon Mansion Roof"}, - 0x10: {"name": "Celadon Mansion Roof House"}, - 0x11: {"name": "Celadon Pokémon Center 1F", - "label": "CeladonPokeCenter1F"}, - 0x12: {"name": "Celadon Pokémon Center 2F Beta", - "label": "CeladonPokeCenter2FBeta"}, - 0x13: {"name": "Celadon Game Corner"}, - 0x14: {"name": "Celadon Game Corner Prize Room"}, - 0x15: {"name": "Celadon Gym"}, - 0x16: {"name": "Celadon Cafe"}, - 0x17: {"name": "Route 16 Fuchsia Speech House"}, - 0x18: {"name": "Route 16 Gate"}, - 0x19: {"name": "Route 7 Saffron Gate"}, - 0x1A: {"name": "Route 17 18 Gate"}, - }, - 22: { - 0x1: {"name": "Route 40"}, - 0x2: {"name": "Route 41"}, - 0x3: {"name": "Cianwood City"}, - 0x4: {"name": "Mania's House"}, - 0x5: {"name": "Cianwood Gym"}, - 0x6: {"name": "Cianwood Pokémon Center 1F", - "label": "CianwoodPokeCenter1F"}, - 0x7: {"name": "Cianwood Pharmacy"}, - 0x8: {"name": "Cianwood City Photo Studio"}, - 0x9: {"name": "Cianwood Lugia Speech House"}, - 0xA: {"name": "Poke Seer's House"}, - 0xB: {"name": "Battle Tower 1F"}, - 0xC: {"name": "Battle Tower Battle Room"}, - 0xD: {"name": "Battle Tower Elevator"}, - 0xE: {"name": "Battle Tower Hallway"}, - 0xF: {"name": "Route 40 Battle Tower Gate"}, - 0x10: {"name": "Battle Tower Outside"}, - }, - 23: { - 0x1: {"name": "Route 2"}, - 0x2: {"name": "Route 22"}, - 0x3: {"name": "Viridian City"}, - 0x4: {"name": "Viridian Gym"}, - 0x5: {"name": "Viridian Nickname Speech House"}, - 0x6: {"name": "Trainer House 1F"}, - 0x7: {"name": "Trainer House B1F"}, - 0x8: {"name": "Viridian Mart"}, - 0x9: {"name": "Viridian Pokémon Center 1F", - "label": "ViridianPokeCenter1F"}, - 0xA: {"name": "Viridian Pokémon Center 2F Beta", - "label": "ViridianPokeCenter2FBeta"}, - 0xB: {"name": "Route 2 Nugget Speech House"}, - 0xC: {"name": "Route 2 Gate"}, - 0xD: {"name": "Victory Road Gate"}, - }, - 24: { - 0x1: {"name": "Route 26"}, - 0x2: {"name": "Route 27"}, - 0x3: {"name": "Route 29"}, - 0x4: {"name": "New Bark Town"}, - 0x5: {"name": "Elm's Lab"}, - 0x6: {"name": "Kris's House 1F"}, - 0x7: {"name": "Kris's House 2F"}, - 0x8: {"name": "Kris's Neighbor's House"}, - 0x9: {"name": "Elm's House"}, - 0xA: {"name": "Route 26 Heal Speech House"}, - 0xB: {"name": "Route 26 Day of Week Siblings House"}, - 0xC: {"name": "Route 27 Sandstorm House"}, - 0xD: {"name": "Route 29 46 Gate"}, - }, - 25: { - 0x1: {"name": "Route 5"}, - 0x2: {"name": "Saffron City"}, - 0x3: {"name": "Fighting Dojo"}, - 0x4: {"name": "Saffron Gym"}, - 0x5: {"name": "Saffron Mart"}, - 0x6: {"name": "Saffron Pokémon Center 1F", - "label": "SaffronPokeCenter1F"}, - 0x7: {"name": "Saffron Pokémon Center 2F Beta", - "label": "SaffronPokeCenter2FBeta"}, - 0x8: {"name": "Mr. Psychic's House"}, - 0x9: {"name": "Saffron Train Station"}, - 0xA: {"name": "Silph Co. 1F"}, - 0xB: {"name": "Copycat's House 1F"}, - 0xC: {"name": "Copycat's House 2F"}, - 0xD: {"name": "Route 5 Underground Entrance"}, - 0xE: {"name": "Route 5 Saffron City Gate"}, - 0xF: {"name": "Route 5 Cleanse Tag Speech House"}, - }, - 26: { - 0x1: {"name": "Route 30"}, - 0x2: {"name": "Route 31"}, - 0x3: {"name": "Cherrygrove City"}, - 0x4: {"name": "Cherrygrove Mart"}, - 0x5: {"name": "Cherrygrove Pokémon Center 1F", - "label": "CherrygrovePokeCenter1F"}, - 0x6: {"name": "Cherrygrove Gym Speech House"}, - 0x7: {"name": "Guide Gent's House"}, - 0x8: {"name": "Cherrygrove Evolution Speech House"}, - 0x9: {"name": "Route 30 Berry Speech House"}, - 0xA: {"name": "Mr. Pokémon's House"}, - 0xB: {"name": "Route 31 Violet Gate"}, - }, -} +from map_names import map_names +# map names with no labels will be generated # generate labels for each map name for map_group_id in map_names.keys(): map_group = map_names[map_group_id] @@ -6834,7 +6356,7 @@ incbin_lines = [] # storage for processed incbin lines processed_incbins = {} -def to_asm(some_object): +def to_asm(some_object, use_asm_rules=False): """shows an object's asm with a label and an ending comment showing the next byte address""" if isinstance(some_object, int): @@ -6850,6 +6372,9 @@ def to_asm(some_object): asmr = asmr.replace("\n"+spacing+"\n", "\n\n"+spacing) asmr = asmr.replace("\n\n"+spacing+spacing, "\n\n"+spacing) asm += spacing + asmr + if use_asm_rules: + asm = asm.replace("\n" + spacing + "; ", "\n; ") + asm = asm.replace("\n" + spacing + ".asm_", "\n.asm_") # show the address of the next byte below this asm += "\n; " + hex(last_address) return asm @@ -7223,7 +6748,7 @@ class Asm: asm_list = AsmList(asm) bank = 0 for line in asm_list: - if line[0:6] == "INCBIN" or line[1:6] == "INCBIN": + if (line[0:6] == "INCBIN" or line[1:6] == "INCBIN") and not any([contaminant+"\"" in line for contaminant in [".2bpp", ".1bpp", ".asm", ".lz"]]): thing = Incbin(line, bank=bank) elif line[0:7] == "SECTION": thing = AsmSection(line) @@ -7288,11 +6813,13 @@ class Asm: # check if the object is already inserted if new_object in self.parts: - print "object was previously inserted ("+str(new_object)+")" + print "object was previously inserted ("+str(new_object)+"; " + hex(new_object.address) + ")" return # check by label - if self.is_label_name_in_file(new_object.label.name): - print "object was previously inserted ("+str(new_object)+") by label: "+new_object.label.name + other_obj = self.is_label_name_in_file(new_object.label.name) + if other_obj: + other_obj = other_obj.object + print "object was previously inserted ("+new_object.label.name+" at "+hex(new_object.address)+") by "+other_obj.label.name+" at "+hex(other_obj.address) return # check by address #if self.does_address_have_label(new_object.address): diff --git a/extras/disassemble_map_scripts.py b/extras/disassemble_map_scripts.py new file mode 100644 index 000000000..21df56924 --- /dev/null +++ b/extras/disassemble_map_scripts.py @@ -0,0 +1,151 @@ +# -*- encoding: utf-8 -*- +""" +Dump out asm for scripting things in bank $25. This script will modify main.asm +and insert all scripting commands. +""" + +import crystal +from gbz80disasm import output_bank_opcodes + +rom = crystal.load_rom() +roml = [ord(x) for x in rom] + +script_command_table_address = 0x96cb1 +script_command_count = 170 + +# a list of addresses for each script command +command_pointers = [crystal.calculate_pointer_from_bytes_at(script_command_table_address + (id * 2), bank=0x25) for id in range(0, 170)] + +# a list of hex addresses for each script command in bank $25 +command_pointers_hex = ["$%.2x" % (x % 0x4000 + 0x4000) for x in command_pointers] + +commands = {} + +# force data into a more usable form +for command in crystal.command_classes: + name = "Script_" + command.macro_name + id = command.id + params = {} + + for (id2, param_type) in command.param_types.items(): + param = { + "name": param_type["name"], + "type": param_type["class"].__name__, + } + params[id2] = param + + if id <= 0xa9: + commands[id] = { + "name": name, + "params": params, + "address": command_pointers[id], + } + +avoid = [ + 0x974b0, + 0x974be, + 0x9754b, + 0x97556, + 0x97562, + 0x9756e, + 0x97540, + + 0x96f8e, # verbosegiveitem2 +] + +class DisassembledScriptCommand(): + """ + Just a temporary object to store information about a script command's asm. + This is used by some of the infrastructure in crystal.py to automatically + insert asm into main.asm, rather than having someone do it manually. + """ + dependencies = None + + def __init__(self, label=None, id=None, address=None, params=None): + self.id = id + self.label = crystal.Label(name=label, address=address, object=self) + self.address = address + self.params = params + + max_byte_count = 0x4000 + + # Some of these scripts need to be truncated before insertion, because + # output_bank_opcodes doesn't know anything about stopping if some of + # the local labels are not resolved yet. + + # Script_if_equal + if address == 0x97540: + max_byte_count = 86 + + # disassemble and laso get the last address + (asm, last_address, last_hl_address, last_a_address, used_3d97) = output_bank_opcodes(address, max_byte_count=max_byte_count, stop_at=command_pointers, include_last_address=False) + + # remove indentation + asm = asm.replace("\n\t", "\n") + if asm[0] == "\t": + asm = asm[1:] + + # remove the last two newlines + while asm[-1] == "\n": + asm = asm[:-1] + + self.asm = asm + self.last_address = last_address + + # make sure this gets dumped into main.asm + #if crystal.script_parse_table[self.address] == None and crystal.script_parse_table[self.last_address] == None: + crystal.script_parse_table[self.address : self.last_address] = self + #else: + # print ".. hm, something is already at " + hex(self.address) + " for " + self.label.name + + def to_asm(self): + #output += self.label + ": ; " + hex(self.address) + "\n" + output = "; script command " + hex(self.id) + "\n" + if len(self.params) > 0: + output += "; parameters:\n" + for (id2, param) in self.params.items(): + output += "; " + param["name"] + " (" + param["type"] + ")\n" + output += "\n" + output += self.asm + return output + + def get_dependencies(*args, **kwargs): + return [] + +# make instances of DisassembledScriptCommand +for (id, command) in commands.items(): + name = command["name"] + params = command["params"] + address = command["address"] + + script_asm = DisassembledScriptCommand(label=name, id=id, address=address, params=params) + #print script_asm.to_asm() + #print crystal.to_asm(script_asm, use_asm_rules=True) + +class ScriptCommandTable(): + address = script_command_table_address + last_address = script_command_table_address + (2 * 170) + dependencies = None + + def __init__(self): + self.label = crystal.Label(name="ScriptCommandTable", address=self.address, object=self) + + # make sure this gets dumped into main.asm + crystal.script_parse_table[self.address : self.last_address] = self + + def get_dependencies(*args, **kwargs): + return [] + + def to_asm(self): + output = "" + for (id, command) in commands.items(): + output += "dw " + command["name"] + "; " + hex(command["address"]) + "\n" + if output[-1] == "\n": + output = output[:-1] + return output +script_command_table = ScriptCommandTable() +#print crystal.to_asm(script_command_table, use_asm_rules=True) + +# automatic asm insertion +asm = crystal.Asm() +asm.insert_and_dump(limit=500) diff --git a/extras/gbz80disasm.py b/extras/gbz80disasm.py index d22f152f1..efb4689ab 100644 --- a/extras/gbz80disasm.py +++ b/extras/gbz80disasm.py @@ -592,7 +592,7 @@ def asm_label(address): # why using a random value when you can use the address? return ".ASM_" + hex(address)[2:] -def output_bank_opcodes(original_offset, max_byte_count=0x4000, debug = False): +def output_bank_opcodes(original_offset, max_byte_count=0x4000, include_last_address=True, stop_at=[], debug = False): #fs = current_address #b = bank_byte #in = input_data -- rom @@ -601,6 +601,10 @@ def output_bank_opcodes(original_offset, max_byte_count=0x4000, debug = False): #ad = end_address #a, oa = current_byte_number + # stop_at can be used to supply a list of addresses to not disassemble + # over. This is useful if you know in advance that there are a lot of + # fall-throughs. + load_labels() load_rom() @@ -622,6 +626,7 @@ def output_bank_opcodes(original_offset, max_byte_count=0x4000, debug = False): byte_labels = {} + first_loop = True output = "" keep_reading = True while offset <= end_address and keep_reading: @@ -629,6 +634,11 @@ def output_bank_opcodes(original_offset, max_byte_count=0x4000, debug = False): is_data = False maybe_byte = current_byte + # stop at any address + if not first_loop and offset in stop_at: + keep_reading = False + break + #first check if this byte already has a label #if it does, use the label #if not, generate a new label @@ -816,6 +826,8 @@ def output_bank_opcodes(original_offset, max_byte_count=0x4000, debug = False): #offset += 1 #current_byte_number += 1 + first_loop = False + #clean up unused labels for label_line in byte_labels.keys(): address = label_line @@ -824,7 +836,8 @@ def output_bank_opcodes(original_offset, max_byte_count=0x4000, debug = False): output = output.replace((label_line["name"] + "\n").lower(), "") #add the offset of the final location - output += "; " + hex(offset) + if include_last_address: + output += "; " + hex(offset) return (output, offset, last_hl_address, last_a_address, used_3d97) diff --git a/extras/map_names.py b/extras/map_names.py new file mode 100644 index 000000000..599b377db --- /dev/null +++ b/extras/map_names.py @@ -0,0 +1,486 @@ +# -*- coding: utf-8 -*- +""" +""" + +# this is modified in crystal.py during run-time +map_names = { + 1: { + 0x1: {"name": "Olivine Pokémon Center 1F", + "label": "OlivinePokeCenter1F"}, + 0x2: {"name": "Olivine Gym"}, + 0x3: {"name": "Olivine Voltorb House"}, + 0x4: {"name": "Olivine House Beta"}, + 0x5: {"name": "Olivine Punishment Speech House"}, + 0x6: {"name": "Olivine Good Rod House"}, + 0x7: {"name": "Olivine Cafe"}, + 0x8: {"name": "Olivine Mart"}, + 0x9: {"name": "Route 38 Ecruteak Gate"}, + 0xA: {"name": "Route 39 Barn"}, + 0xB: {"name": "Route 39 Farmhouse"}, + 0xC: {"name": "Route 38"}, + 0xD: {"name": "Route 39"}, + 0xE: {"name": "Olivine City"}, + }, + 2: { + 0x1: {"name": "Mahogany Red Gyarados Speech House"}, + 0x2: {"name": "Mahogany Gym"}, + 0x3: {"name": "Mahogany Pokémon Center 1F", + "label": "MahoganyPokeCenter1F"}, + 0x4: {"name": "Route 42 Ecruteak Gate"}, + 0x5: {"name": "Route 42"}, + 0x6: {"name": "Route 44"}, + 0x7: {"name": "Mahogany Town"}, + }, + 3: { + 0x1: {"name": "Sprout Tower 1F"}, + 0x2: {"name": "Sprout Tower 2F"}, + 0x3: {"name": "Sprout Tower 3F"}, + 0x4: {"name": "Tin Tower 1F"}, + 0x5: {"name": "Tin Tower 2F"}, + 0x6: {"name": "Tin Tower 3F"}, + 0x7: {"name": "Tin Tower 4F"}, + 0x8: {"name": "Tin Tower 5F"}, + 0x9: {"name": "Tin Tower 6F"}, + 0xA: {"name": "Tin Tower 7F"}, + 0xB: {"name": "Tin Tower 8F"}, + 0xC: {"name": "Tin Tower 9F"}, + 0xD: {"name": "Burned Tower 1F"}, + 0xE: {"name": "Burned Tower B1F"}, + 0xF: {"name": "National Park"}, + 0x10: {"name": "National Park Bug Contest"}, + 0x11: {"name": "Radio Tower 1F"}, + 0x12: {"name": "Radio Tower 2F"}, + 0x13: {"name": "Radio Tower 3F"}, + 0x14: {"name": "Radio Tower 4F"}, + 0x15: {"name": "Radio Tower 5F"}, + 0x16: {"name": "Ruins of Alph Outside"}, + 0x17: {"name": "Ruins of Alph Ho-oh Chamber"}, + 0x18: {"name": "Ruins of Alph Kabuto Chamber"}, + 0x19: {"name": "Ruins of Alph Omanyte Chamber"}, + 0x1A: {"name": "Ruins of Alph Aerodactyl Chamber"}, + 0x1B: {"name": "Ruins of Alph Inner Chamber"}, + 0x1C: {"name": "Ruins of Alph Research Center"}, + 0x1D: {"name": "Ruins of Alph Ho-oh Item Room"}, + 0x1E: {"name": "Ruins of Alph Kabuto Item Room"}, + 0x1F: {"name": "Ruins of Alph Omanyte Item Room"}, + 0x20: {"name": "Ruins of Alph Aerodactyl Item Room"}, + 0x21: {"name": "Ruins of Alph Ho-Oh Word Room"}, + 0x22: {"name": "Ruins of Alph Kabuto Word Room"}, + 0x23: {"name": "Ruins of Alph Omanyte Word Room"}, + 0x24: {"name": "Ruins of Alph Aerodactyl Word Room"}, + 0x25: {"name": "Union Cave 1F"}, + 0x26: {"name": "Union Cave B1F"}, + 0x27: {"name": "Union Cave B2F"}, + 0x28: {"name": "Slowpoke Well B1F"}, + 0x29: {"name": "Slowpoke Well B2F"}, + 0x2A: {"name": "Olivine Lighthouse 1F"}, + 0x2B: {"name": "Olivine Lighthouse 2F"}, + 0x2C: {"name": "Olivine Lighthouse 3F"}, + 0x2D: {"name": "Olivine Lighthouse 4F"}, + 0x2E: {"name": "Olivine Lighthouse 5F"}, + 0x2F: {"name": "Olivine Lighthouse 6F"}, + 0x30: {"name": "Mahogany Mart 1F"}, + 0x31: {"name": "Team Rocket Base B1F"}, + 0x32: {"name": "Team Rocket Base B2F"}, + 0x33: {"name": "Team Rocket Base B3F"}, + 0x34: {"name": "Ilex Forest"}, + 0x35: {"name": "Warehouse Entrance"}, + 0x36: {"name": "Underground Path Switch Room Entrances"}, + 0x37: {"name": "Goldenrod Dept Store B1F"}, + 0x38: {"name": "Underground Warehouse"}, + 0x39: {"name": "Mount Mortar 1F Outside"}, + 0x3A: {"name": "Mount Mortar 1F Inside"}, + 0x3B: {"name": "Mount Mortar 2F Inside"}, + 0x3C: {"name": "Mount Mortar B1F"}, + 0x3D: {"name": "Ice Path 1F"}, + 0x3E: {"name": "Ice Path B1F"}, + 0x3F: {"name": "Ice Path B2F Mahogany Side"}, + 0x40: {"name": "Ice Path B2F Blackthorn Side"}, + 0x41: {"name": "Ice Path B3F"}, + 0x42: {"name": "Whirl Island NW"}, + 0x43: {"name": "Whirl Island NE"}, + 0x44: {"name": "Whirl Island SW"}, + 0x45: {"name": "Whirl Island Cave"}, + 0x46: {"name": "Whirl Island SE"}, + 0x47: {"name": "Whirl Island B1F"}, + 0x48: {"name": "Whirl Island B2F"}, + 0x49: {"name": "Whirl Island Lugia Chamber"}, + 0x4A: {"name": "Silver Cave Room 1"}, + 0x4B: {"name": "Silver Cave Room 2"}, + 0x4C: {"name": "Silver Cave Room 3"}, + 0x4D: {"name": "Silver Cave Item Rooms"}, + 0x4E: {"name": "Dark Cave Violet Entrance"}, + 0x4F: {"name": "Dark Cave Blackthorn Entrance"}, + 0x50: {"name": "Dragon's Den 1F"}, + 0x51: {"name": "Dragon's Den B1F"}, + 0x52: {"name": "Dragon Shrine"}, + 0x53: {"name": "Tohjo Falls"}, + 0x54: {"name": "Diglett's Cave"}, + 0x55: {"name": "Mount Moon"}, + 0x56: {"name": "Underground"}, + 0x57: {"name": "Rock Tunnel 1F"}, + 0x58: {"name": "Rock Tunnel B1F"}, + 0x59: {"name": "Safari Zone Fuchsia Gate Beta"}, + 0x5A: {"name": "Safari Zone Beta"}, + 0x5B: {"name": "Victory Road"}, + }, + 4: { + 0x1: {"name": "Ecruteak House"}, # passage to Tin Tower + 0x2: {"name": "Wise Trio's Room"}, + 0x3: {"name": "Ecruteak Pokémon Center 1F", + "label": "EcruteakPokeCenter1F"}, + 0x4: {"name": "Ecruteak Lugia Speech House"}, + 0x5: {"name": "Dance Theatre"}, + 0x6: {"name": "Ecruteak Mart"}, + 0x7: {"name": "Ecruteak Gym"}, + 0x8: {"name": "Ecruteak Itemfinder House"}, + 0x9: {"name": "Ecruteak City"}, + }, + 5: { + 0x1: {"name": "Blackthorn Gym 1F"}, + 0x2: {"name": "Blackthorn Gym 2F"}, + 0x3: {"name": "Blackthorn Dragon Speech House"}, + 0x4: {"name": "Blackthorn Dodrio Trade House"}, + 0x5: {"name": "Blackthorn Mart"}, + 0x6: {"name": "Blackthorn Pokémon Center 1F", + "label": "BlackthornPokeCenter1F"}, + 0x7: {"name": "Move Deleter's House"}, + 0x8: {"name": "Route 45"}, + 0x9: {"name": "Route 46"}, + 0xA: {"name": "Blackthorn City"}, + }, + 6: { + 0x1: {"name": "Cinnabar Pokémon Center 1F", + "label": "CinnabarPokeCenter1F"}, + 0x2: {"name": "Cinnabar Pokémon Center 2F Beta", + "label": "CinnabarPokeCenter2FBeta"}, + 0x3: {"name": "Route 19 - Fuchsia Gate"}, + 0x4: {"name": "Seafoam Gym"}, + 0x5: {"name": "Route 19"}, + 0x6: {"name": "Route 20"}, + 0x7: {"name": "Route 21"}, + 0x8: {"name": "Cinnabar Island"}, + }, + 7: { + 0x1: {"name": "Cerulean Gym Badge Speech House"}, + 0x2: {"name": "Cerulean Police Station"}, + 0x3: {"name": "Cerulean Trade Speech House"}, + 0x4: {"name": "Cerulean Pokémon Center 1F", + "label": "CeruleanPokeCenter1F"}, + 0x5: {"name": "Cerulean Pokémon Center 2F Beta", + "label": "CeruleanPokeCenter2FBeta"}, + 0x6: {"name": "Cerulean Gym"}, + 0x7: {"name": "Cerulean Mart"}, + 0x8: {"name": "Route 10 Pokémon Center 1F", + "label": "Route10PokeCenter1F"}, + 0x9: {"name": "Route 10 Pokémon Center 2F Beta", + "label": "Route10PokeCenter2FBeta"}, + 0xA: {"name": "Power Plant"}, + 0xB: {"name": "Bill's House"}, + 0xC: {"name": "Route 4"}, + 0xD: {"name": "Route 9"}, + 0xE: {"name": "Route 10 North"}, + 0xF: {"name": "Route 24"}, + 0x10: {"name": "Route 25"}, + 0x11: {"name": "Cerulean City"}, + }, + 8: { + 0x1: {"name": "Azalea Pokémon Center 1F", + "label": "AzaleaPokeCenter1F"}, + 0x2: {"name": "Charcoal Kiln"}, + 0x3: {"name": "Azalea Mart"}, + 0x4: {"name": "Kurt's House"}, + 0x5: {"name": "Azalea Gym"}, + 0x6: {"name": "Route 33"}, + 0x7: {"name": "Azalea Town"}, + }, + 9: { + 0x1: {"name": "Lake of Rage Hidden Power House"}, + 0x2: {"name": "Lake of Rage Magikarp House"}, + 0x3: {"name": "Route 43 Mahogany Gate"}, + 0x4: {"name": "Route 43 Gate"}, + 0x5: {"name": "Route 43"}, + 0x6: {"name": "Lake of Rage"}, + }, + 10: { + 0x1: {"name": "Route 32"}, + 0x2: {"name": "Route 35"}, + 0x3: {"name": "Route 36"}, + 0x4: {"name": "Route 37"}, + 0x5: {"name": "Violet City"}, + 0x6: {"name": "Violet Mart"}, + 0x7: {"name": "Violet Gym"}, + 0x8: {"name": "Earl's Pokémon Academy", + "label": "EarlsPokemonAcademy"}, + 0x9: {"name": "Violet Nickname Speech House"}, + 0xA: {"name": "Violet Pokémon Center 1F", + "label": "VioletPokeCenter1F"}, + 0xB: {"name": "Violet Onix Trade House"}, + 0xC: {"name": "Route 32 Ruins of Alph Gate"}, + 0xD: {"name": "Route 32 Pokémon Center 1F", + "label": "Route32PokeCenter1F"}, + 0xE: {"name": "Route 35 Goldenrod gate"}, + 0xF: {"name": "Route 35 National Park gate"}, + 0x10: {"name": "Route 36 Ruins of Alph gate"}, + 0x11: {"name": "Route 36 National Park gate"}, + }, + 11: { + 0x1: {"name": "Route 34"}, + 0x2: {"name": "Goldenrod City"}, + 0x3: {"name": "Goldenrod Gym"}, + 0x4: {"name": "Goldenrod Bike Shop"}, + 0x5: {"name": "Goldenrod Happiness Rater"}, + 0x6: {"name": "Goldenrod Bill's House"}, + 0x7: {"name": "Goldenrod Magnet Train Station"}, + 0x8: {"name": "Goldenrod Flower Shop"}, + 0x9: {"name": "Goldenrod PP Speech House"}, + 0xA: {"name": "Goldenrod Name Rater's House"}, + 0xB: {"name": "Goldenrod Dept Store 1F"}, + 0xC: {"name": "Goldenrod Dept Store 2F"}, + 0xD: {"name": "Goldenrod Dept Store 3F"}, + 0xE: {"name": "Goldenrod Dept Store 4F"}, + 0xF: {"name": "Goldenrod Dept Store 5F"}, + 0x10: {"name": "Goldenrod Dept Store 6F"}, + 0x11: {"name": "Goldenrod Dept Store Elevator"}, + 0x12: {"name": "Goldenrod Dept Store Roof"}, + 0x13: {"name": "Goldenrod Game Corner"}, + 0x14: {"name": "Goldenrod Pokémon Center 1F", + "label": "GoldenrodPokeCenter1F"}, + 0x15: {"name": "Goldenrod PokéCom Center 2F Mobile", + "label": "GoldenrodPokeComCenter2FMobile"}, + 0x16: {"name": "Ilex Forest Azalea Gate"}, + 0x17: {"name": "Route 34 Ilex Forest Gate"}, + 0x18: {"name": "Day Care"}, + }, + 12: { + 0x1: {"name": "Route 6"}, + 0x2: {"name": "Route 11"}, + 0x3: {"name": "Vermilion City"}, + 0x4: {"name": "Vermilion House Fishing Speech House"}, + 0x5: {"name": "Vermilion Pokémon Center 1F", + "label": "VermilionPokeCenter1F"}, + 0x6: {"name": "Vermilion Pokémon Center 2F Beta", + "label": "VermilionPokeCenter2FBeta"}, + 0x7: {"name": "Pokémon Fan Club"}, + 0x8: {"name": "Vermilion Magnet Train Speech House"}, + 0x9: {"name": "Vermilion Mart"}, + 0xA: {"name": "Vermilion House Diglett's Cave Speech House"}, + 0xB: {"name": "Vermilion Gym"}, + 0xC: {"name": "Route 6 Saffron Gate"}, + 0xD: {"name": "Route 6 Underground Entrance"}, + }, + 13: { + 0x1: {"name": "Route 1"}, + 0x2: {"name": "Pallet Town"}, + 0x3: {"name": "Red's House 1F"}, + 0x4: {"name": "Red's House 2F"}, + 0x5: {"name": "Blue's House"}, + 0x6: {"name": "Oak's Lab"}, + }, + 14: { + 0x1: {"name": "Route 3"}, + 0x2: {"name": "Pewter City"}, + 0x3: {"name": "Pewter Nidoran Speech House"}, + 0x4: {"name": "Pewter Gym"}, + 0x5: {"name": "Pewter Mart"}, + 0x6: {"name": "Pewter Pokémon Center 1F", + "label": "PewterPokeCenter1F"}, + 0x7: {"name": "Pewter Pokémon Center 2F Beta", + "label": "PewterPokeCEnter2FBeta"}, + 0x8: {"name": "Pewter Snooze Speech House"}, + }, + 15: { + 0x1: {"name": "Olivine Port"}, + 0x2: {"name": "Vermilion Port"}, + 0x3: {"name": "Fast Ship 1F"}, + 0x4: {"name": "Fast Ship Cabins NNW, NNE, NE", + "label": "FastShipCabins_NNW_NNE_NE"}, + 0x5: {"name": "Fast Ship Cabins SW, SSW, NW", + "label": "FastShipCabins_SW_SSW_NW"}, + 0x6: {"name": "Fast Ship Cabins SE, SSE, Captain's Cabin", + "label": "FastShipCabins_SE_SSE_CaptainsCabin"}, + 0x7: {"name": "Fast Ship B1F"}, + 0x8: {"name": "Olivine Port Passage"}, + 0x9: {"name": "Vermilion Port Passage"}, + 0xA: {"name": "Mount Moon Square"}, + 0xB: {"name": "Mount Moon Gift Shop"}, + 0xC: {"name": "Tin Tower Roof"}, + }, + 16: { + 0x1: {"name": "Route 23"}, + 0x2: {"name": "Indigo Plateau Pokémon Center 1F", + "label": "IndigoPlateauPokeCenter1F"}, + 0x3: {"name": "Will's Room"}, + 0x4: {"name": "Koga's Room"}, + 0x5: {"name": "Bruno's Room"}, + 0x6: {"name": "Karen's Room"}, + 0x7: {"name": "Lance's Room"}, + 0x8: {"name": "Hall of Fame", + "label": "HallOfFame"}, + }, + 17: { + 0x1: {"name": "Route 13"}, + 0x2: {"name": "Route 14"}, + 0x3: {"name": "Route 15"}, + 0x4: {"name": "Route 18"}, + 0x5: {"name": "Fuchsia City"}, + 0x6: {"name": "Fuchsia Mart"}, + 0x7: {"name": "Safari Zone Main Office"}, + 0x8: {"name": "Fuchsia Gym"}, + 0x9: {"name": "Fuchsia Bill Speech House"}, + 0xA: {"name": "Fuchsia Pokémon Center 1F", + "label": "FuchsiaPokeCenter1F"}, + 0xB: {"name": "Fuchsia Pokémon Center 2F Beta", + "label": "FuchsiaPokeCenter2FBeta"}, + 0xC: {"name": "Safari Zone Warden's Home"}, + 0xD: {"name": "Route 15 Fuchsia Gate"}, + }, + 18: { + 0x1: {"name": "Route 8"}, + 0x2: {"name": "Route 12"}, + 0x3: {"name": "Route 10 South"}, + 0x4: {"name": "Lavender Town"}, + 0x5: {"name": "Lavender Pokémon Center 1F", + "label": "LavenderPokeCenter1F"}, + 0x6: {"name": "Lavender Pokémon Center 2F Beta", + "label": "LavenderPokeCenter2FBeta"}, + 0x7: {"name": "Mr. Fuji's House"}, + 0x8: {"name": "Lavender Town Speech House"}, + 0x9: {"name": "Lavender Name Rater"}, + 0xA: {"name": "Lavender Mart"}, + 0xB: {"name": "Soul House"}, + 0xC: {"name": "Lav Radio Tower 1F"}, + 0xD: {"name": "Route 8 Saffron Gate"}, + 0xE: {"name": "Route 12 Super Rod House"}, + }, + 19: { + 0x1: {"name": "Route 28"}, + 0x2: {"name": "Silver Cave Outside"}, + 0x3: {"name": "Silver Cave Pokémon Center 1F", + "label": "SilverCavePokeCenter1F"}, + 0x4: {"name": "Route 28 Famous Speech House"}, + }, + 20: { + 0x1: {"name": "Pokémon Center 2F", + "label": "PokeCenter2F"}, + 0x2: {"name": "Trade Center"}, + 0x3: {"name": "Colosseum"}, + 0x4: {"name": "Time Capsule"}, + 0x5: {"name": "Mobile Trade Room Mobile"}, + 0x6: {"name": "Mobile Battle Room"}, + }, + 21: { + 0x1: {"name": "Route 7"}, + 0x2: {"name": "Route 16"}, + 0x3: {"name": "Route 17"}, + 0x4: {"name": "Celadon City"}, + 0x5: {"name": "Celadon Dept Store 1F"}, + 0x6: {"name": "Celadon Dept Store 2F"}, + 0x7: {"name": "Celadon Dept Store 3F"}, + 0x8: {"name": "Celadon Dept Store 4F"}, + 0x9: {"name": "Celadon Dept Store 5F"}, + 0xA: {"name": "Celadon Dept Store 6F"}, + 0xB: {"name": "Celadon Dept Store Elevator"}, + 0xC: {"name": "Celadon Mansion 1F"}, + 0xD: {"name": "Celadon Mansion 2F"}, + 0xE: {"name": "Celadon Mansion 3F"}, + 0xF: {"name": "Celadon Mansion Roof"}, + 0x10: {"name": "Celadon Mansion Roof House"}, + 0x11: {"name": "Celadon Pokémon Center 1F", + "label": "CeladonPokeCenter1F"}, + 0x12: {"name": "Celadon Pokémon Center 2F Beta", + "label": "CeladonPokeCenter2FBeta"}, + 0x13: {"name": "Celadon Game Corner"}, + 0x14: {"name": "Celadon Game Corner Prize Room"}, + 0x15: {"name": "Celadon Gym"}, + 0x16: {"name": "Celadon Cafe"}, + 0x17: {"name": "Route 16 Fuchsia Speech House"}, + 0x18: {"name": "Route 16 Gate"}, + 0x19: {"name": "Route 7 Saffron Gate"}, + 0x1A: {"name": "Route 17 18 Gate"}, + }, + 22: { + 0x1: {"name": "Route 40"}, + 0x2: {"name": "Route 41"}, + 0x3: {"name": "Cianwood City"}, + 0x4: {"name": "Mania's House"}, + 0x5: {"name": "Cianwood Gym"}, + 0x6: {"name": "Cianwood Pokémon Center 1F", + "label": "CianwoodPokeCenter1F"}, + 0x7: {"name": "Cianwood Pharmacy"}, + 0x8: {"name": "Cianwood City Photo Studio"}, + 0x9: {"name": "Cianwood Lugia Speech House"}, + 0xA: {"name": "Poke Seer's House"}, + 0xB: {"name": "Battle Tower 1F"}, + 0xC: {"name": "Battle Tower Battle Room"}, + 0xD: {"name": "Battle Tower Elevator"}, + 0xE: {"name": "Battle Tower Hallway"}, + 0xF: {"name": "Route 40 Battle Tower Gate"}, + 0x10: {"name": "Battle Tower Outside"}, + }, + 23: { + 0x1: {"name": "Route 2"}, + 0x2: {"name": "Route 22"}, + 0x3: {"name": "Viridian City"}, + 0x4: {"name": "Viridian Gym"}, + 0x5: {"name": "Viridian Nickname Speech House"}, + 0x6: {"name": "Trainer House 1F"}, + 0x7: {"name": "Trainer House B1F"}, + 0x8: {"name": "Viridian Mart"}, + 0x9: {"name": "Viridian Pokémon Center 1F", + "label": "ViridianPokeCenter1F"}, + 0xA: {"name": "Viridian Pokémon Center 2F Beta", + "label": "ViridianPokeCenter2FBeta"}, + 0xB: {"name": "Route 2 Nugget Speech House"}, + 0xC: {"name": "Route 2 Gate"}, + 0xD: {"name": "Victory Road Gate"}, + }, + 24: { + 0x1: {"name": "Route 26"}, + 0x2: {"name": "Route 27"}, + 0x3: {"name": "Route 29"}, + 0x4: {"name": "New Bark Town"}, + 0x5: {"name": "Elm's Lab"}, + 0x6: {"name": "Kris's House 1F"}, + 0x7: {"name": "Kris's House 2F"}, + 0x8: {"name": "Kris's Neighbor's House"}, + 0x9: {"name": "Elm's House"}, + 0xA: {"name": "Route 26 Heal Speech House"}, + 0xB: {"name": "Route 26 Day of Week Siblings House"}, + 0xC: {"name": "Route 27 Sandstorm House"}, + 0xD: {"name": "Route 29 46 Gate"}, + }, + 25: { + 0x1: {"name": "Route 5"}, + 0x2: {"name": "Saffron City"}, + 0x3: {"name": "Fighting Dojo"}, + 0x4: {"name": "Saffron Gym"}, + 0x5: {"name": "Saffron Mart"}, + 0x6: {"name": "Saffron Pokémon Center 1F", + "label": "SaffronPokeCenter1F"}, + 0x7: {"name": "Saffron Pokémon Center 2F Beta", + "label": "SaffronPokeCenter2FBeta"}, + 0x8: {"name": "Mr. Psychic's House"}, + 0x9: {"name": "Saffron Train Station"}, + 0xA: {"name": "Silph Co. 1F"}, + 0xB: {"name": "Copycat's House 1F"}, + 0xC: {"name": "Copycat's House 2F"}, + 0xD: {"name": "Route 5 Underground Entrance"}, + 0xE: {"name": "Route 5 Saffron City Gate"}, + 0xF: {"name": "Route 5 Cleanse Tag Speech House"}, + }, + 26: { + 0x1: {"name": "Route 30"}, + 0x2: {"name": "Route 31"}, + 0x3: {"name": "Cherrygrove City"}, + 0x4: {"name": "Cherrygrove Mart"}, + 0x5: {"name": "Cherrygrove Pokémon Center 1F", + "label": "CherrygrovePokeCenter1F"}, + 0x6: {"name": "Cherrygrove Gym Speech House"}, + 0x7: {"name": "Guide Gent's House"}, + 0x8: {"name": "Cherrygrove Evolution Speech House"}, + 0x9: {"name": "Route 30 Berry Speech House"}, + 0xA: {"name": "Mr. Pokémon's House"}, + 0xB: {"name": "Route 31 Violet Gate"}, + }, +} diff --git a/extras/pksv.py b/extras/pksv.py index 03ad2d077..f30ab9294 100644 --- a/extras/pksv.py +++ b/extras/pksv.py @@ -298,6 +298,7 @@ pksv_crystal_more_enders = [0x03, 0x04, 0x05, 0x0C, 0x51, 0x52, 0x9B, 0xB2, #maybe? 0xCC, #maybe? + 0x9A, # describedecoration ] # these have no pksv names as of pksv 2.1.1 diff --git a/extras/vba.py b/extras/vba.py new file mode 100644 index 000000000..8ed0d7b5a --- /dev/null +++ b/extras/vba.py @@ -0,0 +1,841 @@ +#!/usr/bin/jython +# -*- encoding: utf-8 -*- +""" +vba-clojure (but really it's jython/python/jvm) + +This is jython, not python. Use jython to run this file. Before running this +file, some of the dependencies need to be constructed. These can be obtained +from the vba-clojure project. + sudo apt-get install g++ libtool openjdk-6-jre openjdk-6-jdk libsdl1.2-dev ant jython + + export JAVA_INCLUDE_PATH=/usr/lib/jvm/java-6-openjdk-amd64/include/ + export JAVA_INCLUDE_PATH2=/usr/lib/jvm/java-6-openjdk-amd64/include/ + + hg clone http://hg.bortreb.com/vba-clojure + cd vba-clojure/java/ + ant all + cd .. + autoreconf -i + ./configure + make + sudo make install + +Make sure vba-clojure bindings are in $CLASSPATH: + export CLASSPATH=$CLASSPATH:java/dist/gb-bindings.jar + +Make sure vba-clojure is available within "java.library.path": + sudo ln -s \ + $HOME/local/vba-clojure/vba-clojure/src/clojure/.libs/libvba.so.0.0.0 \ + /usr/lib/jni/libvba.so + +Also make sure VisualBoyAdvance.cfg is somewhere in the $PATH for VBA to find. +A default configuration is provided in vba-clojure under src/. + +Usage (in jython, not python): + import vba + + # activate the laser beam + vba.load_rom("/path/to/baserom.gbc") + + # make the emulator eat some instructions + vba.nstep(300) + + # save the state because we're paranoid + copyrights = vba.get_state() + # or ... + vba.save_state("copyrights") + # >>> vba.load_state("copyrights") == copyrights + # True + + # play for a while, then press F12 + vba.run() + + # let's save the game again + vba.save_state("unknown-delete-me") + + # and let's go back to the other state + vba.set_state(copyrights) + + # or why not the other way around? + vba.set_state(vba.load_state("unknown-delete-me")) + + vba.get_memory_at(0xDCDA) + vba.set_memory_at(0xDCDB, 0xFF) + vba.get_memory_range(0xDCDA, 10) + +TOOD: + [ ] set a specific register + [ ] get a specific register + [ ] breakpoints + [ ] vgm stuff + [ ] gbz80disasm integration + [ ] pokecrystal.extras integration + +""" + +import os +import sys +import re +from array import array +import string +from copy import copy +import unittest + +# for converting bytes to readable text +from chars import chars + +from map_names import map_names + +# for _check_java_library_path +from java.lang import System + +# for passing states to the emulator +from java.nio import ByteBuffer + +# For getRegisters and other times we have to pass a java int array to a +# function. +import jarray + +# load in the vba-clojure bindings +import com.aurellem.gb.Gb as Gb + +# load the vba-clojure library +Gb.loadVBA() + +from vba_config import * + +if not os.path.exists(rom_path): + raise Exception("rom_path is not configured properly; edit vba_config.py?") + +def _check_java_library_path(): + """ + Returns the value of java.library.path. The vba-clojure library must be + compiled and linked from this location. + """ + return System.getProperty("java.library.path") + +class RomList(list): + """ + Simple wrapper to prevent a giant rom from being shown on screen. + """ + def __init__(self, *args, **kwargs): + list.__init__(self, *args, **kwargs) + + def __repr__(self): + """ + Simplifies this object so that the output doesn't overflow stdout. + """ + return "RomList(too long)" + +button_masks = { + "a": 0x0001, + "b": 0x0002, + "r": 0x0010, + "l": 0x0020, + "u": 0x0040, + "d": 0x0080, + "select": 0x0004, + "start": 0x0008, + "restart": 0x0800, + "listen": -1, # what? +} + +# useful for directly stating what to press +a, b, r, l, u, d, select, start, restart = "a", "b", "r", "l", "u", "d", "select", "start", "restart" + +def button_combiner(buttons): + """ + Combines multiple button presses into an integer. This is used when sending + a keypress to the emulator. + """ + result = 0 + + # String inputs need to be cleaned up so that "start" doesn't get + # recognized as "s" and "t" etc.. + if isinstance(buttons, str): + if "restart" in buttons: + buttons.replace("restart", "") + result |= button_masks["restart"] + if "start" in buttons: + buttons.replace("start", "") + result |= button_masks["start"] + if "select" in buttons: + buttons.replace("select", "") + result |= button_masks["select"] + + for each in buttons: + result |= button_masks[each] + + print "button: " + str(result) + return result + +def load_rom(path=None): + """ + Starts the emulator with a certain ROM. Defaults to rom_path if no + parameters are given. + """ + if path == None: + path = rom_path + try: + root = load_state("root") + except: + # "root.sav" is required because if you create it in the future, you + # will have to shutdown the emulator and possibly lose your state. Some + # functions require there to be at least one root state available to do + # computations between two states. + sys.stderr.write("ERROR: unable to read \"root.sav\", please run" + " generate_root() or get_root() to make an initial save.\n") + Gb.startEmulator(path) + +def shutdown(): + """ + Stops the emulator. Closes the window. The "opposite" of this is the + load_rom function. + """ + Gb.shutdown() + +def step(): + """ + Advances the emulator forward by one step. + """ + Gb.step() + +def nstep(steplimit): + """ + Step the game forward by a certain number of instructions. + """ + for counter in range(0, steplimit): + Gb.step() + +def step_until_capture(): + """ + Loop step() until SDLK_F12 is detected. + """ + Gb.stepUntilCapture() + +# just some aliases for step_until_capture +run = go = step_until_capture + +def translate_chars(charz): + result = "" + for each in charz: + result += chars[each] + return result + +def _create_byte_buffer(data): + """ + Converts data into a ByteBuffer. This is useful for interfacing with the Gb + class. + """ + buf = ByteBuffer.allocateDirect(len(data)) + if isinstance(data[0], int): + for byte in data: + buf.put(byte) + else: + for byte in data: + buf.put(ord(byte)) + return buf + +def set_state(state, do_step=False): + """ + Injects the given state into the emulator. Use do_step if you want to call + step(), which also allows SDL to render the latest frame. Note that the + default is to not step, and that the screen (if it is enabled) will appear + as if it still has the last state loaded. This is normal. + """ + Gb.loadState(_create_byte_buffer(state)) + if do_step: + step() + +def get_state(): + """ + Retrieves the current state of the emulator. + """ + buf = Gb.saveState() + state = [buf.get(x) for x in range(0, buf.capacity())] + arr = array("b") + arr.extend(state) + return arr.tostring() # instead of state + +def save_state(name, state=None, override=False): + """ + Saves the given state to save_state_path. The file format must be ".sav" + (and this will be appended to your string if necessary). + """ + if state == None: + state = get_state() + if len(name) < 4 or name[-4:] != ".sav": + name += ".sav" + save_path = os.path.join(save_state_path, name) + if not override and os.path.exists(save_path): + raise Exception("oops, save state path already exists: " + str(save_path)) + else: + # convert the state into a reasonable output + data = array('b') + data.extend(state) + output = data.tostring() + + file_handler = open(save_path, "wb") + file_handler.write(output) + file_handler.close() + +def load_state(name): + """ + Reads a state from file based on name. Looks in save_state_path for a file + with this name (".sav" is optional). + """ + save_path = os.path.join(save_state_path, name) + if not os.path.exists(save_path): + if len(name) < 4 or name[-4:] != ".sav": + name += ".sav" + save_path = os.path.join(save_state_path, name) + file_handler = open(save_path, "rb") + state = file_handler.read() + file_handler.close() + return state + +def generate_root(): + """ + Restarts the emulator and saves the initial state to "root.sav". + """ + shutdown() + load_rom() + root = get_state() + save_state("root", state=root, override=True) + return root + +def get_root(): + """ + Loads the root state, or restarts the emulator and creates a new root + state. + """ + try: + root = load_state("root") + except: + root = generate_root() + +def get_registers(): + """ + Returns a list of current register values. + """ + register_array = jarray.zeros(Gb.NUM_REGISTERS, "i") + Gb.getRegisters(register_array) + return list(register_array) + +def set_registers(registers): + """ + Applies the set of registers to the CPU. + """ + Gb.writeRegisters(registers) +write_registers = set_registers + +def get_rom(): + """ + Returns the ROM in bytes. + """ + rom_array = jarray.zeros(Gb.ROM_SIZE, "i") + Gb.getROM(rom_array) + return RomList(rom_array) + +def get_ram(): + """ + Returns the RAM in bytes. + """ + ram_array = jarray.zeros(Gb.RAM_SIZE, "i") + Gb.getRAM(ram_array) + return RomList(ram_array) + +def say_hello(): + """ + Test that the VBA/GB bindings are working. + """ + Gb.sayHello() + +def get_memory(): + """ + Returns memory in bytes. + """ + memory_size = 0x10000 + memory = jarray.zeros(memory_size, "i") + Gb.getMemory(memory) + return RomList(memory) + +def set_memory(memory): + """ + Sets memory in the emulator. Use get_memory() to retrieve the current + state. + """ + Gb.writeMemory(memory) + +def get_pixels(): + """ + Returns a list of pixels on the screen display. Broken, probably. Use + screenshot() instead. + """ + sys.stderr.write("ERROR: seems to be broken on VBA's end? Good luck. Use" + " screenshot() instead.\n") + size = Gb.DISPLAY_WIDTH * Gb.DISPLAY_HEIGHT + pixels = jarray.zeros(size, "i") + Gb.getPixels(pixels) + return RomList(pixels) + +def screenshot(filename, literal=False): + """ + Saves a PNG screenshot to the file at filename. Use literal if you want to + store it in the current directory. Default is to save it to screenshots/ + under the project. + """ + screenshots_path = os.path.join(project_path, "screenshots/") + filename = os.path.join(screenshots_path, filename) + if len(filename) < 4 or filename[-4:] != ".png": + filename += ".png" + Gb.nwritePNG(filename) + print "Screenshot saved to: " + str(filename) +save_png = screenshot + +def read_memory(address): + """ + Read an integer at an address. + """ + return Gb.readMemory(address) +get_memory_at = read_memory + +def get_memory_range(start_address, byte_count): + """ + Returns a list of bytes. + + start_address - address to start reading at + byte_count - how many bytes (0 returns just 1 byte) + """ + bytez = [] + for counter in range(0, byte_count): + byte = get_memory_at(start_address + counter) + bytez.append(byte) + return bytez + +def set_memory_at(address, value): + """ + Sets a byte at a certain address in memory. This directly sets the memory + instead of copying the memory from the emulator. + """ + Gb.setMemoryAt(address, value) + +def press(buttons, steplimit=1): + """ + Press a button. Use steplimit to say for how many steps you want to press + the button (try leaving it at the default, 1). + """ + if hasattr(buttons, "__len__"): + number = button_combiner(buttons) + elif isinstance(buttons, int): + number = buttons + else: + number = buttons + for step_counter in range(0, steplimit): + Gb.step(number) + +class Registers: + order = [ + "pc", + "sp", + "af", + "bc", + "de", + "hl", + "iff", + "div", + "tima", + "tma", + "tac", + "if", + "lcdc", + "stat", + "scy", + "scx", + "ly", + "lyc", + "dma", + "wy", + "wx", + "vbk", + "hdma1", + "hdma2", + "hdma3", + "hdma4", + "hdma5", + "svbk", + "ie", + ] + + def __setitem__(self, key, value): + current_registers = get_registers() + current_registers[Registers.order.index(key)] = value + set_registers(current_registers) + + def __getitem__(self, key): + current_registers = get_registers() + return current_registers[Registers.order.index(key)] + + def __list__(self): + return get_registers() + + def _get_register(id): + def constructed_func(self, id=copy(id)): + return get_registers()[id] + return constructed_func + + def _set_register(id): + def constructed_func(self, value, id=copy(id)): + current_registers = get_registers() + current_registers[id] = value + set_registers(current_registers) + return constructed_func + + pc = property(fget=_get_register(0), fset=_set_register(0)) + sp = property(fget=_get_register(1), fset=_set_register(1)) + af = property(fget=_get_register(2), fset=_set_register(2)) + bc = property(fget=_get_register(3), fset=_set_register(3)) + de = property(fget=_get_register(4), fset=_set_register(4)) + hl = property(fget=_get_register(5), fset=_set_register(5)) + iff = property(fget=_get_register(6), fset=_set_register(6)) + div = property(fget=_get_register(7), fset=_set_register(7)) + tima = property(fget=_get_register(8), fset=_set_register(8)) + tma = property(fget=_get_register(9), fset=_set_register(9)) + tac = property(fget=_get_register(10), fset=_set_register(10)) + _if = property(fget=_get_register(11), fset=_set_register(11)) + lcdc = property(fget=_get_register(12), fset=_set_register(12)) + stat = property(fget=_get_register(13), fset=_set_register(13)) + scy = property(fget=_get_register(14), fset=_set_register(14)) + scx = property(fget=_get_register(15), fset=_set_register(15)) + ly = property(fget=_get_register(16), fset=_set_register(16)) + lyc = property(fget=_get_register(17), fset=_set_register(17)) + dma = property(fget=_get_register(18), fset=_set_register(18)) + wy = property(fget=_get_register(19), fset=_set_register(19)) + wx = property(fget=_get_register(20), fset=_set_register(20)) + vbk = property(fget=_get_register(21), fset=_set_register(21)) + hdma1 = property(fget=_get_register(22), fset=_set_register(22)) + hdma2 = property(fget=_get_register(23), fset=_set_register(23)) + hdma3 = property(fget=_get_register(24), fset=_set_register(24)) + hdma4 = property(fget=_get_register(25), fset=_set_register(25)) + hdma5 = property(fget=_get_register(26), fset=_set_register(26)) + svbk = property(fget=_get_register(27), fset=_set_register(27)) + ie = property(fget=_get_register(28), fset=_set_register(28)) + + def __repr__(self): + spacing = "\t" + output = "Registers:\n" + for (id, each) in enumerate(self.order): + output += spacing + each + " = " + hex(get_registers()[id]) + #hex(self[each]) + output += "\n" + return output + +registers = Registers() + +def call(bank, address): + """ + Jumps into a function at a certain address. + + Go into the start menu, pause the game and try call(1, 0x1078) to see a + string printed to the screen. + """ + push = [ + registers.pc, + registers.hl, + registers.de, + registers.bc, + registers.af, + 0x3bb7, + ] + + for value in push: + registers.sp -= 2 + set_memory_at(registers.sp + 1, value >> 8) + set_memory_at(registers.sp, value & 0xFF) + if get_memory_range(registers.sp, 2) != [value & 0xFF, value >> 8]: + print "desired memory values: " + str([value & 0xFF, value >> 8] ) + print "actual memory values: " + str(get_memory_range(registers.sp , 2)) + print "wrong value at " + hex(registers.sp) + " expected " + hex(value) + " but got " + hex(get_memory_at(registers.sp)) + + if bank != 0: + registers["af"] = (bank << 8) | (registers["af"] & 0xFF) + registers["hl"] = address + registers["pc"] = 0x2d63 # FarJump + else: + registers["pc"] = address + +class crystal: + """ + Just a simple namespace to store a bunch of functions for Pokémon Crystal. + """ + + @staticmethod + def walk_through_walls_slow(): + memory = get_memory() + memory[0xC2FA] = 0 + memory[0xC2FB] = 0 + memory[0xC2FC] = 0 + memory[0xC2FD] = 0 + set_memory(memory) + + @staticmethod + def walk_through_walls(): + """ + Lets the player walk all over the map. These values are probably reset + by some of the map/collision functions when you move on to a new + location, so this needs to be executed each step/tick if continuous + walk-through-walls is desired. + """ + set_memory_at(0xC2FA, 0) + set_memory_at(0xC2FB, 0) + set_memory_at(0xC2FC, 0) + set_memory_at(0xC2FD, 0) + + #@staticmethod + #def set_enemy_level(level): + # set_memory_at(0xd213, level) + + @staticmethod + def nstep(steplimit=500): + """ + Steps the CPU forward and calls some functions in between each step, + like to manipulate memory. This is pretty slow. + """ + for step_counter in range(0, steplimit): + crystal.walk_through_walls() + #call(0x1, 0x1078) + step() + + @staticmethod + def disable_triggers(): + set_memory_at(0x23c4, 0xAF) + set_memory_at(0x23d0, 0xAF); + + @staticmethod + def disable_callbacks(): + set_memory_at(0x23f2, 0xAF) + set_memory_at(0x23fe, 0xAF) + + @staticmethod + def get_map_group_id(): + """ + Returns the current map group. + """ + return get_memory_at(0xdcb5) + + @staticmethod + def get_map_id(): + """ + Returns the map number of the current map. + """ + return get_memory_at(0xdcb6) + + @staticmethod + def get_map_name(): + """ + Figures out the current map name. + """ + map_group_id = crystal.get_map_group_id() + map_id = crystal.get_map_id() + return map_names[map_group_id][map_id]["name"] + + @staticmethod + def get_xy(): + """ + (x, y) coordinates of player on map. + Relative to top-left corner of map. + """ + x = get_memory_at(0xdcb8) + y = get_memory_at(0xdcb7) + return (x, y) + + @staticmethod + def menu_select(id=1): + """ + Sets the cursor to the given pokemon in the player's party. This is + under Start -> PKMN. This is useful for selecting a certain pokemon + with fly or another skill. + + This probably works on other menus. + """ + set_memory_at(0xcfa9, id) + + @staticmethod + def is_in_battle(): + """ + Checks whether or not we're in a battle. + """ + return (get_memory_at(0xd22d) != 0) or crystal.is_in_link_battle() + + @staticmethod + def is_in_link_battle(): + return get_memory_at(0xc2dc) != 0 + + @staticmethod + def unlock_flypoints(): + """ + Unlocks different destinations for flying. + + Note: this might start at 0xDCA4 (minus one on all addresses), but not + sure. + """ + set_memory_at(0xDCA5, 0xFF) + set_memory_at(0xDCA6, 0xFF) + set_memory_at(0xDCA7, 0xFF) + set_memory_at(0xDCA8, 0xFF) + + @staticmethod + def get_gender(): + """ + Returns 'male' or 'female'. + """ + gender = get_memory_at(0xD472) + if gender == 0: + return "male" + elif gender == 1: + return "female" + else: + return gender + + @staticmethod + def get_player_name(): + """ + Returns the 7 characters making up the player's name. + """ + bytez = get_memory_range(0xD47D, 7) + name = translate_chars(bytez) + return name + + @staticmethod + def warp(map_group_id, map_id, x, y): + set_memory_at(0xdcb5, map_group_id) + set_memory_at(0xdcb6, map_id) + set_memory_at(0xdcb7, y) + set_memory_at(0xdcb8, x) + set_memory_at(0xd001, 0xFF) + set_memory_at(0xff9f, 0xF1) + set_memory_at(0xd432, 1) + set_memory_at(0xd434, 0 & 251) + + @staticmethod + def warp_pokecenter(): + crystal.warp(1, 1, 3, 3) + crystal.nstep(200) + + @staticmethod + def masterballs(): + # masterball + set_memory_at(0xd8d8, 1) + set_memory_at(0xd8d9, 99) + + # ultraball + set_memory_at(0xd8da, 2) + set_memory_at(0xd8db, 99) + + # pokeballs + set_memory_at(0xd8dc, 5) + set_memory_at(0xd8dd, 99) + + @staticmethod + def get_text(): + """ + Returns alphanumeric text on the screen. Other characters will not be + shown. + """ + output = "" + tiles = get_memory_range(0xc4a0, 1000) + for each in tiles: + if each in chars.keys(): + thing = chars[each] + acceptable = False + + if len(thing) == 2: + portion = thing[1:] + else: + portion = thing + + if portion in string.printable: + acceptable = True + + if acceptable: + output += thing + + # remove extra whitespace + output = re.sub(" +", " ", output) + output = output.strip() + + return output + + @staticmethod + def set_partymon2(): + """ + This causes corruption, so it's not working yet. + """ + memory = get_memory() + memory[0xdcd7] = 2 + memory[0xdcd9] = 0x7 + + memory[0xdd0f] = 0x7 + memory[0xdd10] = 0x1 + + # moves + memory[0xdd11] = 0x1 + memory[0xdd12] = 0x2 + memory[0xdd13] = 0x3 + memory[0xdd14] = 0x4 + + # id + memory[0xdd15] = 0x1 + memory[0xdd16] = 0x2 + + # experience + memory[0xdd17] = 0x2 + memory[0xdd18] = 0x3 + memory[0xdd19] = 0x4 + + # hp + memory[0xdd1a] = 0x5 + memory[0xdd1b] = 0x6 + + # current hp + memory[0xdd31] = 0x10 + memory[0xdd32] = 0x25 + + # max hp + memory[0xdd33] = 0x10 + memory[0xdd34] = 0x40 + + set_memory(memory) + +class TestEmulator(unittest.TestCase): + try: + state = load_state("cheating-12") + except: + if "__name__" == "__main__": + raise Exception("failed to setup unit tests because no save state found") + + def setUp(self): + load_rom() + set_state(self.state) + + def tearDown(self): + shutdown() + + def test_PlaceString(self): + call(0, 0x1078) + + # where to draw the text + registers["hl"] = 0xc4a0 + + # what text to read from + registers["de"] = 0x1276 + + nstep(10) + + text = crystal.get_text() + + self.assertTrue("TRAINER" in text) + +if __name__ == "__main__": + unittest.main() + diff --git a/extras/vba_config.py b/extras/vba_config.py new file mode 100644 index 000000000..8377c8818 --- /dev/null +++ b/extras/vba_config.py @@ -0,0 +1,15 @@ +#!/usr/bin/jython +# -*- encoding: utf-8 -*- +import os + +# by default we assume the user has things in their $HOME +home = os.path.expanduser("~") # or System.getProperty("user.home") + +# and that the pokecrystal project folder is in there somewhere +project_path = os.path.join(home, os.path.join("code", "pokecrystal")) + +# save states are in ~/code/pokecrystal/save-states/ +save_state_path = os.path.join(project_path, "save-states") + +# where is your rom? +rom_path = os.path.join(project_path, "baserom.gbc") |