Some tooling to extract assets
Run: restool.py decode to extract assets into assets/defs/* Run: restool.py compile to compile the assets back into a rom
This commit is contained in:
4
assets/.gitignore
vendored
Normal file
4
assets/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/__pycache__/
|
||||
/defs/*
|
||||
/out.bin
|
||||
/out_is_set.bin
|
||||
80
assets/animtiles_decode.py
Normal file
80
assets/animtiles_decode.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from decode_common import *
|
||||
|
||||
BANK = 0x87
|
||||
|
||||
kInstructionSet = {
|
||||
0x8780b2: ('Delete', ''),
|
||||
0x8780b7: ('Goto', 'G'),
|
||||
0x87813f: ('GotoIfEventHappened', 'HG'),
|
||||
0x878150: ('SetEventHappened', 'H'),
|
||||
0x8781ba: ('WaitUntilAreaBossDead_DoubleRet', ''),
|
||||
0x878303: ('GotoIfBossBitSetInArea', 'BBG'),
|
||||
0x878320: ('SpawnTourianStatueEyeGlow', 'H'),
|
||||
0x87832f: ('SpawnTourianStatueSoul', 'H'),
|
||||
0x87833e: ('GotoIfTourianStatueBusy', 'G'),
|
||||
0x878349: ('TourianStatueSetState', 'H'),
|
||||
0x878352: ('TourianStatueClearState', 'H'),
|
||||
0x87835b: ('Clear3PaletteColors', 'H'),
|
||||
0x878372: ('SpawnPalfxObj', 'H'),
|
||||
0x87837f: ('Write8PaletteColors', 'H'),
|
||||
}
|
||||
|
||||
kCommandByName = {v[0] : (k, v[1]) for k, v in kInstructionSet.items()}
|
||||
|
||||
class AnimtilesParser(InstrParserCommon):
|
||||
instruction_set = kInstructionSet
|
||||
instruction_set_term = {0x8780b7, 0x8780b2}
|
||||
TAG = 'animtiles'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.current_array_size = None
|
||||
|
||||
def handle_draw_command(self, ins, ea):
|
||||
drawp = get_word(ea + 2) | (BANK << 16)
|
||||
assert self.current_array_size != None
|
||||
self.blobs[drawp] = self.current_array_size
|
||||
self.print_line(ea, f' {ins} ! {get_ea_name(drawp)}')
|
||||
return 4
|
||||
|
||||
def visit(self, ea, cur_size = None):
|
||||
if cur_size == None:
|
||||
super().visit(ea)
|
||||
return
|
||||
self.current_array_size = cur_size
|
||||
super().visit(ea)
|
||||
self.process_queue(final = False)
|
||||
|
||||
def process_queue_entry(self, ea):
|
||||
assert ea & 0x8000
|
||||
while ea not in self.visited:
|
||||
self.visited.add(ea)
|
||||
ins = get_word(ea)
|
||||
if ins & 0x8000:
|
||||
ea_org = ea
|
||||
ins = (BANK << 16) | ins
|
||||
if ins not in self.instruction_set:
|
||||
raise Exception(f'Ins {ins:X} not in iset at {ea:X}')
|
||||
name, operands = self.instruction_set[ins]
|
||||
ea += 2
|
||||
r = []
|
||||
for op in operands:
|
||||
if op == 'B':
|
||||
r.append('%d' % get_byte(ea))
|
||||
ea += 1
|
||||
elif op == 'H':
|
||||
r.append('%d' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'G':
|
||||
addr = (BANK << 16) | get_word(ea)
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
self.visit(addr)
|
||||
ea += 2
|
||||
else:
|
||||
assert 0
|
||||
self.print_line(ea_org, f' {name}({", ".join(r)})')
|
||||
if ins in self.instruction_set_term:
|
||||
self.print_line(ea_org + 1, '')
|
||||
break
|
||||
else:
|
||||
ea += self.handle_draw_command(ins, ea)
|
||||
376
assets/consts.py
Normal file
376
assets/consts.py
Normal file
@@ -0,0 +1,376 @@
|
||||
kRoomNames = {
|
||||
(0, 0): "Landing Site",
|
||||
(0, 1): "Crateria Gauntlet Entrance",
|
||||
(0, 2): "Parlor And Alcatraz",
|
||||
(0, 3): "Crateria Power Bomb Room",
|
||||
(0, 4): "Crateria Save Room",
|
||||
(0, 5): "West Ocean",
|
||||
(0, 6): "Bowling Alley Path",
|
||||
(0, 7): "Crateria Kihunter Room",
|
||||
(0, 8): "Elevator To Maridia",
|
||||
(0, 9): "East Ocean",
|
||||
(0, 10): "Forgotten Highway",
|
||||
(0, 11): "Crab Maze",
|
||||
(0, 12): "Tunnel To Maridia Elevator",
|
||||
(0, 13): "Crateria Tube",
|
||||
(0, 14): "Crateria Lake",
|
||||
(0, 15): "Elevator To Red Brinstar",
|
||||
(0, 16): "Crateria Gauntlet",
|
||||
(0, 17): "West Ocean Bridge",
|
||||
(0, 18): "Old Tourian Shaft",
|
||||
(0, 19): "Old Tourian Boss Room",
|
||||
(0, 20): "Elevator To Blue Brinstar",
|
||||
(0, 21): "Silver Torizo Boss Room",
|
||||
(0, 22): "Torizo Flyway",
|
||||
(0, 23): "Map Flyway",
|
||||
(0, 24): "Terminator Room",
|
||||
(0, 25): "Elevator To Green Brinstar",
|
||||
(0, 26): "Lower Mushrooms",
|
||||
(0, 27): "Crateria Map Room",
|
||||
(0, 28): "Green Pirates Shaft",
|
||||
(0, 29): "Old Tourian Super Missile",
|
||||
(0, 30): "Final Missile Bombway",
|
||||
(0, 31): "The Final Missile",
|
||||
(0, 48): "Statue Hallway",
|
||||
(0, 51): "Statue Room",
|
||||
(1, 0): "Green Brinstar Main Shaft",
|
||||
(1, 1): "Spore Spawn Super Room",
|
||||
(1, 2): "Brinstar Pre Map Trap",
|
||||
(1, 3): "Early Super Room",
|
||||
(1, 4): "Brinstar Reserve Tank",
|
||||
(1, 5): "Brinstar Map Room",
|
||||
(1, 6): "Fireflea Room",
|
||||
(1, 7): "Brinstar Missile Station",
|
||||
(1, 8): "Dachora Room",
|
||||
(1, 9): "Big Pink",
|
||||
(1, 10): "Spore Spawn Kihunter Room",
|
||||
(1, 11): "Spore Spawn Boss Room",
|
||||
(1, 12): "Pink Brinstar Power Bomb Room",
|
||||
(1, 13): "Green Hill Zone",
|
||||
(1, 14): "Morph Ball Room",
|
||||
(1, 15): "Construction Zone",
|
||||
(1, 16): "Blue Brinstar Energy Tank Room",
|
||||
(1, 17): "Noob Bridge",
|
||||
(1, 18): "Green Brinstar Beetom Room",
|
||||
(1, 19): "Etecoon Energy Tank Room",
|
||||
(1, 20): "Etecoon Super Room",
|
||||
(1, 21): "Dachora Energy Station",
|
||||
(1, 22): "Spore Spawn Refill Room",
|
||||
(1, 23): "Waterway",
|
||||
(1, 24): "The First Missile",
|
||||
(1, 25): "Pink Brinstar Hopper Room",
|
||||
(1, 26): "Hopper Energy Tank Room",
|
||||
(1, 27): "Pink Brinstar Save Room",
|
||||
(1, 28): "Blue Brinstar Boulder Room",
|
||||
(1, 29): "Blue Brinstar Double Missile Room",
|
||||
(1, 30): "Green Brinstar Save Room",
|
||||
(1, 31): "Etecoon Save Room",
|
||||
(1, 32): "Red Brinstar Shaft",
|
||||
(1, 33): "Xray Gauntlet",
|
||||
(1, 34): "Xray Puzzle",
|
||||
(1, 35): "Hellway",
|
||||
(1, 36): "Catapiller Room",
|
||||
(1, 37): "Beta Power Bomb Room",
|
||||
(1, 38): "Alpha Power Bomb Room",
|
||||
(1, 39): "Red Brinstar Bat Room",
|
||||
(1, 40): "Below Spazer",
|
||||
(1, 41): "Spazer Beam Room",
|
||||
(1, 42): "Warehouse Zeela Room",
|
||||
(1, 43): "Warehouse Energy Tank Room",
|
||||
(1, 44): "Warehouse Kihunter Room",
|
||||
(1, 45): "Mini Kraid Hallway",
|
||||
(1, 46): "Kraid Boss Door",
|
||||
(1, 47): "Kraid Boss Room",
|
||||
(1, 49): "Red Brinstar Energy Station",
|
||||
(1, 50): "Kraid Refill Room",
|
||||
(1, 52): "Kraid Hideout Entrance",
|
||||
(1, 53): "Varia Suit Room",
|
||||
(1, 54): "Kraid Save Room",
|
||||
(1, 55): "Red Brinstar Save Room",
|
||||
(2, 0): "Ice Beam Acid Room",
|
||||
(2, 1): "Cathedral Entrance",
|
||||
(2, 2): "Cathedral",
|
||||
(2, 3): "Business Center",
|
||||
(2, 4): "Ice Beam Gate Room",
|
||||
(2, 5): "Ice Beam Tutorial Room",
|
||||
(2, 6): "Ice Beam Room",
|
||||
(2, 7): "Ice Beam Snake Room",
|
||||
(2, 8): "Crumble Shaft",
|
||||
(2, 9): "Crocomire Speedway",
|
||||
(2, 10): "Crocomire Boss Room",
|
||||
(2, 11): "High Jump Room",
|
||||
(2, 12): "Crocomire Escape",
|
||||
(2, 13): "Hi Jump Energy Tank Room",
|
||||
(2, 14): "Post Crocomire Farming Room",
|
||||
(2, 15): "Post Crocomire Save Room",
|
||||
(2, 16): "Post Crocomire Power Bomb Room",
|
||||
(2, 17): "Post Crocomire Shaft",
|
||||
(2, 18): "Post Crocomire Missile Room",
|
||||
(2, 19): "Grapple Tutorial Room 3",
|
||||
(2, 20): "Grapple Jump Cavern",
|
||||
(2, 21): "Grapple Tutorial Room 2",
|
||||
(2, 22): "Grapple Tutorial Room 1",
|
||||
(2, 23): "Grapple Beam Room",
|
||||
(2, 24): "Norfair Reserve Tank",
|
||||
(2, 25): "Green Bubbles Missile Room",
|
||||
(2, 26): "Bubble Mountain",
|
||||
(2, 27): "Speedbooster Hall",
|
||||
(2, 28): "Speedbooster Room",
|
||||
(2, 29): "Single Chamber",
|
||||
(2, 30): "Double Chamber",
|
||||
(2, 31): "Wave Beam Room",
|
||||
(2, 32): "Spiky Platforms Tunnel",
|
||||
(2, 33): "Volcano Room",
|
||||
(2, 34): "Kronic Boost Room",
|
||||
(2, 35): "Magdollite Tunnel",
|
||||
(2, 36): "Purple Shaft",
|
||||
(2, 37): "Lava Dive Room",
|
||||
(2, 38): "Elevator To Lower Norfair",
|
||||
(2, 39): "Upper Norfair Farming Room",
|
||||
(2, 40): "Rising Tide",
|
||||
(2, 41): "Acid Snakes Tunnel",
|
||||
(2, 42): "Spiky Acid Snakes Tunnel",
|
||||
(2, 43): "Upper Norfair Energy Station",
|
||||
(2, 44): "Purple Refill Room",
|
||||
(2, 45): "Bubble Mountian Bat Room",
|
||||
(2, 46): "Norfair Map Room",
|
||||
(2, 47): "Bubble Mountian Save Room",
|
||||
(2, 48): "Speedbooster Rubble Hallway",
|
||||
(2, 49): "Red Pirate Shaft",
|
||||
(2, 50): "Rocky Norfair Save Room",
|
||||
(2, 51): "Pre Crocomire Save Room",
|
||||
(2, 52): "Ridley Hideout Save Room",
|
||||
(2, 53): "Golden Chozo Statue",
|
||||
(2, 54): "Lower Norfair Main Hall",
|
||||
(2, 55): "Gold Torizo Boss Room",
|
||||
(2, 56): "Fast Ripper Room",
|
||||
(2, 57): "Golden Torizo Energy Station",
|
||||
(2, 58): "Ridley Boss Room",
|
||||
(2, 59): "Ridley Boss Door",
|
||||
(2, 60): "Fast Pillars Setup Room",
|
||||
(2, 62): "Mickey Mouse",
|
||||
(2, 63): "Pillar Room",
|
||||
(2, 64): "Plowerhouse Room",
|
||||
(2, 65): "The Worst Room In The Game",
|
||||
(2, 66): "Amphitheatre",
|
||||
(2, 67): "Lower Norfair Maze Room",
|
||||
(2, 68): "Lower Norfair Power Bomb",
|
||||
(2, 69): "Red Keyhunter Shaft",
|
||||
(2, 70): "Wasteland",
|
||||
(2, 71): "Ninja Pirates Boss Room",
|
||||
(2, 72): "Three Muskateers Room",
|
||||
(2, 73): "Broken Hatchling Container",
|
||||
(2, 74): "Screw Attack Room",
|
||||
(2, 75): "Dragon Rock",
|
||||
(2, 76): "Red Keyhunter Save Room",
|
||||
(3, 0): "Bowling Alley",
|
||||
(3, 1): "Wrecked Ship Entrance",
|
||||
(3, 2): "Attic",
|
||||
(3, 3): "Attic Missile Room",
|
||||
(3, 4): "Wrecked Ship Main Shaft",
|
||||
(3, 5): "Spiky Death Room",
|
||||
(3, 6): "Electric Death Room",
|
||||
(3, 7): "Wrecked Ship Energy Tank Room",
|
||||
(3, 8): "Basement",
|
||||
(3, 9): "Wrecked Ship Map Room",
|
||||
(3, 10): "Phantoon Boss Room",
|
||||
(3, 11): "Sponge Bath",
|
||||
(3, 12): "Wrecked Ship West Super Room",
|
||||
(3, 13): "Wrecked Ship East Super Room",
|
||||
(3, 14): "Gravity Suit Room",
|
||||
(3, 15): "Wrecked Ship Save Room",
|
||||
(4, 0): "West Maridia Save Room",
|
||||
(4, 1): "Glass Tube",
|
||||
(4, 2): "West Maridia Tube",
|
||||
(4, 3): "East Maridia Tube",
|
||||
(4, 4): "Maridia Main Street",
|
||||
(4, 5): "Fish Tank",
|
||||
(4, 6): "Turtle Family Room",
|
||||
(4, 7): "Crab Tunnel",
|
||||
(4, 8): "Puffer Mountian",
|
||||
(4, 9): "Red Fish Room",
|
||||
(4, 10): "Watering Hole",
|
||||
(4, 11): "Northwest Maridia Bug Room",
|
||||
(4, 12): "Crab Shaft",
|
||||
(4, 13): "Pseudo Plasma Spark Room",
|
||||
(4, 14): "Crab Hole",
|
||||
(4, 15): "Tunnel To West Sand Hall",
|
||||
(4, 16): "Plasma Larvae",
|
||||
(4, 17): "Plasma Beam Room",
|
||||
(4, 18): "Thread The Needle Room",
|
||||
(4, 19): "Maridia Elevator Room",
|
||||
(4, 20): "Plasma Spark Room",
|
||||
(4, 21): "Plasma Climb",
|
||||
(4, 22): "Maridia Map Room",
|
||||
(4, 23): "East Maridia Save Room",
|
||||
(4, 24): "Maridia Transit Tube",
|
||||
(4, 25): "Bug Sand Hole",
|
||||
(4, 26): "West Sand Hall",
|
||||
(4, 27): "Oasis",
|
||||
(4, 28): "East Sand Hall",
|
||||
(4, 29): "West Sand Hole Cave",
|
||||
(4, 30): "East Sand Hole Cave",
|
||||
(4, 31): "West Quicksand Room",
|
||||
(4, 32): "East Quicksand Room",
|
||||
(4, 33): "Maridia Aqueduct",
|
||||
(4, 34): "Butterfly Room",
|
||||
(4, 35): "Botwoon Hallway",
|
||||
(4, 36): "Pants Room",
|
||||
(4, 37): "East Pant Room",
|
||||
(4, 38): "Spring Ball Room",
|
||||
(4, 39): "Below Botwoon Quicksand Room",
|
||||
(4, 40): "Colosseum",
|
||||
(4, 41): "Maridia Aqueduct Save Room",
|
||||
(4, 42): "Draygon Boss Door",
|
||||
(4, 43): "Botwoon Speed Hallway",
|
||||
(4, 44): "Draygon Save Room",
|
||||
(4, 45): "Maridia Missile Refill Room",
|
||||
(4, 46): "Plasma Beach Quicksand Room",
|
||||
(4, 47): "Botwoon Quicksand Room",
|
||||
(4, 48): "Shaktool Room",
|
||||
(4, 49): "Halfie Room",
|
||||
(4, 50): "Botwoon Boss Room",
|
||||
(4, 51): "Space Jump Room",
|
||||
(4, 52): "Draygon Energy Refill",
|
||||
(4, 53): "West Cactus Alley",
|
||||
(4, 54): "East Cactus Alley",
|
||||
(4, 55): "Draygon Boss Room",
|
||||
(5, 0): "Tourian Elevator",
|
||||
(5, 1): "Metroid Room 1",
|
||||
(5, 2): "Metroid Room 2",
|
||||
(5, 3): "Metroid Room 3",
|
||||
(5, 4): "Metroid Room 4",
|
||||
(5, 5): "Blue Sidehoppers",
|
||||
(5, 6): "Dust Torizo Room",
|
||||
(5, 7): "Baby Metroid Room",
|
||||
(5, 8): "Tourian Seaweed Room",
|
||||
(5, 9): "Tourian Recharge Room",
|
||||
(5, 10): "Mother Brain Boss Room",
|
||||
(5, 11): "Mother Brain Boss Door",
|
||||
(5, 12): "Rinka Shaft",
|
||||
(5, 13): "Mother Brain Save Room",
|
||||
(5, 14): "Tourian Escape Room 1",
|
||||
(5, 15): "Tourian Escape Room 2",
|
||||
(5, 16): "Tourian Horizontal Escape",
|
||||
(5, 17): "Tourian Vertical Escape",
|
||||
(5, 18): "Tourian Elevator Save Room",
|
||||
(6, 0): "Ceres Shaft",
|
||||
(6, 1): "Ceres Falling Tile Room",
|
||||
(6, 2): "Ceres Stairs Room",
|
||||
(6, 3): "Ceres Computer Room",
|
||||
(6, 4): "Ceres 58 Escape",
|
||||
(6, 5): "Ceres Ridley Boss Room",
|
||||
(7, 0): "Debug Room",
|
||||
}
|
||||
|
||||
kEnemyNameToAddr = {
|
||||
'NODATA': 0xb4dd89,
|
||||
'ATOMIC': 0xb4dd97,
|
||||
'BANG': 0xb4dda5,
|
||||
'BATTA1': 0xb4ddb3,
|
||||
'BATTA2': 0xb4ddc1,
|
||||
'BATTA3': 0xb4ddcf,
|
||||
'BOTOON': 0xb4dddd,
|
||||
'BOYON': 0xb4ddeb,
|
||||
'DESSGEEGA': 0xb4ddf9,
|
||||
'DORI': 0xb4de07,
|
||||
'DRAGON': 0xb4de15,
|
||||
'EBI': 0xb4de23,
|
||||
'EYE': 0xb4de31,
|
||||
'NAMI': 0xb4de3f,
|
||||
'FISH': 0xb4de4d,
|
||||
'GAI': 0xb4de5b,
|
||||
'GAMET': 0xb4de69,
|
||||
'GEEGA': 0xb4de77,
|
||||
'GERUDA': 0xb4de85,
|
||||
'GRAVY': 0xb4de93,
|
||||
'HACHI1': 0xb4dea1,
|
||||
'HAND': 0xb4deaf,
|
||||
'HIBASHI': 0xb4debd,
|
||||
'HIRU': 0xb4decb,
|
||||
'HOLTZ': 0xb4ded9,
|
||||
'HOTARY': 0xb4dee7,
|
||||
'HZOOMER': 0xb4def5,
|
||||
'KAGO': 0xb4df03,
|
||||
'KAME': 0xb4df11,
|
||||
'KAMER': 0xb4df1f,
|
||||
'KANI': 0xb4df2d,
|
||||
'KOMA': 0xb4df3b,
|
||||
'KZAN': 0xb4df49,
|
||||
'LAVAMAN': 0xb4df57,
|
||||
'MELLA': 0xb4df65,
|
||||
'MEMU': 0xb4df73,
|
||||
'MERO': 0xb4df81,
|
||||
'METALEE': 0xb4df8f,
|
||||
'METMOD': 0xb4df9d,
|
||||
'METROID': 0xb4dfab,
|
||||
'MULTI': 0xb4dfb9,
|
||||
'MZOOMER': 0xb4dfc7,
|
||||
'NDRA': 0xb4dfd5,
|
||||
'NOMI': 0xb4dfe3,
|
||||
'NOVA': 0xb4dff1,
|
||||
'OUM': 0xb4dfff,
|
||||
'OUMU': 0xb4e00d,
|
||||
'PIPE': 0xb4e01b,
|
||||
'POLYP': 0xb4e029,
|
||||
'PUROMI': 0xb4e037,
|
||||
'PUU': 0xb4e045,
|
||||
'PUYO': 0xb4e053,
|
||||
'REFLEC': 0xb4e061,
|
||||
'RINKA': 0xb4e06f,
|
||||
'RIO': 0xb4e07d,
|
||||
'RIPPER': 0xb4e08b,
|
||||
'RIPPER2': 0xb4e099,
|
||||
'ROBO': 0xb4e0a7,
|
||||
'RSTONE': 0xb4e0b5,
|
||||
'SABOTEN': 0xb4e0c3,
|
||||
'SBUG': 0xb4e0d1,
|
||||
'SCLAYD': 0xb4e0df,
|
||||
'SDEATH': 0xb4e0ed,
|
||||
'SHUTTER': 0xb4e0fb,
|
||||
'SHUTTER2': 0xb4e109,
|
||||
'SIDE': 0xb4e117,
|
||||
'SKREE': 0xb4e125,
|
||||
'SPA': 0xb4e133,
|
||||
'SQUEEWPT': 0xb4e141,
|
||||
'SSIDE': 0xb4e14f,
|
||||
'STOKE': 0xb4e15d,
|
||||
'TOGE': 0xb4e16b,
|
||||
'VIOLA': 0xb4e179,
|
||||
'WAVER': 0xb4e187,
|
||||
'YARD': 0xb4e195,
|
||||
'ZEB': 0xb4e1a3,
|
||||
'ZEBBO': 0xb4e1b1,
|
||||
'ZEELA': 0xb4e1bf,
|
||||
'ZOA': 0xb4e1cd,
|
||||
'ZOOMER': 0xb4e1db,
|
||||
'BATTA1Br': 0xb4e1e9,
|
||||
'BATTA1No': 0xb4e1f7,
|
||||
'BATTA1Na': 0xb4e205,
|
||||
'BATTA1Ma': 0xb4e213,
|
||||
'BATTA1Tu': 0xb4e221,
|
||||
'BATTA2Br': 0xb4e22f,
|
||||
'BATTA2No': 0xb4e23d,
|
||||
'BATTA2Na': 0xb4e24b,
|
||||
'BATTA2Ma': 0xb4e259,
|
||||
'BATTA2Tu': 0xb4e267,
|
||||
'BATTA3Br': 0xb4e275,
|
||||
'BATTA3No': 0xb4e283,
|
||||
'BATTA3Na': 0xb4e291,
|
||||
'BATTA3Ma': 0xb4e29f,
|
||||
'BATTA3Tu': 0xb4e2ad,
|
||||
'FUNE': 0xb4e2bb,
|
||||
'HACHI2': 0xb4e2c9,
|
||||
'HACHI3': 0xb4e2d7,
|
||||
'ROBO2': 0xb4e2e5,
|
||||
}
|
||||
|
||||
kPlmHeaderAddrs = {0x84b62f, 0x84b74f, 0x84ef8b, 0x84b717, 0x84d0d4, 0x84db44, 0x84b79f, 0x84b6f7, 0x84df6d, 0x84d038, 0x84b78f, 0x84ef67, 0x84b653, 0x84c848, 0x84b6a3, 0x84ef9f, 0x84b763, 0x84b6df, 0x84cffc, 0x84efaf, 0x84b8eb, 0x84df85, 0x84eedf, 0x84d704, 0x84b7b3, 0x84db56, 0x84ef97, 0x84bb30, 0x84ef93, 0x84d020, 0x84d0a0, 0x84df5d, 0x84d080, 0x84d090, 0x84d127, 0x84c832, 0x84b70f, 0x84d0b4, 0x84d0e8, 0x84b753, 0x84b71f, 0x84c83e, 0x84ef9b, 0x84b633, 0x84b66b, 0x84b6bf, 0x84d0c4, 0x84b6cb, 0x84d6d6, 0x84bb05, 0x84ef03, 0x84c860, 0x84c80a, 0x84ef37, 0x84b7a3, 0x84b65b, 0x84b7eb, 0x84ef7b, 0x84d064, 0x84d0f2, 0x84d010, 0x84c86c, 0x84b64b, 0x84b6e3, 0x84b71b, 0x84b781, 0x84d0dc, 0x84b743, 0x84d05c, 0x84ef33, 0x84c842, 0x84b727, 0x84eee7, 0x84b663, 0x84ef47, 0x84b687, 0x84ef77, 0x84d08c, 0x84d00c, 0x84b777, 0x84efc3, 0x84c8b4, 0x84b793, 0x84d02c, 0x84b63f, 0x84b73f, 0x84baf4, 0x84d088, 0x84ef6f, 0x84b7af, 0x84d044, 0x84ef1b, 0x84d6fc, 0x84b67f, 0x84c8a2, 0x84d014, 0x84eedb, 0x84db48, 0x84ef4b, 0x84ef7f, 0x84c854, 0x84b968, 0x84d004, 0x84efa3, 0x84eef3, 0x84b7bb, 0x84efc7, 0x84d07c, 0x84b6af, 0x84efcb, 0x84b63b, 0x84b713, 0x84d034, 0x84b6cf, 0x84d070, 0x84b673, 0x84d6de, 0x84eeeb, 0x84b647, 0x84c80e, 0x84c896, 0x84c884, 0x84b9c1, 0x84c822, 0x84b8f9, 0x84c890, 0x84d0d0, 0x84c8c6, 0x84d094, 0x84ef1f, 0x84c81a, 0x84b703, 0x84d6f8, 0x84efab, 0x84d030, 0x84d70c, 0x84ef2f, 0x84c81e, 0x84d068, 0x84ef17, 0x84d6ee, 0x84d078, 0x84b6f3, 0x84cfec, 0x84d03c, 0x84b677, 0x84ef5f, 0x84eeef, 0x84d018, 0x84b78b, 0x84efa7, 0x84ef07, 0x84df71, 0x84d13b, 0x84b6c7, 0x84d0e0, 0x84d0b8, 0x84c812, 0x84cff4, 0x84efbb, 0x84b6d7, 0x84d048, 0x84c836, 0x84b723, 0x84d0c0, 0x84ef4f, 0x84b6eb, 0x84db60, 0x84d028, 0x84eef7, 0x84b68b, 0x84b6b7, 0x84c84e, 0x84b643, 0x84c806, 0x84b8ac, 0x84b964, 0x84b9ed, 0x84df81, 0x84ef63, 0x84c8c2, 0x84d0ac, 0x84b73b, 0x84c85a, 0x84ef8f, 0x84c8d0, 0x84c826, 0x84c8ba, 0x84c88a, 0x84df65, 0x84cff8, 0x84d6da, 0x84b65f, 0x84b6ab, 0x84b76b, 0x84d0cc, 0x84b757, 0x84d01c, 0x84b76f, 0x84d098, 0x84efcf, 0x84b6db, 0x84ef5b, 0x84b75b, 0x84c82a, 0x84b66f, 0x84b7ab, 0x84b974, 0x84d708, 0x84b6bb, 0x84d0d8, 0x84eed3, 0x84b6e7, 0x84df59, 0x84ef3b, 0x84d04c, 0x84cff0, 0x84d113, 0x84df61, 0x84d0b0, 0x84c8ca, 0x84df7d, 0x84b6d3, 0x84d040, 0x84b667, 0x84c878, 0x84b7bf, 0x84eeff, 0x84db52, 0x84db5a, 0x84ef53, 0x84b6c3, 0x84b72b, 0x84b6fb, 0x84c83a, 0x84b70b, 0x84efb3, 0x84df69, 0x84efbf, 0x84b773, 0x84b75f, 0x84ef3f, 0x84c8ae, 0x84ef13, 0x84b7b7, 0x84ef83, 0x84b7a7, 0x84d0a8, 0x84b68f, 0x84c87e, 0x84ef57, 0x84ef0b, 0x84b637, 0x84b79b, 0x84b737, 0x84c8a8, 0x84c82e, 0x84b69f, 0x84d058, 0x84b697, 0x84d060, 0x84b797, 0x84c866, 0x84c89c, 0x84eefb, 0x84b72f, 0x84b6ff, 0x84b707, 0x84d0bc, 0x84c872, 0x84b657, 0x84ef0f, 0x84ef23, 0x84b747, 0x84ef73, 0x84b693, 0x84d054, 0x84d700, 0x84ef27, 0x84c8be, 0x84eee3, 0x84ef87, 0x84b67b, 0x84b6b3, 0x84df75, 0x84b69b, 0x84ef2b, 0x84d06c, 0x84ef43, 0x84d050, 0x84eed7, 0x84d0c8, 0x84b767, 0x84d0e4, 0x84b6a7, 0x84d024, 0x84b733, 0x84d008, 0x84b683, 0x84b74b, 0x84db4c, 0x84d6ea, 0x84efb7, 0x84ef6b, 0x84c816, 0x84df79, 0x84d084, 0x84d09c, 0x84b64f, 0x84ba48, 0x84d0a4, 0x84b6ef, 0x84d074, 0x84d6f2, 0x84d000}
|
||||
kEprojHeaders = {0x86ad6c, 0x868aaf, 0x86b4b1, 0x86ec48, 0x86de7a, 0x86afe5, 0x86f337, 0x86dafe, 0x868c24, 0x869c37, 0x86e0e0, 0x869650, 0x8696ce, 0x86a17b, 0x86dfca, 0x86bab0, 0x86e6d2, 0x86b5cb, 0x86d2d0, 0x86ad7a, 0x869dcc, 0x86cc5b, 0x86af76, 0x86e659, 0x86d2c2, 0x86a9af, 0x868f9d, 0x869634, 0x86a9a1, 0x86cb9f, 0x86a993, 0x86bd5a, 0x86966c, 0x86cbbb, 0x86eba0, 0x86d298, 0x869742, 0x86ab07, 0x86965e, 0x869c61, 0x86a3b0, 0x868bc2, 0x86b428, 0x86cb91, 0x868c08, 0x86cc85, 0x86cc69, 0x86a395, 0x86e64b, 0x86b751, 0x86b743, 0x86aeb6, 0x86967a, 0x86cb4b, 0x868c16, 0x869734, 0x86c18c, 0x86d904, 0x86ba78, 0x86a977, 0x86be41, 0x86cb13, 0x869c6f, 0x86be33, 0x86cb3d, 0x86cbad, 0x86a969, 0x86cefc, 0x86de6c, 0x869696, 0x86a95b, 0x86d2b4, 0x86f498, 0x86cf26, 0x86be25, 0x86f345, 0x86cb2f, 0x86a189, 0x86cf18, 0x8696a4, 0x868e50, 0x86ba86, 0x86cb83, 0x86cb21, 0x86ba94, 0x86d02e, 0x869688, 0x868bd0, 0x86babe, 0x86cb59, 0x86ec95, 0x868f8f, 0x8690c1, 0x868e6c, 0x86b31a, 0x86b1c0, 0x86de88, 0x86d920, 0x869e90, 0x86cf0a, 0x86d2a6, 0x8696c0, 0x868e5e, 0x86af68, 0x868bec, 0x869c53, 0x869c45, 0x86c17e, 0x86aff3, 0x86dfbc, 0x86cb75, 0x868bfa, 0x86dbf2, 0x86e517, 0x86ba5c, 0x86aea8, 0x868bde, 0x86d2de, 0x86a387, 0x869642, 0x86cc77, 0x86cb67, 0x869c29, 0x869dbe, 0x86a985, 0x86e525, 0x86af84, 0x86ba6a, 0x869db0, 0x86baa2, 0x86e675, 0x86ad5e, 0x86e667, 0x8696b2, 0x86a379, 0x86e509, 0x86bbc7, 0x86d912}
|
||||
kEprojInstrLists={0x86eca3, 0x86ade5, 0x86b2ef, 0x86a48e, 0x868c38, 0x86a48a, 0x86b61d, 0x86d04a, 0x86976c, 0x868ffb, 0x86945f, 0x86d03c, 0x869772, 0x869574, 0x8697ac, 0x86ab41, 0x869782, 0x86dc06, 0x86b3e5, 0x8697f8, 0x86cf56, 0x86ab25, 0x86b1a8, 0x86a28b, 0x86b79f, 0x86c8e1, 0x86b5f3, 0x86b3cd, 0x86d218, 0x86d052, 0x86f363}
|
||||
|
||||
kAnimtilesDefs = {0x87824b, 0x878251, 0x878257, 0x87825d, 0x878263, 0x878269, 0x87826f, 0x878275, 0x87827b, 0x878281, 0x878287, 0x87828d, 0x8782ab, 0x8782c9, 0x8782e7, 0x8782fd, 0x87854c, 0x878552, 0x878558, 0x87855e}
|
||||
|
||||
kPalfxDefs = {0x8de194, 0x8de198, 0x8de19c, 0x8de1a0, 0x8de1a4, 0x8de1a8, 0x8de1ac, 0x8de1b0, 0x8de1b4, 0x8de1b8, 0x8de1bc, 0x8de1c0, 0x8de1c4, 0x8de1c8, 0x8de1cc, 0x8de1d0, 0x8de1d4, 0x8de1d8, 0x8de1dc, 0x8de1e0, 0x8de1e4, 0x8de1e8, 0x8de1ec, 0x8de1f0, 0x8de1f4, 0x8de1f8, 0x8de1fc, 0x8de200, 0x8df745, 0x8df749, 0x8df74d, 0x8df751, 0x8df755, 0x8df759, 0x8df75d, 0x8df761, 0x8df765, 0x8df769, 0x8df76d, 0x8df771, 0x8df775, 0x8df779, 0x8df77d, 0x8df781, 0x8df785, 0x8df789, 0x8df78d, 0x8df791, 0x8df795, 0x8df799, 0x8df79d, 0x8df7a1, 0x8df7a5, 0x8dffc9, 0x8dffcd, 0x8dffd1, 0x8dffd5, 0x8dffd9, 0x8dffdd, 0x8dffe1, 0x8dffe5, 0x8dffe9, 0x8dffed}
|
||||
|
||||
kEnemyPopulation = {0xa6aa2f, 0xa6aa3f, 0xa6c987, 0xa6c997, 0xa6c9a7, 0xa6c9b7, 0xa6c9c7, 0xa6c9d7, 0xa6c9e7, 0xa6c9f7, 0xa6ca07, 0xa6ca17, 0xa6ca27, 0xa6ca37, 0xa6fce1, 0xa6fcf9, 0xa98ae5, 0xa98af5, 0xa98b05, 0xa98b15, 0xa98b25, 0xa9be28}
|
||||
186
assets/decode_common.py
Normal file
186
assets/decode_common.py
Normal file
@@ -0,0 +1,186 @@
|
||||
import pickle
|
||||
|
||||
def load_names():
|
||||
xs = [a.split(' ') for a in open('names.txt', 'r').read().splitlines()]
|
||||
return {b : int(a, 16) for a, b in xs}
|
||||
|
||||
name2ea = load_names()
|
||||
name2ea = {k.replace('?', '') : v for k, v in name2ea.items()}
|
||||
ea2name = {v:k for k,v in name2ea.items()}
|
||||
|
||||
g_marked_addresses = set()
|
||||
|
||||
def mark_address(ea):
|
||||
g_marked_addresses.add(ea)
|
||||
return ea
|
||||
|
||||
def get_ea_name(v, short_label = False, unknown_prefix = 'unk'):
|
||||
if v == None: return None
|
||||
if v == 0: return 'null'
|
||||
r = ea2name.get(v)
|
||||
if r == None:
|
||||
if short_label and v not in g_marked_addresses:
|
||||
r = 'lbl_%X' % (v)
|
||||
else:
|
||||
r = '%s_%X' % (unknown_prefix, v)
|
||||
return r
|
||||
|
||||
class Rom:
|
||||
def __init__(self):
|
||||
self.data = open('../sm.smc', 'rb').read()
|
||||
|
||||
def map(self, addr):
|
||||
assert addr & 0x8000
|
||||
return (((addr >> 16) << 15) | (addr & 0x7fff)) & 0x3fffff
|
||||
|
||||
def get_byte(self, addr):
|
||||
return self.data[self.map(addr)]
|
||||
|
||||
def get_word(self, addr):
|
||||
addr = self.map(addr)
|
||||
return self.data[addr] + self.data[addr + 1] * 256
|
||||
|
||||
def get_long(self, addr):
|
||||
addr = self.map(addr)
|
||||
return self.data[addr] + self.data[addr + 1] * 256 + self.data[addr + 2] * 65536
|
||||
|
||||
def get_bytes(self, addr, n):
|
||||
addr = self.map(addr)
|
||||
return self.data[addr:addr+n]
|
||||
|
||||
ROM = Rom()
|
||||
get_byte = ROM.get_byte
|
||||
get_word = ROM.get_word
|
||||
|
||||
|
||||
def translate_old_layout():
|
||||
for k, v in kInstructionSet.items():
|
||||
k = k | 0x8D0000
|
||||
t = ea2name[k]
|
||||
name = t.replace('PalInstr_', '')
|
||||
args, _ = v
|
||||
tostr = {1 : 'B', 2 : 'H', 3 : 'L'}
|
||||
args = ''.join(tostr[a] for a in args)
|
||||
if name.startswith('Goto'):
|
||||
args = args[:-1] + 'G'
|
||||
print(" 0x%x: ('%s', '%s')," % (k, name, args))
|
||||
|
||||
def sex8(v):
|
||||
return v if v < 0x80 else v - 0x100
|
||||
def sex16(v):
|
||||
return v if v < 0x8000 else v - 0x10000
|
||||
|
||||
class InstrParserCommon:
|
||||
forbidden_addrs = set()
|
||||
def __init__(self):
|
||||
self.visited = set()
|
||||
self.missing_isets = set()
|
||||
self.queue = []
|
||||
self.queue_pos = 0
|
||||
self.label = set()
|
||||
self.lines = []
|
||||
self.blobs = {}
|
||||
|
||||
def print_line(self, ea, line):
|
||||
self.lines.append((ea, line))
|
||||
|
||||
def visit(self, ea):
|
||||
assert ea & 0x8000
|
||||
if ea in self.forbidden_addrs:
|
||||
return
|
||||
self.label.add(ea)
|
||||
if ea not in self.visited:
|
||||
self.queue.append(ea)
|
||||
|
||||
def process_queue(self, final = True):
|
||||
while self.queue_pos < len(self.queue):
|
||||
if (ea := self.queue[self.queue_pos]) not in self.visited:
|
||||
self.process_queue_entry(ea)
|
||||
self.queue_pos += 1
|
||||
if final:
|
||||
self.queue = None
|
||||
|
||||
def print(self, file):
|
||||
print('<?%s\n' % self.TAG, file = file)
|
||||
for ea, line in sorted(self.lines, key = lambda x: x[0]):
|
||||
if ea in self.label:
|
||||
print(f'{get_ea_name(ea, short_label = True)}:', file = file)
|
||||
print(line, file = file)
|
||||
print('?>', file = file)
|
||||
print('', file = file)
|
||||
|
||||
def decompress(rom, ea):
|
||||
ea = rom.map(ea)
|
||||
ea_start = ea
|
||||
data = rom.data
|
||||
rv = bytearray()
|
||||
while True:
|
||||
b = data[ea]; ea += 1
|
||||
if b == 0xff:
|
||||
return rv, ea - ea_start
|
||||
if (b & 0xe0) == 0xe0:
|
||||
cmd = (8 * b) & 0xe0
|
||||
size = ((b & 3) << 8 | data[ea]) + 1; ea += 1
|
||||
else:
|
||||
cmd = b & 0xe0
|
||||
size = (b & 0x1f) + 1
|
||||
if cmd & 0x80:
|
||||
want_xor = 0xff if cmd & 0x20 else 0x00
|
||||
if cmd >= 0xc0:
|
||||
src_pos = len(rv) - data[ea]; ea += 1
|
||||
else:
|
||||
src_pos = data[ea] | data[ea + 1] * 256; ea += 2
|
||||
for i in range(size):
|
||||
rv.append(rv[src_pos + i] ^ want_xor)
|
||||
elif cmd == 0x20:
|
||||
b = data[ea]; ea += 1
|
||||
for i in range(size):
|
||||
rv.append(b)
|
||||
elif cmd == 0x40:
|
||||
b, b2 = data[ea], data[ea + 1]; ea += 2
|
||||
while size:
|
||||
rv.append(b)
|
||||
if size <= 1:
|
||||
break
|
||||
rv.append(b2)
|
||||
size -= 2
|
||||
elif cmd == 0x60:
|
||||
b = data[ea]; ea += 1
|
||||
for i in range(size):
|
||||
rv.append(b)
|
||||
b = (b + 1) & 0xff
|
||||
else:
|
||||
for i in range(size):
|
||||
rv.append(data[ea]); ea += 1
|
||||
|
||||
|
||||
def get_compressed_size(rom, ea):
|
||||
ea = rom.map(ea)
|
||||
ea_start = ea
|
||||
data = rom.data
|
||||
while True:
|
||||
b = data[ea]; ea += 1
|
||||
if b == 0xff:
|
||||
return ea - ea_start
|
||||
if (b & 0xe0) == 0xe0:
|
||||
cmd = (8 * b) & 0xe0
|
||||
size = ((b & 3) << 8 | data[ea]) + 1; ea += 1
|
||||
else:
|
||||
cmd = b & 0xe0
|
||||
size = (b & 0x1f) + 1
|
||||
if cmd & 0x80:
|
||||
if cmd >= 0xc0:
|
||||
ea += 1
|
||||
else:
|
||||
ea += 2
|
||||
elif cmd == 0x20:
|
||||
ea += 1
|
||||
elif cmd == 0x40:
|
||||
ea += 2
|
||||
elif cmd == 0x60:
|
||||
ea += 1
|
||||
else:
|
||||
ea += size
|
||||
|
||||
|
||||
|
||||
548
assets/enemy_instr_decode.py
Normal file
548
assets/enemy_instr_decode.py
Normal file
@@ -0,0 +1,548 @@
|
||||
from decode_common import *
|
||||
|
||||
BANK = None
|
||||
|
||||
kInstructionSet = {
|
||||
0xa288c5: ('BouncingGoofball_Instr_88C5', ''),
|
||||
0xa288c6: ('BouncingGoofball_Instr_88C6', ''),
|
||||
0xa2897e: ('MiniCrocomire_Instr_897E', 'H'),
|
||||
0xa28990: ('MiniCrocomire_Instr_8990', ''),
|
||||
0xa2899d: ('MiniCrocomire_Instr_899D', ''),
|
||||
0xa29381: ('MaridiaBeybladeTurtle_Instr_9381', ''),
|
||||
0xa29412: ('MaridiaBeybladeTurtle_Instr_9412', ''),
|
||||
0xa29447: ('MaridiaBeybladeTurtle_Instr_9447', ''),
|
||||
0xa29451: ('MaridiaBeybladeTurtle_Instr_9451', ''),
|
||||
0xa2946b: ('MaridiaBeybladeTurtle_Instr_946B', ''),
|
||||
0xa29485: ('MaridiaBeybladeTurtle_Instr_9485', ''),
|
||||
0xa294a1: ('MaridiaBeybladeTurtle_Instr_94A1', ''),
|
||||
0xa294c7: ('MaridiaBeybladeTurtle_Instr_94C7', ''),
|
||||
0xa294d1: ('MaridiaBeybladeTurtle_Instr_94D1', ''),
|
||||
0xa29f2a: ('SpikeShootingPlant_Instr_9F2A', ''),
|
||||
0xa2a095: ('SpikeShootingPlant_Instr_A095', ''),
|
||||
0xa2a56d: ('MaridiaSpikeyShell_Instr_A56D', ''),
|
||||
0xa2a571: ('MaridiaSpikeyShell_Instr_A571', ''),
|
||||
0xa2a0a7: ('SpikeShootingPlant_Instr_A0A7', 'H'),
|
||||
0xa2b9c7: ('Rinka_Instr_B9C7', ''),
|
||||
0xa2be8e: ('NorfairLavajumpingEnemy_Instr_BE8E', ''),
|
||||
0xa2bbc3: ('EnemyInstr_Rio_Instr_1', ''),
|
||||
0xa2c1c9: ('NorfairRio_Instr_C1C9', ''),
|
||||
0xa2c1d4: ('NorfairRio_Instr_C1D4', ''),
|
||||
0xa2c1df: ('NorfairRio_Instr_C1DF', ''),
|
||||
0xa2c1ea: ('NorfairRio_Instr_C1EA', ''),
|
||||
0xa2c1f5: ('NorfairRio_Instr_C1F5', ''),
|
||||
0xa2c200: ('NorfairRio_Instr_C200', ''),
|
||||
0xa2c20b: ('NorfairRio_Instr_C20B', ''),
|
||||
0xa2c216: ('NorfairRio_Instr_C216', ''),
|
||||
0xa2c221: ('NorfairRio_Instr_C221', ''),
|
||||
0xa2c22c: ('NorfairRio_Instr_C22C', ''),
|
||||
0xa2c237: ('NorfairRio_Instr_C237', ''),
|
||||
0xa2c6d2: ('LowerNorfairRio_Instr_C6D2', ''),
|
||||
0xa2c6dd: ('LowerNorfairRio_Instr_C6DD', ''),
|
||||
0xa2c6e8: ('LowerNorfairRio_Instr_C6E8', ''),
|
||||
0xa2b9b3: ('Rinka_Instr_B9B3', ''),
|
||||
0xa2b9bd: ('Rinka_Instr_B9BD', ''),
|
||||
0xa2cb6b: ('MaridiaLargeSnail_Instr_CB6B', ''),
|
||||
0xa2ccb3: ('MaridiaLargeSnail_Instr_CCB3', ''),
|
||||
0xa2ccbe: ('MaridiaLargeSnail_Instr_CCBE', ''),
|
||||
0xa2ccc9: ('MaridiaLargeSnail_Instr_CCC9', ''),
|
||||
0xa2e5fb: ('LavaSeahorse_Instr_E5FB', ''),
|
||||
0xa386e3: ('Waver_Instr_1', ''),
|
||||
0xa38956: ('Metalee_Instr_1', ''),
|
||||
0xa39096: ('MaridiaFish_Instr_3', ''),
|
||||
0xa390a0: ('MaridiaFish_Instr_1', ''),
|
||||
0xa390aa: ('MaridiaFish_Instr_2', ''),
|
||||
0xa39c6b: ('PlatformThatFallsWithSamus_Instr_3', ''),
|
||||
0xa39c76: ('PlatformThatFallsWithSamus_Instr_4', ''),
|
||||
0xa39c81: ('PlatformThatFallsWithSamus_Instr_1', ''),
|
||||
0xa39c8c: ('PlatformThatFallsWithSamus_Instr_2', ''),
|
||||
0xa3aa68: ('Sidehopper_Func_1', 'H'),
|
||||
0xa3aafe: ('Sidehopper_Instr_1', ''),
|
||||
0xa3b429: ('MaridiaRefillCandy_Instr_1', ''),
|
||||
0xa3b434: ('MaridiaRefillCandy_Instr_2', ''),
|
||||
0xa3b43f: ('MaridiaRefillCandy_Instr_3', ''),
|
||||
0xa3ba78: ('Bang_Instr_1', ''),
|
||||
0xa3baa8: ('Bang_Instr_2', ''),
|
||||
0xa3c6a4: ('Skree_Instr_1', ''),
|
||||
0xa3cc36: ('MaridiaSnail_Instr_1', 'A'),
|
||||
0xa3cc3f: ('MaridiaSnail_Instr_2', 'A'),
|
||||
0xa3cc48: ('MaridiaSnail_Instr_4', 'H'),
|
||||
0xa3cc5f: ('MaridiaSnail_Instr_3', 'II'),
|
||||
0xa3cc78: ('MaridiaSnail_Instr_5', ''),
|
||||
0xa3dfc2: ('WreckedShipOrangeZoomer_Func_1', 'A'),
|
||||
0xa3dbc8: ('Reflec_Instr_1', 'H'),
|
||||
0xa3e660: ('Zoomer_Instr_SetPreinstr', 'A'),
|
||||
0xa3eaa5: ('Metroid_Instr_2', ''),
|
||||
0xa3eab1: ('Metroid_Instr_1', ''),
|
||||
0xa486a6: ('Crocomire_Instr_1', ''),
|
||||
0xa48cfb: ('Crocomire_Instr_11', ''),
|
||||
0xa48fc7: ('Crocomire_Instr_2', ''),
|
||||
0xa48ffa: ('Crocomire_Instr_3', ''),
|
||||
0xa48fdf: ('Crocomire_Instr_4', ''),
|
||||
0xa48752: ('Crocomire_Instr_14', ''),
|
||||
0xa48d07: ('Crocomire_Instr_7', ''),
|
||||
0xa48d13: ('Crocomire_Instr_19', ''),
|
||||
0xa48fff: ('Crocomire_Instr_15', ''),
|
||||
0xa4907f: ('Crocomire_Instr_18', ''),
|
||||
0xa4908f: ('Crocomire_Instr_12', ''),
|
||||
0xa49094: ('Crocomire_Instr_17', ''),
|
||||
0xa4905b: ('Crocomire_Instr_13', ''),
|
||||
0xa4901d: ('Crocomire_Instr_16', ''),
|
||||
0xa49a9b: ('Crocomire_Instr_8', ''),
|
||||
0xa49aa0: ('Crocomire_Instr_6', ''),
|
||||
0xa49aa5: ('Crocomire_Instr_9', ''),
|
||||
0xa49aaa: ('Crocomire_Instr_5', ''),
|
||||
0xa49aaf: ('Crocomire_Instr_20', ''),
|
||||
0xa49ab4: ('Crocomire_Instr_21', ''),
|
||||
0xa49ab9: ('Crocomire_Instr_22', ''),
|
||||
0xa49abe: ('Crocomire_Instr_23', ''),
|
||||
0xa49ac3: ('Crocomire_Instr_24', ''),
|
||||
0xa49ac8: ('Crocomire_Instr_10', ''),
|
||||
0xa49acd: ('Crocomire_Instr_25', ''),
|
||||
0xa49ad2: ('Crocomire_Instr_26', ''),
|
||||
0xa49ad7: ('Crocomire_Instr_27', ''),
|
||||
0xa594dd: ('Draygon_Instr_1', 'GGGG'),
|
||||
0xa59736: ('Draygon_Instr_13', 'A'),
|
||||
0xa5973f: ('Draygon_Instr_8', ''),
|
||||
0xa59752: ('Draygon_Instr_7', ''),
|
||||
0xa59765: ('Draygon_Instr_6', ''),
|
||||
0xa59778: ('Draygon_Instr_9', ''),
|
||||
0xa59895: ('Draygon_Instr_2', ''),
|
||||
0xa598d3: ('Draygon_Instr_11', ''),
|
||||
0xa598ef: ('Draygon_Instr_5', ''),
|
||||
0xa59b9a: ('Draygon_Instr_15', ''),
|
||||
0xa59c8a: ('Draygon_Instr_17', ''),
|
||||
0xa59e0a: ('Draygon_Instr_14', 'II'),
|
||||
0xa59f57: ('Draygon_Instr_16', 'A'),
|
||||
0xa59f60: ('Draygon_Instr_10', 'H'),
|
||||
0xa59f6e: ('Draygon_Instr_4', 'H'),
|
||||
0xa59f7c: ('Draygon_Instr_12', ''),
|
||||
0xa59fae: ('Draygon_Instr_18', ''),
|
||||
0xa5c47b: ('Draygon_Instr_3', 'A'),
|
||||
0xa5e75f: ('Draygon_Instr_25', ''),
|
||||
0xa5e771: ('Draygon_Instr_24', ''),
|
||||
0xa5e9b1: ('Draygon_Instr_26', ''),
|
||||
0xa5e87c: ('Draygon_Instr_27', ''),
|
||||
0xa5e96e: ('Draygon_Instr_28', ''),
|
||||
0xa5e82d: ('Draygon_Instr_21', 'HH'),
|
||||
0xa5e872: ('Draygon_Instr_22', 'H'),
|
||||
0xa5e895: ('Draygon_Instr_23', 'H'),
|
||||
0xa5e8a3: ('Draygon_Instr_55', 'H'),
|
||||
0xa5e8b1: ('Draygon_Instr_30', ''),
|
||||
0xa5e8ba: ('Draygon_Instr_20', 'A'),
|
||||
0xa5e8ca: ('Draygon_Instr_29', 'H'),
|
||||
0xa5e91c: ('Draygon_Instr_19', 'H'),
|
||||
0xa68daf: ('FireGeyser_Instr_1', ''),
|
||||
0xa68e13: ('FireGeyser_Instr_2', ''),
|
||||
0xa68e2d: ('FireGeyser_Instr_3', ''),
|
||||
0xa68e41: ('FireGeyser_Instr_4', ''),
|
||||
0xa68e55: ('FireGeyser_Instr_5', ''),
|
||||
0xa68e69: ('FireGeyser_Instr_6', ''),
|
||||
0xa68e7d: ('FireGeyser_Instr_7', ''),
|
||||
0xa68e91: ('FireGeyser_Instr_8', ''),
|
||||
0xa68ea5: ('FireGeyser_Instr_9', ''),
|
||||
0xa68eb9: ('FireGeyser_Instr_10', ''),
|
||||
0xa68ecd: ('FireGeyser_Instr_11', ''),
|
||||
0xa68ee1: ('FireGeyser_Instr_12', ''),
|
||||
0xa68ef5: ('FireGeyser_Instr_13', ''),
|
||||
0xa68f09: ('FireGeyser_Instr_14', ''),
|
||||
0xa68f1d: ('FireGeyser_Instr_15', ''),
|
||||
0xa68f31: ('FireGeyser_Instr_16', ''),
|
||||
0xa68f45: ('FireGeyser_Instr_17', ''),
|
||||
0xa68f59: ('FireGeyser_Instr_18', ''),
|
||||
0xa68f6d: ('FireGeyser_Instr_19', ''),
|
||||
0xa68f81: ('FireGeyser_Instr_20', ''),
|
||||
0xa68f95: ('FireGeyser_Instr_21', ''),
|
||||
0xa68fa9: ('FireGeyser_Instr_22', ''),
|
||||
0xa68fbd: ('FireGeyser_Instr_23', ''),
|
||||
0xa68fd1: ('FireGeyser_Instr_24', ''),
|
||||
0xa69b26: ('FakeKraid_Instr_2', ''),
|
||||
0xa69b74: ('FakeKraid_Instr_1', ''),
|
||||
0xa69bb2: ('FakeKraid_Instr_3', ''),
|
||||
0xa69bc4: ('FakeKraid_Instr_4', ''),
|
||||
0xa69c02: ('FakeKraid_Instr_5', ''),
|
||||
0xa6bfc9: ('BabyMetroid_Instr_2', 'G'),
|
||||
0xa6bfe1: ('BabyMetroid_Instr_3', '6'),
|
||||
0xa6bff2: ('BabyMetroid_Instr_1', 'G'),
|
||||
0xa6bff8: ('BabyMetroid_Instr_4', 'G'),
|
||||
0xa6e4be: ('Ridley_Instr_5', ''),
|
||||
0xa6e4ca: ('Ridley_Instr_6', ''),
|
||||
0xa6e4d2: ('Ridley_Instr_10', 'G'),
|
||||
0xa6e4e9: ('sub_A6E4E9', 'H'),
|
||||
0xa6e4ee: ('Ridley_Instr_4', 'GG'),
|
||||
0xa6e4f8: ('Ridley_Instr_3', 'G'),
|
||||
0xa6e501: ('Ridley_Instr_2', 'H'),
|
||||
0xa6e517: ('Ridley_Instr_1', 'G'),
|
||||
0xa6e51f: ('Ridley_Instr_14', 'II'),
|
||||
0xa6e71c: ('Ridley_Instr_9', ''),
|
||||
0xa6e727: ('Ridley_Instr_7', ''),
|
||||
0xa6e72f: ('Ridley_Instr_8', ''),
|
||||
0xa6e84d: ('Ridley_Instr_11', ''),
|
||||
0xa6e904: ('Ridley_Instr_12', ''),
|
||||
0xa6e909: ('Ridley_Instr_13', ''),
|
||||
0xa6e969: ('Ridley_Instr_15', ''),
|
||||
0xa6e976: ('Ridley_Instr_16', ''),
|
||||
0xa6f11d: ('CeresSteam_Instr_1', ''),
|
||||
0xa6f127: ('CeresSteam_Instr_2', 'GG'),
|
||||
0xa6f135: ('CeresSteam_Instr_3', ''),
|
||||
0xa6f63e: ('CeresDoor_Instr_6', 'G'),
|
||||
0xa6f66a: ('CeresDoor_Instr_4', 'G'),
|
||||
0xa6f678: ('CeresDoor_Instr_8', 'G'),
|
||||
0xa6f68b: ('CeresSteam_Instr_4', ''),
|
||||
0xa6f695: ('CeresDoor_Instr_1', ''),
|
||||
0xa6f69f: ('CeresDoor_Instr_3', ''),
|
||||
0xa6f6a6: ('CeresSteam_Instr_5', ''),
|
||||
0xa6f6b0: ('CeresDoor_Instr_5', ''),
|
||||
0xa6f6b3: ('CeresDoor_Instr_2', ''),
|
||||
0xa6f6bd: ('CeresDoor_Instr_7', ''),
|
||||
0xa78a8f: ('Kraid_Instr_9', ''),
|
||||
0xa7b633: ('Kraid_Instr_1', ''),
|
||||
0xa7b636: ('Kraid_Instr_DecYpos', ''),
|
||||
0xa7b63c: ('Kraid_Instr_IncrYpos_Shake', ''),
|
||||
0xa7b64e: ('Kraid_Instr_PlaySound_0x76', ''),
|
||||
0xa7b65a: ('Kraid_Instr_XposMinus3', ''),
|
||||
0xa7b667: ('Kraid_Instr_XposMinus3b', ''),
|
||||
0xa7b674: ('Kraid_Instr_XposPlus3', ''),
|
||||
0xa7b683: ('Kraid_Instr_MoveHimRight', ''),
|
||||
0xa8878f: ('MiniDraygon_Instr_2', ''),
|
||||
0xa8879b: ('MiniDraygon_Instr_1', ''),
|
||||
0xa887b6: ('MiniDraygon_Instr_3', ''),
|
||||
0xa887cb: ('MiniDraygon_Instr_4', ''),
|
||||
0xa89625: ('Fune_Instr_2', ''),
|
||||
0xa89631: ('Fune_Instr_6', ''),
|
||||
0xa8964a: ('Fune_Instr_7', ''),
|
||||
0xa89663: ('Fune_Instr_1', ''),
|
||||
0xa8967c: ('Fune_Instr_4', ''),
|
||||
0xa89695: ('Fune_Instr_3', ''),
|
||||
0xa896b4: ('Fune_Instr_5', ''),
|
||||
0xa8ae26: ('NorfairLavaMan_Instr_8', ''),
|
||||
0xa8ae30: ('NorfairLavaMan_Instr_14', ''),
|
||||
0xa8ae3a: ('NorfairLavaMan_Instr_2', ''),
|
||||
0xa8ae45: ('NorfairLavaMan_Instr_7', ''),
|
||||
0xa8ae50: ('NorfairLavaMan_Instr_10', ''),
|
||||
0xa8ae5a: ('NorfairLavaMan_Instr_12', ''),
|
||||
0xa8ae64: ('NorfairLavaMan_Instr_9', ''),
|
||||
0xa8ae88: ('NorfairLavaMan_Instr_11', ''),
|
||||
0xa8ae96: ('NorfairLavaMan_Instr_13', ''),
|
||||
0xa8aeba: ('NorfairLavaMan_Instr_5', ''),
|
||||
0xa8aeca: ('NorfairLavaMan_Instr_15', ''),
|
||||
0xa8aee4: ('NorfairLavaMan_Instr_4', ''),
|
||||
0xa8aefe: ('NorfairLavaMan_Instr_16', ''),
|
||||
0xa8af18: ('NorfairLavaMan_Instr_6', ''),
|
||||
0xa8af32: ('sub_A8AF32', ''),
|
||||
0xa8af44: ('NorfairLavaMan_Instr_3', ''),
|
||||
0xa8a0c7: ('YappingMaw_Instr_2', ''),
|
||||
0xa8a0d9: ('YappingMaw_Instr_4', ''),
|
||||
0xa8a10f: ('YappingMaw_Instr_3', ''),
|
||||
0xa8a0eb: ('YappingMaw_Instr_5', ''),
|
||||
0xa8a0fd: ('YappingMaw_Instr_7', ''),
|
||||
0xa8a121: ('YappingMaw_Instr_6', ''),
|
||||
0xa8a133: ('YappingMaw_Instr_1', ''),
|
||||
0xa8ae12: ('NorfairLavaMan_Instr_1', 'H'),
|
||||
0xa8b75e: ('Beetom_Instr_1', ''),
|
||||
0xa8cd09: ('WreckedShipRobot_Instr_4', ''),
|
||||
0xa8cda4: ('WreckedShipRobot_Instr_9', ''),
|
||||
0xa8cdea: ('WreckedShipRobot_Instr_6', ''),
|
||||
0xa8ce85: ('WreckedShipRobot_Instr_8', ''),
|
||||
0xa8cecb: ('WreckedShipRobot_Instr_7', ''),
|
||||
0xa8cecf: ('WreckedShipRobot_Instr_15', ''),
|
||||
0xa8cf6a: ('WreckedShipRobot_Instr_18', ''),
|
||||
0xa8cfb0: ('WreckedShipRobot_Instr_16', ''),
|
||||
0xa8d04b: ('WreckedShipRobot_Instr_17', ''),
|
||||
0xa8d091: ('WreckedShipRobot_Instr_3', ''),
|
||||
0xa8d0c2: ('WreckedShipRobot_Instr_10', ''),
|
||||
0xa8d0c6: ('WreckedShipRobot_Instr_14', ''),
|
||||
0xa8d0d2: ('WreckedShipRobot_Instr_2', ''),
|
||||
0xa8d100: ('WreckedShipRobot_Instr_13', ''),
|
||||
0xa8d107: ('WreckedShipRobot_Instr_1', ''),
|
||||
0xa8d131: ('WreckedShipRobot_Instr_12', ''),
|
||||
0xa8d13d: ('WreckedShipRobot_Instr_5', ''),
|
||||
0xa8d16b: ('WreckedShipRobot_Instr_11', ''),
|
||||
0xa8df1c: ('WalkingLavaSeahorse_Instr_4', ''),
|
||||
0xa8df33: ('WalkingLavaSeahorse_Instr_3', ''),
|
||||
0xa8df39: ('WalkingLavaSeahorse_Instr_5', ''),
|
||||
0xa8df3f: ('WalkingLavaSeahorse_Instr_6', ''),
|
||||
0xa8df63: ('WalkingLavaSeahorse_Instr_2', ''),
|
||||
0xa8df71: ('WalkingLavaSeahorse_Instr_1', ''),
|
||||
0xa8e61d: ('WreckedShipSpark_Instr_2', ''),
|
||||
0xa8e62a: ('WreckedShipSpark_Instr_1', ''),
|
||||
0xa8f526: ('KiHunter_Instr_1', ''),
|
||||
0xa8f5e4: ('KiHunter_Instr_2', ''),
|
||||
0xa8f67f: ('KiHunter_Instr_3', ''),
|
||||
0xa8f6d2: ('KiHunter_Instr_4', ''),
|
||||
0xa8f6d8: ('KiHunter_Instr_5', ''),
|
||||
0xa995b6: ('MotherBrain_Instr_MoveBodyUp10Left4', ''),
|
||||
0xa995c0: ('MotherBrain_Instr_MoveBodyUp16Left4', ''),
|
||||
0xa995ca: ('MotherBrain_Instr_MoveBodyUp12Right2', ''),
|
||||
0xa995de: ('MotherBrain_Instr_MoveDown12Left4', ''),
|
||||
0xa995e8: ('MotherBrain_Instr_MoveDown16Right2', ''),
|
||||
0xa995f2: ('MotherBrain_Instr_MoveDown10Right2', ''),
|
||||
0xa995fc: ('MotherBrain_Instr_MoveUp2Right1', ''),
|
||||
0xa9960c: ('MotherBrain_Instr_MoveRight2', ''),
|
||||
0xa9961c: ('MotherBrain_Instr_MoveUp1', ''),
|
||||
0xa99622: ('MotherBrain_Instr_MoveUp1Right3_Sfx', ''),
|
||||
0xa99638: ('MotherBrain_Instr_Down2Right15', ''),
|
||||
0xa99648: ('MotherBrain_Instr_Down4Right6', ''),
|
||||
0xa99658: ('MotherBrain_Instr_Up4Left2', ''),
|
||||
0xa99668: ('MotherBrain_Instr_Up2Left1_Sfx', ''),
|
||||
0xa9967e: ('MotherBrain_Instr_Up2Left1_Sfx2', ''),
|
||||
0xa99694: ('MotherBrain_Instr_MoveLeft2', ''),
|
||||
0xa996a4: ('MotherBrain_Instr_MoveDown1', ''),
|
||||
0xa996aa: ('MotherBrain_Instr_MoveDown1Left3', ''),
|
||||
0xa996ba: ('MotherBrain_Instr_MoveUp2Left15_Sfx', ''),
|
||||
0xa996d0: ('MotherBrain_Instr_MoveUp4Left6', ''),
|
||||
0xa996e0: ('MotherBrain_Instr_MoveDown4Right2', ''),
|
||||
0xa996f0: ('MotherBrain_Instr_MoveDown2Right1', ''),
|
||||
0xa99700: ('MotherBrain_Instr_SetPose_Standing', ''),
|
||||
0xa99708: ('MotherBrain_Instr_SetPose_Walking', ''),
|
||||
0xa99710: ('MotherBrain_Instr_SetPose_Crouched', ''),
|
||||
0xa99718: ('MotherBrain_Instr_SetPose_CrouchedTrans', ''),
|
||||
0xa99720: ('MotherBrain_Instr_SetPose_DeathBeamMode', ''),
|
||||
0xa99728: ('MotherBrain_Instr_SetPose_LeaningDown', ''),
|
||||
0xa99ac8: ('MotherBrain_Instr_SpawnEprojToOffset', 'IIH'),
|
||||
0xa99aef: ('MotherBrain_Instr_SpawnDeathBeamEproj', ''),
|
||||
0xa99b05: ('MotherBrain_Instr_IncrBeamAttackPhase', ''),
|
||||
0xa99b0f: ('MotherBrain_Instr_Goto', 'G'),
|
||||
0xa99b14: ('MotherBrain_Instr_EnableNeckMovementGoto', 'G'),
|
||||
0xa99b20: ('MotherBrain_Instr_DisableNeckMovement', ''),
|
||||
0xa99b28: ('MotherBrain_Instr_QueueSfx2', 'H'),
|
||||
0xa99b32: ('MotherBrain_Instr_QueueSfx3', 'H'),
|
||||
0xa99b3c: ('MotherBrain_Instr_SpawnDroolEproj', ''),
|
||||
0xa99b6d: ('MotherBrain_Instr_SpawnPurpleBreath', ''),
|
||||
0xa99b77: ('MotherBrain_Instr_SetMainShakeTimer50', ''),
|
||||
0xa99c65: ('MotherBrain_Instr_GotoEitherOr', ''),
|
||||
0xa99cad: ('MotherBrain_Instr_MaybeGoto', ''),
|
||||
0xa99d0d: ('MotherBrain_Instr_MaybeGoto2', ''),
|
||||
0xa99d21: ('MotherBrain_Instr_Goto2', ''),
|
||||
0xa99df7: ('MotherBrain_Instr_QueueShitroidAttackSfx', ''),
|
||||
0xa99e29: ('MotherBrain_Instr_SpawnBlueRingEproj', ''),
|
||||
0xa99e37: ('MotherBrain_Instr_AimRingsAtShitroid', ''),
|
||||
0xa99e5b: ('MotherBrain_Instr_AimRingsAtSamus', ''),
|
||||
0xa99ea3: ('MotherBrain_Instr_IncrShitroidAttackCtr', ''),
|
||||
0xa99eb5: ('MotherBrain_Instr_SetShitroidAttackCtr0', ''),
|
||||
0xa99ebd: ('MotherBrain_Instr_SpawnBombEproj', 'H'),
|
||||
0xa99f46: ('MotherBrain_Instr_SpawnLaserEproj', ''),
|
||||
0xa99f84: ('MotherBrain_Instr_SpawnRainbowEproj', ''),
|
||||
0xa99f8e: ('MotherBrain_Instr_SetupFxForRainbowBeam', ''),
|
||||
0xa9cfb4: ('Shitroid_Instr_1', ''),
|
||||
0xa9cfca: ('Shitroid_Instr_2', ''),
|
||||
0xa9ecd0: ('sub_A9ECD0', ''),
|
||||
0xa9f920: ('Shitroid_Instr_3', ''),
|
||||
0xa9f936: ('Shitroid_Instr_4', ''),
|
||||
0xa9f990: ('Shitroid_Instr_6', ''),
|
||||
0xa9f994: ('Shitroid_Instr_5', 'G'),
|
||||
0xaab09c: ('Torizo_Instr_3', 'A'),
|
||||
0xaab11d: ('Torizo_Instr_31', ''),
|
||||
0xaab1be: ('Torizo_Instr_33', ''),
|
||||
0xaab224: ('Torizo_Instr_36', ''),
|
||||
0xaab22e: ('Torizo_Instr_37', ''),
|
||||
0xaab238: ('Torizo_Instr_35', ''),
|
||||
0xaab24d: ('Torizo_Instr_38', ''),
|
||||
0xaab271: ('Torizo_Instr_6', ''),
|
||||
0xaab94d: ('Torizo_Instr_5', ''),
|
||||
0xaab951: ('Torizo_Instr_9', ''),
|
||||
0xaac2c8: ('Torizo_Instr_7', ''),
|
||||
0xaac2c9: ('Torizo_Instr_2', ''),
|
||||
0xaac2d1: ('Torizo_Instr_8', ''),
|
||||
0xaac2d9: ('Torizo_Instr_25', 'GG'),
|
||||
0xaac2ed: ('Torizo_Instr_22', 'A'),
|
||||
0xaac2f7: ('Torizo_Instr_19', ''),
|
||||
0xaac2fd: ('Torizo_Instr_32', ''),
|
||||
0xaac303: ('Torizo_Instr_30', 'H'),
|
||||
0xaac32f: ('Torizo_Instr_34', ''),
|
||||
0xaac34a: ('Torizo_Instr_24', ''),
|
||||
0xaac35b: ('Torizo_Instr_12', ''),
|
||||
0xaac36d: ('Torizo_Instr_10', ''),
|
||||
0xaac377: ('Torizo_Instr_11', ''),
|
||||
0xaac38a: ('Torizo_Instr_29', ''),
|
||||
0xaac3a0: ('Torizo_Instr_1', ''),
|
||||
0xaac3b6: ('Torizo_Instr_28', ''),
|
||||
0xaac3cc: ('Torizo_Instr_4', 'H'),
|
||||
0xaac41e: ('Torizo_Instr_40', 'H'),
|
||||
0xaac470: ('Torizo_Instr_16', 'H'),
|
||||
0xaac4e5: ('Torizo_Instr_27', 'H'),
|
||||
0xaac55a: ('Torizo_Instr_23', 'G'),
|
||||
0xaac567: ('Torizo_Instr_14', 'G'),
|
||||
0xaac58b: ('Torizo_Instr_15', 'G'),
|
||||
0xaac5a4: ('Torizo_Instr_26', 'GG'),
|
||||
0xaac5cb: ('Torizo_Instr_18', ''),
|
||||
0xaac5e3: ('Torizo_Instr_20', 'H'),
|
||||
0xaac5f2: ('Torizo_Instr_44', 'H'),
|
||||
0xaac601: ('Torizo_Instr_21', 'H'),
|
||||
0xaac610: ('Torizo_Instr_17', ''),
|
||||
0xaac618: ('Torizo_Instr_13', ''),
|
||||
0xaacace: ('Torizo_Instr_39', 'G'),
|
||||
0xaacade: ('Torizo_Instr_41', ''),
|
||||
0xaacae2: ('Torizo_Instr_42', ''),
|
||||
0xaacdd7: ('Torizo_Instr_48', ''),
|
||||
0xaad0e9: ('Torizo_Instr_57', ''),
|
||||
0xaad0f3: ('Torizo_Instr_58', 'G'),
|
||||
0xaad17b: ('Torizo_Instr_59', ''),
|
||||
0xaad187: ('Torizo_Instr_62', ''),
|
||||
0xaad1e7: ('Torizo_Instr_63', ''),
|
||||
0xaad38f: ('Torizo_Instr_56', ''),
|
||||
0xaad397: ('Torizo_Instr_60', ''),
|
||||
0xaad39f: ('Torizo_Instr_46', ''),
|
||||
0xaad3e0: ('Torizo_Instr_47', ''),
|
||||
0xaad3ea: ('Torizo_Instr_49', 'G'),
|
||||
0xaad436: ('Torizo_Instr_61', 'H'),
|
||||
0xaad445: ('Torizo_Instr_53', 'G'),
|
||||
0xaad474: ('Torizo_Instr_55', 'G'),
|
||||
0xaad49b: ('Torizo_Instr_52', 'G'),
|
||||
0xaad4ba: ('Torizo_Instr_50', 'G'),
|
||||
0xaad4f3: ('Torizo_Instr_43', ''),
|
||||
0xaad4fd: ('Torizo_Instr_51', 'G'),
|
||||
0xaad526: ('Torizo_Instr_45', 'GG'),
|
||||
0xaad54d: ('Torizo_Instr_54', 'H'),
|
||||
0xaad9ba: ('Shaktool_Instr_1', ''),
|
||||
0xaad931: ('Shaktool_Instr_2', ''),
|
||||
0xaad93f: ('Shaktool_Instr_3', ''),
|
||||
0xaad94a: ('Shaktool_Instr_4', ''),
|
||||
0xaad953: ('Shaktool_Instr_5', ''),
|
||||
0xaad99f: ('Shaktool_Instr_6', ''),
|
||||
0xaae429: ('Shaktool_Instr_9', ''),
|
||||
0xaae436: ('Shaktool_Instr_11', ''),
|
||||
0xaae43d: ('Shaktool_Instr_10', ''),
|
||||
0xaae57f: ('Shaktool_Instr_8', ''),
|
||||
0xaae587: ('Shaktool_Instr_13', ''),
|
||||
0xaae58f: ('Shaktool_Instr_12', 'I'),
|
||||
0xaae5d8: ('Shaktool_Instr_7', 'H'),
|
||||
0xaae6f0: ('Shaktool_Instr_14', ''),
|
||||
0xb2ee40: ('SpacePirates_Instr_MovePixelsDownAndChangeDirFaceRight', 'I'),
|
||||
0xb2ee72: ('SpacePirates_Instr_MovePixelsDownAndChangeDirFaceLeft', 'I'),
|
||||
0xb2eea4: ('SpacePirates_Instr_RandomNewDirFaceR', ''),
|
||||
0xb2eebc: ('SpacePirates_Instr_RandomNewDirFaceL', ''),
|
||||
0xb2eed4: ('SpacePirates_Instr_PrepareWallJumpR', ''),
|
||||
0xb2eefd: ('SpacePirates_Instr_PrepareWallJumpL', ''),
|
||||
0xb2ef2a: ('SpacePirates_Instr_FireLaserL', ''),
|
||||
0xb2ef5d: ('SpacePirates_Instr_FireLaserR', ''),
|
||||
0xb2ef83: ('SpacePirates_Instr_SetEnemyFunc', 'A'),
|
||||
0xb2ef93: ('SpacePirates_Instr_PlaySfx', ''),
|
||||
0xb2f536: ('SpacePirates_Instr_20', 'H'),
|
||||
0xb2f546: ('SpacePirates_Instr_16', 'H'),
|
||||
0xb2f554: ('sub_B2F554', ''),
|
||||
0xb2f564: ('SpacePirates_Instr_15', 'HII'),
|
||||
0xb2f590: ('SpacePirates_Instr_18', ''),
|
||||
0xb2f5b3: ('sub_B2F5B3', ''),
|
||||
0xb2f5d6: ('SpacePirates_Instr_17', ''),
|
||||
0xb2f969: ('SpacePirates_Instr_19', ''),
|
||||
0xb2f985: ('SpacePirates_F985', ''),
|
||||
0xb2fa3d: ('SpacePirates_Instr_21', ''),
|
||||
0xb2fa59: ('SpacePirates_FA59', ''),
|
||||
0xb2fc68: ('SpacePirates_Instr_12', 'I'),
|
||||
0xb2fc90: ('SpacePirates_Instr_14', 'I'),
|
||||
0xb2fcb8: ('SpacePirates_Instr_11', 'A'),
|
||||
0xb2fcc8: ('SpacePirates_Instr_13', ''),
|
||||
0xb394c7: ('Botwoon_Instr_1', ''),
|
||||
0xb394d7: ('Botwoon_Instr_2', ''),
|
||||
0xb394e7: ('Botwoon_Instr_3', ''),
|
||||
0xb394f7: ('Botwoon_Instr_4', ''),
|
||||
0xb39507: ('Botwoon_Instr_5', ''),
|
||||
0xb39517: ('Botwoon_Instr_6', ''),
|
||||
0xb39527: ('Botwoon_Instr_7', ''),
|
||||
0xb39537: ('Botwoon_Instr_8', ''),
|
||||
0xb39547: ('Botwoon_Instr_9', ''),
|
||||
0xb39557: ('Botwoon_Instr_10', ''),
|
||||
0xb39567: ('Botwoon_Instr_SetSpitting', ''),
|
||||
0xb39572: ('Botwoon_Instr_QueueSpitSfx', ''),
|
||||
0xb3e545: ('EscapeEtecoon_Instr_1', 'G'),
|
||||
0xb3e610: ('EscapeEtecoon_Instr_2', 'I'),
|
||||
0xb3eaa8: ('EscapeDachora_Instr_2', 'G'),
|
||||
0xb3eab8: ('EscapeDachora_Instr_3', 'G'),
|
||||
0xb3eac9: ('EscapeDachora_Instr_1', ''),
|
||||
0xb3ead7: ('EscapeDachora_Instr_4', ''),
|
||||
}
|
||||
|
||||
commons = {
|
||||
0x806b: ('SetAiPreInstr', 'A'),
|
||||
0x8074: ('ClearAiPreInstr', ''),
|
||||
0x807c: ('StopScript', ''),
|
||||
0x808a: ('Call', 'A'),
|
||||
0x80b5: ('CallFar', 'L'),
|
||||
0x80ed: ('Goto', 'G'),
|
||||
0x8110: ('DecTimerAndGoto', 'G'),
|
||||
0x8123: ('SetTimer', 'H'),
|
||||
0x812f: ('Sleep', ''),
|
||||
0x813a: ('WaitNframes', 'H'),
|
||||
0x814b: ('CopyToVram', 'HLX'),
|
||||
0x8173: ('EnableOffScreenProcessing', ''),
|
||||
0x817d: ('DisableOffScreenProcessing', ''),
|
||||
}
|
||||
|
||||
kInstrTerminator = {
|
||||
0x807C, 0x80ED, 0x812F,
|
||||
0xa6BFF8, 0xa29451, 0xa8CECB, 0xa8D0C2, 0xa8F526, 0xa99B0F, 0xa99B14, 0xa99C65, 0xa99d21, 0xa9cfb4, 0xA9CFCA, 0xa9f920, 0xa9f936, 0xa9f990,
|
||||
0xaaC2F7, 0xaaC2FD, 0xb2EEA4, 0xb2EEBC, 0xb2FCC8
|
||||
}
|
||||
|
||||
|
||||
for x in [0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3]:
|
||||
for k, v in commons.items():
|
||||
kInstructionSet[k + (x << 16)] = v
|
||||
|
||||
kCommandByName = {v[0] : (k, v[1]) for k, v in kInstructionSet.items()}
|
||||
|
||||
class EnemyInstrParser(InstrParserCommon):
|
||||
instruction_set = kInstructionSet
|
||||
instruction_set_term = kInstrTerminator
|
||||
TAG = 'enemy'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.sprite_maps = set()
|
||||
|
||||
def handle_draw_command(self, ins, ea):
|
||||
drawp = get_word(ea + 2) | (ea & 0xff0000)
|
||||
self.sprite_maps.add(drawp)
|
||||
#assert drawp != 0xa6f142, hex(ea)
|
||||
self.print_line(ea, f' {ins} ! {get_ea_name(drawp)}')
|
||||
return 4
|
||||
|
||||
def process_queue_entry(self, ea):
|
||||
assert ea & 0x8000
|
||||
while ea not in self.visited:
|
||||
self.visited.add(ea)
|
||||
ins = get_word(ea)
|
||||
if ins & 0x8000:
|
||||
ea_org = ea
|
||||
ins = (ea & 0xff0000) | ins
|
||||
if ins not in self.instruction_set:
|
||||
print(f'// Ins {ins:X} not in iset at {ea:X}')
|
||||
return
|
||||
name, operands = self.instruction_set[ins]
|
||||
ea += 2
|
||||
r = []
|
||||
for op in operands:
|
||||
if op == 'H':
|
||||
r.append('%d' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'I':
|
||||
r.append('%d' % sex16(get_word(ea)))
|
||||
ea += 2
|
||||
elif op == 'X':
|
||||
r.append('0x%x' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'L':
|
||||
r.append(get_ea_name(get_word(ea) + get_byte(ea+2) * 65536))
|
||||
ea += 3
|
||||
elif op == 'A':
|
||||
addr = (ea & 0xff0000) | get_word(ea)
|
||||
r.append(get_ea_name(addr))
|
||||
ea += 2
|
||||
elif op == '6':
|
||||
addr = 0xa60000 | get_word(ea)
|
||||
r.append(get_ea_name(addr))
|
||||
ea += 2
|
||||
elif op == 'G':
|
||||
addr = (ea & 0xff0000) | get_word(ea)
|
||||
assert addr & 0x8000, hex(ea)
|
||||
r.append(get_ea_name(addr, short_label = True))
|
||||
self.visit(addr)
|
||||
ea += 2
|
||||
else:
|
||||
assert 0, op
|
||||
self.print_line(ea_org, f' {name}({", ".join(r)})')
|
||||
if ins in self.instruction_set_term or (ins & 0xffff) in self.instruction_set_term:
|
||||
self.print_line(ea_org + 1, '')
|
||||
break
|
||||
else:
|
||||
ea += self.handle_draw_command(ins, ea)
|
||||
204
assets/eproj_decode.py
Normal file
204
assets/eproj_decode.py
Normal file
@@ -0,0 +1,204 @@
|
||||
import pickle
|
||||
from decode_common import *
|
||||
|
||||
BANK = 0x86
|
||||
|
||||
kEprojInstructionSet = {
|
||||
0x868154: ('Delete', ''),
|
||||
0x868159: ('Sleep', ''),
|
||||
0x868161: ('SetPreInstr_', 'A'),
|
||||
0x86816a: ('ClearPreInstr', ''),
|
||||
0x868171: ('CallFunc', 'L'),
|
||||
0x86818b: ('CallFuncWithParam', 'LH'),
|
||||
0x8681ab: ('Goto', 'G'),
|
||||
0x8681c6: ('DecTimerAndGotoIfNonZero', 'G'),
|
||||
0x8681d5: ('SetTimer', 'H'),
|
||||
0x8681de: ('nullsub_82', ''),
|
||||
0x8681df: ('MoveRandomlyWithinRadius', 'BBBB'),
|
||||
0x868230: ('SetProjectileProperties', 'X'),
|
||||
0x86823c: ('ClearProjectileProperties', 'X'),
|
||||
0x868248: ('EnableCollisionWithSamusProj', ''),
|
||||
0x868252: ('DisableCollisionWithSamusProj', ''),
|
||||
0x86825c: ('DisableCollisionWithSamus', ''),
|
||||
0x868266: ('EnableCollisionWithSamus', ''),
|
||||
0x868270: ('SetToNotDieOnContact', ''),
|
||||
0x86827a: ('SetToDieOnContact', ''),
|
||||
0x868284: ('SetLowPriority', ''),
|
||||
0x86828e: ('SetHighPriority', ''),
|
||||
0x868298: ('SetXyRadius', 'BB'),
|
||||
0x8682a1: ('SetXyRadiusZero', ''),
|
||||
0x8682a5: ('CalculateDirectionTowardsSamus', ''),
|
||||
0x8682d5: ('WriteColorsToPalette', 'HHH'),
|
||||
0x8682fd: ('QueueMusic', 'B'),
|
||||
0x868309: ('QueueSfx1_Max6', 'B'),
|
||||
0x868312: ('QueueSfx2_Max6', 'B'),
|
||||
0x86831b: ('QueueSfx3_Max6', 'B'),
|
||||
0x868324: ('QueueSfx1_Max15', 'B'),
|
||||
0x86832d: ('QueueSfx2_Max15', 'B'),
|
||||
0x868336: ('QueueSfx3_Max15', 'B'),
|
||||
0x86833f: ('QueueSfx1_Max3', 'B'),
|
||||
0x868348: ('QueueSfx2_Max3', 'B'),
|
||||
0x868351: ('QueueSfx3_Max3', 'B'),
|
||||
0x86835a: ('QueueSfx1_Max9', 'B'),
|
||||
0x868363: ('QueueSfx2_Max9', 'B'),
|
||||
0x86836c: ('QueueSfx3_Max9', 'B'),
|
||||
0x868375: ('QueueSfx1_Max1', 'B'),
|
||||
0x86837e: ('QueueSfx2_Max1', 'B'),
|
||||
0x868387: ('QueueSfx3_Max1', 'B'),
|
||||
0x868c68: ('SpawnEnemyDropsWithDraygonsEyeDrops', ''),
|
||||
0x868cf6: ('EnemyProjInstr_SetPreInstrA', ''),
|
||||
0x868d99: ('EprojInstr_868D99', ''),
|
||||
0x869270: ('EprojInstr_9270', ''),
|
||||
0x8695ba: ('EprojInstr_95BA', ''),
|
||||
0x8695ed: ('EprojInstr_95ED', ''),
|
||||
0x869620: ('EprojInstr_9620', ''),
|
||||
0x86980e: ('EprojInstr_980E', ''),
|
||||
0x86a050: ('SetPreInstrAndRun', 'A'),
|
||||
0x86a3be: ('EprojInstr_A3BE', ''),
|
||||
0x86ab8a: ('SpawnEnemyDrops', 'aa'),
|
||||
0x86af36: ('ResetXYpos1', ''),
|
||||
0x86b269: ('SetVelTowardsSamus1', ''),
|
||||
0x86b7ea: ('SpawnTourianStatueUnlockingParticle', ''),
|
||||
0x86b7f5: ('Earthquake', ''),
|
||||
0x86b818: ('SpawnTourianStatueUnlockingParticleTail', ''),
|
||||
0x86c173: ('SwitchJump', ''),
|
||||
0x86c1b4: ('UserPalette0', ''),
|
||||
0x86caee: ('MotherBrainPurpleBreathIsActive', ''),
|
||||
0x86c42e: ('sub_86C42E', ''),
|
||||
0x86c8d0: ('Add12ToY', ''),
|
||||
0x86d1c7: ('EprojInstr_D1C7', ''),
|
||||
0x86d1ce: ('EprojInstr_D1CE', ''),
|
||||
0x86d5f2: ('SetN00bTubeShardX', 'SS'),
|
||||
0x86d5e1: ('AssignNewN00bTubeShardVelocity', ''),
|
||||
0x86d69a: ('SetXvelRandom', ''),
|
||||
0x86dc5a: ('EprojInstr_DC5A', ''),
|
||||
0x86dc61: ('SpawnEnemyDrops_0', ''),
|
||||
0x86dfea: ('EprojInstr_DFEA', ''),
|
||||
0x86e533: ('SetYVel', 'I'),
|
||||
0x86ee8b: ('QueueSfx2_9', ''),
|
||||
0x86b13e: ('sub_86B13E', ''),
|
||||
0x86b272: ('SetVelTowardsSamus2', ''),
|
||||
0x86b3b8: ('GotoIfFunc1', 'G'),
|
||||
0x86d15c: ('EprojInstr_D15C', ''),
|
||||
0x86d1b6: ('EprojInstr_D1B6', ''),
|
||||
0x86d62a: ('EprojInstr_D62A', 'H'),
|
||||
0x86dc77: ('SpawnSporesEproj', ''),
|
||||
0x86ef10: ('HandleRespawningEnemy', ''),
|
||||
0x86eeaf: ('EprojInstr_EEAF', ''),
|
||||
0x86eea3: ('QueueSfx2_B', ''),
|
||||
0x86ee97: ('QueueSfx2_24', ''),
|
||||
0x86ece3: ('EprojInstr_ECE3', 'H'),
|
||||
0x86ed17: ('EprojInstr_ED17', 'H'),
|
||||
0x86ad92: ('GotoDependingOnXDirection', 'HG'),
|
||||
0x869475: ('DisableCollisionsWithSamus', ''),
|
||||
0x86a456: ('GotoWithProbability25', 'G'),
|
||||
0x86af92: ('MoveY_Minus4', ''),
|
||||
0x86b841: ('AddToYpos', 'H'),
|
||||
}
|
||||
|
||||
kCommandByName = {v[0] : (k, v[1]) for k, v in kEprojInstructionSet.items()}
|
||||
|
||||
|
||||
kEprojTerminator = {0x868154, 0x868159, 0x8681AB, 0x86c173}
|
||||
kEprojTerminatorEA = {0x86B818}
|
||||
|
||||
if 0:
|
||||
name2ea = pickle.loads(open('names.pickle', 'rb').read())
|
||||
name2ea = {k.replace('?', '') : v for k, v in name2ea.items()}
|
||||
ea2name = {v:k for k,v in name2ea.items()}
|
||||
for k, v in kEprojInstructionSet.items():
|
||||
k = k | 0x860000
|
||||
t = ea2name[k]
|
||||
name = t.replace('EprojInstr_', '')
|
||||
args, _ = v
|
||||
tostr = {1 : 'B', 2 : 'H', 3 : 'L'}
|
||||
args = ''.join(tostr[a] for a in args)
|
||||
if name.startswith('Goto'):
|
||||
args = args[:-1] + 'G'
|
||||
print(" 0x%x: ('%s', '%s')," % (k, name, args))
|
||||
|
||||
class EprojParser(InstrParserCommon):
|
||||
forbidden_addrs = {0x868a39}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.sprite_maps = set()
|
||||
|
||||
def print(self, file):
|
||||
print('<?eproj\n', file = file)
|
||||
for ea, line in sorted(self.lines, key = lambda x: x[0]):
|
||||
if ea in self.label:
|
||||
print(f'{get_ea_name(ea, short_label = True)}:', file = file)
|
||||
print(line, file = file)
|
||||
print('?>', file = file)
|
||||
print('', file = file)
|
||||
|
||||
def process_queue_entry(self, ea):
|
||||
assert ea & 0x8000
|
||||
while True:
|
||||
if ea in self.visited or ea in kEprojTerminatorEA:
|
||||
break
|
||||
|
||||
self.visited.add(ea)
|
||||
ins = get_word(ea)
|
||||
if ins & 0x8000:
|
||||
ea_org = ea
|
||||
ins = 0x860000 | ins
|
||||
if ins not in kEprojInstructionSet:
|
||||
print(f'// Ins {ins:X} not in iset at {ea:X}')
|
||||
self.missing_isets.add(ea & 0xff0000 | ins)
|
||||
return
|
||||
name, operands = kEprojInstructionSet[ins]
|
||||
ea += 2
|
||||
r = []
|
||||
for op in operands:
|
||||
if op == 'B':
|
||||
r.append('%d' % get_byte(ea))
|
||||
ea += 1
|
||||
elif op == 'H':
|
||||
r.append('%d' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'I':
|
||||
r.append('%d' % sex16(get_word(ea)))
|
||||
ea += 2
|
||||
elif op == 'X':
|
||||
r.append('0x%x' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'L':
|
||||
r.append(get_ea_name(get_word(ea) + get_byte(ea+2) * 65536))
|
||||
ea += 3
|
||||
elif op == 'G':
|
||||
addr = 0x860000 | get_word(ea)
|
||||
assert addr & 0x8000
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
self.visit(addr)
|
||||
ea += 2
|
||||
elif op == 'A':
|
||||
addr = 0x860000 | get_word(ea)
|
||||
assert addr & 0x8000
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
ea += 2
|
||||
elif op == 'a':
|
||||
addr = 0xa00000 | get_word(ea)
|
||||
assert addr & 0x8000
|
||||
r.append(get_ea_name(addr))
|
||||
ea += 2
|
||||
elif op == 'S':
|
||||
addr = 0x8D0000 | get_word(ea)
|
||||
assert addr & 0x8000
|
||||
r.append(get_ea_name(addr))
|
||||
self.sprite_maps.add(addr)
|
||||
ea += 2
|
||||
else:
|
||||
assert 0
|
||||
self.print_line(ea_org, f' {name}({", ".join(r)})')
|
||||
if ins in kEprojTerminator:
|
||||
self.print_line(ea_org + 1, '')
|
||||
break
|
||||
else:
|
||||
drawp = get_word(ea + 2) | 0x8D0000
|
||||
assert drawp & 0x8000, hex(ea)
|
||||
assert drawp != 0x868000
|
||||
self.sprite_maps.add(drawp)
|
||||
self.print_line(ea, f' {ins} ! {get_ea_name(drawp)}')
|
||||
ea += 4
|
||||
19217
assets/names.txt
Normal file
19217
assets/names.txt
Normal file
File diff suppressed because it is too large
Load Diff
95
assets/palfx_decode.py
Normal file
95
assets/palfx_decode.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from decode_common import *
|
||||
|
||||
BANK = 0x8D
|
||||
|
||||
kInstructionSet = {
|
||||
0x8dc595: ('Wait', ''),
|
||||
0x8dc599: ('ColorPlus2', ''),
|
||||
0x8dc5a2: ('ColorPlus3', ''),
|
||||
0x8dc5ab: ('ColorPlus4', ''),
|
||||
0x8dc5b4: ('ColorPlus8', ''),
|
||||
0x8dc5bd: ('ColorPlus9', ''),
|
||||
0x8dc5c6: ('ColorPlus15', ''),
|
||||
0x8dc5cf: ('Delete', ''),
|
||||
0x8dc5d4: ('SetPreInstr', 'A'),
|
||||
0x8dc5dd: ('ClearPreInstr', ''),
|
||||
0x8dc61e: ('Goto', 'G'),
|
||||
0x8dc639: ('DecTimerGoto', 'G'),
|
||||
0x8dc648: ('SetTimer', 'B'),
|
||||
0x8dc655: ('SetColorIndex', 'H'),
|
||||
0x8dc65e: ('QueueMusic', 'B'),
|
||||
0x8dc66a: ('QueueSfx1', 'B'),
|
||||
0x8dc673: ('QueueSfx2', 'B'),
|
||||
0x8dc67c: ('QueueSfx3', 'B'),
|
||||
0x8df1c6: ('SetPalfxIndex', 'B'),
|
||||
}
|
||||
|
||||
kCommandByName = {v[0] : (k, v[1]) for k, v in kInstructionSet.items()}
|
||||
|
||||
class PalfxParser(InstrParserCommon):
|
||||
instruction_set = kInstructionSet
|
||||
instruction_set_term = {0x8DC5CF, 0x8DC61E}
|
||||
TAG = 'palfx'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def handle_draw_command(self, ins, ea):
|
||||
xs = []
|
||||
while (x := get_word(ea + 2 + len(xs) * 2)) & 0x8000 == 0:
|
||||
xs.append('%.4x' % x)
|
||||
s = f'{ins:2} ' if ins != None else ''
|
||||
s = f' {s}! {" ".join(xs)}'
|
||||
if x != 0xc595:
|
||||
s += ' !'
|
||||
self.print_line(ea, s)
|
||||
return 2 + len(xs) * 2
|
||||
|
||||
def process_queue_entry(self, ea):
|
||||
assert ea & 0x8000
|
||||
is_finish = True
|
||||
while ea not in self.visited:
|
||||
self.visited.add(ea)
|
||||
ins = get_word(ea)
|
||||
if ins & 0x8000:
|
||||
if ins == 0xc595:
|
||||
assert not is_finish
|
||||
ea += 2
|
||||
is_finish = True
|
||||
continue
|
||||
ea_org = ea
|
||||
ins = (BANK << 16) | ins
|
||||
if ins not in self.instruction_set:
|
||||
raise Exception(f'Ins {ins:X} not in iset at {ea:X}')
|
||||
name, operands = self.instruction_set[ins]
|
||||
ea += 2
|
||||
r = []
|
||||
for op in operands:
|
||||
if op == 'B':
|
||||
r.append('%d' % get_byte(ea))
|
||||
ea += 1
|
||||
elif op == 'H':
|
||||
r.append('%d' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'G':
|
||||
addr = (BANK << 16) | get_word(ea)
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
self.visit(addr)
|
||||
ea += 2
|
||||
elif op == 'A':
|
||||
addr = (BANK << 16) | get_word(ea)
|
||||
assert addr & 0x8000
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
ea += 2
|
||||
else:
|
||||
assert 0
|
||||
self.print_line(ea_org, f' {name}({", ".join(r)})')
|
||||
if ins in self.instruction_set_term:
|
||||
self.print_line(ea_org + 1, '')
|
||||
break
|
||||
else:
|
||||
if not is_finish:
|
||||
ea -= 2
|
||||
ins = None
|
||||
ea += self.handle_draw_command(ins, ea)
|
||||
is_finish = False
|
||||
271
assets/plm_decode.py
Normal file
271
assets/plm_decode.py
Normal file
@@ -0,0 +1,271 @@
|
||||
from decode_common import *
|
||||
|
||||
BANK = 0x84
|
||||
|
||||
kPlmInstructionSet = {
|
||||
0x8486b4: ('Sleep', ''),
|
||||
0x8486bc: ('Delete', ''),
|
||||
0x8486c1: ('PreInstr', 'A'),
|
||||
0x8486ca: ('ClearPreInstr', ''),
|
||||
0x8486d1: ('UNUSED_CallFunction', 'L'),
|
||||
0x8486eb: ('CallFunctionWithArg', 'LH'),
|
||||
0x84870b: ('CallFunction', 'L'),
|
||||
0x848724: ('Goto', 'G'),
|
||||
0x848729: ('UNUSED_sub_848729', 'B'),
|
||||
0x84873f: ('DecrementAndGotoIfNonzero', 'G'),
|
||||
0x84874e: ('SetTimer', 'B'),
|
||||
0x84875a: ('UNUSED_sub_84875A', 'H'),
|
||||
0x848764: ('LoadItemPlmGfx', '9BBBBBBBB'),
|
||||
0x8487e5: ('CopyFromRamToVram', 'HLH'),
|
||||
0x84880e: ('GotoIfBossBitSet', 'BG'),
|
||||
0x848821: ('UNUSED_sub_848821', 'B'),
|
||||
0x84882d: ('GotoIfEventSet', 'HG'),
|
||||
0x84883e: ('SetEvent', 'H'),
|
||||
0x848848: ('GotoIfChozoSet', 'G'),
|
||||
0x848865: ('SetRoomChozoBit', ''),
|
||||
0x84887c: ('GotoIfItemBitSet', 'G'),
|
||||
0x848899: ('SetItemBit', ''),
|
||||
0x8488b0: ('PickupBeamAndShowMessage', 'HB'),
|
||||
0x8488f3: ('PickupEquipmentAndShowMessage', 'HB'),
|
||||
0x84891a: ('PickupEquipmentAddGrappleShowMessage', 'H'),
|
||||
0x848941: ('PickupEquipmentAddXrayShowMessage', 'H'),
|
||||
0x848968: ('CollectHealthEnergyTank', 'H'),
|
||||
0x848986: ('CollectHealthReserveTank', 'H'),
|
||||
0x8489a9: ('CollectAmmoMissileTank', 'H'),
|
||||
0x8489d2: ('CollectAmmoSuperMissileTank', 'H'),
|
||||
0x8489fb: ('CollectAmmoPowerBombTank', 'H'),
|
||||
0x848a24: ('SetLinkReg', 'G'),
|
||||
0x848a2e: ('Call', 'G'),
|
||||
0x848a3a: ('Return', ''),
|
||||
0x848a40: ('UNUSED_sub_848A40', ''),
|
||||
0x848a59: ('UNUSED_sub_848A59', ''),
|
||||
0x848a72: ('GotoIfDoorBitSet', 'G'),
|
||||
0x848a91: ('IncrementDoorHitCounterAndJGE', 'BG'),
|
||||
0x848acd: ('IncrementArgumentAndJGE', 'BG'),
|
||||
0x848af1: ('SetBTS', 'B'),
|
||||
0x848b05: ('DrawPlmBlock', ''),
|
||||
0x848b17: ('DrawPlmBlock_', ''),
|
||||
0x848b55: ('ProcessAirScrollUpdate', ''),
|
||||
0x848b93: ('ProcessSolidScrollUpdate', ''),
|
||||
0x848bd1: ('QueueMusic', 'B'),
|
||||
0x848bdd: ('ClearMusicQueueAndQueueTrack', 'B'),
|
||||
0x848c07: ('QueueSfx1_Max6', 'B'),
|
||||
0x848c10: ('QueueSfx2_Max6', 'B'),
|
||||
0x848c19: ('QueueSfx3_Max6', 'B'),
|
||||
0x848c22: ('QueueSfx1_Max15', 'B'),
|
||||
0x848c2b: ('QueueSfx2_Max15', 'B'),
|
||||
0x848c34: ('QueueSfx3_Max15', 'B'),
|
||||
0x848c3d: ('QueueSfx1_Max3', 'B'),
|
||||
0x848c46: ('QueueSfx2_Max3', 'B'),
|
||||
0x848c4f: ('QueueSfx_Max3', 'B'),
|
||||
0x848c58: ('QueueSfx1_Max9', 'B'),
|
||||
0x848c61: ('QueueSfx2_Max9', 'B'),
|
||||
0x848c6a: ('QueueSfx3_Max9', 'B'),
|
||||
0x848c73: ('QueueSfx1_Max1', 'B'),
|
||||
0x848c7c: ('QueueSfx2_Max1', 'B'),
|
||||
0x848c85: ('QueueSfx3_Max1', 'B'),
|
||||
0x848c8f: ('ActivateMapStation', ''),
|
||||
0x848caf: ('ActivateEnergyStation', ''),
|
||||
0x848cd0: ('ActivateMissileStation', ''),
|
||||
0x848cf1: ('ActivateSaveStationAndGotoIfNo', 'H'),
|
||||
0x848d39: ('UNUSED_sub_848D39', ''),
|
||||
0x848d41: ('GotoIfSamusNear', 'BBG'),
|
||||
0x848d89: ('UNUSED_sub_848D89', ''),
|
||||
0x84ab00: ('MovePlmDownOneBlock', ''),
|
||||
0x84ab51: ('Scroll_0_1_Blue', ''),
|
||||
0x84ab59: ('MovePlmDownOneBlock_0', ''),
|
||||
0x84abd6: ('ABD6', ''),
|
||||
0x84ac9d: ('DealDamage_2', ''),
|
||||
0x84acb1: ('GiveInvincibility', ''),
|
||||
0x84ad43: ('Draw0x38FramesOfRightTreadmill', ''),
|
||||
0x84ad58: ('Draw0x38FramesOfLeftTreadmill', ''),
|
||||
0x84ae35: ('GotoIfSamusHealthFull', 'G'),
|
||||
0x84aebf: ('GotoIfMissilesFull', 'G'),
|
||||
0x84b00e: ('PlaceSamusOnSaveStation', ''),
|
||||
0x84b024: ('DisplayGameSavedMessageBox', ''),
|
||||
0x84b030: ('EnableMovementAndSetSaveStationUsed', ''),
|
||||
0x84b9b9: ('SetCrittersEscapedEvent', ''),
|
||||
0x84ba6f: ('JumpIfSamusHasNoBombs', 'G'),
|
||||
0x84bb25: ('MovePlmRight4Blocks', ''),
|
||||
0x84bbdd: ('ClearTrigger', ''),
|
||||
0x84bbe1: ('SpawnEproj', 'H'),
|
||||
0x84bbf0: ('WakeEprojAtPlmPos', 'H'),
|
||||
0x84be3f: ('SetGreyDoorPreInstr', ''),
|
||||
0x84cd93: ('SetBtsTo1', ''),
|
||||
0x84d155: ('FxBaseYPos_0x2D2', ''),
|
||||
0x84d2f9: ('GotoIfRoomArgLess', 'HG'),
|
||||
0x84d30b: ('SpawnFourMotherBrainGlass', 'HHHH'),
|
||||
0x84d357: ('SpawnTorizoStatueBreaking', 'H'),
|
||||
0x84d3c7: ('QueueSong1MusicTrack', ''),
|
||||
0x84d3d7: ('TransferWreckedShipChozoSpikesToSlopes', ''),
|
||||
0x84d3f4: ('TransferWreckedShipSlopesToChozoSpikes', ''),
|
||||
0x84d476: ('UNUSED_sub_84D476', ''),
|
||||
0x84d489: ('UNUSED_sub_84D489', ''),
|
||||
0x84d4be: ('nullsub_70', ''),
|
||||
0x84d525: ('EnableWaterPhysics', ''),
|
||||
0x84d52c: ('SpawnN00bTubeCrackEproj', ''),
|
||||
0x84d536: ('DiagonalEarthquake', ''),
|
||||
0x84d543: ('Spawn10shardsAnd6n00bs', ''),
|
||||
0x84d5e6: ('DisableSamusControls', ''),
|
||||
0x84d5ee: ('EnableSamusControls', ''),
|
||||
0x84d77a: ('ShootEyeDoorProjectileWithProjectileArg', 'H'),
|
||||
0x84d790: ('SpawnEyeDoorSweatEproj', 'H'),
|
||||
0x84d79f: ('SpawnTwoEyeDoorSmoke', ''),
|
||||
0x84d7b6: ('SpawnEyeDoorSmokeProjectile', ''),
|
||||
0x84d7c3: ('MoveUpAndMakeBlueDoorFacingRight', ''),
|
||||
0x84d7da: ('MoveUpAndMakeBlueDoorFacingLeft', ''),
|
||||
0x84db8e: ('DamageDraygonTurret', ''),
|
||||
0x84dbb8: ('DamageDraygonTurretFacingDownRight', ''),
|
||||
0x84dbf7: ('DamageDraygonTurretFacingUpRight', ''),
|
||||
0x84dc36: ('DamageDraygonTurret2', ''),
|
||||
0x84dc60: ('DamageDraygonTurretFacingDownLeft', ''),
|
||||
0x84dc9f: ('DamageDraygonTurretFacingUpLeft', ''),
|
||||
0x84e04f: ('DrawItemFrame0', ''),
|
||||
0x84e067: ('DrawItemFrame1', ''),
|
||||
0x84e29d: ('ClearChargeBeamCounter', ''),
|
||||
0x84e63b: ('E63B', ''),
|
||||
}
|
||||
|
||||
kPlmTerminator = { 0x8486BC, 0x848724, 0x848A3A }
|
||||
|
||||
# Sometimes Sleep is used in a way assumes Sleep never wakes up,
|
||||
# unless we do this we'll decode random instructions.
|
||||
kPlmTerminatorAtEA = { 0x84B89A, 0x84B8DA, 0x84DB42, 0x84BB03, 0x84B7E9 }
|
||||
|
||||
kCommandByName = {v[0] : (k, v[1]) for k, v in kPlmInstructionSet.items()}
|
||||
#for k, v in kPlmInstructionSet.items():
|
||||
# k = k | 0x840000
|
||||
# t = ea2name[k]
|
||||
# t = t.replace('PlmInstr_', '')
|
||||
# print("0x%x: ('%s', %s)," % (k, t, repr(tuple(v[0]))))
|
||||
|
||||
#for k, v in kPlmInstructionSet.items():
|
||||
# name, args = v
|
||||
# tostr = {1 : 'B', 2 : 'H', 3 : 'L'}
|
||||
# args = ''.join(tostr[a] for a in args)
|
||||
# if name.startswith('Goto') or k == 0x848A24 or k == 0x848A2E:
|
||||
# args = args[:-1] + 'G'
|
||||
# print(" 0x%x: ('%s', '%s')," % (k, name, args))
|
||||
|
||||
def plm_header_size(ea):
|
||||
return 6 if (ea >= 0x84c842 and ea < 0x84c8ba) or ea in (0x84c8ca, 0x84BAF4) else 4
|
||||
|
||||
class PlmParser(InstrParserCommon):
|
||||
instruction_set = kPlmInstructionSet
|
||||
instruction_set_term = {}
|
||||
TAG = 'plm'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.draw_cmds = {}
|
||||
|
||||
def parse_draw_commands(self, ea):
|
||||
assert ea & 0x8000
|
||||
if ea in self.draw_cmds:
|
||||
return
|
||||
last = None
|
||||
r = []
|
||||
self.draw_cmds[ea] = r
|
||||
while True:
|
||||
n = get_word(ea)
|
||||
assert (n & 0x7f00) == 0
|
||||
cmd = ''
|
||||
if last != None:
|
||||
if last & 0xff != 0: cmd = f'{cmd}{sex8(last&0xff)}x '
|
||||
if last >> 8 != 0: cmd = f'{cmd}{sex8(last>>8)}y '
|
||||
cmd += 'v ' if n & 0x8000 else 'h '
|
||||
cmd += " ".join('%.4x' % get_word(ea + 2 + i * 2) for i in range(n & 0xff))
|
||||
r.append(cmd)
|
||||
ea += 2 + (n & 0xff) * 2
|
||||
last = get_word(ea)
|
||||
ea += 2
|
||||
if last == 0:
|
||||
break
|
||||
|
||||
def print(self, file):
|
||||
draw_cmd_printed = set()
|
||||
output, queued = [], []
|
||||
last_insertion_pos = 0
|
||||
def inject_queued():
|
||||
if len(queued):
|
||||
queued.append('')
|
||||
output[last_insertion_pos:last_insertion_pos] = queued
|
||||
del queued[:]
|
||||
for ea, line in sorted(self.lines, key = lambda x: x[0]):
|
||||
if isinstance(line, str):
|
||||
if ea in self.label:
|
||||
output.append(f'{get_ea_name(ea, short_label = True)}:')
|
||||
output.append(line)
|
||||
if line == '':
|
||||
inject_queued()
|
||||
last_insertion_pos = len(output)
|
||||
elif line not in draw_cmd_printed:
|
||||
draw_cmd_printed.add(line)
|
||||
name = get_ea_name(line)
|
||||
for i, v in enumerate(self.draw_cmds[line]):
|
||||
t = name if i == 0 else (' ' * len(name))
|
||||
queued.append(t + ' ! ' + v)
|
||||
inject_queued()
|
||||
print('<?plm\n', file = file)
|
||||
for line in output:
|
||||
print(line, file = file)
|
||||
print('?>', file = file)
|
||||
print('', file = file)
|
||||
|
||||
def process_queue_entry(self, ea):
|
||||
assert ea & 0x8000
|
||||
while True:
|
||||
if ea in self.visited:
|
||||
break
|
||||
self.visited.add(ea)
|
||||
ins = get_word(ea)
|
||||
if ins & 0x8000:
|
||||
ea_org = ea
|
||||
ins = 0x840000 | ins
|
||||
if ins not in kPlmInstructionSet:
|
||||
print(f'// Ins {ins:X} not in iset at {ea:X}')
|
||||
self.missing_isets.add(ea & 0xff0000 | ins)
|
||||
return
|
||||
name, operands = kPlmInstructionSet[ins]
|
||||
ea += 2
|
||||
r = []
|
||||
for op in operands:
|
||||
if op == 'B':
|
||||
r.append('%d' % get_byte(ea))
|
||||
ea += 1
|
||||
elif op == 'H':
|
||||
r.append('%d' % get_word(ea))
|
||||
ea += 2
|
||||
elif op == 'L':
|
||||
r.append(get_ea_name(get_word(ea) + get_byte(ea+2) * 65536))
|
||||
ea += 3
|
||||
elif op == 'A':
|
||||
addr = 0x840000 | get_word(ea)
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
ea += 2
|
||||
elif op == '9':
|
||||
addr = 0x890000 | get_word(ea)
|
||||
r.append(get_ea_name(addr))
|
||||
self.blobs[addr] = 256
|
||||
ea += 2
|
||||
elif op == 'G':
|
||||
addr = 0x840000 | get_word(ea)
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
self.visit(addr)
|
||||
ea += 2
|
||||
else:
|
||||
assert 0
|
||||
self.print_line(ea_org, f' {name}({", ".join(r)})')
|
||||
if ins in kPlmTerminator or ea_org in kPlmTerminatorAtEA:
|
||||
self.print_line(ea_org + 1, '')
|
||||
break
|
||||
else:
|
||||
drawp = get_word(ea + 2) | 0x840000
|
||||
self.print_line(ea, f' {ins} ! {get_ea_name(drawp)}')
|
||||
self.print_line(ea, drawp)
|
||||
try:
|
||||
drawp_end = self.parse_draw_commands(drawp)
|
||||
except:
|
||||
print(f'Crash while processing draw commants at {ea:X}')
|
||||
raise
|
||||
ea += 4
|
||||
59
assets/projectile_decode.py
Normal file
59
assets/projectile_decode.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from decode_common import *
|
||||
|
||||
BANK = 0x93
|
||||
|
||||
kProjInstructionSet = {
|
||||
0x93822F: ('Delete', ''),
|
||||
0x938239: ('Goto', 'G'),
|
||||
}
|
||||
|
||||
kCommandByName = {v[0] : (k, v[1]) for k, v in kProjInstructionSet.items()}
|
||||
kCommandByName['Draw'] = (None, 'HABBH')
|
||||
|
||||
kProjTerminator = { 0x938239, 0x93822F}
|
||||
|
||||
class ProjParser(InstrParserCommon):
|
||||
instruction_set = kProjInstructionSet
|
||||
instruction_set_term = kProjTerminator
|
||||
TAG = 'proj'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.sprite_maps = set()
|
||||
|
||||
def handle_draw_command(self, ins, ea):
|
||||
drawp = get_word(ea + 2) | (BANK << 16)
|
||||
self.sprite_maps.add(drawp)
|
||||
self.print_line(ea, f' Draw({ins}, {get_ea_name(drawp)}, {get_byte(ea + 4)}, {get_byte(ea + 5)}, {get_word(ea + 6)})')
|
||||
return 8
|
||||
|
||||
def process_queue_entry(self, ea):
|
||||
assert ea & 0x8000
|
||||
while True:
|
||||
if ea in self.visited:
|
||||
break
|
||||
self.visited.add(ea)
|
||||
ins = get_word(ea)
|
||||
if ins & 0x8000:
|
||||
ea_org = ea
|
||||
ins = (BANK << 16) | ins
|
||||
if ins not in self.instruction_set:
|
||||
print(f'// Ins {ins:X} not in iset at {ea:X}')
|
||||
return
|
||||
name, operands = self.instruction_set[ins]
|
||||
ea += 2
|
||||
r = []
|
||||
for op in operands:
|
||||
if op == 'G':
|
||||
addr = (BANK << 16) | get_word(ea)
|
||||
r.append(get_ea_name(addr, short_label=True))
|
||||
self.visit(addr)
|
||||
ea += 2
|
||||
else:
|
||||
assert 0
|
||||
self.print_line(ea_org, f' {name}({", ".join(r)})')
|
||||
if ins in self.instruction_set_term:
|
||||
self.print_line(ea_org + 1, '')
|
||||
break
|
||||
else:
|
||||
ea += self.handle_draw_command(ins, ea)
|
||||
1567
assets/restool.py
Normal file
1567
assets/restool.py
Normal file
File diff suppressed because it is too large
Load Diff
512
assets/text_lexer.py
Normal file
512
assets/text_lexer.py
Normal file
@@ -0,0 +1,512 @@
|
||||
import re
|
||||
import plm_decode
|
||||
import projectile_decode
|
||||
import eproj_decode
|
||||
import animtiles_decode
|
||||
import palfx_decode
|
||||
import enemy_instr_decode
|
||||
|
||||
NUMBER_RE = re.compile(r'(-?)(0x[0-9a-fA-F]+|\d+)')
|
||||
HEX_NUMBER4_RE = re.compile(r'[0-9a-fA-F]{4}')
|
||||
IDENT_RE = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
|
||||
HEX_NUMBER_RE = re.compile(r'[0-9a-fA-F]+')
|
||||
|
||||
class Lexer:
|
||||
def __init__(self):
|
||||
self.expectation = []
|
||||
|
||||
def set_file(self, filename):
|
||||
self.token, self.token_arg = None, None
|
||||
self.has_token = False
|
||||
self.mode = ''
|
||||
self.text = open(filename).read()
|
||||
self.text_pos = 0
|
||||
self.lineno = 1
|
||||
self.start_lineno = 1
|
||||
self.start_line = 0
|
||||
self.filename = filename
|
||||
|
||||
def next_char(self):
|
||||
if self.text_pos >= len(self.text):
|
||||
return ''
|
||||
c = self.text[self.text_pos]
|
||||
self.text_pos += 1
|
||||
return c
|
||||
|
||||
def fill(self):
|
||||
self.start_lineno = self.lineno
|
||||
while True:
|
||||
c = self.next_char()
|
||||
if c != ' ' and c != '\t' and c != '\n':
|
||||
break
|
||||
if c == '\n':
|
||||
self.lineno += 1
|
||||
self.start_line = self.text_pos
|
||||
|
||||
if c in ('', '{', '}', ':', ',', '(', ')', '!', '@', '$', '='):
|
||||
return c, True
|
||||
|
||||
if self.mode == 'tile4':
|
||||
if (m := HEX_NUMBER4_RE.match(self.text, self.text_pos - 1)) != None:
|
||||
self.text_pos = m.end()
|
||||
return 'tile4', int(m.group(0), 16)
|
||||
|
||||
if self.mode == 'hex':
|
||||
if (m := HEX_NUMBER_RE.match(self.text, self.text_pos - 1)) != None:
|
||||
self.text_pos = m.end()
|
||||
return 'hex_data', m.group(0)
|
||||
else:
|
||||
if c >= '0' and c <= '9' or c == '-':
|
||||
m = NUMBER_RE.match(self.text, self.text_pos - 1)
|
||||
self.text_pos = m.end()
|
||||
return 'number', int(m.group(0), 0)
|
||||
|
||||
if c >= 'a' and c <= 'z' or c >= 'A' and c <= 'Z' or c == '_':
|
||||
m = IDENT_RE.match(self.text, self.text_pos - 1)
|
||||
self.text_pos = m.end()
|
||||
return 'ident', m.group(0)
|
||||
|
||||
if c == '<' and self.text.startswith('?', self.text_pos):
|
||||
self.text_pos += 1
|
||||
return 'code_start', True
|
||||
|
||||
if c == '?' and self.text.startswith('>', self.text_pos):
|
||||
self.text_pos += 1
|
||||
return 'code_end', True
|
||||
|
||||
if c == '"':
|
||||
l = 0
|
||||
while (c := self.next_char()) not in ('', '"'):
|
||||
l += 1
|
||||
return 'string', self.text[self.text_pos - l - 1 : self.text_pos - 1]
|
||||
self.syntax_error(f"Unexpected character '{c}'")
|
||||
|
||||
def is_next_or_newline(self, symbol):
|
||||
if rv := self.is_next(symbol):
|
||||
return rv
|
||||
return True if self.lineno != self.start_lineno else None
|
||||
|
||||
def is_next(self, expected):
|
||||
if not self.has_token:
|
||||
self.token, self.token_arg = self.fill()
|
||||
self.has_token = True
|
||||
del self.expectation[:]
|
||||
self.expectation.append(expected)
|
||||
if self.token != expected:
|
||||
return None
|
||||
self.has_token = False
|
||||
return self.token_arg
|
||||
|
||||
def peek_next(self, expected):
|
||||
if not self.has_token:
|
||||
self.token, self.token_arg = self.fill()
|
||||
self.has_token = True
|
||||
del self.expectation[:]
|
||||
return self.token == expected
|
||||
|
||||
def expect(self, expected):
|
||||
if rv := self.is_next(expected):
|
||||
return rv
|
||||
self.syntax_error()
|
||||
|
||||
def syntax_error(self, msg = 'Error'):
|
||||
expects = ', '.join(f"'{a}'" for a in self.expectation)
|
||||
tok = self.token_arg if self.token=='ident' else self.token
|
||||
self.error(f"{msg} at '{tok}'. Expecting: {expects}")
|
||||
raise Exception()
|
||||
|
||||
def error(self, msg):
|
||||
msg = f'{self.filename}:{self.lineno}: {msg}'
|
||||
raise Exception(msg)
|
||||
|
||||
class Parser(Lexer):
|
||||
def parse_function_args(self):
|
||||
if self.is_next(')'):
|
||||
return ()
|
||||
args = []
|
||||
while True:
|
||||
if (number := self.is_next('number')) != None:
|
||||
args.append(number)
|
||||
elif (ident := self.is_next('ident')) != None:
|
||||
args.append(ident)
|
||||
else:
|
||||
self.syntax_error()
|
||||
if self.is_next(')'):
|
||||
return args
|
||||
self.expect(',')
|
||||
|
||||
def parse_draw_def(self):
|
||||
result = []
|
||||
x, y = 0, 0
|
||||
while True:
|
||||
if (num := self.is_next('number')) != None:
|
||||
ident = self.is_next('ident')
|
||||
if ident == 'x':
|
||||
x = num
|
||||
continue
|
||||
elif ident == 'y':
|
||||
y = num
|
||||
continue
|
||||
elif (ident := self.is_next('ident')) != None:
|
||||
if ident in ('h', 'v'):
|
||||
self.mode = 'tile4'
|
||||
r = []
|
||||
result.append((ident, x, y, r))
|
||||
while True:
|
||||
if (num := self.is_next('tile4')) != None:
|
||||
r.append(num)
|
||||
elif self.is_next('!') != None:
|
||||
self.mode = ''
|
||||
x, y = 0, 0
|
||||
break
|
||||
elif self.is_next_or_newline(';'):
|
||||
self.mode = ''
|
||||
return result
|
||||
else:
|
||||
self.syntax_error()
|
||||
continue
|
||||
self.syntax_error()
|
||||
|
||||
def parse_code_common(self, tag, module, spr_bank = None):
|
||||
if (ident := self.is_next('ident')) != None:
|
||||
if self.is_next(':'): # label
|
||||
self.add_label(ident, module)
|
||||
return
|
||||
if self.is_next('('): # command
|
||||
args = self.parse_function_args()
|
||||
self.add_call(ident, module, args)
|
||||
return
|
||||
elif spr_bank and (number := self.is_next('number')) != None:
|
||||
self.expect('!')
|
||||
ident = self.expect('ident')
|
||||
self.write_word(number)
|
||||
if spr_bank == 'auto': spr_bank = self.get_ea() >> 16
|
||||
self.write_word(self.get_addr_in_bank(ident, spr_bank))
|
||||
return
|
||||
self.syntax_error()
|
||||
|
||||
def parse_plm_code(self):
|
||||
if (ident := self.is_next('ident')) != None:
|
||||
if self.is_next(':'): # label
|
||||
self.add_label(ident, plm_decode)
|
||||
elif self.is_next('('): # command
|
||||
args = self.parse_function_args()
|
||||
self.add_call(ident, plm_decode, args)
|
||||
elif self.is_next('!'):
|
||||
self.add_draw_def(ident, self.parse_draw_def())
|
||||
else:
|
||||
self.syntax_error()
|
||||
elif (number := self.is_next('number')) != None:
|
||||
self.expect('!')
|
||||
self.add_draw_cmd(number, self.expect('ident'))
|
||||
else:
|
||||
self.syntax_error()
|
||||
|
||||
def parse_proj_code(self):
|
||||
self.parse_code_common('proj', projectile_decode, None)
|
||||
|
||||
def parse_eproj_code(self):
|
||||
self.parse_code_common('eproj', eproj_decode, 0x8d)
|
||||
|
||||
def parse_animtiles_code(self):
|
||||
self.parse_code_common('animtiles', animtiles_decode, 0x87)
|
||||
|
||||
def parse_enemy_code(self):
|
||||
self.parse_code_common('enemy', enemy_instr_decode, 'auto')
|
||||
|
||||
def parse_palfx_code(self):
|
||||
module = palfx_decode
|
||||
if (ident := self.is_next('ident')) != None:
|
||||
if self.is_next(':'): # label
|
||||
self.add_label(ident, module)
|
||||
return
|
||||
if self.is_next('('): # command
|
||||
args = self.parse_function_args()
|
||||
self.add_call(ident, module, args)
|
||||
return
|
||||
else:
|
||||
if (number := self.is_next('number')) != None:
|
||||
self.write_word(number)
|
||||
self.expect('!')
|
||||
self.mode = 'tile4'
|
||||
while (v := self.is_next('tile4')) != None:
|
||||
assert v & 0x8000 == 0
|
||||
self.write_word(v)
|
||||
self.mode = ''
|
||||
if not self.is_next('!'):
|
||||
self.write_word(0xc595) # Wait
|
||||
return
|
||||
self.syntax_error()
|
||||
|
||||
def parse_toplevel(self, filename):
|
||||
print('Parsing', filename)
|
||||
self.set_file(filename)
|
||||
kCodeParsers = {
|
||||
'plm': self.parse_plm_code,
|
||||
'proj': self.parse_proj_code,
|
||||
'eproj': self.parse_eproj_code,
|
||||
'animtiles': self.parse_animtiles_code,
|
||||
'palfx' : self.parse_palfx_code,
|
||||
'enemy' : self.parse_enemy_code,
|
||||
}
|
||||
while not self.is_next(''):
|
||||
if self.is_next('code_start'):
|
||||
parser = kCodeParsers.get(self.expect('ident'))
|
||||
if parser == None:
|
||||
self.syntax_error()
|
||||
while not self.is_next('code_end'):
|
||||
parser()
|
||||
else:
|
||||
ident = self.expect('ident')
|
||||
name = self.expect('ident')
|
||||
if self.is_next('{'):
|
||||
self.parse_fields(ident, name, None)
|
||||
elif self.is_next('='):
|
||||
self.parse_assignment_value(ident, name)
|
||||
else:
|
||||
self.syntax_error()
|
||||
|
||||
def parse_fields(self, fields_ident, fields_name, args):
|
||||
self.begin_field(fields_ident, fields_name, args)
|
||||
has_comma = True
|
||||
allow_list_values = True
|
||||
while not self.is_next('}'):
|
||||
if not has_comma:
|
||||
self.syntax_error()
|
||||
if (ident := self.is_next('ident')) != None:
|
||||
if self.is_next(',') or self.peek_next('}'):
|
||||
if not allow_list_values:
|
||||
self.error(f"Error: keyword arguments need to be first, found '{ident}'")
|
||||
self.add_field_value(None if ident == 'null' else ident)
|
||||
has_comma = True
|
||||
continue
|
||||
args = self.parse_function_args() if self.is_next('(') else None
|
||||
name = self.is_next('ident')
|
||||
if self.is_next('{'):
|
||||
self.parse_fields(ident, name, args)
|
||||
has_comma, allow_list_values = True, False
|
||||
continue
|
||||
if args == None and name != None and self.is_next('='):
|
||||
self.parse_assignment_value(ident, name)
|
||||
continue
|
||||
if name == None and args == None and self.is_next(':'):
|
||||
self.add_field_key_value(ident, self.parse_field_value())
|
||||
has_comma, allow_list_values = self.is_next_or_newline(',') != None, False
|
||||
continue
|
||||
elif (number := self.is_next('number')) != None:
|
||||
if not allow_list_values:
|
||||
self.error(f"Error: keyword arguments need to be first, found '{number}'")
|
||||
self.add_field_value(number)
|
||||
has_comma = self.is_next_or_newline(',') != None
|
||||
continue
|
||||
self.syntax_error()
|
||||
self.end_field()
|
||||
|
||||
def parse_assignment_value(self, ident, name):
|
||||
self.expect('$')
|
||||
if self.expect('ident') != 'hex':
|
||||
self.error("expecting 'hex'")
|
||||
self.expect('(')
|
||||
self.mode = 'hex'
|
||||
hex_data = []
|
||||
while d := self.is_next('hex_data'):
|
||||
hex_data.append(d)
|
||||
self.expect(')')
|
||||
self.mode = ''
|
||||
class HexField:
|
||||
def __init__(self):
|
||||
self.value = bytes.fromhex("".join(hex_data))
|
||||
self.ident = ident
|
||||
self.name = name
|
||||
self.write_global_object(self, HexField())
|
||||
|
||||
def parse_field_value(self):
|
||||
if (number := self.is_next('number')) != None:
|
||||
return number
|
||||
elif (string := self.is_next('string')) != None:
|
||||
return string
|
||||
elif (ident := self.is_next('ident')) != None:
|
||||
return None if ident == 'null' else ident
|
||||
self.syntax_error()
|
||||
|
||||
class WriterFields:
|
||||
def __init__(self, ident, name, args):
|
||||
self.ident = ident
|
||||
self.name = name
|
||||
self.args = args
|
||||
self.list = []
|
||||
self.dict = {}
|
||||
|
||||
class ParserAndWriter(Parser):
|
||||
def __init__(self, write_global_object, get_name_ea):
|
||||
super().__init__()
|
||||
self.write_global_object = write_global_object
|
||||
self.get_name_ea = get_name_ea
|
||||
self.cur_addr = 0
|
||||
self.output = bytearray(0x300000)
|
||||
self.output_is_set = bytearray(0x300000)
|
||||
self.relocs = []
|
||||
self.label_addr = {}
|
||||
self.stack = []
|
||||
|
||||
def check_int_range(self, v, mi, ma):
|
||||
if not isinstance(v, int):
|
||||
self.error(f'Value {v} is not an integer')
|
||||
if v < mi or v > ma:
|
||||
self.error(f'Value {v} out of range [{mi}, {ma}]')
|
||||
|
||||
def get_addr(self, name):
|
||||
result = self.get_name_ea(name)
|
||||
if result == None:
|
||||
self.error(f'Unknown address {name}')
|
||||
return result
|
||||
|
||||
def get_addr_in_bank(self, name, bank):
|
||||
result = self.get_addr(name)
|
||||
if bank != None and (result >> 16) != bank:
|
||||
self.error(f'Address {name} is not in bank {bank:x}')
|
||||
return result & 0xffff
|
||||
|
||||
def add_label(self, lbl, module):
|
||||
addr = self.get_name_ea(lbl, bank = module.BANK)
|
||||
if addr != None:
|
||||
self.cur_addr = addr
|
||||
self.label_addr[lbl] = self.cur_addr
|
||||
|
||||
def add_call(self, cmd, module, args):
|
||||
cmdvalue, argspec = module.kCommandByName[cmd]
|
||||
if len(args) != len(argspec):
|
||||
self.error(f'Wrong number of arguments to {cmd}, got {len(args)}, expecting {len(argspec)}')
|
||||
if cmdvalue != None:
|
||||
if module.BANK != None and (cmdvalue >> 16) != module.BANK:
|
||||
self.error(f'Not in bank {module.BANK:X}: {v}')
|
||||
self.write_word(cmdvalue & 0xffff)
|
||||
for i in range(len(args)):
|
||||
a, v = argspec[i], args[i]
|
||||
if a == 'B':
|
||||
self.write_byte(v)
|
||||
elif a in ('H', 'X', 'I'):
|
||||
self.write_word(v & 0xffff)
|
||||
elif a == 'L': # long address
|
||||
self.write_long(self.get_addr(v))
|
||||
elif a == 'A': # address in bank same bank
|
||||
self.write_word(self.get_addr_in_bank(v, module.BANK))
|
||||
elif a == '9': # address in bank 89
|
||||
self.write_word(self.get_addr_in_bank(v, 0x89))
|
||||
elif a == 'a': # address in bank a0
|
||||
self.write_word(self.get_addr_in_bank(v, 0xa0))
|
||||
elif a == '6': # address in bank a0
|
||||
self.write_word(self.get_addr_in_bank(v, 0xa6))
|
||||
elif a == 'S': # spritemap in bank 8d
|
||||
self.write_word(self.get_addr_in_bank(v, 0x8d))
|
||||
elif a == 'G': # goto
|
||||
self.add_reloc(v, module.BANK)
|
||||
else:
|
||||
raise Exception(f'Unknown command character: {a}')
|
||||
|
||||
def add_reloc(self, target, bank):
|
||||
assert isinstance(target, str), target
|
||||
v = self.label_addr.get(target)
|
||||
if v == None:
|
||||
v = self.get_name_ea(target)
|
||||
if v != None:
|
||||
if bank != None and (v >> 16) != bank:
|
||||
self.error(f"Label '{target}/{v:x}' referenced from {self.cur_addr:x} is not in bank {bank:x}")
|
||||
self.write_word(v & 0xffff)
|
||||
else:
|
||||
self.relocs.append((self.cur_addr, target, bank))
|
||||
self.cur_addr += 2
|
||||
|
||||
def apply_relocs(self):
|
||||
for addr, target, bank in self.relocs:
|
||||
v = self.label_addr.get(target)
|
||||
if v == None:
|
||||
v = self.get_name_ea(target)
|
||||
if v == None:
|
||||
self.error(f"Unknown label '{target}' referenced from {addr:x}")
|
||||
if (v >> 16) != bank:
|
||||
self.error(f"Label '{target}' referenced from {addr:x} is not in bank {bank:x}")
|
||||
self.cur_addr = addr
|
||||
self.write_word(v & 0xffff)
|
||||
|
||||
def add_draw_cmd(self, number, cmd):
|
||||
self.write_word(number)
|
||||
self.write_word(self.get_addr_in_bank(cmd, 0x84))
|
||||
|
||||
def add_draw_def(self, ident, draw_def):
|
||||
backup = self.cur_addr
|
||||
self.cur_addr = 0x840000 | self.get_addr_in_bank(ident, 0x84)
|
||||
for i, line in enumerate(draw_def):
|
||||
if i != 0:
|
||||
self.write_word((line[1] & 0xff) | (line[2] & 0xff) << 8)
|
||||
self.write_word((line[0] == 'v') * 0x8000 | len(line[3]))
|
||||
for a in line[3]:
|
||||
self.write_word(a)
|
||||
self.write_word(0)
|
||||
self.cur_addr = backup
|
||||
|
||||
def begin_field(self, ident, name, args):
|
||||
wf = WriterFields(ident, name, args)
|
||||
self.stack.append(wf)
|
||||
|
||||
def add_field_value(self, value):
|
||||
self.stack[-1].list.append(value)
|
||||
|
||||
def add_field_key_value(self, key, value):
|
||||
self.stack[-1].dict[key] = value
|
||||
|
||||
def end_field(self):
|
||||
wf = self.stack.pop()
|
||||
if len(self.stack):
|
||||
self.add_field_value(wf)
|
||||
else:
|
||||
self.write_global_object(self, wf)
|
||||
|
||||
def write_byte(self, v):
|
||||
assert self.cur_addr != None
|
||||
|
||||
self.check_int_range(v, 0, 255)
|
||||
addr = self.cur_addr
|
||||
assert addr & 0x8000
|
||||
ea = (((addr >> 16) << 15) | (addr & 0x7fff)) & 0x3fffff
|
||||
if self.output_is_set[ea] and self.output[ea] != v:
|
||||
self.error('Address 0x%x already set to a different value (0x%x vs 0x%x)' % (self.cur_addr, self.output[ea], v))
|
||||
self.output[ea], self.output_is_set[ea] = v, 1
|
||||
self.cur_addr += 1
|
||||
|
||||
def write_word(self, v):
|
||||
self.check_int_range(v, -32768, 65535)
|
||||
self.write_byte(v & 0xff)
|
||||
self.write_byte((v >> 8) & 0xff)
|
||||
|
||||
def write_long(self, v):
|
||||
self.write_byte(v & 0xff)
|
||||
self.write_byte((v >> 8) & 0xff)
|
||||
self.write_byte(v >> 16)
|
||||
|
||||
def write_bytes(self, v):
|
||||
for b in v:
|
||||
self.write_byte(b)
|
||||
|
||||
def write_bytes_with_wrap(self, v):
|
||||
for b in v:
|
||||
if (self.cur_addr & 0xffff) == 0:
|
||||
self.cur_addr += 0x8000
|
||||
self.write_byte(b)
|
||||
|
||||
def write_symbol_word(self, v, bank):
|
||||
if v == 0 or v == None:
|
||||
self.write_word(0)
|
||||
else:
|
||||
self.add_reloc(v, bank)
|
||||
|
||||
def write_symbol_long(self, v):
|
||||
if not isinstance(v, int):
|
||||
ea = self.get_name_ea(v)
|
||||
if ea == None:
|
||||
self.error(f'Unknown address {v}')
|
||||
v = ea
|
||||
self.write_long(v)
|
||||
|
||||
def get_ea(self):
|
||||
return self.cur_addr
|
||||
Reference in New Issue
Block a user