I've been hacking SPA the past couple weeks. Here's some of what I've found. I don't really follow the hacking community, so I'll probably use nonstandard terminology in places. Note that 0xxxxx/1xxxxx addresses are offset internally as 2xxxxx/3xxxxx addresses, as the NGPC mounts the ROM at 200000-3FFFFF. Level data- The main level data is listed at 19DE1A. Each level gets 14 bytes, as follows - 4: Level data 4: Some procedure I haven't gone through 2: Sonic's starting height (width is fixed) 2: "Background" color index (often not actually the background, but some other useful color) 2: [UPDATE:] Water level - affects physics, splashes, Sonic palette and drowning; everything else is level design Here's the listing. Code (Text): LvAddr StHgt bgclr Water [2A3590] [39E742] a8 01 02 00 00 00 | NSI1 [2B472E] [39E742] 48 02 02 00 00 00 | NSI2 [2E0BA8] [39E755] 90 03 26 01 00 00 | SP1 [2E9F14] [39E76E] C0 05 26 01 97 00 | SP2 [31D04C] [39E78A] 18 04 70 01 00 00 | CC1 [326824] [39E78A] E8 01 70 01 00 00 | CC2 [34CA24] [39E729] 00 03 9C 01 87 02 | AR1 [35582E] [39E729] 30 03 9C 01 A7 02 | AR2 [35BB62] [39E7C5] A8 00 27 00 00 00 | SC [36BCC2] [39E7C6] 00 02 27 00 00 00 | AB [38A188] [39E7E2] 68 00 F0 01 00 00 | GA1 [393240] [39E7E2] C0 04 F0 01 00 00 | GA2 [396E4C] [39E728] F8 00 B8 01 00 00 | LU [36BCC2] [39E728] 18 02 02 00 00 00 | AB? [396EAA] [39E742] 60 00 02 00 00 00 | CS [2A3590] [39E742] A8 01 02 00 00 00 | TS1 [2B472E] [39E742] 48 02 02 00 00 00 | TS2 Map Formats- NGPC graphics are composed of 8x8 4color tiles, with 16 4color palettes for each of the foreground, background and sprites (so 48 total). SPA has a large set of universal palettes; everything else is done per-level. I'll be referring to the NGPC memory map for what's drawn from the hardware rather than just rewriting it. The SPA level designs are constructed of tiles using 4x4 blocks, which assign the tiles their palettes and orientation, in the format specified by the hardware for the scroll maps (see memory map 9000). The RAM tile positions are as such hardcoded, so structural tiles are constant throughout a level. The blocks for a level are listed out at the address at LvAddr+14; the number varies per level, but maxxes out at between 4096 and 8192 (as determined crudely by the fact that my level mapping code works). The tiles themselves are at the address at LvAddr+30; their format is specified by the memory map's pattern table (A000). There's exactly 512, since the NGPC has space for 512 tiles and once the level is loaded they stay. A certain number of spots are used for sprites and fluctuate during play, but this is more or less ignored here. Foreground palettes are at LvAddr+18, background at LvAddr+22. Sprites I haven't yet bothered to find but I assume they're close by. Note that what's stored here isn't actually palettes per se - those are all at 3C748. What IS here are the 16 palette indices (out of thousands) that are actually USED in the level. OK, the map itself. At LvAddr there's a two byte level width in blocks, and two byte height in blocks. There's actually TWO maps, one for the background (LvAddr+10) and foreground (LvAddr+26). Each is a straight listing of 2byte block indices, with the level ordered in rows. Pretty straightforward. I've used all this info to make maps of each level, which you can find here, along with the generating PHP source. They're lacking sprites for the moment, but I'll rectify that some time soon. One interesting note is that the rings are baked into the foreground map instead of acting as sprites, which as I understand it is unusual among the Sonic games. With the NGPC's 64 sprite limit, SPA would otherwise be limited to about 8 rings on screen otherwise. IIRC, the game actually has copies of each block with various rings present and removed. Sprite Formats- The sprite tiles (dec10916 of them) are at CB34. Here's a sheet of them all. The tiles are composited into actual sprites using data at 296. That format is as far as I know nonstandard, so I'll describe it below. First two bytes - number of tiles Varying quantities of either one tile at a time... { Two bytes - tile index One byte - row offset (signed) One byte - col offset (signed) } *AND/OR* multiple consecutive tiles... { Two bytes - FFFF (to note that there is extra stuff here) Two bytes - number of tiles Two bytes - initial tile index One byte - row offset (signed) One byte - col offset (signed) } <- in this case, tiles after the first each go 8px below the last ... to match tile count. Each sprite goes straight one after the other in this fashion, with no regular offsets or anything. Frankly I think it's a bit of a mess, but I guess it worked for them. Here's a sheet of all the sprites, properly composited. Unfortunately the offsets for some of the sprites are all over the place, but I erred on the side of having some of the sprites overlap rather than making the image absolutely huge (and it's already pretty ridiculously huge, at 1024x30080). The odd ghostly sprites, most obvious after the Sonics at the beginning, are used to give a sprite more than four colors while staying within the 16 palette limit. And if anyone can think of what sprites 428-430 are used for, please clue me in. o_O Anyway... I have some notes on the routines and procedures, but I tried to avoid disassembling whenever possible, both because asm is a pain to begin with and because I can't even find definitions for many of the opcodes so I've had to deduce them myself. That's what I get for the NGPC using such a weird obscure processor. Also, it appears to me that either the game was compiled from some higher level language or the programmers had really bizarre habits; I don't really have enough experience with asm to say for sure, but I don't think it's SOP to call subroutines four lines long that are halfway across the ROM. I also have notes on a whole bunch of the RAM locations and their purpose. Buuuuut I am tired! so I think I'll sleep instead and write them up later. XD Cheerio~
Heh, I didn't know Aquatic Relix had those funny faces within the wall bricks. Those maps rule, great job finding those formats! EDIT: Isn't that a Sonic2-style shield?
Holy shit, this something I've wanted to look into for awhile now, looks like ya beat me. :P SPA hacking tool perhaps? *shotshot*
Hey Hey, only 5 posts and you're already a Tech Member. Good job! While I myself am not too interested in Sonic Pocket Adventure hacking, this will surely, and hopefully, inspire more hacking of this game, and possibly others. Oh, nice sprite sheet by the way. Can't put my finger on the sprites you mentioned though, and they don't look like shields to me... I dunno though, maybe I'm not looking hard enough.
Those are indeed the shields. I was thrown by how huge they are, but on looking back in the game, they really are that much bigger than Sonic. :X As for the RAM data. I don't have the level of comprehensive info on the RAM that I'm gaining with 8-bit Sonic, simply because the tools aren't there, so I've really only found what was convenient or I was interested in. There's a debug version of NeoPop that lets you pause emulation and check the memory, but it mysteriously completely stopped working on my PC; even downloading it into different directories with different ROMs didn't work. But even if it did work it's not nearly as useful as Meka's watch-it-as-it-runs capability. As it is I'm more or less reduced to analyzing savestates and source. It's worth noting that NeoPop's savestates are basically a RAM dump with two bytes appended at the beginning, whereas every other emulator seems to use less useful formats (and be less useful in general). Aaaanyway. I'll just list out what I have. It's worth minding that 0100-6C00 is general usage RAM and 8000-BFFF is VRAM. The NGPC has far more RAM than SPA actually uses, so I haven't found any RAM locations that're reused so far. Everything up to 4000 is left unused, and the rest still has huge blocks of 0s. Broadly speaking, SPA uses 49xx-50xx for sprite/display data, and 67xx-6Bxx for status data. 49F8-49FC: Sprite work variables 4Axx: I *think* these are the various sprite statuses, but haven't nailed down 4B6E-4C6D: Sprite workarea; tiles are assembled and tagged and copied to 8800-88FF 4C6E: Er... I forget, but it does SOMETHING XD 4CEE: Likewise 4CF0-4D2F: Palette workarea; goes to 8C00-8CFF 5056-5057: Background scroll, goes to 8032-8033 506C-506D: Foreground scroll, goes to 8034-8035 5086: Sprite scroll 67A4.2: Camera X 67A6.2: Camera Y 67A8.4: Ring counter 67CC.2: Timer, as a straight frame count (30fps). My procedural knowledge thus far is primarily confined to the display crankers. I'll use NGPC 200000 offsets for differentiation from RAM addresses. 23C663: The 'master' drawing procedure. Calls a bunch of subroutines and then hangs around and waits for an interrupt while writing 4E to 6F (the NGPC resets if 6F goes unwritten to for 100ms). The following addresses are the various subroutines it calls and my labels for them. 23E488 "Slashnburn": Dumps FFs and 00s all over the sprite stuff to get ready for the next frame 3DFE39 "FgMap" (23C496 w/ 0->A): Offsets the space at 9000-93FF by two and sticks 01FF at the front 23C1F8 "PreSpCalc": Initializes the sprite work variables to signal to SpCalc that it's starting anew 23E884: I don't have complete notes on this, got distracted by the sprite layout algorithm 23C03D "SpCalc": The big one. Does all the sprite compilation work. 23E1BA: I think this is some kind of tile moving shenanigans 29112A "MinuWork": Hijinks around the 5056-5070 area 23BFE2 "SpCopy": Copies stuff from sprite workarea to the sprite real area 23E17D "PltCopy": Same thing, for palettes 2911B9 "ScrCopy": Same thing, for scrolling The next order of business is to find how the sprites are encoded in the level design so I can splice them onto the maps. I suspect that the relevant address is at LvAddr+6, as it's the only piece of data there I haven't pinned down. Also if anyone plans to put this to use, I should make you aware of a few errors I've tended to make in case any persist. I often typo Es in addresses as 3s and more occasionally As as 4s; I blame l33tsp3ak clouding my mind. :/ RAM addresses might also be 2 higher than they should be if I neglected to correct the savestate offset. And occasionally I've calculated terminating addresses incorrectly. Sooooo that's it for now. Further bulletins as events warrant. I originally was doing all this with the goal of making maps for TSC and pinning the puzzle piece locations (which I learn is now already done), so whatever else I do/n't do will be fueled by random curiosity or requests. As for making a fullfledged hacker... maybe at some point, but it's pretty far down on the priority list, so don't hold your breath. :P Edit: A few more notes. I'm assuming that finding the level sprite encoding will also provide the palettes the game uses, in which case I can update the grayscale sprite sheet with colored ones. Also, while I certainly don't mind being given credit for rips, don't feel obligated to. As far as I'm concerned, it's the artists who made them, my contribution is secondary. Edit2: Here's the disassembly I've been using for research. Be warned, it's a 50MB text file, don't try to open it in Notepad; I use Visual Studio myself. I used an obscure NGPC-specific program and disassembled the whole thing indiscriminately, so large chunks of it are gibberish it tried to pull from data.