|
TTRlovesMittens Random nobody Level: 4 Posts: 1/3 EXP: 257 For next: 22 Since: 08-04-15 Since last post: 6.7 years Last activity: 6.6 years |
|
| VERY VERY LONG DETAILED POST. tl;dr I figured out how sprites are stored in the memory, and probably how they're storied in the CHD, just gotta search the CHD
Hey guys! First post here with something that could get actual sprite dumping, and with some more elbow grease, full ripping done! However, it's a very long and fairly detailed post. I am also posting on my iPhone because my internet situation is shaky right now, so the formatting is going to be lackluster. For example, I can't do embedded images that easily right now. For now, I've uploaded all my example images to imgur, and they're organized and titled in a way that should be simple to follow along with this post. Images: http://imgur.com/a/VZAfw The first image is what I like to call the "table of workspaces." This table, located around 887f8320 is the index of spaces in the memory that the game directly stores sprites on a per-set basis. For example, Jago's pointer in this table leads close by to 887f8360, while Fulgore is stored at 887f8B60. All characters will occupy the same space regardless which palette or player side they occupy. So, if two Jagos are playing, the game will load "Jago" once and apply his palettes to the same set. This is standard. So, basically you get the pointers at the 320 table depending on which character, then you get a second table with a list of addresses in 4 byte chunks that lead elsewhere in the memory. Each one of these is a "workspace" where the game will dump sprite data. These tables are always in the same place regardless of the stage, characters chosen, etc. How this table works is next. The second image is set up with two callings of Jago. In the red boxes are bytes that I like to think of as the "Action" number. Each action number calls a new set of sprites. The first box pointing the Player 1 is the Player 1 Action, and Player Two is below. In this screenshot I froze the debugger to change this value. I changed P1 to Action 3F and P2 to action 02. The results of this change can be seen in the post picture, where I let Mame run to the next vblank. The sprites have now changed to a new set! If you let this go on, the game will play the ANIMATION of the move that was going on before, in this case neutral, but use the sprite set specificed in the bytes I just edited. This can lead to some wonky animations, but that isn't the point! How does this information help us? Well, this way: if you know the specific Action number of a move you want, you can actually find where the game will dump that action's sprites. For example, I changed Jago P2's action in the next image to his state where he dies to a disembowl ultimate (for example, his own sword ultimate). I chose it because that state stops and suspends animation when it finishes. How I got it to play without ending the game or darkening the screen? There's another two byte values that I haven't discussed. They will auto trigger any animation, in proper form, on demand. However, this is something I'll document later, but will be useful to future sprite ripping tools. Probably I guess. Anyway, this Action number is AD. What I did is I then when back to the sprite table in the beginning, followed the 320 pointer to the 360 Jago workspace table. Now, you multiply the AD value IN HEX by 4. This gives you the table offset that will lead you to the entry you need. So, ADh x 04h = 2B4h. 360h + 2B4h = 614. This leads to the second image, where I go to the 614 value and, as expected, you get a cozy address pointer leading to the area where the sprites for action AD are being dumped! So, the next image I changed things to make it simpler to A) find the damn sprites and B) to better show details of the format. This consists of the same character, Jago (he's my debugging gimp), in the neutral Action (Action 00). Following the paragraphs before this. Both Jagos will pull from the same data table. You will then be pointed to the 360 table of all of Jago's Actions. 00 times 4 is still 00, so there is no offset for neutral Action, this leads us to the pointer 880D8000. This is where the game is dumping this action's sprites. If you go to this address in the memory, you get my next image. This is where the good stuff happens. In the first Red box of this image is the specifier that shows, in 00XX00YY format, what max sprite amount is being DISPLAYED (XX) and what Action is being specified (YY). The game always loads all the sprites needed, so editing the display number lower means that all sprites after that value won't display, and editing it to load more does nothing. There's some kind of independent check. Editing YY does nothing. I think. The next set of values, the Aqua box, is the offsets where each new sprite begins. The first value, 00, is some weird thing. It leads to another thing somewhere, and editing the XX in the previous 4 byte word to 00 (this value) cancels out the sprite. So 00 leads somewhere I don't understand. The next 4 bytes though is an actual sprite location. This continues until the Action is out of sprites. The Yellow box is the BEGINNING OF A SPRITE. If you take the 880D8000 and add 5C to it, it points to this box as the first sprite of the set. This box denotes WHERE it'll start placing it on the screen in AAAABBBB format. Messing with these values changes where the sprite is on an (X,Y) plane, and you only really need to edit them like 00AA00BB, editing them as full 2 byte sets will move it crazy distances. The Magenta box is the DIMENSIONS of the sprite. In this example, the sprite detailed is 007F pixels tall, and 004F pixels wide. Editing this can be buggy. Editing the height downwards clips the sprite, editing it higher crashes the game, and editing the width does nothing discernible until you corrupt other values. This is straight forward. The Orange box is where things get WEIRD. If you edited the width, editing the first byte of this box will make the line continue on. It will continue to dump values until it's reached the value you've specified. If you make the sprite with FFFF and then edit this first byte to FF, the game will dump the pixels of the sprite in a single line but NOT crash. The next byte is a X offset that shows how far away from the "edge" of the sprite this line is. Future values that function this same way are also marked by red dots. The next byte is weird, editing it reveals pixels, but it's different than how it usually works. This byte controls 2? Pixels instead of just one like the others. I have a feeling this tweaks the transparency at the end of the line, or ALL lines. The last byte is some kind of control. If you edit it, it'll crash the game. I call these "Control" bytes. I've labeled the first few lines of this sprite this way: if a control byte happens without an X offset byte, I mark it with an orange dot. If it occurs WITH an offset byte, I box them and red dot the offset byte. The last box I mark, with the 0B control, should have a red dot on the second byte. That's also an offset. The rest are uncompressed, coding pixels at 1 byte per pixel at in a 00-FF or 255 color palette. The game stores them line by line like this, offsetting in the X direction when it needs to. This is exactly why we couldn't find it using tile viewers. It does NOT store then like we usually store sprites. It stores them line by line, dropping the line whenever full transparent pixels are there. Isn't that dumb? I think it's really dumb. Don't believe me? Avoiding corrupting the control bytes or the offset bytes, I corrupted all the pixels to return a CC value for each byte that codes a pixel. This means the fills that pixel with color number 205/255. This is where the benefit of having two differently paletted Jagos comes in. CC codes for a dark gray on palette 01 Jago (black one), but a lighter gray on the 03 palette Jago (white one). All pixels up to that point are accounted for visually, so the control bytes can't be coding for pixels like the normal ones, and the offset is easily seen, if you mess it up you'll have a like of pixels jutting out. So, this leaves my investigations at a few questions I can't answer: The "control bytes" in the sprite data seem random to me. Why do they seem to show up irregularly? When I think that I figured it out, it just doesn't happen. The occur at the end of lines, but sometimes not. They occur with offset marks, but sometimes not. They're bizarre and make no sense Line breaks, how does the game know when to break the line and move up? The game prints out the sprite from left to right, bottom to top. How does it know when to move up? If someone can investigate these two questions, a sprite dumping hook can be coded to do the following routine: Load an animation by forcing a new animation using the animation force byte. Find the action related to this animation by reading the action byte. Go to the sprite workspace table related to the character being animated, add the offset related to the action, and find the workspace where this action is. Go to the workspace, read the max frame, sprite location table, and begin dumping sprite by reading the data according to its specifications and palette info specified by the user/game Load the next animation, repeat until all animations/actions have been dumped. I hope this gets people working on dumping the sprites, the ones that are out there aren't that great, however I feel that the sprites in the CHD are probably stored this way too, if the background BMPs are uncompressed as well. It's just the game stores sprite funny with this line by line system instead of square tiles, which means tile viewers are useless. If we look hard enough and figure out the format, we can either look for sprite data and rip it using these specifications, or force the game to load random areas of sprites hoping to load something they forgot to delete and rip them/get them to display in game! |

