Skip to content

Instantly share code, notes, and snippets.

@bbayles
Created July 20, 2025 12:10
Show Gist options
  • Select an option

  • Save bbayles/f66d815e76ddd4988e9e4c6809bcfbaf to your computer and use it in GitHub Desktop.

Select an option

Save bbayles/f66d815e76ddd4988e9e4c6809bcfbaf to your computer and use it in GitHub Desktop.
The Crow: City of Angels for Saturn - generate all level passwords
LETTER_MAP = {"A": 2, "B": 3, "X": 0, "Y": 1}
def rotate_right(value, positions, bit_width=32):
# This is the function at 06031e3c (NTSC-U Saturn verison)
mask = (1 << bit_width) - 1
value &= mask
positions %= bit_width # Handle positions > bit_width
return ((value >> positions) | (value << (bit_width - positions))) & mask
def check_password_accumulator(acc):
# Adapted from Ghidra's decompilation of the functions at 06031718
# and 06031eec (NTSC-U Saturn version)
# Decrypt using fixed key
acc = acc ^ 0xA5A5A
# Extract low 4 bits
uVar3 = acc & 0xF
# Extract high 14 bits and XOR with low 4 bits, keep low 4 bits
starting_stage = (uVar3 ^ (acc >> 0xE)) & 0xF
# Extract bits 4-13: (acc & 0x3ff0) >> 4
# Extract bits 0-1, shift to bits 8-9: (acc & 3) << 8
# Low 4 bits shifted to bits 4-7: uVar3 << 4
# Low 4 bits in bits 0-3: uVar3
# XOR the extracted middle bits with the constructed value
rotate_input = ((acc & 0x3FF0) >> 4) ^ ((acc & 3) << 8 | uVar3 << 4 | uVar3)
# Rotate by the calculated amount
uVar1 = rotate_right(rotate_input, starting_stage, 10)
# Extract components from rotated result
uVar3 = (uVar1 & 0xC) >> 2 # Bits 2-3
# Validation check: sum of components should equal original high bits
if (
starting_stage == 0
or ((uVar1 >> 4) + uVar3 + (uVar1 & 3) + starting_stage) & 0x3F != acc >> 0xE
):
return ()
# Store extracted components globally
sVar2 = uVar1 >> 4
lives = uVar3 # Bits 2-3 of rotated value
difficulty_level = uVar1 & 3 # Bits 0-1 of rotated value
# Calculate some derived value
if sVar2 == 0:
starting_health = 2
else:
starting_health = sVar2 * 2 + 1
return (starting_stage, lives, difficulty_level, starting_health)
def check_password(password_letters):
if len(password_letters) != 10:
return ()
accumulator = 0
for i, letter in enumerate(password_letters):
mapped_letter = LETTER_MAP.get(letter, -1) & 0xFFFFFFFF
accumulator |= (mapped_letter << 2 * i) & 0xFFFFFFFF
return check_password_accumulator(accumulator)
if __name__ == "__main__":
from itertools import product
seen = set()
for prod in product("ABXY", repeat=10):
password_letters = "".join(prod)
params = check_password(password_letters)
if not params:
continue
starting_stage, lives, difficulty_level, starting_health = params
if (lives == 3) and (starting_health == 0x7F) and (1 <= difficulty_level <= 3):
if (starting_stage, difficulty_level) not in seen:
seen.add((starting_stage, difficulty_level))
print(password_letters, *params, sep="\t")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment