I’ve hacked/disassembled/poked at Metroid 2 intermittently over the past couple of years, but it wasn’t until literally last week that I got around to coding anything that could edit it. A while back some of us at metroidconstruction tried getting together to make an editor, but, um, nothing really came of it.
I wrote some stuff in Python to read the various kinds of level data, then I tried displaying it.
My code takes around ~15 seconds to render a full map. I know I’m using an interpreted language, but I suspect something is a tad unoptimized (probably either file-io related or the fact that I literally have functions returning bitmaps).
is a good question, and a hard one at that.
Phyton 2.x, I assume?
I curse the day i agreed to take python3 for the project at work that i am involved in atm, because IF you get a recommendation, ''of course it is for 2.x, you silly! why'd you use 3 you fool?''
Because We Can™
Anyway, in the request lib for doing lowlevel network & IP messaging stuff, an interesting issue has reared its head this week:
Namely if you have the wireless connection to wayside going down and up again, new resolve.conf information never seems to get through to pythons innards, the lib and/or the application, and that’s a case we have to considerrrrrrrrrrrrrrrrrrr without kicking the application or god beware, rebooting the host, hrmmmmmmmmmmmm~~~~~~~~
I think most people finally up and switched to 3 in 2015-2016. I still fuck up the print statement and iteritems occasionally but otherwise I’ve made my peace (though 3’s urllib is still terrible at unicode, you should be using requests instead)
Admittedly most of my Python work lately has been limited-use internal tools but I haven’t run into very much that’s actually stuck in 2.X anymore. Maybe I’m just not looking in the “right” categories.
Thanks for the suggestion. I’ve decided to go with it.
Python 3, actually. I was aware that it was incompatible with 2.x, but wasn’t really aware of the pros/cons. I’m relatively new to Python, and not really sure why I decided to do this project in that language, but here we are.
In the process of porting/refactoring my rendering code from using Pillow to PyQt, I produced this lovely image.
The weird colors are because I had been ignoring tiles with indexes >= 128, and in the process of moving the code over I removed the code that initialized the bitmaps with a solid fill-color, so I got some surprisingly nice looking uninitialized data.
That was enough to convince me to add support for the upper half of the tileset (it’s loaded separately), which also happens to be necessary for rendering the surface area (the ship takes a boatload of tiles).
So yeah, here’s a map with the surface area. Marvel at the differences between the beginning and ending landing sites. (Also, the new code renders it about 3 times faster.)
Here's the state of the "gui":
Currently the only interactive portion is the file dialog, but hey it’s a start. The tutorials and documentation for PyQt have been very useful.
Another feature I added, as seen in the gui, is a visualization of the scroll data for each bank.
Behold
Again, there are vestiges of unused rooms in some of these that I plan on documenting on TCRF eventually.
there are several reasons why i got burnt out on this, many of which boil down to “I don’t want to program some absurd spaghetti code to edit variable-length data in place in a binary file when an assembler could do those things for free”
with that in mind, I’ve been working on a disassembly for the past couple of weeks, and it feels like it’s might actually come together
the end goal of this (whether i make the editor or someone else does) is to have a workflow that more closely resembles a homebrew game, with an editor that operates on free-floating files rather than an ossified binary blob
I was investigating how doors work, but was slightly confused. They had code that differed depending on if the door was hit from the right or the left, but I didn’t see any code path that would allow them to control which direction they spawned.
After just a little poking at some ram addresses, I discovered the terrifying truth:
lmao
turns out that the left and right door caps are a single object, conjoined at all times
compared to what i’m used to with these games, this is a bewildering (brilliant) solution to this problem
Figured out things well enough that I was able to improve the controls with just a little effort:
This patch moderately improves the controls of the spider ball. Now, when resting on corners (and in every other spider ball state), the game will properly respond to any sensible cardinal or diagonal direction.
Context: I’ve been working on having the contents of the banks be “fully relocatable”, which entails making sure all the pointer constants in the code/data use labels instead of hardcoded numbers. Reaching this milestone for each bank is important because it will allow hackers to add code/data to banks without worrying about breaking things.
I’ve been doing informal tests here by just putting an extra nop at the beginning of the bank and seeing if anything breaks. Apparently, in this case, I didn’t have labels for (a) the pointer table for enemy sprites or (b) the palette table for the fade-in effect, which resulted in that lovely, terrifying mess. (I’m surprised the game didn’t crash, honestly.)
With those things fixed now, bank 1 Seems To Work Fine Now (fingers crossed).
Immediately after the table that defines the transverse (sideways) motion of the wave beam, there’s another table that is unused, but appears to work with the wave beam as well.
.waveSpeedTable: ; 01:5183 - Wave speed table (transverse velocity)
db $00, $07, $05, $02, $00, $fe, $fb, $f9, $00, $f9, $fb, $fe, $00, $02, $05, $07
db $80
.waveSpeedTable_alt: ; 01:5194 - Unused (alternate wave table -- motion blur makes it looks very spazer-like)
db $0A, $F6, $F6, $0A, $0A, $F6, $F6, $0A, $0A, $F6, $F6, $0A, $80
Here’s what it looks like in action.
Apologies for my gif recorder making it look jankier than it is, but in person it mainly looks like a blurrier version of the spazer. Perhaps this was a earlier version of the spazer, or perhaps the visual similarity made them choose to make the wave beam’s pattern more sophisticated.
now i just need to multiply that number by 20 and i’ll be caught up with the link’s awakening disassembly (that’s how code works right?)
In the metroid queen fight, the game does not allow you to pause. Fixing this is as simple as changing a conditional ret to a nop in the function that determines if you are allowed to pause (some other conditions include “are you standing on a save point” or “is a door transition in progress”).
I thought enabling this would cause some terrifying gamebreaking bug to happen, but no it just causes a couple minor graphical glitches:
First, her feet keep on moving if they were moving when you paused (she’s quite impatient with you, it seems). This is because part of the animation logic for her feet is done during VBlank (lmao). Fixing this would be pretty simple, imho.
Second, the palette effect is not being applied to the lower half of the background layer. I believe this is related to the fact that the game uses raster interrupts to change her palette when she gets hit. Fixing this bit seems like it would be slightly harder.
The other day, since I was nearing completion of a full bank of code, I decided to take the symbol file the assembler generates and create a spreadsheet to properly track my progress for each bank:
Porg Repor
S - Satisfactory
N - Needs Comments (or conversion to .chr or whatever)
X - :-(
Progress = (S + .5*N)/Total
Bank
0
1
2
3
4
5
6
7
8
Total
S
91
45
87
19
----
17
2
0
23
284
C
66
16
0
8
----
20
24
11
11
156
X
58
18
2
60
----
16
0
0
0
154
Total
215
79
89
87
----
53
26
11
34
594
Progress
58%
67%
98%
26%
----
51%
54%
50%
84%
60.9%
These counts are of labels (but not sub-labels), which mostly means functions. Also, some fairly substantial chunks of the rom (e.g. enemy data, doors, sprite data) are only represented as a single labels for the purposes of this analysis.
Bank 0 is the core of the game. It is always loaded and fixed in place.
Bank 1 contains sprite data, the code for drawing Samus and the HUD, and a lot of odds and ends.
Bank 2 is almost entirely enemy AI routines. It is essentially done.
Bank 3 is the enemy map data, enemy headers, and the mess known as the queen’s AI (handled entirely separately from the enemies).
Bank 4 is the music engine and song data, hermetically sealed from the rest of the game. A buddy of mine offered to do it themselves, and they say they’re almost done.
Bank 5 is the title screen, credits, and door data.
Banks 6-8 are mostly graphics and tilesets, with a few odds and ends here and there. There are a lot of graphics files that are stored as plaintext .asm that I should probably convert to .chr files.
Banks 9-F are the level map data and required basically zero work, so they are excluded from this table.
So yeah, I guess it turns out the buddy of mine who said when I started “yeah we should be able to get this done by the end of the year” wasn’t that far off!
I discovered that the problem with keeping track of progress numerically like this is that sometimes you pick low-hanging fruit instead of important stuff so you can reach arbitrary important milestones:
Trying to get back into this, I felt inspired to resolve a persistent bugbear: several tables in the ROM are indexed by an enemy’s sprite number (e.g. sprite pointer, hitbox pointer, damage value, etc.). Keeping these all properly correlated by hand in a text editor is a huge pain (like, if you want to edit them), so I spent some time the other day shoving them all into a spreadsheet (.csv) and making a tool to convert it into a bunch of .asm files at assembly-time: