News:

Don't forget to visit the main site! There's lots of helpful docs, patches, and more!

Main Menu

Notes, etc.

Started by RT-55J, December 25, 2013, 06:48:58 PM

Previous topic - Next topic

RT-55J

We all know about this feature-bare editor. It's pretty difficult to use.

For one, figuring out how to even view a room in it's proper tileset is an issue. The editor has you select between 8 "indexes" and 9 "palettes", so you 72 combinations to sort through, of which only 8 produce a valid-looking tilesets (one palette appears to be unused).

Valid Tilesets

Index Pal What Area?
1 1 Caves - Omega Metroids, Catacombs w/Vines
2 2 Ruins - Inside
3 9 Ruins - Final Ruins (Tourian-esque)
4 3 Metroid Queen's Room
5 4 Caves - First/Last
6 5 Landing Site/Last Metroid
7 6 Caves - w/Acid
8 8 Caves - Outside Ruins
? 7 Unused??


For reference, here's a picture of what they look like in the editor:
[spoiler="Tilesets"][/spoiler]

(Yes, the Queen's tileset is a bit glitched. It's probably that way because it might have made debugging the scene easier.)

Also, here they are a bit more organized. I made this ages ago, so who know how good it is.
[spoiler="Teh TILEZ"][/spoiler]

What the editor calls palettes are the game's 8x8 pixel tiles. Indexes are the 16x16 px metatile arrangements. Screens are 16x16 metatiles large.

The gameworld is composed of 7 different maps, which are 16x16 screens in size. Each map uses a different bank of screens. According to the editor, the permissible range for screen IDs is 0x45-0x7F (probably for CPU memory mapping reasons), which is about 58 screens. So with 7 maps, that's about 406 screens to work with for the whole game (and assuming 256 bytes/screen, we have 101 kilobytes of level data (not counting the enemy data) (how does that compare with NEStroid's, snarfblam?)). To my knowledge, there's only a couple of unused screens overall, though there are a number of orphaned/inaccessible screens on the maps (scrapped areas, perhaps).

Using the editor I made screenshot maps of the first three internal maps, but then decided it was too tedious to bother finishing the remaining four (at least for now). Anyhow, here's a list of each map and a general overview of their contents.

Map 0x09
- The main caverns for the game's four main ruins.
Map 0x0A
- The four-directional scrolling caverns with acid.
- The first indoor room in the game.
- The horizontal corridors from the last cave (with all the sludge and stuff)
- A screen from the interior of the ruins, with a wall you can bomb-thru.
- A couple Metroid fight rooms.
- The most visually obnoxious filler-room ever.
Map 0x0B
- Horizontally scrolling rooms from just about every area of the game (except for the Omega Metroids' area and the ruins' interiors)
- One Omega Metroid fight room
- The save room for the Omegas' area.
Map 0x0C
- Same thing as Map 3, except with vertically scrolling rooms.
Map 0x0D
- Interiors of Ruins 1 and 2.
- Vertical rooms from the Queen's area (labs).
- A gamma metroid fight room
Map 0x0E
- Interiors of Ruins 3 and 4
- Horizontal rooms from the Queen's area (labs).
- Two metroid fight rooms
- The broken statue room
- The last refill room (filler screen)
Map 0x0F
- The first landing site, with a huge unused portion
- The landing site w/the exit from the last area
- Horizontal corridors from the introductory area
- Horizontal corridors from the Omegas' area
- The large cavern from the last area (with the sludge pit)
- The Queen's rooms


Now, about the acid... you might notice from Map 3 that there are no real screens on the map with the acid drained. That's because there aren't any at all. It has something to do with the tileset itself. Take a look at this picture of the acid cave tileset (2-2):



It's pretty easy to see that the tileset kind of repeats itself (the breakable blocks are a dead giveaway).

Now here I interleaved the first and second parts to show the differences between them. (The second part comes out a bit short, but that's probably just from the programmer naively assuming the metatile array is limited to 128 entries.)



Notice how there is a lot less acid in the second part?

My guess on how the game works with acid is that, rather than switching tilesets or anything like that, it just adds something like +0x44 to the tile-index at load-time if the acid is drained for a particular room.

That's all for now. Have a Merry rest-of-your-Christmas!


RT-55J

It looks like it's that time of the month again! (...wait wut)

A couple nights ago I decided to take a look at this in a hex editor and graphics editor to assemble a very rough "ROM map". I honestly don't know much about Game Boy's architecture or Z80 assembly, but I can notice patterns. Most of it is very placeholder-y, but I did figure out a couple things re:level data, which is good.

Metroid II - ROM Map

Bank 0 - 0x00000-03FFF
Bank 1 - 0x04000-07FFF
- some promising (untested) patterns here
Bank 2 - 0x08000-0BFFF
Bank 3 - 0x0C000-0FFFF
- again, patterns
Bank 4 - 0x10000-13FFF
Bank 5 - 0x14000-17FFF
- Graphics!
- Title screen tiles, fonts
- Samus ending tiles
Bank 6 - 0x18000-1BFFF
- In-game Samus sprites, HUD
- Enemies/Critters
- Samus' gunship tiles, unreadable stuff
Bank 7 - 0x1C000-1FFFF
- Graphics
- In-game tilesets, power ups
Bank 8 - 0x20000-23FFF
- Garbled stuff that definitely isn't code
- Graphics
- Metroids
- A couple more tilesets
- The queen's face

===== LEVEL DATA =====
Bank 9 - 0x24000-27FFF
Bank A - 0x28000-2BFFF
Bank B - 0x2C000-2FFFF
Bank C - 0x30000-33FFF
Bank D - 0x34000-37FFF
Bank E - 0x38000-3BFFF
Bank F - 0x3C000-3FFFF
===== LEVEL DATA =====


Each level data bank follows the same pattern:

0x200 bytes - screen pointers for the 16x16 map
- some-endian or another (007F points to 7F00)
- Pointers are based of CPU's memory map (what it can see at a particular moment)
- Pointers need not be multiples of 0x100. This can have some interesting effects
0x100 bytes - scroll data for each screen of the 16x16 screen map
- It's a bitwise format. One byte per screen. Setting the bits of the second nybble to 1 prevents scrolling in that direction.
  - TODO: Document what bit does what, see if the first nybble does anything.
0x200 bytes - MAGIC, relating to screen transitions
- 2 bytes per screen, it seems. Definitely not pointers. 0x0000 does nothing.
   - The effects are probably determined by code in some other bank.
     - Or maybe something bitwise.
0x100 bytes each until the next bank - screens
- 1 byte per 16x16 tile, completely uncompressed

Enemy/item data is definitely not contained in these banks.


Due to the pointer stuff mentioned above, one could view screen data in the banks not as a set of discrete screens, but a continuous cylinder (or rather, screw) of tiles with a circumference of 16 that spans that height of the Game Boy's memory map. Here's some pictures to demonstrate:


Here I changed the room ID of the screen left of the start from 0x007E to 0x087F (or something). The result is that I got a horizontally offset version of the first screen.


For this one I changed the screen ID from 0x007E to 0x807E (or something), resulting in it being offset vertically by half a screen.


And here I changed it from 0x007E to 0x0040, which makes it point to the map pointer array. You can see how each entry is two bytes, with the first one always (without hacking) being 0x00.


Now, as far as future progress is concerned, I think our next area of inquiry should be the deciphering of the screen transitioning MAGIC in each bank. I've been able to do some consistently cool stuff with it, but I haven't found any meaningful patterns. My most successful trial runs have been from copying values from other places on the map. A lot of times, just putting in some random values triggers the cacophonous inside-ruins music and hangs the game upon touching the screen edge.

I could provide some ROM address for a breakpoint in a debugging emulator or something, though at this point I feel like I'm reaching the territory of "I don't know what I'm talking about".

Gravity

#3
I'm very glad to see some activity again in this board. I would've made a complete M2 hack years ago if m2edit wasn't so limited.

Even with the limitations I still had started planning a hack anyway, making a few visual editing docs to help with planning. I'm not sure why I never shared that. I guess I thought nobody would find it useful.

The first is a small but handy color-coded listing of what palettes and indexes to use for which rooms. Though it's similar to what you put in your first post, it's more immediate to see what you need to set for each room.


Edit: Actually, I could've swore I originally saw something similar to that somewhere but I can't find any traces of it on the internet anymore.


Ironically I too had started making screenshot maps, for the sake of planning edits to rooms. Only M-1 was completed. The most useful feature is probably the fact that's it's color coded it according to how often rooms are used. Although m2edit shows you that already, I find having a visual representation of the room layouts at the same time is much more useful for planning.

Gray means the room arrangement is used at least 4 times.
Red = 3 times
Orange = 2 times
Yellow = unique

M-1 50% scale
http://i.imgur.com/vkFpOxf.png

M-1 100% scale (large image)
http://i.imgur.com/dkg1bAH.png

I do still have the Photoshop files for those, and an unfinished M-2, but I doubt anyone would be interested in those.

RT-55J



It looks like the X were busy taking over Samus' ship while she was exterminating the Metroids.

I did some brute force testing of the *MAGIC* array by changing the values corresponding to the first screen (all tests were done by walking to the left from the start). Plus I also tested some values I saw elsewhere.

Change 0x3C276 to 09 or 0F (to force a scroll transition)
Address 0x3c3EC-ED
*Interesting Result

0000 - Samus' priority: hi
     - Perfectly normal scrolling transition
0001 - Screen transition to random screen, no tileset change
0002 - Samus' priority: Hi
     - Normal scroll transition to adjacent screen.
0003 - Samus' priority: Hi
     - Normal scroll transition to adjacent screen.
0004 - Samus' priority: Hi
     - Tileset partially changes, music changes to Ruins 3 catacombs (noisy bugs), game hangs before it would have scrolled.
0008 - Samus' priority: Lo
     - Normal scroll transition to adjacent screen.
000E - Game hangs
*000F - Samus' sprite priority: lo
     - Screen transition directly to Metroid queen, no proper tileset change, exit room, then game crashes.
0010 - Samus' priority: Hi
*0012 - Game hangs, tiles get slowly erased, then the music crashes on a white screen
001C - Switches to inside ruins music, then hangs
002F - Samus' priority: Lo
     - Normal scroll transition to adjacent screen.
0100 - Samus' priority: hi
     - Scroll transition to a Metroid fight.
010F - Samus' priority: Lo
     - Normal scroll transition to adjacent screen.
*3F04 - Scroll transition, sprite tiles changed (garbles ship graphics)
3F0F - Scroll transition, Samus' priority: Lo
4609 - Scroll transition, wrong tileset with an enemy (metroid?)
4809 - Game hangs
B209 - Proper fade transition to Ruins 4 outside
C208 - Scroll transition to mistiled area.
CE01 - Scroll transition to psuedo-norfair, wrong tileset
DF01 - Proper transition to first cave
DF0F - Back to title screen


So, these bytes can determine:
- Samus's sprite priority on a given screen.
- Which map/screen to warp to upon scroll transition.
- Changes in tileset upon scroll transition.
- Changes in enemy graphics upon scroll transition.
- Whatever random junk the code wants to mess up.

I don't see any rhyme/reason to any of it, save that 0xnnn8 (bin:wwwwxxxxyyyy1zzz) usually sets Samus's sprite priority to behind the background layer (eg for the ship).

"Game hangs" means that all motion stops, but the music still plays (so it's not a full-on crash).

Clearly, there ain't no crackin' this without ASM skills.

RT-55J

#5
It's been a while!

I mentioned in another topic that I wanted to figure out how to read ASM and such. Now that the semester is over I have more time to beat my head into a wall figuring this stuff out. Huzzah!

Now, last time I was able to find the routine that reads from the area's (~MAGIC~) screen transition array when a screen transition occurs. I couldn't figure out what it does, but now I'm a bit wiser, so I made a verbose description of what the subroutine does (well, kind of).

Dumb code:

ROM0:0C37 FA 8B D0         ld   a,(D08B)
ROM0:0C3A FE 11            cp   a,11
ROM0:0C3C 20 10            jr   nz,0C4E ;If the contents of $D08B do not equal $11, jump ahead to $0C4E
ROM0:0C3E FA 20 D0         ld   a,(D020)
ROM0:0C41 FE 0B            cp   a,0B
ROM0:0C43 38 09            jr   c,0C4E ;If the contents of $D020 are less than $0B, jump ahead
ROM0:0C45 FE 0F            cp   a,0F
ROM0:0C47 30 05            jr   nc,0C4E ;...but also jump ahead if it's not greater than $0F
ROM0:0C49 3E 05            ld   a,05
ROM0:0C4B EA 20 D0         ld   (D020),a ;If all those tests are failed, set $D020 in WRAM to $05

ROM0:0C4E AF               xor  a ;Bitwise xor A with itself... Wouldn't that just clear the register?
ROM0:0C4F EA 22 C4         ld   (C422),a ;...if that is the case, $C422 and $D07D get set to zero.
ROM0:0C52 EA 7D D0         ld   (D07D),a
ROM0:0C55 3E FF            ld   a,FF ;Set the following addresses to $FF
ROM0:0C57 21 30 DD         ld   hl,DD30 ; | $DD30
ROM0:0C5A 77               ld   (hl),a ; |
ROM0:0C5B 21 40 DD         ld   hl,DD40 ; | $DD40
ROM0:0C5E 77               ld   (hl),a ; |
ROM0:0C5F 21 50 DD         ld   hl,DD50 ; | $DD50
ROM0:0C62 77               ld   (hl),a ; |
ROM0:0C63 EA 9E D0         ld   (D09E),a ; | +Whatever address is given by $D09E in WRAM

;Calculates the index of the area's screen transition array to be loaded. Somehow.
ROM0:0C66 F0 C9            ld   a,(ff00+C9) ;Load the contents of $FFC9 into reg A
ROM0:0C68 CB 37            swap a ;Swap the nybbles
ROM0:0C6A 5F               ld   e,a ;Load the result into reg E
ROM0:0C6B F0 CB            ld   a,(ff00+CB) ;Load the contents of $FFCB into reg A
ROM0:0C6D 83               add  e ;A <- A + E
ROM0:0C6E 5F               ld   e,a ;Store the result in reg E
ROM0:0C6F 16 00            ld   d,00 ;Set D to $00
ROM0:0C71 CB 23            sla  e ;Shift E left
ROM0:0C73 CB 12            rl   d ;Have D take in the carry-out of the previous operation (rotate left)

ROM0:0C75 21 00 43         ld   hl,4300 ;Load the number $4300 into the HL reg.
ROM0:0C78 19               add  hl,de ;Add the 16-bit offset from the DE reg into HL.
ROM0:0C79 2A               ldi  a,(hl) ;Load the value at the address contained by HL to reg A, then INC HL by 1
ROM0:0C7A EA 8E D0         ld   (D08E),a ;Store the result in WRAM
ROM0:0C7D 7E               ld   a,(hl) ;Load the next byte into reg A
ROM0:0C7E CB 9F            res  3,a ;Set bit 3 (76543210) to zero
ROM0:0C80 EA 8F D0         ld   (D08F),a ;Store the result in WRAM

ROM0:0C83 3E 02            ld   a,02
ROM0:0C85 EA 58 C4         ld   (C458),a ;Set the value at address $C458 to $02

ROM0:0C88 AF               xor  a
ROM0:0C89 EA 9B D0         ld   (D09B),a ;Clear $D09B?

ROM0:0C8C FA A0 D0         ld   a,(D0A0)
ROM0:0C8F A7               and  a ;Bitwise AND A with itself? Wouldn't that just do nothing?
ROM0:0C90 C8               ret  z ;exit subroutine if $D0A0 is zero?

ROM0:0C91 F0 80            ld   a,(ff00+80)
ROM0:0C93 E6 0F            and  a,0F
ROM0:0C95 FE 06            cp   a,06
ROM0:0C97 C0               ret  nz ;exit subroutine if the lower nybble of $FF80 is not equal to $06

ROM0:0C98 3E 9D            ld   a,9D ;Set $D08E/D08F to $9D and $01
ROM0:0C9A EA 8E D0         ld   (D08E),a
ROM0:0C9D 3E 01            ld   a,01
ROM0:0C9F EA 8F D0         ld   (D08F),a
ROM0:0CA2 C9               ret  ;exit subroutine


Basically it does some random stuff (I can only guess what some of the addresses mean), and it stores the bytes from the screen transition array to $D08E/$D08F in WRAM, except that it zeroes out bit 3 because that's used elsewhere to determine Samus' sprite priority.

Anyhow, I figured that this $D08E address is probably pretty important. Long story short: I found a routine that goes from $239C to $26EA in bank 0 that interprets that address as an index in a pointer table, which points to a bunch of 64-byte strings that can get copied to RAM and do stuff.

Level Data
0x200 bytes - screen pointers
0x100 bytes - scroll data
0x200 bytes - MAGIC, relating to screen transitions
- One 2-byte word per screen.
  - bytes are in reverse order
  - bit 3 of the second byte (bin:wwwwxxxxyyyy-zzz) determines sprite priority. It is otherwise ignored.
  - The word serves as an index to a pointer table.
  - The pointer table begins at 0x142E5 (Bank 5), ends at 0x146E4
    - To have the index point to the ROM address of the pointer in question:
       1. Zero the sprite priority bit.
       2. Swap the bytes.
       3. Double the result
       4. Add 0x142E5
    - The pointers are likewise byteswapped, and refer to bank 5.
      - Add 0x10000 to the pointer to get the desired ROM address.
      - The game copies 64 (0x40) bytes from the address in question to $D700 in RAM.
        - I don't think all the bytes get used (the pointers do no point to evenly spaced intervals; $FF probably acts as a terminating byte).
0x100 bytes each - screens


I verified that this works by looking up the data corresponding to the first screen's exit (into the first cave), copying it to a location used by a different exit number, and changing the exit number of a different screen to the new exit number. Both exits, despite having different IDs, worked identically.

Then, just for the halibut, I added $FF partway through the new exit's data. This is what I got:



EDIT: moar:

This is exciting stuff.

P.S. I have a wiki account (rt-55j), but I don't seem to have editing privileges. Can I has the editing privileges?