Author Topic: Spirte sheets  (Read 2480 times)

mrhaboobi

  • Hero Member
  • *****
  • Posts: 693
Spirte sheets
« on: January 21, 2010, 02:24:28 PM »
Hey Guys.

im dabbling in XNA and PSX coding, just getting my head general game concepts.

Wanted to use some PCE/TG16 games as a way to learn wondering if anyone knows of a good sprite resource for games like Legend of hero tonma, star soliders etc.. just wanting to re create the odd game and learn before making something totally original ( i cant draw for peanuts :) )..

hope someone might be able to help

greg
Looking for (MINT ONLY)
US Manual : Magical Chase, Shockman 
US Box : Turrican,  Soldier Blade, New Adventure Island, Neutopia II
Other : Sapphire OBI, Turbo Play Aug/Sept 90, April/May 92, Turbo Edge Spring 90

PC Engine Special Cards : Bomberman User Battle

Alydnes Super Grafx

Arkhan

  • Hero Member
  • *****
  • Posts: 14142
  • Fuck Elmer.
    • Incessant Negativity Software
Re: Spirte sheets
« Reply #1 on: January 22, 2010, 01:11:28 AM »
You could always load one of the games up in Mednafen, and look at the VRAM with the debugger:

alt+d and then alt+2

then press right to flip to the sprite page.... :)
[Fri 19:34]<nectarsis> been wanting to try that one for awhile now Ope
[Fri 19:33]<Opethian> l;ol huge dong

I'm a max level Forum Warrior.  I'm immortal.
If you're not ready to defend your claims, don't post em.

mrhaboobi

  • Hero Member
  • *****
  • Posts: 693
