summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/constants.py740
-rwxr-xr-xtools/script_extractor.py6
-rwxr-xr-xtools/script_extractor2.py393
3 files changed, 1136 insertions, 3 deletions
diff --git a/tools/constants.py b/tools/constants.py
new file mode 100644
index 0000000..ee3069e
--- /dev/null
+++ b/tools/constants.py
@@ -0,0 +1,740 @@
+boosters = {
+ 0x00: "BOOSTER_COLOSSEUM_NEUTRAL",
+ 0x01: "BOOSTER_COLOSSEUM_GRASS",
+ 0x02: "BOOSTER_COLOSSEUM_FIRE",
+ 0x03: "BOOSTER_COLOSSEUM_WATER",
+ 0x04: "BOOSTER_COLOSSEUM_LIGHTNING",
+ 0x05: "BOOSTER_COLOSSEUM_FIGHTING",
+ 0x06: "BOOSTER_COLOSSEUM_TRAINER",
+ 0x07: "BOOSTER_EVOLUTION_NEUTRAL",
+ 0x08: "BOOSTER_EVOLUTION_GRASS",
+ 0x09: "BOOSTER_EVOLUTION_FIRE",
+ 0x0a: "BOOSTER_EVOLUTION_WATER",
+ 0x0b: "BOOSTER_EVOLUTION_FIGHTING",
+ 0x0c: "BOOSTER_EVOLUTION_PSYCHIC",
+ 0x0d: "BOOSTER_EVOLUTION_TRAINER",
+ 0x0e: "BOOSTER_MYSTERY_NEUTRAL",
+ 0x0f: "BOOSTER_MYSTERY_GRASS_COLORLESS",
+ 0x10: "BOOSTER_MYSTERY_WATER_COLORLESS",
+ 0x11: "BOOSTER_MYSTERY_LIGHTNING_COLORLESS",
+ 0x12: "BOOSTER_MYSTERY_FIGHTING_COLORLESS",
+ 0x13: "BOOSTER_MYSTERY_TRAINER_COLORLESS",
+ 0x14: "BOOSTER_LABORATORY_NEUTRAL",
+ 0x15: "BOOSTER_LABORATORY_GRASS",
+ 0x16: "BOOSTER_LABORATORY_WATER",
+ 0x17: "BOOSTER_LABORATORY_PSYCHIC",
+ 0x18: "BOOSTER_LABORATORY_TRAINER",
+ 0x19: "BOOSTER_ENERGY_LIGHTNING_FIRE",
+ 0x1a: "BOOSTER_ENERGY_WATER_FIGHTING",
+ 0x1b: "BOOSTER_ENERGY_GRASS_PSYCHIC",
+ 0x1c: "BOOSTER_ENERGY_RANDOM",
+ 0xff: "NO_BOOSTER",
+}
+
+cards = {
+ 0x00: "VARIABLE_CARD",
+ 0x01: "GRASS_ENERGY",
+ 0x02: "FIRE_ENERGY",
+ 0x03: "WATER_ENERGY",
+ 0x04: "LIGHTNING_ENERGY",
+ 0x05: "FIGHTING_ENERGY",
+ 0x06: "PSYCHIC_ENERGY",
+ 0x07: "DOUBLE_COLORLESS_ENERGY",
+ 0x08: "BULBASAUR",
+ 0x09: "IVYSAUR",
+ 0x0a: "VENUSAUR1",
+ 0x0b: "VENUSAUR2",
+ 0x0c: "CATERPIE",
+ 0x0d: "METAPOD",
+ 0x0e: "BUTTERFREE",
+ 0x0f: "WEEDLE",
+ 0x10: "KAKUNA",
+ 0x11: "BEEDRILL",
+ 0x12: "EKANS",
+ 0x13: "ARBOK",
+ 0x14: "NIDORANF",
+ 0x15: "NIDORINA",
+ 0x16: "NIDOQUEEN",
+ 0x17: "NIDORANM",
+ 0x18: "NIDORINO",
+ 0x19: "NIDOKING",
+ 0x1a: "ZUBAT",
+ 0x1b: "GOLBAT",
+ 0x1c: "ODDISH",
+ 0x1d: "GLOOM",
+ 0x1e: "VILEPLUME",
+ 0x1f: "PARAS",
+ 0x20: "PARASECT",
+ 0x21: "VENONAT",
+ 0x22: "VENOMOTH",
+ 0x23: "BELLSPROUT",
+ 0x24: "WEEPINBELL",
+ 0x25: "VICTREEBEL",
+ 0x26: "GRIMER",
+ 0x27: "MUK",
+ 0x28: "EXEGGCUTE",
+ 0x29: "EXEGGUTOR",
+ 0x2a: "KOFFING",
+ 0x2b: "WEEZING",
+ 0x2c: "TANGELA1",
+ 0x2d: "TANGELA2",
+ 0x2e: "SCYTHER",
+ 0x2f: "PINSIR",
+ 0x30: "CHARMANDER",
+ 0x31: "CHARMELEON",
+ 0x32: "CHARIZARD",
+ 0x33: "VULPIX",
+ 0x34: "NINETAILS1",
+ 0x35: "NINETAILS2",
+ 0x36: "GROWLITHE",
+ 0x37: "ARCANINE1",
+ 0x38: "ARCANINE2",
+ 0x39: "PONYTA",
+ 0x3a: "RAPIDASH",
+ 0x3b: "MAGMAR1",
+ 0x3c: "MAGMAR2",
+ 0x3d: "FLAREON1",
+ 0x3e: "FLAREON2",
+ 0x3f: "MOLTRES1",
+ 0x40: "MOLTRES2",
+ 0x41: "SQUIRTLE",
+ 0x42: "WARTORTLE",
+ 0x43: "BLASTOISE",
+ 0x44: "PSYDUCK",
+ 0x45: "GOLDUCK",
+ 0x46: "POLIWAG",
+ 0x47: "POLIWHIRL",
+ 0x48: "POLIWRATH",
+ 0x49: "TENTACOOL",
+ 0x4a: "TENTACRUEL",
+ 0x4b: "SEEL",
+ 0x4c: "DEWGONG",
+ 0x4d: "SHELLDER",
+ 0x4e: "CLOYSTER",
+ 0x4f: "KRABBY",
+ 0x50: "KINGLER",
+ 0x51: "HORSEA",
+ 0x52: "SEADRA",
+ 0x53: "GOLDEEN",
+ 0x54: "SEAKING",
+ 0x55: "STARYU",
+ 0x56: "STARMIE",
+ 0x57: "MAGIKARP",
+ 0x58: "GYARADOS",
+ 0x59: "LAPRAS",
+ 0x5a: "VAPOREON1",
+ 0x5b: "VAPOREON2",
+ 0x5c: "OMANYTE",
+ 0x5d: "OMASTAR",
+ 0x5e: "ARTICUNO1",
+ 0x5f: "ARTICUNO2",
+ 0x60: "PIKACHU1",
+ 0x61: "PIKACHU2",
+ 0x62: "PIKACHU3",
+ 0x63: "PIKACHU4",
+ 0x64: "FLYING_PIKACHU",
+ 0x65: "SURFING_PIKACHU1",
+ 0x66: "SURFING_PIKACHU2",
+ 0x67: "RAICHU1",
+ 0x68: "RAICHU2",
+ 0x69: "MAGNEMITE1",
+ 0x6a: "MAGNEMITE2",
+ 0x6b: "MAGNETON1",
+ 0x6c: "MAGNETON2",
+ 0x6d: "VOLTORB",
+ 0x6e: "ELECTRODE1",
+ 0x6f: "ELECTRODE2",
+ 0x70: "ELECTABUZZ1",
+ 0x71: "ELECTABUZZ2",
+ 0x72: "JOLTEON1",
+ 0x73: "JOLTEON2",
+ 0x74: "ZAPDOS1",
+ 0x75: "ZAPDOS2",
+ 0x76: "ZAPDOS3",
+ 0x77: "SANDSHREW",
+ 0x78: "SANDSLASH",
+ 0x79: "DIGLETT",
+ 0x7a: "DUGTRIO",
+ 0x7b: "MANKEY",
+ 0x7c: "PRIMEAPE",
+ 0x7d: "MACHOP",
+ 0x7e: "MACHOKE",
+ 0x7f: "MACHAMP",
+ 0x80: "GEODUDE",
+ 0x81: "GRAVELER",
+ 0x82: "GOLEM",
+ 0x83: "ONIX",
+ 0x84: "CUBONE",
+ 0x85: "MAROWAK1",
+ 0x86: "MAROWAK2",
+ 0x87: "HITMONLEE",
+ 0x88: "HITMONCHAN",
+ 0x89: "RHYHORN",
+ 0x8a: "RHYDON",
+ 0x8b: "KABUTO",
+ 0x8c: "KABUTOPS",
+ 0x8d: "AERODACTYL",
+ 0x8e: "ABRA",
+ 0x8f: "KADABRA",
+ 0x90: "ALAKAZAM",
+ 0x91: "SLOWPOKE1",
+ 0x92: "SLOWPOKE2",
+ 0x93: "SLOWBRO",
+ 0x94: "GASTLY1",
+ 0x95: "GASTLY2",
+ 0x96: "HAUNTER1",
+ 0x97: "HAUNTER2",
+ 0x98: "GENGAR",
+ 0x99: "DROWZEE",
+ 0x9a: "HYPNO",
+ 0x9b: "MR_MIME",
+ 0x9c: "JYNX",
+ 0x9d: "MEWTWO1",
+ 0x9e: "MEWTWO2",
+ 0x9f: "MEWTWO3",
+ 0xa0: "MEW1",
+ 0xa1: "MEW2",
+ 0xa2: "MEW3",
+ 0xa3: "PIDGEY",
+ 0xa4: "PIDGEOTTO",
+ 0xa5: "PIDGEOT1",
+ 0xa6: "PIDGEOT2",
+ 0xa7: "RATTATA",
+ 0xa8: "RATICATE",
+ 0xa9: "SPEAROW",
+ 0xaa: "FEAROW",
+ 0xab: "CLEFAIRY",
+ 0xac: "CLEFABLE",
+ 0xad: "JIGGLYPUFF1",
+ 0xae: "JIGGLYPUFF2",
+ 0xaf: "JIGGLYPUFF3",
+ 0xb0: "WIGGLYTUFF",
+ 0xb1: "MEOWTH1",
+ 0xb2: "MEOWTH2",
+ 0xb3: "PERSIAN",
+ 0xb4: "FARFETCHD",
+ 0xb5: "DODUO",
+ 0xb6: "DODRIO",
+ 0xb7: "LICKITUNG",
+ 0xb8: "CHANSEY",
+ 0xb9: "KANGASKHAN",
+ 0xba: "TAUROS",
+ 0xbb: "DITTO",
+ 0xbc: "EEVEE",
+ 0xbd: "PORYGON",
+ 0xbe: "SNORLAX",
+ 0xbf: "DRATINI",
+ 0xc0: "DRAGONAIR",
+ 0xc1: "DRAGONITE1",
+ 0xc2: "DRAGONITE2",
+ 0xc3: "PROFESSOR_OAK",
+ 0xc4: "IMPOSTER_PROFESSOR_OAK",
+ 0xc5: "BILL",
+ 0xc6: "MR_FUJI",
+ 0xc7: "LASS",
+ 0xc8: "IMAKUNI_CARD",
+ 0xc9: "POKEMON_TRADER",
+ 0xca: "POKEMON_BREEDER",
+ 0xcb: "CLEFAIRY_DOLL",
+ 0xcc: "MYSTERIOUS_FOSSIL",
+ 0xcd: "ENERGY_RETRIEVAL",
+ 0xce: "SUPER_ENERGY_RETRIEVAL",
+ 0xcf: "ENERGY_SEARCH",
+ 0xd0: "ENERGY_REMOVAL",
+ 0xd1: "SUPER_ENERGY_REMOVAL",
+ 0xd2: "SWITCH",
+ 0xd3: "POKEMON_CENTER",
+ 0xd4: "POKE_BALL",
+ 0xd5: "SCOOP_UP",
+ 0xd6: "COMPUTER_SEARCH",
+ 0xd7: "POKEDEX",
+ 0xd8: "PLUSPOWER",
+ 0xd9: "DEFENDER",
+ 0xda: "ITEM_FINDER",
+ 0xdb: "GUST_OF_WIND",
+ 0xdc: "DEVOLUTION_SPRAY",
+ 0xdd: "POTION",
+ 0xde: "SUPER_POTION",
+ 0xdf: "FULL_HEAL",
+ 0xe0: "REVIVE",
+ 0xe1: "MAINTENANCE",
+ 0xe2: "POKEMON_FLUTE",
+ 0xe3: "GAMBLER",
+ 0xe4: "RECYCLE",
+ 0xff: "$ff",
+}
+
+decks = {
+ 0x00: "SAMS_PRACTICE_DECK_ID",
+ 0x01: "PRACTICE_PLAYER_DECK_ID",
+ 0x02: "SAMS_NORMAL_DECK_ID",
+ 0x03: "CHARMANDER_AND_FRIENDS_DECK_ID",
+ 0x04: "CHARMANDER_EXTRA_DECK_ID",
+ 0x05: "SQUIRTLE_AND_FRIENDS_DECK_ID",
+ 0x06: "SQUIRTLE_EXTRA_DECK_ID",
+ 0x07: "BULBASAUR_AND_FRIENDS_DECK_ID",
+ 0x08: "BULBASAUR_EXTRA_DECK_ID",
+ 0x09: "LIGHTNING_AND_FIRE_DECK_ID",
+ 0x0a: "WATER_AND_FIGHTING_DECK_ID",
+ 0x0b: "GRASS_AND_PSYCHIC_DECK_ID",
+ 0x0c: "LEGENDARY_MOLTRES_DECK_ID",
+ 0x0d: "LEGENDARY_ZAPDOS_DECK_ID",
+ 0x0e: "LEGENDARY_ARTICUNO_DECK_ID",
+ 0x0f: "LEGENDARY_DRAGONITE_DECK_ID",
+ 0x10: "FIRST_STRIKE_DECK_ID",
+ 0x11: "ROCK_CRUSHER_DECK_ID",
+ 0x12: "GO_GO_RAIN_DANCE_DECK_ID",
+ 0x13: "ZAPPING_SELFDESTRUCT_DECK_ID",
+ 0x14: "FLOWER_POWER_DECK_ID",
+ 0x15: "STRANGE_PSYSHOCK_DECK_ID",
+ 0x16: "WONDERS_OF_SCIENCE_DECK_ID",
+ 0x17: "FIRE_CHARGE_DECK_ID",
+ 0x18: "IM_RONALD_DECK_ID",
+ 0x19: "POWERFUL_RONALD_DECK_ID",
+ 0x1a: "INVINCIBLE_RONALD_DECK_ID",
+ 0x1b: "LEGENDARY_RONALD_DECK_ID",
+ 0x1c: "MUSCLES_FOR_BRAINS_DECK_ID",
+ 0x1d: "HEATED_BATTLE_DECK_ID",
+ 0x1e: "LOVE_TO_BATTLE_DECK_ID",
+ 0x1f: "EXCAVATION_DECK_ID",
+ 0x20: "BLISTERING_POKEMON_DECK_ID",
+ 0x21: "HARD_POKEMON_DECK_ID",
+ 0x22: "WATERFRONT_POKEMON_DECK_ID",
+ 0x23: "LONELY_FRIENDS_DECK_ID",
+ 0x24: "SOUND_OF_THE_WAVES_DECK_ID",
+ 0x25: "PIKACHU_DECK_ID",
+ 0x26: "BOOM_BOOM_SELFDESTRUCT_DECK_ID",
+ 0x27: "POWER_GENERATOR_DECK_ID",
+ 0x28: "ETCETERA_DECK_ID",
+ 0x29: "FLOWER_GARDEN_DECK_ID",
+ 0x2a: "KALEIDOSCOPE_DECK_ID",
+ 0x2b: "GHOST_DECK_ID",
+ 0x2c: "NAP_TIME_DECK_ID",
+ 0x2d: "STRANGE_POWER_DECK_ID",
+ 0x2e: "FLYIN_POKEMON_DECK_ID",
+ 0x2f: "LOVELY_NIDORAN_DECK_ID",
+ 0x30: "POISON_DECK_ID",
+ 0x31: "ANGER_DECK_ID",
+ 0x32: "FLAMETHROWER_DECK_ID",
+ 0x33: "RESHUFFLE_DECK_ID",
+ 0x34: "IMAKUNI_DECK_ID",
+ 0xff: "$ff",
+}
+
+directions = [
+ "NORTH",
+ "EAST",
+ "SOUTH",
+ "WEST",
+]
+
+events = [
+ "EVENT_FLAG_00",
+ "EVENT_FLAG_01",
+ "EVENT_TEMP_TALKED_TO_IMAKUNI",
+ "EVENT_TEMP_BATTLED_IMAKUNI",
+ "EVENT_FLAG_04",
+ "EVENT_FLAG_05",
+ "EVENT_FLAG_06",
+ "EVENT_FLAG_07",
+ "EVENT_FLAG_08",
+ "EVENT_FLAG_09",
+ "EVENT_FLAG_0A",
+ "EVENT_BEAT_AMY",
+ "EVENT_FLAG_0C",
+ "EVENT_FLAG_0D",
+ "EVENT_FLAG_0E",
+ "EVENT_FLAG_0F",
+ "EVENT_FLAG_10",
+ "EVENT_FLAG_11",
+ "EVENT_FLAG_12",
+ "EVENT_IMAKUNI_STATE",
+ "EVENT_FLAG_14",
+ "EVENT_BEAT_SARA",
+ "EVENT_BEAT_AMANDA",
+ "EVENT_FLAG_17",
+ "EVENT_FLAG_18",
+ "EVENT_FLAG_19",
+ "EVENT_FLAG_1A",
+ "EVENT_FLAG_1B",
+ "EVENT_FLAG_1C",
+ "EVENT_FLAG_1D",
+ "EVENT_FLAG_1E",
+ "EVENT_FLAG_1F",
+ "EVENT_FLAG_20",
+ "EVENT_FLAG_21",
+ "EVENT_RECEIVED_LEGENDARY_CARD",
+ "EVENT_FLAG_23",
+ "EVENT_FLAG_24",
+ "EVENT_FLAG_25",
+ "EVENT_FLAG_26",
+ "EVENT_FLAG_27",
+ "EVENT_FLAG_28",
+ "EVENT_FLAG_29",
+ "EVENT_FLAG_2A",
+ "EVENT_FLAG_2B",
+ "EVENT_FLAG_2C",
+ "EVENT_FLAG_2D",
+ "EVENT_MEDAL_COUNT",
+ "EVENT_FLAG_2F",
+ "EVENT_FLAG_30",
+ "EVENT_FLAG_31",
+ "EVENT_FLAG_32",
+ "EVENT_JOSHUA_STATE",
+ "EVENT_IMAKUNI_ROOM",
+ "EVENT_FLAG_35",
+ "EVENT_IMAKUNI_WIN_COUNT",
+ "EVENT_FLAG_37",
+ "EVENT_FLAG_38",
+ "EVENT_FLAG_39",
+ "EVENT_FLAG_3A",
+ "EVENT_FLAG_3B",
+ "FLAG_BEAT_BRITTANY",
+ "EVENT_FLAG_3D",
+ "EVENT_FLAG_3E",
+ "EVENT_FLAG_3F",
+ "EVENT_FLAG_40",
+ "EVENT_FLAG_41",
+ "EVENT_FLAG_42",
+ "EVENT_FLAG_43",
+ "EVENT_FLAG_44",
+ "EVENT_FLAG_45",
+ "EVENT_FLAG_46",
+ "EVENT_FLAG_47",
+ "EVENT_FLAG_48",
+ "EVENT_FLAG_49",
+ "EVENT_FLAG_4A",
+ "EVENT_FLAG_4B",
+ "EVENT_FLAG_4C",
+ "EVENT_FLAG_4D",
+ "EVENT_FLAG_4E",
+ "EVENT_FLAG_4F",
+ "EVENT_FLAG_50",
+ "EVENT_FLAG_51",
+ "EVENT_FLAG_52",
+ "EVENT_FLAG_53",
+ "EVENT_FLAG_54",
+ "EVENT_FLAG_55",
+ "EVENT_FLAG_56",
+ "EVENT_FLAG_57",
+ "EVENT_FLAG_58",
+ "EVENT_FLAG_59",
+ "EVENT_FLAG_5A",
+ "EVENT_FLAG_5B",
+ "EVENT_FLAG_5C",
+ "EVENT_FLAG_5D",
+ "EVENT_FLAG_5E",
+ "EVENT_FLAG_5F",
+ "EVENT_FLAG_60",
+ "EVENT_FLAG_61",
+ "EVENT_FLAG_62",
+ "EVENT_FLAG_63",
+ "EVENT_FLAG_64",
+ "EVENT_FLAG_65",
+ "EVENT_FLAG_66",
+ "EVENT_FLAG_67",
+ "EVENT_FLAG_68",
+ "EVENT_FLAG_69",
+ "EVENT_FLAG_6A",
+ "EVENT_FLAG_6B",
+ "EVENT_FLAG_6C",
+ "EVENT_FLAG_6D",
+ "EVENT_FLAG_6E",
+ "EVENT_FLAG_6F",
+ "EVENT_FLAG_70",
+ "EVENT_FLAG_71",
+ "EVENT_FLAG_72",
+ "EVENT_FLAG_73",
+ "EVENT_FLAG_74",
+ "EVENT_FLAG_75",
+ "EVENT_FLAG_76",
+]
+
+maps = [
+ "OVERWORLD_MAP",
+ "MASON_LABORATORY",
+ "DECK_MACHINE_ROOM",
+ "ISHIHARAS_HOUSE",
+ "FIGHTING_CLUB_ENTRANCE",
+ "FIGHTING_CLUB_LOBBY",
+ "FIGHTING_CLUB",
+ "ROCK_CLUB_ENTRANCE",
+ "ROCK_CLUB_LOBBY",
+ "ROCK_CLUB",
+ "WATER_CLUB_ENTRANCE",
+ "WATER_CLUB_LOBBY",
+ "WATER_CLUB",
+ "LIGHTNING_CLUB_ENTRANCE",
+ "LIGHTNING_CLUB_LOBBY",
+ "LIGHTNING_CLUB",
+ "GRASS_CLUB_ENTRANCE",
+ "GRASS_CLUB_LOBBY",
+ "GRASS_CLUB",
+ "PSYCHIC_CLUB_ENTRANCE",
+ "PSYCHIC_CLUB_LOBBY",
+ "PSYCHIC_CLUB",
+ "SCIENCE_CLUB_ENTRANCE",
+ "SCIENCE_CLUB_LOBBY",
+ "SCIENCE_CLUB",
+ "FIRE_CLUB_ENTRANCE",
+ "FIRE_CLUB_LOBBY",
+ "FIRE_CLUB",
+ "CHALLENGE_HALL_ENTRANCE",
+ "CHALLENGE_HALL_LOBBY",
+ "CHALLENGE_HALL",
+ "POKEMON_DOME_ENTRANCE",
+ "POKEMON_DOME",
+ "HALL_OF_HONOR",
+]
+
+npcs = [
+ "",
+ "NPC_DRMASON",
+ "NPC_RONALD1",
+ "NPC_ISHIHARA",
+ "NPC_IMAKUNI",
+ "NPC_05",
+ "NPC_06",
+ "NPC_SAM",
+ "NPC_TECH1",
+ "NPC_TECH2",
+ "NPC_TECH3",
+ "NPC_TECH4",
+ "NPC_TECH5",
+ "NPC_TECH6",
+ "NPC_CLERK1",
+ "NPC_CLERK2",
+ "NPC_CLERK3",
+ "NPC_CLERK4",
+ "NPC_CLERK5",
+ "NPC_CLERK6",
+ "NPC_CLERK7",
+ "NPC_CLERK8",
+ "NPC_CLERK9",
+ "NPC_CHRIS",
+ "NPC_MICHAEL",
+ "NPC_JESSICA",
+ "NPC_MITCH",
+ "NPC_MATTHEW",
+ "NPC_RYAN",
+ "NPC_ANDREW",
+ "NPC_GENE",
+ "NPC_SARA",
+ "NPC_AMANDA",
+ "NPC_JOSHUA",
+ "NPC_AMY",
+ "NPC_JENNIFER",
+ "NPC_NICHOLAS",
+ "NPC_BRANDON",
+ "NPC_ISAAC",
+ "NPC_BRITTANY",
+ "NPC_KRISTIN",
+ "NPC_HEATHER",
+ "NPC_NIKKI",
+ "NPC_ROBERT",
+ "NPC_DANIEL",
+ "NPC_STEPHANIE",
+ "NPC_MURRAY1",
+ "NPC_JOSEPH",
+ "NPC_DAVID",
+ "NPC_ERIK",
+ "NPC_RICK",
+ "NPC_JOHN",
+ "NPC_ADAM",
+ "NPC_JONATHAN",
+ "NPC_KEN",
+ "NPC_COURTNEY",
+ "NPC_STEVE",
+ "NPC_JACK",
+ "NPC_ROD",
+ "NPC_CLERK10",
+ "NPC_GIFT_CENTER_CLERK",
+ "NPC_MAN1",
+ "NPC_WOMAN1",
+ "NPC_CHAP1",
+ "NPC_GAL1",
+ "NPC_LASS1",
+ "NPC_CHAP2",
+ "NPC_LASS2",
+ "NPC_PAPPY1",
+ "NPC_LAD1",
+ "NPC_LAD2",
+ "NPC_CHAP3",
+ "NPC_CLERK12",
+ "NPC_CLERK13",
+ "NPC_HOST",
+ "NPC_SPECS1",
+ "NPC_BUTCH",
+ "NPC_GRANNY1",
+ "NPC_LASS3",
+ "NPC_MAN2",
+ "NPC_PAPPY2",
+ "NPC_LASS4",
+ "NPC_HOOD1",
+ "NPC_GRANNY2",
+ "NPC_GAL2",
+ "NPC_LAD3",
+ "NPC_GAL3",
+ "NPC_CHAP4",
+ "NPC_MAN3",
+ "NPC_SPECS2",
+ "NPC_SPECS3",
+ "NPC_WOMAN2",
+ "NPC_MANIA",
+ "NPC_PAPPY3",
+ "NPC_GAL4",
+ "NPC_CHAMP",
+ "NPC_HOOD2",
+ "NPC_LASS5",
+ "NPC_CHAP5",
+ "NPC_AARON",
+ "NPC_GUIDE",
+ "NPC_TECH7",
+ "NPC_TECH8",
+ "NPC_TORCH",
+ "NPC_LEGENDARY_CARD_TOP_LEFT",
+ "NPC_LEGENDARY_CARD_TOP_RIGHT",
+ "NPC_LEGENDARY_CARD_LEFT_SPARK",
+ "NPC_LEGENDARY_CARD_BOTTOM_LEFT",
+ "NPC_LEGENDARY_CARD_BOTTOM_RIGHT",
+ "NPC_LEGENDARY_CARD_RIGHT_SPARK",
+ "NPC_6E",
+ "NPC_6F",
+ "NPC_MURRAY2",
+ "NPC_RONALD2",
+ "NPC_RONALD3",
+ "NPC_73",
+]
+
+sfxs = [
+ "SFX_STOP",
+ "SFX_01",
+ "SFX_02",
+ "SFX_03",
+ "SFX_04",
+ "SFX_05",
+ "SFX_06",
+ "SFX_07",
+ "SFX_08",
+ "SFX_09",
+ "SFX_0A",
+ "SFX_0B",
+ "SFX_0C",
+ "SFX_0D",
+ "SFX_0E",
+ "SFX_0F",
+ "SFX_10",
+ "SFX_11",
+ "SFX_12",
+ "SFX_13",
+ "SFX_14",
+ "SFX_15",
+ "SFX_16",
+ "SFX_17",
+ "SFX_18",
+ "SFX_19",
+ "SFX_1A",
+ "SFX_1B",
+ "SFX_1C",
+ "SFX_1D",
+ "SFX_1E",
+ "SFX_1F",
+ "SFX_20",
+ "SFX_21",
+ "SFX_22",
+ "SFX_23",
+ "SFX_24",
+ "SFX_25",
+ "SFX_26",
+ "SFX_27",
+ "SFX_28",
+ "SFX_29",
+ "SFX_2A",
+ "SFX_2B",
+ "SFX_2C",
+ "SFX_2D",
+ "SFX_2E",
+ "SFX_2F",
+ "SFX_30",
+ "SFX_31",
+ "SFX_32",
+ "SFX_33",
+ "SFX_34",
+ "SFX_35",
+ "SFX_36",
+ "SFX_37",
+ "SFX_38",
+ "SFX_39",
+ "SFX_3A",
+ "SFX_3B",
+ "SFX_3C",
+ "SFX_3D",
+ "SFX_3E",
+ "SFX_3F",
+ "SFX_40",
+ "SFX_41",
+ "SFX_42",
+ "SFX_43",
+ "SFX_44",
+ "SFX_45",
+ "SFX_46",
+ "SFX_47",
+ "SFX_48",
+ "SFX_49",
+ "SFX_4A",
+ "SFX_4B",
+ "SFX_4C",
+ "SFX_4D",
+ "SFX_4E",
+ "SFX_4F",
+ "SFX_50",
+ "SFX_51",
+ "SFX_52",
+ "SFX_53",
+ "SFX_54",
+ "SFX_55",
+ "SFX_56",
+ "SFX_57",
+ "SFX_58",
+ "SFX_59",
+ "SFX_5A",
+ "SFX_5B",
+ "SFX_5C",
+ "SFX_5D",
+ "SFX_5E",
+ "SFX_5F",
+]
+
+songs = [
+ "MUSIC_STOP",
+ "MUSIC_TITLESCREEN",
+ "MUSIC_DUEL_THEME_1",
+ "MUSIC_DUEL_THEME_2",
+ "MUSIC_DUEL_THEME_3",
+ "MUSIC_PAUSE_MENU",
+ "MUSIC_PC_MAIN_MENU",
+ "MUSIC_DECK_MACHINE",
+ "MUSIC_CARD_POP",
+ "MUSIC_OVERWORLD",
+ "MUSIC_POKEMON_DOME",
+ "MUSIC_CHALLENGE_HALL",
+ "MUSIC_CLUB_1",
+ "MUSIC_CLUB_2",
+ "MUSIC_CLUB_3",
+ "MUSIC_RONALD",
+ "MUSIC_IMAKUNI",
+ "MUSIC_HALL_OF_HONOR",
+ "MUSIC_CREDITS",
+ "MUSIC_UNUSED_13",
+ "MUSIC_UNUSED_14",
+ "MUSIC_MATCH_START_1",
+ "MUSIC_MATCH_START_2",
+ "MUSIC_MATCH_START_3",
+ "MUSIC_MATCH_VICTORY",
+ "MUSIC_MATCH_LOSS",
+ "MUSIC_MATCH_DRAW",
+ "MUSIC_UNUSED_1B",
+ "MUSIC_BOOSTER_PACK",
+ "MUSIC_MEDAL",
+ "MUSIC_UNUSED_1E",
+]
diff --git a/tools/script_extractor.py b/tools/script_extractor.py
index 8869c66..ed8eaf9 100755
--- a/tools/script_extractor.py
+++ b/tools/script_extractor.py
@@ -86,9 +86,9 @@ def decodeLine(scriptList, game_data, loc, ignore_broken, locList):
wordLoc = (game_data[loc] + (game_data[loc+1]<<8))
if wordLoc == 0000:
if macroMode:
- ret += "NO_JUMP, "
+ ret += "NULL, "
else:
- ret += "\tdw NO_JUMP\n"
+ ret += "\tdw NULL\n"
else:
if macroMode:
ret += ".ows_" + format(wordLoc+0x8000,"04x") + ", "
@@ -285,7 +285,7 @@ def createList(): # this is a func just so all this can go at the bottom
("Func_d3e0", "", DO_NOT_QUIT,""),
("Func_d3fe", "q", DO_NOT_QUIT,""),
("Func_d408", "b", DO_NOT_QUIT,""),
- ("Func_d40f", "q", DO_NOT_QUIT,""),
+ ("ScriptCommand_PlaySong", "b", DO_NOT_QUIT,"play_song"),
("ScriptCommand_PlaySFX", "b", DO_NOT_QUIT,"play_sfx"),
("ScriptCommand_PauseSong", "", DO_NOT_QUIT,"pause_song"),
("ScriptCommand_ResumeSong", "", DO_NOT_QUIT,"resume_song"),
diff --git a/tools/script_extractor2.py b/tools/script_extractor2.py
new file mode 100755
index 0000000..0e835e4
--- /dev/null
+++ b/tools/script_extractor2.py
@@ -0,0 +1,393 @@
+#!/usr/bin/env python
+
+import argparse
+
+from constants import boosters
+from constants import cards
+from constants import decks
+from constants import directions
+from constants import events
+from constants import maps
+from constants import npcs
+from constants import sfxs
+from constants import songs
+
+args = None
+rom = None
+
+# script command names and parameter lists
+script_commands = {
+ 0xe7: { "name": "start_script", "params": [] },
+
+ 0x00: { "name": "end_script_loop", "params": [] },
+ 0x01: { "name": "close_advanced_text_box", "params": [] },
+ 0x02: { "name": "print_text_string", "params": [ "text" ] },
+ 0x03: { "name": "Func_ccdc", "params": [ "text" ] }, # print text and ???
+ 0x04: { "name": "ask_question_jump", "params": [ "text", "label" ] },
+ 0x05: { "name": "start_battle", "params": [ "prizes", "deck", "song" ] },
+ 0x06: { "name": "print_variable_text", "params": [ "text", "text" ] },
+ 0x07: { "name": "Func_cda8", "params": [ "text", "text" ] }, # print variable text and ???
+ 0x08: { "name": "print_text_quit_fully", "params": [ "text" ] },
+ 0x09: { "name": "Func_cdcb", "params": [] },
+ 0x0a: { "name": "move_active_npc_by_direction", "params": [ "movement_table" ] },
+ 0x0b: { "name": "close_text_box", "params": [] },
+ 0x0c: { "name": "give_booster_packs", "params": [ "booster", "booster", "booster" ] },
+ 0x0d: { "name": "jump_if_card_owned", "params": [ "card", "label" ] },
+ 0x0e: { "name": "jump_if_card_in_collection", "params": [ "card", "label" ] },
+ 0x0f: { "name": "give_card", "params": [ "card" ] },
+ 0x10: { "name": "take_card", "params": [ "card" ] },
+ 0x11: { "name": "Func_cf53", "params": [ "label" ] }, # jump if any energy cards in collection
+ 0x12: { "name": "Func_cf7b", "params": [] }, # remove all energy cards from collection
+ 0x13: { "name": "jump_if_enough_cards_owned", "params": [ "word_decimal", "label" ] },
+ 0x14: { "name": "fight_club_pupil_jump", "params": [ "label", "label", "label", "label", "label" ] },
+ 0x15: { "name": "Func_cfc6", "params": [ "byte" ] },
+ 0x16: { "name": "Func_cfd4", "params": [] },
+ 0x17: { "name": "Func_d00b", "params": [] }, # get card name for EVENT_FLAG_2B
+ 0x18: { "name": "Func_d025", "params": [ "label" ] }, # jump if card in EVENT_FLAG_2B is in collection or decks
+ 0x19: { "name": "Func_d032", "params": [ "label" ] }, # jump if card in EVENT_FLAG_2B is in collection
+ 0x1a: { "name": "Func_d03f", "params": [] }, # remove card in EVENT_FLAG_2B from collection
+ 0x1b: { "name": "script_jump", "params": [ "label" ] },
+ 0x1c: { "name": "try_give_medal_pc_packs", "params": [] },
+ 0x1d: { "name": "set_player_direction", "params": [ "direction" ] },
+ 0x1e: { "name": "move_player", "params": [ "direction", "byte_decimal" ] },
+ 0x1f: { "name": "show_card_received_screen", "params": [ "card" ] },
+ 0x20: { "name": "set_dialog_npc", "params": [ "npc" ] },
+ 0x21: { "name": "set_next_npc_and_script", "params": [ "npc", "label" ] },
+ 0x22: { "name": "Func_d095", "params": [ "byte", "byte", "byte" ] }, # LOADED_NPC_FIELD_05 and LOADED_NPC_FIELD_06
+ 0x23: { "name": "Func_d0be", "params": [ "byte", "byte" ] }, # coords
+ 0x24: { "name": "do_frames", "params": [ "byte_decimal" ] },
+ 0x25: { "name": "Func_d0d9", "params": [ "byte", "byte", "label" ] }, # coords, jump if npc coords match
+ 0x26: { "name": "jump_if_player_coords_match", "params": [ "byte_decimal", "byte_decimal", "label" ] },
+ 0x27: { "name": "move_active_npc", "params": [ "movement" ] },
+ 0x28: { "name": "give_one_of_each_trainer_booster", "params": [] },
+ 0x29: { "name": "Func_d103", "params": [ "npc", "label" ] }, # jump if npc loaded
+ 0x2a: { "name": "Func_d125", "params": [ "event" ] }, # give medal
+ 0x2b: { "name": "Func_d135", "params": [ "byte" ] }, # load current map name into tx ram slot
+ 0x2c: { "name": "Func_d16b", "params": [ "byte" ] }, # load current npc name into tx ram slot
+ 0x2d: { "name": "Func_cd4f", "params": [ "prizes", "deck", "song" ] },
+ 0x2e: { "name": "Func_cd94", "params": [ "text", "text", "text" ] },
+ 0x2f: { "name": "move_wram_npc", "params": [ "movement" ] },
+ 0x30: { "name": "Func_cdd8", "params": [] },
+ 0x31: { "name": "Func_cdf5", "params": [ "byte", "byte" ] }, # coords
+ 0x32: { "name": "Func_d195", "params": [] }, # pick challenge hall opponent
+ 0x33: { "name": "Func_d1ad", "params": [] }, # open menu
+ 0x34: { "name": "Func_d1b3", "params": [] }, # pick trade card
+ 0x35: { "name": "quit_script_fully", "params": [] },
+ 0x36: { "name": "Func_d244", "params": [ "byte" ] },
+ 0x37: { "name": "choose_deck_to_duel_against_multichoice", "params": [] },
+ 0x38: { "name": "open_deck_machine", "params": [ "byte" ] },
+ 0x39: { "name": "choose_starter_deck_multichoice", "params": [] },
+ 0x3a: { "name": "enter_map", "params": [ "byte", "map", "byte_decimal", "byte_decimal", "direction" ] },
+ 0x3b: { "name": "move_arbitrary_npc", "params": [ "npc", "movement" ] },
+ 0x3c: { "name": "Func_d209", "params": [] }, # pick legendary card
+ 0x3d: { "name": "Func_d38f", "params": [ "byte" ] },
+ 0x3e: { "name": "Func_d396", "params": [ "byte" ] },
+ 0x3f: { "name": "Func_cd76", "params": [] },
+ 0x40: { "name": "Func_d39d", "params": [ "byte" ] },
+ 0x41: { "name": "Func_d3b9", "params": [] },
+ 0x42: { "name": "try_give_pc_pack", "params": [ "byte" ] },
+ 0x43: { "name": "script_nop", "params": [] },
+ 0x44: { "name": "Func_d3d4", "params": [] },
+ 0x45: { "name": "Func_d3e0", "params": [] },
+ 0x46: { "name": "Func_d3fe", "params": [ "song" ] },
+ 0x47: { "name": "Func_d408", "params": [ "song" ] }, # set default song
+ 0x48: { "name": "play_song", "params": [ "song" ] },
+ 0x49: { "name": "play_sfx", "params": [ "sfx" ] },
+ 0x4a: { "name": "pause_song", "params": [] },
+ 0x4b: { "name": "resume_song", "params": [] },
+ 0x4c: { "name": "Func_d41d", "params": [] }, # play default song
+ 0x4d: { "name": "wait_for_song_to_finish", "params": [] },
+ 0x4e: { "name": "Func_d435", "params": [ "byte" ] },
+ 0x4f: { "name": "ask_question_jump_default_yes", "params": [ "text", "label" ] },
+ 0x50: { "name": "show_sam_normal_multichoice", "params": [] },
+ 0x51: { "name": "show_sam_tutorial_multichoice", "params": [] },
+ 0x52: { "name": "Func_d43d", "params": [] },
+ 0x53: { "name": "end_script_loop_2", "params": [] },
+ 0x54: { "name": "end_script_loop_3", "params": [] },
+ 0x55: { "name": "end_script_loop_4", "params": [] },
+ 0x56: { "name": "end_script_loop_5", "params": [] },
+ 0x57: { "name": "end_script_loop_6", "params": [] },
+ 0x58: { "name": "script_set_flag_value", "params": [ "event", "byte" ] },
+ 0x59: { "name": "jump_if_flag_zero_1", "params": [ "event", "label" ] },
+ 0x5a: { "name": "jump_if_flag_nonzero_1", "params": [ "event", "label" ] },
+ 0x5b: { "name": "jump_if_flag_equal", "params": [ "event", "byte", "label" ] },
+ 0x5c: { "name": "jump_if_flag_not_equal", "params": [ "event", "byte", "label" ] },
+ 0x5d: { "name": "jump_if_flag_not_less_than", "params": [ "event", "byte", "label" ] },
+ 0x5e: { "name": "jump_if_flag_less_than", "params": [ "event", "byte", "label" ] },
+ 0x5f: { "name": "max_out_flag_value", "params": [ "event" ] },
+ 0x60: { "name": "zero_out_flag_value", "params": [ "event" ] },
+ 0x61: { "name": "jump_if_flag_nonzero_2", "params": [ "event", "label"] },
+ 0x62: { "name": "jump_if_flag_zero_2", "params": [ "event", "label" ] },
+ 0x63: { "name": "increment_flag_value", "params": [ "event" ] },
+ 0x64: { "name": "end_script_loop_7", "params": [] },
+ 0x65: { "name": "end_script_loop_8", "params": [] },
+ 0x66: { "name": "end_script_loop_9", "params": [] },
+ 0x67: { "name": "end_script_loop_10", "params": [] },
+}
+
+quit_commands = [
+ 0x00,
+ 0x08,
+ 0x1b,
+ 0x35,
+ 0x53,
+ 0x54,
+ 0x55,
+ 0x56,
+ 0x57,
+ 0x64,
+ 0x65,
+ 0x66,
+ 0x67,
+]
+
+# length in bytes of each type of parameter
+param_lengths = {
+ "byte": 1,
+ "byte_decimal": 1,
+ "booster": 1,
+ "card": 1,
+ "deck": 1,
+ "direction": 1,
+ "event": 1,
+ "map": 1,
+ "npc": 1,
+ "prizes": 1,
+ "sfx": 1,
+ "song": 1,
+ "word_decimal": 2,
+ "movement": 2,
+ "movement_table": 2,
+ "text": 2,
+ "label": 2,
+}
+
+def get_bank(address):
+ return int(address / 0x4000)
+
+def get_relative_address(address):
+ if address < 0x4000:
+ return address
+ return (address % 0x4000) + 0x4000
+
+# get absolute pointer stored at an address in the rom
+# if bank is None, assumes the pointer refers to the same bank as the bank it is located in
+def get_pointer(address, bank=None):
+ raw_pointer = rom[address + 1] * 0x100 + rom[address]
+ if raw_pointer < 0x4000:
+ bank = 0
+ if bank is None:
+ bank = get_bank(address)
+ return (raw_pointer % 0x4000) + bank * 0x4000
+
+def make_address_comment(address):
+ return " ; {:x} ({:x}:{:x})\n".format(address, get_bank(address), get_relative_address(address))
+
+def make_blob(start, output, end=None):
+ return { "start": start, "output": output, "end": end if end else start }
+
+def dump_movement(address):
+ blobs = []
+ blobs.append(make_blob(address, "NPCMovement_{:x}:".format(address) + make_address_comment(address)))
+ while 1:
+ movement = rom[address]
+ if movement == 0xff:
+ blobs.append(make_blob(address, "\tdb ${:02x}\n\n".format(movement), address + 1))
+ break
+ if movement == 0xfe:
+ jump = rom[address + 1]
+ if jump > 127:
+ jump -= 256
+ blobs.append(make_blob(address, "\tdb ${:02x}, {}\n\n".format(movement, jump), address + 2))
+ break
+ blobs.append(make_blob(address, "\tdb {}".format(directions[movement & 0b01111111]) + (" | NO_MOVE\n" if movement & 0b10000000 else "\n"), address + 1))
+ address += 1
+ return blobs
+
+def dump_movement_table(address):
+ blobs = []
+ blobs.append(make_blob(address, "NPCMovementTable_{:x}:".format(address) + make_address_comment(address)))
+ for i in range(4):
+ pointer = get_pointer(address)
+ blobs.append(make_blob(address, "\tdw NPCMovement_{:x}\n".format(pointer) + ("\n" if i == 3 else ""), address + 2))
+ blobs += dump_movement(pointer)
+ address += 2
+ return blobs
+
+# parse a script starting at the given address
+# returns a list of all commands
+def dump_script(start_address, address=None, visited=set()):
+ blobs = []
+ branches = set()
+ if address is None:
+ blobs.append(make_blob(start_address, "Script_{:x}:".format(start_address) + make_address_comment(start_address)))
+ address = start_address
+ else:
+ blobs.append(make_blob(address, ".ows_{:x}\n".format(address)))
+ if address in visited:
+ return blobs
+ visited.add(address)
+ while 1:
+ command_address = address
+ command_id = rom[command_address]
+ command = script_commands[command_id]
+ address += 1
+ macro_mode = not command["name"].startswith("Func_")
+ if macro_mode:
+ output = "\t{}".format(command["name"])
+ else:
+ output = "\trun_command {}".format(command["name"])
+ # print all params for current command
+ for i in range(len(command["params"])):
+ param = rom[address]
+ param_type = command["params"][i]
+ param_length = param_lengths[param_type]
+ if param_type == "byte":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " ${:02x}".format(param)
+ elif param_type == "byte_decimal":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(param)
+ elif param_type == "booster":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(boosters[param])
+ elif param_type == "card":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(cards[param])
+ elif param_type == "deck":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(decks[param])
+ elif param_type == "direction":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(directions[param])
+ elif param_type == "event":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(events[param])
+ elif param_type == "map":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(maps[param])
+ elif param_type == "npc":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(npcs[param])
+ elif param_type == "prizes":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " PRIZES_{}".format(param)
+ elif param_type == "sfx":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(sfxs[param])
+ elif param_type == "song":
+ if not macro_mode:
+ output += "\n\tdb"
+ output += " {}".format(songs[param])
+ elif param_type == "word_decimal":
+ if not macro_mode:
+ output += "\n\tdw"
+ output += " {}".format(param + rom[address + 1] * 0x100)
+ elif param_type == "movement":
+ param = get_pointer(address)
+ label = "NPCMovement_{:x}".format(param)
+ if not macro_mode:
+ output += "\n\tdw"
+ output += " {}".format(label)
+ blobs += dump_movement(param)
+ elif param_type == "movement_table":
+ param = get_pointer(address)
+ label = "NPCMovementTable_{:x}".format(param)
+ if not macro_mode:
+ output += "\n\tdw"
+ output += " {}".format(label)
+ blobs += dump_movement_table(param)
+ elif param_type == "text":
+ text_id = param + rom[address + 1] * 0x100
+ if not macro_mode:
+ if text_id == 0x0000:
+ output += "\n\tdw"
+ else:
+ output += "\n\ttx"
+ if text_id == 0x0000:
+ output += " NULL"
+ else:
+ output += " Text{:04x}".format(text_id)
+ elif param_type == "label":
+ param = get_pointer(address)
+ if param == 0x0000:
+ label = "NULL"
+ elif param == start_address:
+ label = "Script_{:x}".format(param)
+ else:
+ label = ".ows_{:x}".format(param)
+ if param > start_address or args.allow_backward_jumps:
+ branches.add(param)
+ if not macro_mode:
+ output += "\n\tdw"
+ output += " {}".format(label)
+ address += param_length
+ if macro_mode and i < len(command["params"]) - 1:
+ output += ","
+ output += "\n"
+ blobs.append(make_blob(command_address, output, address))
+ if command_id in quit_commands:
+ if rom[address] == 0xc9:
+ blobs.append(make_blob(address, "\tret\n", address + 1))
+ address += 1
+ blobs.append(make_blob(address, "; 0x{:x}\n\n".format(address)))
+ break
+ for branch in branches:
+ blobs += dump_script(start_address, branch, visited)
+ return blobs
+
+def fill_gap(start, end):
+ output = ""
+ for address in range(start, end):
+ output += "\tdb ${:x}\n".format(rom[address])
+ output += "\n"
+ return output
+
+def sort_and_filter(blobs):
+ blobs.sort(key=lambda b: (b["start"], b["end"], not b["output"].startswith(";")))
+ filtered = []
+ for blob, next in zip(blobs, blobs[1:]+[None]):
+ if next and blob["start"] == next["start"] and blob["output"] == next["output"]:
+ continue
+ if next and blob["end"] < next["start"] and get_bank(blob["end"]) == get_bank(next["start"]):
+ if args.fill_gaps:
+ blob["output"] += fill_gap(blob["end"], next["start"])
+ else:
+ blob["output"] += "; gap from 0x{:x} to 0x{:x}\n\n".format(blob["end"], next["start"])
+ filtered.append(blob)
+ if len(filtered) > 0:
+ filtered[-1]["output"] = filtered[-1]["output"].rstrip("\n")
+ return filtered
+
+if __name__ == "__main__":
+ ap = argparse.ArgumentParser(description="Pokemon TCG Script Extractor")
+ ap.add_argument("-b", "--allow-backward-jumps", action="store_true", help="extract scripts that are found before the starting address")
+ ap.add_argument("-g", "--fill-gaps", action="store_true", help="use 'db's to fill the gaps between visited locations")
+ ap.add_argument("-i", "--ignore-errors", action="store_true", help="silently proceed to the next address if an error occurs")
+ ap.add_argument("-r", "--rom", default="baserom.gbc", help="rom file to extract script from")
+ ap.add_argument("addresses", nargs="+", help="addresses to extract from")
+ args = ap.parse_args()
+ rom = bytearray(open(args.rom, "rb").read())
+ blobs = []
+ for address in args.addresses:
+ try:
+ blobs += dump_script(int(address, 16))
+ except:
+ if not args.ignore_errors:
+ raise
+ blobs = sort_and_filter(blobs)
+ output = ""
+ for blob in blobs:
+ output += blob["output"]
+ print(output)