summaryrefslogtreecommitdiff
path: root/extras
diff options
context:
space:
mode:
Diffstat (limited to 'extras')
-rw-r--r--extras/crystal.py507
-rw-r--r--extras/disassemble_map_scripts.py151
-rw-r--r--extras/gbz80disasm.py17
-rw-r--r--extras/map_names.py486
-rw-r--r--extras/pksv.py1
-rw-r--r--extras/vba.py841
-rw-r--r--extras/vba_config.py15
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")