Re: Spirte sheets
« Reply #2 on: January 27, 2010, 06:49:07 AM »
Well gave your suggestion a go, and no luck, sprite sheet was a dark blue, nothing on it at all, even after scrolling down a long way .. :(  id love to get the arcade versions of the sprites..
Looking for (MINT ONLY)
US Manual : Magical Chase, Shockman 
US Box : Turrican,  Soldier Blade, New Adventure Island, Neutopia II
Other : Sapphire OBI, Turbo Play Aug/Sept 90, April/May 92, Turbo Edge Spring 90

PC Engine Special Cards : Bomberman User Battle

Alydnes Super Grafx

sunteam_paul

  • Hero Member
  • *****
  • Posts: 4732
Re: Spirte sheets
« Reply #3 on: January 27, 2010, 07:22:16 AM »
Hmm, you could always use magic engine and toggle the background off before taking screenshots.
The PC Engine Software Bible
Quote from: Tatsujin
I just felt in a hole!

mrhaboobi

  • Hero Member
  • *****
  • Posts: 693
Re: Spirte sheets
« Reply #4 on: January 27, 2010, 07:23:42 AM »
Yeah that would maybe work, i was hoping to avoid having to manually extract the sprites :(  might be the only option.. ive found some sheets on the net, but they are far from complete..
Looking for (MINT ONLY)
US Manual : Magical Chase, Shockman 
US Box : Turrican,  Soldier Blade, New Adventure Island, Neutopia II
Other : Sapphire OBI, Turbo Play Aug/Sept 90, April/May 92, Turbo Edge Spring 90

PC Engine Special Cards : Bomberman User Battle

Alydnes Super Grafx

Tom

  • Guest
Re: Spirte sheets
« Reply #5 on: January 27, 2010, 12:51:39 PM »
You're dabbling in XNA and PSX coding? Then you know a high level language like C already, I take it? One of your options is to build a utility to read save states. Thought you'd need to be able to pause and frame increment (and save state). Have the util rip the graphics with the right associated subpalette for the correct colors, for each save state instances. PCE sprites are easy because they are 16x16 cells and have to be laid out in a specific way in vram - which makes rebuilding the sprites from the organized cells very easy. Or you could parse the SAT and SATB in the save state and build out the sprites that way too. Pixel perfect, manual labor for slicing/dicing/transparent color removal, etc.
« Last Edit: January 27, 2010, 12:53:30 PM by Tom »

mrhaboobi

  • Hero Member
  • *****
  • Posts: 693
Re: Spirte sheets
« Reply #6 on: January 27, 2010, 12:54:49 PM »
i know c# and .net and dabbled in c, but not that experienced in c.    i might have thought someone would have done this, or perhaps i need to think about it myself.. If i knew the information about the position of graphics within the ram and the subpallet information then i could rip the sprites, 16x16 does make it very easy, i assume spirtes are built from multiple 16x16 sprites to build up background graphics etc?
Looking for (MINT ONLY)
US Manual : Magical Chase, Shockman 
US Box : Turrican,  Soldier Blade, New Adventure Island, Neutopia II
Other : Sapphire OBI, Turbo Play Aug/Sept 90, April/May 92, Turbo Edge Spring 90

PC Engine Special Cards : Bomberman User Battle

Alydnes Super Grafx

Tom

  • Guest
Re: Spirte sheets
« Reply #7 on: January 27, 2010, 02:40:47 PM »
i know c# and .net and dabbled in c, but not that experienced in c.    i might have thought someone would have done this, or perhaps i need to think about it myself.. If i knew the information about the position of graphics within the ram and the subpallet information then i could rip the sprites, 16x16 does make it very easy, i assume spirtes are built from multiple 16x16 sprites to build up background graphics etc?

 Well background graphics use 8x8 cells. Completely different from the sprite format too, not just size. In your case, that's a good thing (because you can visually identify what section of vram are for sprites and what are for tiles). But yes, all sprites are made from 16x16 cells. An entry in the SAT is defined in size by Xn and Yn cells. I.e. A 32x16 sprite is 2x1 cell in the SAT(B).

 Do a straight conversion of all 64k vram to bg tile sheet, sprite cell sheet, then parse the SAT/SATB (both) and build a sheet from that. On the tile and sprite bitmaps of vram, maybe even build one set per subpalette entry. That way the first two layers (in all 32 subpalettes, but only 16 for tiles and a separate 16 for sprites), you can visually identify any sprites cells of animation that stay in vram. Sometimes it's easier to rip just those with them showing on the correct subpalette. Other times it's best to uses the parse SAT bitmap layer. Hell, maybe even provide X/Y coords for each SAT entry on another layer. Because some sprite animations are a conjunction of layered sprites and will still need manual cut/pasting - if you want to capture them as a single frame of pseudo sprite animation. Parsing the SAT also allows to you turn off any sprite at any time, or change their X/Y coords if you have overlapping sprites messin up your clean rip. Anyway, whatever's easiest for you to work with.

 As for the parsing the save states, mednafen is compressed with 7z - so they need to be decompressed. ME has an option for compression or not (or last I checked it did). And for hardware, pce's graphics layer and structure is very simplistic in design. Nothing convoluted or tricky (unless you've never dealt with planar pixels before). all sprites have a special bounding address they must be at and same with tiles. I.e. the BAT (tilemap layer) can only point to vram in tile length segments. You can't point to segments less than 32 bytes - because you take the BAT entry, shift the 16bit value to the left 4 places. This gives you all 0's in the lower end. A la every 32bytes. SAT is similar, but wider than 32bytes. The BAT is always at $0000 of vram and the SATB is always pointed by the SATB pointer reg. Find that reg in the save state, and you'll find the location of the SATB in vram ($0000-7ffff but these are WORDs addresses (unsigned short)). The SAT itself is somewhere in the save state too. Make sure to build out both though (SAT and SATB), because one can be out of sync with the other depending on the point in which the save state is made and any given game. For technical information of what the SAT(B) and BAT entries look like/do - take a look at the Magickit assembler documents (sprites.txt and vdc.txt are the files you're looking for).

 There's no file for BAT and tiles, so I'll explain that here. The BAT is the tilemap. It *always* starts at address $0000. You can't change it. That's easy enough. How you look at the tilemap depends on how the MAWR register is set. This tells you how many BAT entries to read in a row, before skipping down to the next row. That's important because if you assume 32 entries when it's set to 64 wide entries by the MAWR reg, it will look wrong. You could do a build/bitmap of the whole BG layer like this. You can also apply X/Y coords from the BXR and BYR regs of the VDC, but most cases this isn't practical because games are usually changing it mid screen - and depending on where the save state stops at, could anyway and give a 'false' location (like a menu bar at the top or bottom of the screen). The BAT entry is one WORD (unsigned short). The lower 12bits are the tile number. That number * 32 gives the byte offset (or num *16 for WORD offset). Remember to AND off the top 4 bits before doing that <_<; The upper 4bits is the palette number for the tile. So SubPalette=BAT_Entry>>12. When you get a pixel from a tile, and the value is *not* zero, then multiply the subpalette by 16, then add the tile color number: if (tile_color !=0) { index=(SubPalette*16)+tile_color; } else { index=0; }. Since this is a tile, you use that index to point into the array of WORDs in palette ram. There are 512 WORD entries in palette ram, but only the first 256 entries are for tiles. So _RGB=PaletteRam[index*2]. The 16bit entry is as follows: xxxxxxxGGGRRRBBB. X= don't care. To get the correct color on a PC display, take each three bits and multiply them by 36. Don't forget to shift right on RRR and GGG (all the way to the right) before multiplying. This'll get you the 8bit for each R/G/B for PC compatible display. And lastly, the tileformat itself. A tile is 8x8 pixels. Each pixel is 4bits. So colors 0 to 15 (remember color 0 in any subpalette is index 0). The 4bits are stored in planar format. And to make things complicated, it's called composite planar format (i.e. two 2bit tiles make a 4bit complete tile).

 If a tile is at memory address 0x800 (WORD offset):

First two planes: (0x800 offset)
 (D0->D15)
row 0: 0000000011111111
row 1: 0000000011111111
row 2: 0000000011111111
etc

(+0x10 WORD offset from base tile address 0x800 = 0x810)
last two planes:
 (D0->D15)
row 0: 2222222233333333
row 1: 2222222233333333
row 2: 2222222233333333
etc
 
 0=plane 0. 1=plane 1. 2= plane 2. 3=plane 3. The plane is the pixel in a 1bit field. Like all binary, bits in higher places(to the left) hold larger values than bits in smaller places (to the right).

 4bit pixel  = D3:D2:D1:D0. Or better put: 4bit pixel  = P3:P2:P1:P0. (P=plane bit)

 Eight rows tall makes 8x8 pixels. Your job is to collect all 4 bits and shift them into the correct places. So a 4bit tile color for pixel 0 of row 0 is... D3=row0:D8 2nd offset,D2=row0:D0, D1=row0:D8 1st offset,D0=row0:D0 1st offset. So for row 1, pixel 7:

base offset:
row 0: 0000000011111111
row 1: 000000(D0)0111111(D1)1
row 2: 0000000011111111

base offset+0x10 WORDs:
row 0: 2222222233333333
row 1: 222222(D2)2333333(D3)3
row 2: 2222222233333333

 As you already know, x86 is little endian. So is the VDC. So that's the good news. But that doesn't mean it's necessarily stored in little endian or even in WORDs. Might be completely in unsigned INT format. Really depends on the emulator. Also remember, you're not randomly access pixels here. You're reading them out in sequential fashion for conversion. Which makes it easier, so you should store each plane of each row into four variables. Then shift out each variable left once for each sequential pixel for that row. Code would look similar to this:

 int plane0;
 int plane1;
 int plane2;
 int plane3;

 // at some point ZERO out all four planes before reading in a byte

 // Also remember - little endian. So read the WORD as two reversed bytes
 fread(&plane1,1,1,infile);
 fread(&plane0,1,1,infile);

 fseek(TileAddress+0x20,SEEK_SET);
 fread(&plane3,1,1,infile);
 fread(&plane2,1,1,infile);

 for(int i;i<8;i++)
{
  TileIndex[n+i]=0;
  plane0<<=1;
  plane1<<=1;
  plane2<<=1;
  plane3<<=1;
  TileIndex[n+i] |= (plane0 & 0x0100) ? 0x01 | 0x00;
  TileIndex[n+i] |= (plane0 & 0x0100) ? 0x02 | 0x00;
  TileIndex[n+i] |= (plane0 & 0x0100) ? 0x04 | 0x00;
  TileIndex[n+i] |= (plane0 & 0x0100) ? 0x08 | 0x00;
}

 The logic for var3 = (var0) ? var1 | var2 is short hand C for:

 If(plane0 && 0x0100)
 { TileIndex[n+i] = TileIndex[n+i] | 0x01; }
 else { TileIndex[n+i] = TileIndex[n+i] | 0x00; }

 Of course my example above only reads/converts 1 row of 8pixels. You'd need another outer loop and file stream reposition logic to read in the rest of the rows are you go down the 8x8 tiles. So n=_row*8 in TileIndex[n+i].

 Thankfully, sprites aren't stored in this 'composite' format. But they still planar. Stacked planes just like above, just not interleaved. And in rows of 16 pixels, not 8. So you'll have to read a WORD (short) in at a time, not a byte. If vram *is* stored in INT format in the save state, then I recommend just reading in the 64k to a 32k SHORT array (be sure to convert it to proper SHORT format first). That'll get rid of those pesky I/O handlers and directly use indexes.

 I know you said sprites, but sometimes the BG layer is used as a large sprite in games. So it's good to know. Hope this helps. It's good to have a customer build like this for ripping, because it gives you more control. But, some people are happy just to grab every frame with layers disabled and manually cut/paste.
« Last Edit: January 27, 2010, 02:48:48 PM by Tom »

mrhaboobi

  • Hero Member
  • *****
  • Posts: 693
Re: Spirte sheets
« Reply #8 on: January 27, 2010, 02:44:38 PM »
man awesome reply :) will re read and take it all in and see where i get to :)..
Looking for (MINT ONLY)
US Manual : Magical Chase, Shockman 
US Box : Turrican,  Soldier Blade, New Adventure Island, Neutopia II
Other : Sapphire OBI, Turbo Play Aug/Sept 90, April/May 92, Turbo Edge Spring 90

PC Engine Special Cards : Bomberman User Battle

Alydnes Super Grafx

TheOldMan

  • Hero Member
  • *****
  • Posts: 958
Re: Spirte sheets
« Reply #9 on: January 27, 2010, 02:56:48 PM »
So Tom: Since fonts are stored in the lower half of VRAM, does that imply that they are stored in the composite planer format, rather than the standard 1-bit, 1-plane format ? No wonder my sprites don't display right when I stuff them into unused font characters.....

Tom

  • Guest
Re: Spirte sheets
« Reply #10 on: January 27, 2010, 05:16:44 PM »
So Tom: Since fonts are stored in the lower half of VRAM, does that imply that they are stored in the composite planer format, rather than the standard 1-bit, 1-plane format ? No wonder my sprites don't display right when I stuff them into unused font characters.....

 Lower half as in the lower 64kbyte of the 128kbyte addressing? Or lower half as in the 32kbyte of the 64kbyte attached to the VDC? Both BAT entries and SAT entries give you a full 128kbyte (64kWORD) addressable range, but only the lower 64kbyte (32kWORD) is valid. Anything in the upper half is open bus (phantom vram fetch garbage). So don't access anything above $7fff (WORDs).

 But for the full 64kbyte(32kword) range, you can technically put tiles and sprites anywhere, as long as they are aligned to that segment. If you put a tile at $0808(word), It'll take up 0x10 words ending at $817. But the BAT can only do addresses in 0x10 word segments. Which means $80 and $81 are address values $800 and $810. And since you write a tile to $808, it'll be interpreted incorrectly. So the BAT can never incorrect access out of bounds of a tile segment of vram, you yourself can manually put a tile in the wrong address range. A BAT entry can point to any 32kword address range of your complete installed vram capacity (even in the BAT area, and even in the SATB list), but only in 32byte/0x10word segments.

 Sprites are very similar. A SATB entry can point to anywhere in the installed vram range, but there are much bigger limitations. The cell size determines how the sprites fetched from vram. Ohh(!)... you can select offsets in 16x16 cells sizes (0x40words) but when you use cells sizes larger than 16x16, you need to use a different address calculation. First off, let me say that SAT entries allow you to address/point to vram in half 16x16 cell increments. So normally, you would think you would have to use +0x40words increments/offsets for vram (16x16=256 x 4bits = 1024bits / 8bits(byte) = 128bytes or 80words or 0x40words in hex). But this isn't the case. The SAT allows you to address sprites in +0x20 increments/offsets. To point to what could the middle of a 16x16 sprite. Tiles cannot do this. The reason why you can do this is simple: the VDC has a 2bit pixel index mode besides the normal 4bit pixel index. You can independently turn this on/off from the BG. Only one game does this: Fighting Run. Either to save on graphics for the sprite frames, or to upload all animations to vram which effectively gives them double the space but at the cost of 1/4 the color count per SAT entry (you can still assign any one of sixteen subpalettes). And it just so happens that a 2bpp 16x16 cell sprite is 0x20words.

 So when dealing with sprite "numbers" in the SAT, please leave the lower bit set to 0 just to play it safe (use sprite numbers in multiples of 2 instead of 1). Now, here comes the tricky part. The X/Y cell size also effects/limits where/how you can organize your sprite data in memory. If you are using a 32x16 sprite, or 2x1 cell, you can't use any ol' 0x20 or 0x40 address offset. You have to be aligned to 0x80! If you have such a sprite setup in vram and it doesn't fall on this 0x80 segment, then it doesn't show one if the two cells (or sometimes weird garbage or some other undesirable effect). The reason is that the VDC is really fast and fetches all these sprite cells in the tiny spaced called hblank. That's an incredible amount of data to fetch in such a short amount of time, even more incredible for '87. Hell, even the SNES and Genesis doesn't do that (they take the whole scanline to fetch all the pixels, but use a line buffer so the next scanline can show what they fetched on the previous line. But this is also why you can't access vram during active display on either console. Or are severely limited to a small amount of free vram read/write slots). Anyway, in order to fetch so much sprite data - they took advantage of addressing in logic. Even addresses or segments are easier to calculate and reduce logic/gates.

 With that said, a 2x1 cell sprite must be aligned to a 0x80word segmented address. A 2x2 (32x32) uses 0x100word addressing. A 2x4 (32x64) uses a 0x200word aligned address. To get the sprite number, you divide the real (word) address of the aligned sprite by 0x20. For instance, 32x32 sprites can only use sprite numbers being a multiple of 8. A 32x32 sprite at address 0x5000words is sprite #0x280. A 32x32 sprite at address 0x5100words is sprite #0x288. 2x2 cell sprite at vram address is 0x5200words is sprite pattern number #0x290. A 32x16 sprite can only use numbers in increments of +4. And 16x16 sprites can only use increments of +2. Finally, a 32x64 sprite uses increments of +0x10 (or 16). I honestly don't even remember how the VDC acts when you have odd offset with larger sprites (lowest bit set), but I wouldn't recommend doing that anyway. Misaligned sprites are never a good thing. It's best to keep them aligned by first 0x40 cells size, then the multiple of that depending on the layout.

 A quick cell matrix chart:

