1
0
mirror of https://github.com/zeldaret/oot.git synced 2024-09-21 12:54:51 +00:00
oot/tools/disasm_elf_msg.py
fig02 fd24ad6e67
Document elf_message scripting system (#1398)
* `cUpElfMsgs` -> `cUpMsgs`

Motivation:
ElfMessage is the name of the system that handles c-up messages from navi, but also messages from child/adult saria
so it would make sense to name this after the system name like "ElfMsg"
but "cUpElf" primarily reads like (to me) "c up navi" since the actor implementing navi is EnElf

I think it'd be better to clear that confusion between "navi" and "elfmsg system" by just not using "elf" in the name

* Standardize c-up navi messages ElfMessage data to be named after "CUp"

* -> "Navi"

* Make symbols in `elf_message_` segments static

* new docs

* change commands, format

* introduce navi file id

* use enum

* remove concat

* add clarifying comment for skip

* concat cmd type

* Hints -> QuestHint

* rename command header

* s -> g

* missed enum name

* singular

* missed one

Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
2022-10-15 13:24:13 -04:00

257 lines
7.8 KiB
Python

#!/usr/bin/env python3
import struct, sys
item_ids = {
0x00 : "ITEM_STICK",
0x01 : "ITEM_NUT",
0x02 : "ITEM_BOMB",
0x03 : "ITEM_BOW",
0x04 : "ITEM_ARROW_FIRE",
0x05 : "ITEM_DINS_FIRE",
0x06 : "ITEM_SLINGSHOT",
0x07 : "ITEM_OCARINA_FAIRY",
0x08 : "ITEM_OCARINA_TIME",
0x09 : "ITEM_BOMBCHU",
0x0A : "ITEM_HOOKSHOT",
0x0B : "ITEM_LONGSHOT",
0x0C : "ITEM_ARROW_ICE",
0x0D : "ITEM_FARORES_WIND",
0x0E : "ITEM_BOOMERANG",
0x0F : "ITEM_LENS",
0x10 : "ITEM_BEAN",
0x11 : "ITEM_HAMMER",
0x12 : "ITEM_ARROW_LIGHT",
0x13 : "ITEM_NAYRUS_LOVE",
0x14 : "ITEM_BOTTLE",
0x15 : "ITEM_POTION_RED",
0x16 : "ITEM_POTION_GREEN",
0x17 : "ITEM_POTION_BLUE",
0x18 : "ITEM_FAIRY",
0x19 : "ITEM_FISH",
0x1A : "ITEM_MILK_BOTTLE",
0x1B : "ITEM_LETTER_RUTO",
0x1C : "ITEM_BLUE_FIRE",
0x1D : "ITEM_BUG",
0x1E : "ITEM_BIG_POE",
0x1F : "ITEM_MILK_HALF",
0x20 : "ITEM_POE",
0x21 : "ITEM_WEIRD_EGG",
0x22 : "ITEM_CHICKEN",
0x23 : "ITEM_LETTER_ZELDA",
0x24 : "ITEM_MASK_KEATON",
0x25 : "ITEM_MASK_SKULL",
0x26 : "ITEM_MASK_SPOOKY",
0x27 : "ITEM_MASK_BUNNY",
0x28 : "ITEM_MASK_GORON",
0x29 : "ITEM_MASK_ZORA",
0x2A : "ITEM_MASK_GERUDO",
0x2B : "ITEM_MASK_TRUTH",
0x2C : "ITEM_SOLD_OUT",
0x2D : "ITEM_POCKET_EGG",
0x2E : "ITEM_POCKET_CUCCO",
0x2F : "ITEM_COJIRO",
0x30 : "ITEM_ODD_MUSHROOM",
0x31 : "ITEM_ODD_POTION",
0x32 : "ITEM_SAW",
0x33 : "ITEM_SWORD_BROKEN",
0x34 : "ITEM_PRESCRIPTION",
0x35 : "ITEM_FROG",
0x36 : "ITEM_EYEDROPS",
0x37 : "ITEM_CLAIM_CHECK",
0x38 : "ITEM_BOW_ARROW_FIRE",
0x39 : "ITEM_BOW_ARROW_ICE",
0x3A : "ITEM_BOW_ARROW_LIGHT",
0x3B : "ITEM_SWORD_KOKIRI",
0x3C : "ITEM_SWORD_MASTER",
0x3D : "ITEM_SWORD_BGS",
0x3E : "ITEM_SHIELD_DEKU",
0x3F : "ITEM_SHIELD_HYLIAN",
0x40 : "ITEM_SHIELD_MIRROR",
0x41 : "ITEM_TUNIC_KOKIRI",
0x42 : "ITEM_TUNIC_GORON",
0x43 : "ITEM_TUNIC_ZORA",
0x44 : "ITEM_BOOTS_KOKIRI",
0x45 : "ITEM_BOOTS_IRON",
0x46 : "ITEM_BOOTS_HOVER",
0x47 : "ITEM_BULLET_BAG_30",
0x48 : "ITEM_BULLET_BAG_40",
0x49 : "ITEM_BULLET_BAG_50",
0x4A : "ITEM_QUIVER_30",
0x4B : "ITEM_QUIVER_40",
0x4C : "ITEM_QUIVER_50",
0x4D : "ITEM_BOMB_BAG_20",
0x4E : "ITEM_BOMB_BAG_30",
0x4F : "ITEM_BOMB_BAG_40",
0x50 : "ITEM_BRACELET",
0x51 : "ITEM_GAUNTLETS_SILVER",
0x52 : "ITEM_GAUNTLETS_GOLD",
0x53 : "ITEM_SCALE_SILVER",
0x54 : "ITEM_SCALE_GOLDEN",
0x55 : "ITEM_SWORD_KNIFE",
0x56 : "ITEM_WALLET_ADULT",
0x57 : "ITEM_WALLET_GIANT",
0x58 : "ITEM_SEEDS",
0x59 : "ITEM_FISHING_POLE",
0x5A : "ITEM_SONG_MINUET",
0x5B : "ITEM_SONG_BOLERO",
0x5C : "ITEM_SONG_SERENADE",
0x5D : "ITEM_SONG_REQUIEM",
0x5E : "ITEM_SONG_NOCTURNE",
0x5F : "ITEM_SONG_PRELUDE",
0x60 : "ITEM_SONG_LULLABY",
0x61 : "ITEM_SONG_EPONA",
0x62 : "ITEM_SONG_SARIA",
0x63 : "ITEM_SONG_SUN",
0x64 : "ITEM_SONG_TIME",
0x65 : "ITEM_SONG_STORMS",
0x66 : "ITEM_MEDALLION_FOREST",
0x67 : "ITEM_MEDALLION_FIRE",
0x68 : "ITEM_MEDALLION_WATER",
0x69 : "ITEM_MEDALLION_SPIRIT",
0x6A : "ITEM_MEDALLION_SHADOW",
0x6B : "ITEM_MEDALLION_LIGHT",
0x6C : "ITEM_KOKIRI_EMERALD",
0x6D : "ITEM_GORON_RUBY",
0x6E : "ITEM_ZORA_SAPPHIRE",
0x6F : "ITEM_STONE_OF_AGONY",
0x70 : "ITEM_GERUDO_CARD",
0x71 : "ITEM_SKULL_TOKEN",
0x72 : "ITEM_HEART_CONTAINER",
0x73 : "ITEM_HEART_PIECE",
0x74 : "ITEM_KEY_BOSS",
0x75 : "ITEM_COMPASS",
0x76 : "ITEM_DUNGEON_MAP",
0x77 : "ITEM_KEY_SMALL",
0x78 : "ITEM_MAGIC_SMALL",
0x79 : "ITEM_MAGIC_LARGE",
0x7A : "ITEM_HEART_PIECE_2",
0x7B : "ITEM_INVALID_1",
0x7C : "ITEM_INVALID_2",
0x7D : "ITEM_INVALID_3",
0x7E : "ITEM_INVALID_4",
0x7F : "ITEM_INVALID_5",
0x80 : "ITEM_INVALID_6",
0x81 : "ITEM_INVALID_7",
0x82 : "ITEM_MILK",
0x83 : "ITEM_RECOVERY_HEART",
0x84 : "ITEM_RUPEE_GREEN",
0x85 : "ITEM_RUPEE_BLUE",
0x86 : "ITEM_RUPEE_RED",
0x87 : "ITEM_RUPEE_PURPLE",
0x88 : "ITEM_RUPEE_GOLD",
0x89 : "ITEM_INVALID_8",
0x8A : "ITEM_STICKS_5",
0x8B : "ITEM_STICKS_10",
0x8C : "ITEM_NUTS_5",
0x8D : "ITEM_NUTS_10",
0x8E : "ITEM_BOMBS_5",
0x8F : "ITEM_BOMBS_10",
0x90 : "ITEM_BOMBS_20",
0x91 : "ITEM_BOMBS_30",
0x92 : "ITEM_ARROWS_SMALL",
0x93 : "ITEM_ARROWS_MEDIUM",
0x94 : "ITEM_ARROWS_LARGE",
0x95 : "ITEM_SEEDS_30",
0x96 : "ITEM_BOMBCHUS_5",
0x97 : "ITEM_BOMBCHUS_20",
0x98 : "ITEM_STICK_UPGRADE_20",
0x99 : "ITEM_STICK_UPGRADE_30",
0x9A : "ITEM_NUT_UPGRADE_30",
0x9B : "ITEM_NUT_UPGRADE_40",
0xFC : "ITEM_LAST_USED",
0xFE : "ITEM_NONE_FE",
0xFF : "ITEM_NONE",
}
def disas_elfmsgs(start):
baserom = None
with open("baserom.z64", "rb") as infile:
baserom = bytearray(infile.read())
branches = []
pos = start
while (True):
print(f"/* {pos - start:04X} {((pos - start) // 4):3} */ ", end="")
b0, b1, b2, b3 = struct.unpack(">BBBB", baserom[pos:pos+4])
elf_message_types = {
0x00: "CHECK",
0x20: "UNK_1",
0x40: "UNK_2",
0x60: "SKIP",
0xE0: "END",
}
cont = True
branch_to = None
# Get Type
elf_message_type = b0 & 0xE0
ARG_0 = elf_message_types[elf_message_type]
if elf_message_type in [0, 0x20, 0x40, 0xE0]:
if elf_message_type == 0xE0:
cont = False
ARG_1 = f"0x{(b2 & 0xFF):04X}"
elif elf_message_type == 0x60:
branch_to = 4 * (b2 & 0xFF)
ARG_1 = (b2 & 0xFF)
else:
assert False , "Encountered unknown type"
ARG_2 = f"{bool(b0 & 1)}".lower()
# Get condition
condition_type = b0 & 0x1E
if condition_type == 0:
if elf_message_type == 0xE0 and b1 == 0 and not (b0 & 1):
print(f"QUEST_HINT_END({ARG_1}),")
else:
print(f"QUEST_HINT_FLAG({ARG_0}, {ARG_1}, {ARG_2}, 0x{b1:02X}), /* eventChkInf[{(b1 >> 4) & 0xF}] & 0x{1 << (b1 & 0xF):X} */")
assert b3 == 0
elif condition_type == 2:
print(f"QUEST_HINT_DUNGEON_ITEM({ARG_0}, {ARG_1}, {ARG_2}, {item_ids[b1]}),")
assert b3 == 0
elif condition_type == 4:
print(f"QUEST_HINT_ITEM({ARG_0}, {ARG_1}, {ARG_2}, {item_ids[b1]}, {item_ids[b3]}),")
elif condition_type == 6:
condition_other_type = b1 & 0xF0
if condition_other_type == 0:
print(f"QUEST_HINT_STRENGTH_UPG({ARG_0}, {ARG_1}, {ARG_2}, {b1 & 0xF}),")
assert b3 == 0
elif condition_other_type == 0x10:
print(f"QUEST_HINT_BOOTS({ARG_0}, {ARG_1}, {ARG_2}, {item_ids[b3]}),")
assert (b1 & 0xF) == 0
elif condition_other_type == 0x20:
print(f"QUEST_HINT_SONG({ARG_0}, {ARG_1}, {ARG_2}, {item_ids[b3]}),")
assert (b1 & 0xF) == 0
elif condition_other_type == 0x30:
print(f"QUEST_HINT_MEDALLION({ARG_0}, {ARG_1}, {ARG_2}, {item_ids[b3]}),")
assert (b1 & 0xF) == 0
elif condition_other_type == 0x40:
print(f"QUEST_HINT_MAGIC({ARG_0}, {ARG_1}, {ARG_2}),")
assert (b1 & 0xF) == 0
assert b3 == 0
else:
assert False , "Encountered unknown condition (other) type"
else:
assert False , "Encountered unknown condition type"
# Control flow
if branch_to is not None:
branches.append(branch_to)
pos += 4
if not cont:
print("")
if not cont and all([dst < pos - start for dst in branches]):
break
disas_elfmsgs(int(sys.argv[1],16))