In this edition:
I break the password encoding scheme for The Crow: City of Angels while looking for cheat codes.
I don’t find any, but I do find an exploitable bug that gives you (practically) unlimited lives.
Details are below!
Edge Magazine wrote this about the Saturn version of The Crow: City of Angels:
The indistinct nature of The Crow's animations means that very often, aligning your character with opponents is down to luck. Awful collision detection, repetitive gameplay and the absence of a two player mode make Acclaim's licence one of the worst Saturn releases to date.
Ouch. The PlayStation version didn’t review any better — IGN said “Leave this one for the crows.” Is there anything interesting about this game?
Yes! These two things intrigued me:
Cheat sites like GameFAQs list some special passwords that enable cheat effects for the PlayStation version, but just the standard level passwords for the Saturn version.
The “Infinite energy” password listed for PlayStation is weird; it starts you in the middle of the game and eventually stops working.
Are there cheat effects in the Saturn version? What’s going on with the PlayStation version’s invincibility cheat? I decided to investigate.
Infinite energy
Let’s start with the Saturn version. I used my usual memory snapshot comparison trick (you can do it with a tool like Cheat Engine) to locate the code responsible for the password screen.
The function at 06031718
(NTSC-U version) is handling input when you’re entering a password. When you choose a letter, that function converts it into a number: A is 2; B is 3; X is 0; Y is 1; everything else is -1.
The bits from each number are shifted and packed into a 20 bit value, with the last letter at the highest bits. Here’s a password for “Level 9” from GameFAQs being converted into its binary representation:
The function at 06031eec
validates passwords in their packed form. For valid ones, it extracts four pieces of information: stage, difficulty level, lives, and health.
These values are encoded into the password in a complex way. I gave Claude Opus 4 some decompiled code from Ghidra and asked it to give me a Python equivalent. Then I repeated the process for the PlayStation version.
Using Claude’s Python code as a starting point, I wrote some scripts to generate every possible password and print out the unique ones for each difficulty level that give you maximum health — they are at the bottom of this article as appendices.
None of the Saturn version’s passwords enable special effects the way some of the PlayStation version’s do, alas.
But there’s something interesting about the password checking function: it doesn’t validate things very thoroughly. For example, it will accept passwords for stages beyond 11. The password YAYXAYYAYA is for stage 12, which doesn’t exist. The game doesn’t reject it, but it freezes up on the loading screen when you use it.
The game also accepts passwords that have the number of lives set to 0. When you’re playing the game normally, losing your last life triggers a “game over” sequence. Here’s the relevant logic (in the function at 0603cecc
):
if health == 0:
lives -= 1
if lives == 0:
do_game_over_thing()
else:
# Full health for the next life
health = 0x7f
You can see the problem: if the lives
variable is at 0, then subtracting 1 from it gives -1. But the game is only checking for the specific value 0, not “any integer below 1.”
So if you enter one of these degenerate passwords, you can play through your 0th life, then your -1st life, then your -2nd life. Lives are stored as a 16 bit value, so you will eventually wrap around to positive numbers, at which point you’ll only have 32,767 lives remaining.
This is actually what’s going on with the “infinite energy” password that’s listed on the cheat sites for PlayStation. Its encoded values are:
Difficulty level: 1
Stage number: 3
Number of lives: 0
Health: 71
The trick stops working in the last stage because the game helpfully gives you three lives before that stage starts. The relevant logic in the Saturn version is at 06048d60
:
if stage_number == 11:
lives = 3
Here are passwords to start the game with effectively unlimited lives:
Saturn: Y, A, B, X, B, Y, Y, B, A, A
PlayStation: Triangle, Circle, X, Square, X, Triangle, Triangle, X, Circle, Circle
So there kind of is a cheat effect for the Saturn version!
Staff messages (PlayStation)
The PlayStation version’s password checking function (at 80015b60
in the NTSC-U version) compares the binary version of your input to five special values before passing it on to the validation function described above.
Four of those special values correspond to known cheat effects: Debug mode, “Giraffe” mode, “Skinny man” mode, and FMV select.
But the fifth one doesn’t seem to have been published before. My script from above can produce it, however:
Square, Triangle, Circle, X, Square, Triangle, Circle, X, Square, Triangle
Entering this gets you Alternate Credits!
These start by describing how you can change the scrolling speed and direction with the D-pad. Then the programmers, Stefan and Gard, give some thanks:
Stefan is Stefan Postuma and Gard is Gard Abrahamsen. Both of them worked at Gray Matter. And… hey, wait a minute — didn’t Stefan Postuma sneak some alternate credits into Phoenix 3 for 3DO? Yes! I’ve caught him doing it again.
God mode
The debug mode code for PlayStation enables this information display:
It’s not terribly clear, but the last bit of information is telling you whether God mode is enabled.
There are some scraps of this screen’s code in the Saturn version, but the remnants aren’t hooked up to anything. Here are some of the strings:
06039c08 "hexpos %d %d dir %d god Mode On"
06039c28 "hexpos %d %d dir %d"
06039c3c "Zbuff Clipping On"
06039c50 "Zbuff Clipping Off"
06039c64 "camera %d %d area %d god Mode Off"
I couldn’t get them to display, but from what I can see, God Mode would have been enabled by pressing X on controller 2.
The flag for it does work, even though the menu that was supposed to trigger it doesn’t. You can make yourself invincible with this Action Replay code:
16066336 0001 # Set God mode
Outro
Subscribe to Rings of Saturn to get more retro game reverse engineering articles every week:
For more on decoding game password systems, see these posts:
Appendix 1: Passwords for the Saturn version
These passwords start you at the given level with the default number of lives and full health.
For difficulty level 1:
| Stage | Password |
|-------|------------|
| 1 | BBBAXXBYBA |
| 2 | YBYYAXYXBA |
| 3 | BBBBYXBBBA |
| 4 | YAYAXYYABA |
| 5 | BYBYXBBYXA |
| 6 | YYYYAXYXXA |
| 7 | BYBYXAABXA |
| 8 | YAYAAYBAXA |
| 9 | BBABXXBYYA |
| 10 | YBBBAXYXYA |
| 11 | BBBAXXBBYA |
For difficulty level 2:
| Stage | Password |
|-------|------------|
| 1 | ABXBYXAXBA |
| 2 | ABAAYXABBA |
| 3 | ABAYYXAABA |
| 4 | AYAYXAAYXA |
| 5 | AYAYBAAXXA |
| 6 | AYAYYBABXA |
| 7 | AYAYYXAAXA |
| 8 | ABABYXBYYA |
| 9 | ABABYXXXYA |
| 10 | ABBBYXABYA |
| 11 | ABXBYXAAYA |
For difficulty level 3:
| Stage | Password |
|-------|------------|
| 1 | YBYBAXYBBA |
| 2 | BBBBXXBABA |
| 3 | YXYXABYYXA |
| 4 | BYBYXABXXA |
| 5 | YYYYAAYBXA |
| 6 | BYBYXABAXA |
| 7 | YXYXABYYYA |
| 8 | BBBBXXBXYA |
| 9 | YBYBAXYBYA |
| 10 | BBBBXXBAYA |
| 11 | YXYXABYYAB |
Appendix 2: Passwords for the PlayStation version
These passwords start you at the given level with the default number of lives and full health.
For difficulty level 1:
| Stage | Password |
|-------|------------|
| 1 | XXXCSSXTXC |
| 2 | TXTTCSTSXC |
| 3 | XXXXTSXXXC |
| 4 | TCTCSTTCXC |
| 5 | XTXTSXXTSC |
| 6 | TTTTCSTSSC |
| 7 | XTXTSCCXSC |
| 8 | TCTCCTXCSC |
| 9 | XXCXSSXTTC |
| 10 | TXXXCSTSTC |
| 11 | XXXCSSXXTC |
For difficulty level 2:
| Stage | Password |
|-------|------------|
| 1 | CXSXTSCSXC |
| 2 | CXCCTSCXXC |
| 3 | CXCTTSCCXC |
| 4 | CTCTSCCTSC |
| 5 | CTCTXCCSSC |
| 6 | CTCTTXCXSC |
| 7 | CTCTTSCCSC |
| 8 | CXCXTSXTTC |
| 9 | CXCXTSSSTC |
| 10 | CXXXTSCXTC |
| 11 | CXSXTSCCTC |
For difficulty level 3:
| Stage | Password |
|-------|------------|
| 1 | TXTXCSTXXC |
| 2 | XXXXSSXCXC |
| 3 | TSTSCXTTSC |
| 4 | XTXTSCXSSC |
| 5 | TTTTCCTXSC |
| 6 | XTXTSCXCSC |
| 7 | TSTSCXTTTC |
| 8 | XXXXSSXSTC |
| 9 | TXTXCSTXTC |
| 10 | XXXXSSXCTC |
| 11 | TSTSCXTTCX |