Code: [Select]
1x1 = 0
2x1 = 0|1
2x2 = 0|1|2|3
2x4 = 0|1|2|3|4|5|6|7

1x2 = 0|2
1x4 = 0|2|4|6

 What they look like in relation to view:

Code: [Select]
1x1 = 0
 2x1 = 0|1

 2x2 = 0|1
       2|3
         
 2x4 = 0|1
       2|3
       4|5
       6|7

 1x2 = 0
       2

 1x4 = 0
       2
       4
       6


 I don't remember of you can use odd setups for slim vertical sprites. I'll have to test on the real hardware again to see. Anyway, besides the sprite cell and tile cells being incompatible with each other, then can be anywhere in vram - but with sprites having a complex alignment concerns.

 :)

TheOldMan

  • Hero Member
  • *****
  • Posts: 958
Re: Spirte sheets
« Reply #11 on: February 17, 2010, 02:50:53 AM »
Ok Tom, after many, many read-throughs, I finally think I've got it. Well, as least
what I'm doing appears to work :-)
I originally meant the lower 16K of the 32K addessable. But you are right; BAT tiles
can be located anywhere in the 32K space. Inside a font *is* possible, but....
What I missed originally was that BAT entries (call them cells for short) are only
sort of set up like tiles. Where tiles have 16 pixels per word, BAT cells only have 8.
And that would not be a problem, except the bit planes for tiles are 8 words apart,
where the bit planes for cells are *packed* into single words. So the code I had to
draw into a sprite area just would not work when drawing into a BAT cell.
Once I understood that I had to handle 2 bit planes at once for a BAT cell, I did some
rearranging and got it so I could draw directly into a BAT cell (and it's easier
than trying to draw into a sprite).
Unfortunately, because of that simple fact, it is pointless to try and embed a sprite
in the font; you would have to re-arrange all the data to get it to work. Same thing
applies to trying to use a sprite sheet to draw text characters; the data gets loaded in
the wrong format, and would need re-arranged.
What does work (and laugh at me if you will) is to load the font, and then load the sprites
seperately over top of the area of the font you want to replace. Saves space in VRAM
(which was already tight due to how the line stuff works), and things are in the right
format. Maybe I'll update the cycle game to do that some day :-) Then there might be room
for better explosions (lol).
Thanks for the explanation. It really did help.