2012-09-06 08:35:45 +00:00
//http://wiki.superfamicom.org/snes/show/Backgrounds
2012-09-08 20:03:04 +00:00
//http://board.zsnes.com/phpBB3/viewtopic.php?f=10&t=13029&start=75 yoshis island offset per tile demos. and other demos of advanced modes
//but we wont worry about offset per tile modes here.
2012-09-06 08:35:45 +00:00
2012-09-24 07:46:54 +00:00
//helpful detailed reg list
//http://wiki.superfamicom.org/snes/show/Registers
2012-09-06 08:35:45 +00:00
using System ;
2013-11-13 23:36:21 +00:00
namespace BizHawk.Emulation.Cores.Nintendo.SNES
2012-09-06 08:35:45 +00:00
{
2012-12-25 20:36:04 +00:00
public unsafe class SNESGraphicsDecoder : IDisposable
2012-09-06 08:35:45 +00:00
{
2012-10-01 00:18:33 +00:00
public class PaletteSelection
{
public PaletteSelection ( ) { }
public PaletteSelection ( int start , int size )
{
this . start = start ;
this . size = size ;
}
public int start , size ;
}
2012-09-06 08:35:45 +00:00
public struct Dimensions
{
2012-09-08 20:03:04 +00:00
public Dimensions ( int w , int h ) { Width = w ; Height = h ; }
public int Width , Height ;
2012-09-06 08:35:45 +00:00
public override string ToString ( )
{
return string . Format ( "{0}x{1}" , Width , Height ) ;
}
}
public enum ScreenSize
{
2012-10-02 09:28:57 +00:00
AAAA_32x32 = 0 , ABAB_64x32 = 1 , AABB_32x64 = 2 , ABCD_64x64 = 3 ,
Hacky_1x1 = 4 ,
2012-09-06 08:35:45 +00:00
}
public static Dimensions SizeInTilesForBGSize ( ScreenSize size )
2012-09-08 20:03:04 +00:00
{
2012-10-02 09:28:57 +00:00
if ( size = = ScreenSize . Hacky_1x1 ) return new Dimensions ( 1 , 1 ) ;
2012-09-08 20:03:04 +00:00
var ret = SizeInBlocksForBGSize ( size ) ;
ret . Width * = 32 ;
ret . Height * = 32 ;
return ret ;
}
2012-11-23 09:10:18 +00:00
public static Dimensions [ , ] ObjSizes = new Dimensions [ , ]
{
{ new Dimensions ( 8 , 8 ) , new Dimensions ( 16 , 16 ) } ,
{ new Dimensions ( 8 , 8 ) , new Dimensions ( 32 , 32 ) } ,
{ new Dimensions ( 8 , 8 ) , new Dimensions ( 64 , 64 ) } ,
{ new Dimensions ( 16 , 16 ) , new Dimensions ( 32 , 32 ) } ,
{ new Dimensions ( 16 , 16 ) , new Dimensions ( 64 , 64 ) } ,
{ new Dimensions ( 32 , 32 ) , new Dimensions ( 64 , 64 ) } ,
{ new Dimensions ( 16 , 32 ) , new Dimensions ( 32 , 64 ) } ,
{ new Dimensions ( 16 , 32 ) , new Dimensions ( 32 , 32 ) }
} ;
2012-09-08 20:03:04 +00:00
public static Dimensions SizeInBlocksForBGSize ( ScreenSize size )
2012-09-06 08:35:45 +00:00
{
switch ( size )
{
2012-09-08 20:03:04 +00:00
case ScreenSize . AAAA_32x32 : return new Dimensions ( 1 , 1 ) ;
case ScreenSize . ABAB_64x32 : return new Dimensions ( 2 , 1 ) ;
case ScreenSize . AABB_32x64 : return new Dimensions ( 1 , 2 ) ;
case ScreenSize . ABCD_64x64 : return new Dimensions ( 2 , 2 ) ;
2012-09-06 08:35:45 +00:00
default : throw new Exception ( ) ;
}
}
2012-12-02 02:51:30 +00:00
public enum BGMode
{
2012-12-02 23:58:20 +00:00
Unavailable , Text , Mode7 , Mode7Ext , Mode7DC , OBJ
2012-12-02 02:51:30 +00:00
}
2012-12-02 08:00:59 +00:00
/// <summary>
/// is a BGMode a mode7 type (mode7, mode7ext, mode7DC)
/// </summary>
public static bool BGModeIsMode7Type ( BGMode BGMode ) { return BGMode = = SNESGraphicsDecoder . BGMode . Mode7 | | BGMode = = SNESGraphicsDecoder . BGMode . Mode7DC | | BGMode = = SNESGraphicsDecoder . BGMode . Mode7Ext ; }
2012-12-02 02:51:30 +00:00
/// <summary>
/// this class is not 'smart' - it wont recompute values for you. it's meant to be read only (we should find some way to protect write access to make that clear)
/// </summary>
2012-09-06 08:35:45 +00:00
public class BGInfo
{
2012-12-02 02:51:30 +00:00
public BGInfo ( int num )
{
}
/// <summary>
/// what type of BG is it?
/// </summary>
public BGMode BGMode ;
/// <summary>
/// is this BGMode a mode7 type (mode7, mode7ext, mode7DC)
/// </summary>
2012-12-02 08:00:59 +00:00
public bool BGModeIsMode7Type { get { return BGModeIsMode7Type ( BGMode ) ; } }
2012-12-02 02:51:30 +00:00
2012-09-09 19:02:13 +00:00
/// <summary>
/// Is the layer even enabled?
/// </summary>
public bool Enabled { get { return Bpp ! = 0 ; } }
2012-09-06 08:35:45 +00:00
/// <summary>
/// screen and tiledata register values
/// </summary>
public int SCADDR , TDADDR ;
/// <summary>
/// SCSIZE register
/// </summary>
public int SCSIZE ;
2012-10-01 00:18:33 +00:00
/// <summary>
/// which Mode this BG came from
/// </summary>
public int Mode ;
2012-09-06 08:35:45 +00:00
/// <summary>
/// the address of the screen data
/// </summary>
2012-12-02 02:51:30 +00:00
public int ScreenAddr ;
2012-09-06 08:35:45 +00:00
/// <summary>
/// the address of the tile data
/// </summary>
2012-12-02 02:51:30 +00:00
public int TiledataAddr ;
2012-09-06 08:35:45 +00:00
/// <summary>
/// Screen size (shape, really.)
/// </summary>
public ScreenSize ScreenSize { get { return ( ScreenSize ) SCSIZE ; } }
/// <summary>
/// the BPP of the BG, as derived from the current mode
/// </summary>
public int Bpp ;
2012-09-08 20:03:04 +00:00
/// <summary>
/// value of the tilesize register; 1 implies 16x16 tiles
/// </summary>
public int TILESIZE ;
2012-11-26 06:30:30 +00:00
/// <summary>
/// enabled on MAIN Screen via $212C
/// </summary>
public bool MainEnabled ;
/// <summary>
/// enabled on SUB Screen via $212D
/// </summary>
public bool SubEnabled ;
/// <summary>
/// enabled for color math via $2131
/// </summary>
public bool MathEnabled ;
2012-12-03 18:59:08 +00:00
/// <summary>
/// scroll registers
/// </summary>
public int HOFS , VOFS ;
2012-09-08 20:03:04 +00:00
/// <summary>
/// TileSize; 8 or 16
/// </summary>
public int TileSize { get { return TILESIZE = = 1 ? 16 : 8 ; } }
2012-09-06 08:35:45 +00:00
/// <summary>
/// The size of the layer, in tiles
/// </summary>
2012-12-02 02:51:30 +00:00
public Dimensions ScreenSizeInTiles
{
get
{
if ( BGMode = = SNESGraphicsDecoder . BGMode . Text )
return SizeInTilesForBGSize ( ScreenSize ) ;
else return new Dimensions ( 128 , 128 ) ;
}
}
2012-09-08 20:03:04 +00:00
/// <summary>
/// The size of the layer, in pixels. This has factored in the selection of 8x8 or 16x16 tiles
/// </summary>
public Dimensions ScreenSizeInPixels
{
get
{
return new Dimensions ( ScreenSizeInTiles . Width * TileSize , ScreenSizeInTiles . Height * TileSize ) ;
}
}
2012-10-01 00:18:33 +00:00
/// <summary>
/// returns information about what colors could possibly be used for this bg
/// </summary>
public PaletteSelection PaletteSelection ;
2012-09-06 08:35:45 +00:00
}
public class BGInfos
{
2012-12-02 02:51:30 +00:00
BGInfo [ ] bgs = new BGInfo [ 4 ] { new BGInfo ( 1 ) , new BGInfo ( 2 ) , new BGInfo ( 3 ) , new BGInfo ( 4 ) } ;
2012-09-06 08:35:45 +00:00
public BGInfo BG1 { get { return bgs [ 0 ] ; } }
public BGInfo BG2 { get { return bgs [ 1 ] ; } }
public BGInfo BG3 { get { return bgs [ 2 ] ; } }
public BGInfo BG4 { get { return bgs [ 3 ] ; } }
public BGInfo this [ int index ] { get { return bgs [ index - 1 ] ; } }
}
public class ModeInfo
{
/// <summary>
/// the mode number, i.e. Mode 7
/// </summary>
public int MODE ;
}
2012-11-23 23:44:45 +00:00
public class OAMInfo
{
public int Index { private set ; get ; }
public int X { private set ; get ; }
public int Y { private set ; get ; }
public int Tile { private set ; get ; }
2012-12-03 07:50:23 +00:00
public int Name { private set ; get ; }
2012-11-23 23:44:45 +00:00
public int Table { private set ; get ; }
public int Palette { private set ; get ; }
public int Priority { private set ; get ; }
public bool VFlip { private set ; get ; }
public bool HFlip { private set ; get ; }
public int Size { private set ; get ; }
2012-12-03 07:50:23 +00:00
/// <summary>
/// tiledata address
/// </summary>
public int Address { private set ; get ; }
public OAMInfo ( SNESGraphicsDecoder dec , ScreenInfo si , int num )
2012-11-23 23:44:45 +00:00
{
Index = num ;
int lowaddr = num * 4 ;
X = dec . oam [ lowaddr + + ] ;
Y = dec . oam [ lowaddr + + ] ;
2012-12-03 07:50:23 +00:00
Name = dec . oam [ lowaddr + + ] ;
2012-11-23 23:44:45 +00:00
Table = dec . oam [ lowaddr ] & 1 ;
Palette = ( dec . oam [ lowaddr ] > > 1 ) & 7 ;
Priority = ( dec . oam [ lowaddr ] > > 4 ) & 3 ;
HFlip = ( ( dec . oam [ lowaddr ] > > 6 ) & 1 ) = = 1 ;
VFlip = ( ( dec . oam [ lowaddr ] > > 7 ) & 1 ) = = 1 ;
int highaddr = num / 4 ;
int shift = ( num % 4 ) * 2 ;
int high = dec . oam [ 512 + highaddr ] ;
high > > = shift ;
int x = high & 1 ;
high > > = 1 ;
Size = high & 1 ;
2012-12-03 17:57:19 +00:00
X | = ( x < < 8 ) ;
2012-11-23 23:44:45 +00:00
X = ( X < < 23 ) > > 23 ;
2012-12-03 07:50:23 +00:00
Tile = Table * 256 + Name ;
Address = 32 * Tile ;
if ( Tile < 256 )
Address + = si . OBJTable0Addr ;
else
Address + = si . OBJTable1Addr - ( 256 * 32 ) ;
Address & = 0xFFFF ;
2012-11-23 23:44:45 +00:00
}
}
2012-09-06 08:35:45 +00:00
public class ScreenInfo
{
2012-12-03 07:50:23 +00:00
public Dimensions ObjSizeBounds ;
public Dimensions ObjSizeBoundsSquare ;
2012-09-06 08:35:45 +00:00
public BGInfos BG = new BGInfos ( ) ;
public ModeInfo Mode = new ModeInfo ( ) ;
2012-09-24 06:47:34 +00:00
2012-11-26 06:30:30 +00:00
public bool Mode1_BG3_Priority { private set ; get ; }
2012-09-24 06:47:34 +00:00
public bool SETINI_Mode7ExtBG { private set ; get ; }
public bool SETINI_HiRes { private set ; get ; }
public bool SETINI_Overscan { private set ; get ; }
public bool SETINI_ObjInterlace { private set ; get ; }
public bool SETINI_ScreenInterlace { private set ; get ; }
2012-09-24 07:46:54 +00:00
public int CGWSEL_ColorMask { private set ; get ; }
public int CGWSEL_ColorSubMask { private set ; get ; }
public int CGWSEL_AddSubMode { private set ; get ; }
public bool CGWSEL_DirectColor { private set ; get ; }
2012-11-26 06:30:30 +00:00
public int CGADSUB_AddSub { private set ; get ; }
public bool CGADSUB_Half { private set ; get ; }
2012-09-24 07:46:54 +00:00
2012-11-23 09:10:18 +00:00
public int OBSEL_Size { private set ; get ; }
public int OBSEL_NameSel { private set ; get ; }
public int OBSEL_NameBase { private set ; get ; }
public int OBJTable0Addr { private set ; get ; }
public int OBJTable1Addr { private set ; get ; }
2012-11-26 06:30:30 +00:00
public bool OBJ_MainEnabled { private set ; get ; }
public bool OBJ_SubEnabled { private set ; get ; }
public bool OBJ_MathEnabled { private set ; get ; }
public bool BK_MathEnabled { private set ; get ; }
2012-12-03 18:59:08 +00:00
public int M7HOFS { private set ; get ; }
public int M7VOFS { private set ; get ; }
public int M7A { private set ; get ; }
public int M7B { private set ; get ; }
public int M7C { private set ; get ; }
public int M7D { private set ; get ; }
public int M7X { private set ; get ; }
public int M7Y { private set ; get ; }
public int M7SEL_REPEAT { private set ; get ; }
public bool M7SEL_HFLIP { private set ; get ; }
public bool M7SEL_VFLIP { private set ; get ; }
2012-12-25 20:36:04 +00:00
public static ScreenInfo GetScreenInfo ( LibsnesApi api )
2012-09-24 06:47:34 +00:00
{
var si = new ScreenInfo ( ) ;
2013-11-22 09:33:56 +00:00
si . Mode1_BG3_Priority = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3_PRIORITY ) = = 1 ;
2012-11-26 06:30:30 +00:00
2013-11-22 09:33:56 +00:00
si . OBSEL_Size = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . OBSEL_SIZE ) ;
si . OBSEL_NameSel = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . OBSEL_NAMESEL ) ;
si . OBSEL_NameBase = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . OBSEL_NAMEBASE ) ;
2012-11-23 09:10:18 +00:00
2012-12-03 07:50:23 +00:00
si . ObjSizeBounds = ObjSizes [ si . OBSEL_Size , 1 ] ;
int square = Math . Max ( si . ObjSizeBounds . Width , si . ObjSizeBounds . Height ) ;
si . ObjSizeBoundsSquare = new Dimensions ( square , square ) ;
2012-11-23 09:10:18 +00:00
si . OBJTable0Addr = si . OBSEL_NameBase < < 14 ;
si . OBJTable1Addr = ( si . OBJTable0Addr + ( ( si . OBSEL_NameSel + 1 ) < < 13 ) ) & 0xFFFF ;
2013-11-22 09:33:56 +00:00
si . SETINI_Mode7ExtBG = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . SETINI_MODE7_EXTBG ) = = 1 ;
si . SETINI_HiRes = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . SETINI_HIRES ) = = 1 ;
si . SETINI_Overscan = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . SETINI_OVERSCAN ) = = 1 ;
si . SETINI_ObjInterlace = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . SETINI_OBJ_INTERLACE ) = = 1 ;
si . SETINI_ScreenInterlace = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . SETINI_SCREEN_INTERLACE ) = = 1 ;
2012-09-24 06:47:34 +00:00
2013-11-22 09:33:56 +00:00
si . CGWSEL_ColorMask = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGWSEL_COLORMASK ) ;
si . CGWSEL_ColorSubMask = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGWSEL_COLORSUBMASK ) ;
si . CGWSEL_AddSubMode = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGWSEL_ADDSUBMODE ) ;
si . CGWSEL_DirectColor = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGWSEL_DIRECTCOLOR ) = = 1 ;
2012-09-24 07:46:54 +00:00
2013-11-22 09:33:56 +00:00
si . CGADSUB_AddSub = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_MODE ) ;
si . CGADSUB_Half = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_HALF ) = = 1 ;
2012-11-26 06:30:30 +00:00
2013-11-22 09:33:56 +00:00
si . OBJ_MainEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TM_OBJ ) = = 1 ;
si . OBJ_SubEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TS_OBJ ) = = 1 ;
si . OBJ_MathEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_OBJ ) = = 1 ;
si . BK_MathEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_BACKDROP ) = = 1 ;
2012-11-26 06:30:30 +00:00
2013-11-22 09:33:56 +00:00
si . Mode . MODE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG_MODE ) ;
2012-09-24 06:47:34 +00:00
si . BG . BG1 . Bpp = ModeBpps [ si . Mode . MODE , 0 ] ;
si . BG . BG2 . Bpp = ModeBpps [ si . Mode . MODE , 1 ] ;
si . BG . BG3 . Bpp = ModeBpps [ si . Mode . MODE , 2 ] ;
si . BG . BG4 . Bpp = ModeBpps [ si . Mode . MODE , 3 ] ;
2012-12-02 02:51:30 +00:00
//initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later)
for ( int i = 1 ; i < = 4 ; i + + )
si . BG [ i ] . BGMode = si . BG [ i ] . Bpp = = 0 ? BGMode . Unavailable : BGMode . Text ;
2012-09-24 06:47:34 +00:00
2013-11-22 09:33:56 +00:00
si . BG . BG1 . TILESIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG1_TILESIZE ) ;
si . BG . BG2 . TILESIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG2_TILESIZE ) ;
si . BG . BG3 . TILESIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3_TILESIZE ) ;
si . BG . BG4 . TILESIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG4_TILESIZE ) ;
si . BG . BG1 . SCSIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG1_SCSIZE ) ;
si . BG . BG2 . SCSIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG2_SCSIZE ) ;
si . BG . BG3 . SCSIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3_SCSIZE ) ;
si . BG . BG4 . SCSIZE = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG4_SCSIZE ) ;
si . BG . BG1 . SCADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG1_SCADDR ) ;
si . BG . BG2 . SCADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG2_SCADDR ) ;
si . BG . BG3 . SCADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3_SCADDR ) ;
si . BG . BG4 . SCADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG4_SCADDR ) ;
si . BG . BG1 . TDADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG1_TDADDR ) ;
si . BG . BG2 . TDADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG2_TDADDR ) ;
si . BG . BG3 . TDADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3_TDADDR ) ;
si . BG . BG4 . TDADDR = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG4_TDADDR ) ;
si . BG . BG1 . MainEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TM_BG1 ) = = 1 ;
si . BG . BG2 . MainEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TM_BG2 ) = = 1 ;
si . BG . BG3 . MainEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TM_BG3 ) = = 1 ;
si . BG . BG4 . MainEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TM_BG4 ) = = 1 ;
si . BG . BG1 . SubEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TS_BG1 ) = = 1 ;
si . BG . BG2 . SubEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TS_BG2 ) = = 1 ;
si . BG . BG3 . SubEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TS_BG3 ) = = 1 ;
si . BG . BG4 . SubEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . TS_BG4 ) = = 1 ;
si . BG . BG1 . MathEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_BG1 ) = = 1 ;
si . BG . BG2 . MathEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_BG2 ) = = 1 ;
si . BG . BG3 . MathEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_BG3 ) = = 1 ;
si . BG . BG4 . MathEnabled = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . CGADSUB_BG4 ) = = 1 ;
si . BG . BG1 . HOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG1HOFS ) ;
si . BG . BG1 . VOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG1VOFS ) ;
si . BG . BG2 . HOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG2HOFS ) ;
si . BG . BG2 . VOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG2VOFS ) ;
si . BG . BG3 . HOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3HOFS ) ;
si . BG . BG3 . VOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG3VOFS ) ;
si . BG . BG4 . HOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG4HOFS ) ;
si . BG . BG4 . VOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . BG4VOFS ) ;
si . M7HOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7HOFS ) ;
si . M7VOFS = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7VOFS ) ;
si . M7A = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7A ) ;
si . M7B = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7B ) ;
si . M7C = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7C ) ;
si . M7D = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7D ) ;
si . M7X = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7X ) ;
si . M7Y = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7Y ) ;
si . M7Y = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7Y ) ;
si . M7SEL_REPEAT = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7SEL_REPEAT ) ;
si . M7SEL_HFLIP = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7SEL_HFLIP ) ! = 0 ;
si . M7SEL_VFLIP = api . QUERY_peek_logical_register ( LibsnesApi . SNES_REG . M7SEL_VFLIP ) ! = 0 ;
2012-12-03 18:59:08 +00:00
2012-10-01 00:18:33 +00:00
for ( int i = 1 ; i < = 4 ; i + + )
2012-12-02 02:51:30 +00:00
{
2012-10-01 00:18:33 +00:00
si . BG [ i ] . Mode = si . Mode . MODE ;
2012-12-02 02:51:30 +00:00
si . BG [ i ] . TiledataAddr = si . BG [ i ] . TDADDR < < 13 ;
si . BG [ i ] . ScreenAddr = si . BG [ i ] . SCADDR < < 9 ;
}
//fixup irregular things for mode 7
if ( si . Mode . MODE = = 7 )
{
si . BG . BG1 . TiledataAddr = 0 ;
si . BG . BG1 . ScreenAddr = 0 ;
if ( si . CGWSEL_DirectColor )
{
si . BG . BG1 . BGMode = BGMode . Mode7DC ;
}
else
si . BG . BG1 . BGMode = BGMode . Mode7 ;
if ( si . SETINI_Mode7ExtBG )
{
si . BG . BG2 . BGMode = BGMode . Mode7Ext ;
si . BG . BG2 . Bpp = 7 ;
si . BG . BG2 . TiledataAddr = 0 ;
si . BG . BG2 . ScreenAddr = 0 ;
}
}
2012-10-01 00:18:33 +00:00
//determine which colors each BG could use
switch ( si . Mode . MODE )
{
case 0 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 32 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 32 , 32 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 64 , 32 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 96 , 32 ) ;
break ;
case 1 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 32 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
case 2 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
case 3 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 256 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
case 4 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 256 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 32 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
case 5 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 32 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
case 6 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 32 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
case 7 :
si . BG . BG1 . PaletteSelection = new PaletteSelection ( 0 , 256 ) ;
si . BG . BG2 . PaletteSelection = new PaletteSelection ( 0 , 128 ) ;
si . BG . BG3 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
si . BG . BG4 . PaletteSelection = new PaletteSelection ( 0 , 0 ) ;
break ;
}
2012-09-24 06:47:34 +00:00
return si ;
}
2012-09-06 08:35:45 +00:00
}
static int [ , ] ModeBpps = new [ , ] {
{ 2 , 2 , 2 , 2 } ,
{ 4 , 4 , 2 , 0 } ,
{ 4 , 4 , 0 , 0 } ,
{ 8 , 4 , 0 , 0 } ,
{ 8 , 2 , 0 , 0 } ,
{ 4 , 2 , 0 , 0 } ,
{ 4 , 0 , 0 , 0 } ,
{ 8 , 0 , 0 , 0 } ,
{ 8 , 7 , 0 , 0 }
} ;
public ScreenInfo ScanScreenInfo ( )
{
2012-12-25 20:36:04 +00:00
return ScreenInfo . GetScreenInfo ( api ) ;
2012-09-06 08:35:45 +00:00
}
2012-09-08 20:03:04 +00:00
//the same basic color table that libsnes uses to convert from snes 555 to rgba32
2012-09-24 07:46:54 +00:00
static int [ ] directColorTable = new int [ 256 ] ; //8bpp gfx -> rgb555
2012-09-06 08:35:45 +00:00
static SNESGraphicsDecoder ( )
{
2012-09-24 07:46:54 +00:00
//make directColorTable
for ( int i = 0 ; i < 256 ; i + + )
{
int r = i & 7 ;
int g = ( i > > 3 ) & 7 ;
int b = ( i > > 6 ) & 3 ;
r < < = 2 ;
g < < = 2 ;
b < < = 3 ;
int color = ( b < < 10 ) | ( g < < 5 ) | r ;
directColorTable [ i ] = color ;
}
2012-09-06 08:35:45 +00:00
}
2012-11-25 20:06:31 +00:00
int [ ] colortable ;
2012-11-23 23:44:45 +00:00
public byte * vram , oam ;
2012-09-08 20:03:04 +00:00
public ushort * cgram , vram16 ;
2012-12-25 20:36:04 +00:00
LibsnesApi api ;
public SNESGraphicsDecoder ( LibsnesApi api , SnesColors . ColorType pal )
2012-09-06 08:35:45 +00:00
{
2012-12-25 20:36:04 +00:00
this . api = api ;
2012-11-25 20:06:31 +00:00
colortable = SnesColors . GetLUT ( pal ) ;
2013-11-22 09:33:56 +00:00
IntPtr block = ( IntPtr ) api . QUERY_get_memory_data ( LibsnesApi . SNES_MEMORY . VRAM ) ;
2012-11-23 23:44:45 +00:00
vram = ( byte * ) block ;
vram16 = ( ushort * ) block ;
2013-11-22 09:33:56 +00:00
block = ( IntPtr ) api . QUERY_get_memory_data ( LibsnesApi . SNES_MEMORY . CGRAM ) ;
2012-11-23 23:44:45 +00:00
cgram = ( ushort * ) block ;
2013-11-22 09:33:56 +00:00
block = ( IntPtr ) api . QUERY_get_memory_data ( LibsnesApi . SNES_MEMORY . OAM ) ;
2012-11-23 23:44:45 +00:00
oam = ( byte * ) block ;
2012-09-06 08:35:45 +00:00
}
2012-12-25 20:36:04 +00:00
public void Dispose ( )
{
//todo - unhook from api?
}
2012-09-08 20:03:04 +00:00
public struct TileEntry
2012-09-06 08:35:45 +00:00
{
public ushort tilenum ;
public byte palette ;
public TileEntryFlags flags ;
2012-11-26 06:30:30 +00:00
public int address ;
2012-09-06 08:35:45 +00:00
}
public enum TileEntryFlags : byte
{
2012-12-02 02:51:30 +00:00
None = 0 , Priority = 1 , Horz = 2 , Vert = 4 ,
2012-09-06 08:35:45 +00:00
}
2012-09-21 17:21:21 +00:00
/// <summary>
/// decodes a mode7 BG. youll still need to paletteize and colorize it.
/// </summary>
2012-09-24 06:47:34 +00:00
public void DecodeMode7BG ( int * screen , int stride , bool extBg )
2012-09-21 17:21:21 +00:00
{
2012-09-24 06:47:34 +00:00
int [ ] tileCache = _tileCache [ extBg ? 17 : 7 ] ;
2012-09-21 17:21:21 +00:00
for ( int ty = 0 , tidx = 0 ; ty < 128 ; ty + + )
{
for ( int tx = 0 ; tx < 128 ; tx + + , tidx + + )
{
int tileEntry = vram [ tidx * 2 ] ;
int src = tileEntry * 64 ;
for ( int py = 0 , pix = src ; py < 8 ; py + + )
{
for ( int px = 0 ; px < 8 ; px + + , pix + + )
{
int dst = ( ty * 8 + py ) * stride + ( tx * 8 + px ) ;
int srcData = tileCache [ pix ] ;
screen [ dst ] = srcData ;
}
}
}
}
}
2012-10-02 09:28:57 +00:00
/// <summary>
/// returns a tilemap which might be resized into 8x8 physical tiles if the 16x16 logical tilesize is specified
/// </summary>
//TileEntry[] AdaptTilemap(TileEntry[] map8x8, int tilesWide, int tilesTall, int tilesize)
//{
// if (tilesize == 8) return map8x8;
// int numTiles = tilesWide * tilesTall;
// var ret = new TileEntry[numTiles * 4];
// for(int y=0;y<tilesTall;y++)
// {
// for (int x = 0; x < tilesWide; x++)
// {
// int si = tilesWide * y + x;
// int di = tilesHigh
// for (int tx = 0; tx < 2; tx++)
// {
// for (int ty = 0; ty < 2; ty++)
// {
// }
// }
// }
// }
//}
2012-09-06 08:35:45 +00:00
/// <summary>
2012-09-08 20:03:04 +00:00
/// decodes a BG. youll still need to paletteize and colorize it.
/// someone else has to take care of calculating the starting color from the mode and layer number.
2012-09-06 08:35:45 +00:00
/// </summary>
2012-09-08 20:03:04 +00:00
public void DecodeBG ( int * screen , int stride , TileEntry [ ] map , int tiledataBaseAddr , ScreenSize size , int bpp , int tilesize , int paletteStart )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
int ncolors = 1 < < bpp ;
int [ ] tileBuf = new int [ 16 * 16 ] ;
var dims = SizeInTilesForBGSize ( size ) ;
int count8x8 = tilesize / 8 ;
int tileSizeBytes = 8 * bpp ;
int baseTileNum = tiledataBaseAddr / tileSizeBytes ;
int [ ] tileCache = _tileCache [ bpp ] ;
2012-09-21 08:10:14 +00:00
int tileCacheMask = tileCache . Length - 1 ;
2012-09-08 20:03:04 +00:00
int screenWidth = dims . Width * count8x8 * 8 ;
for ( int mty = 0 ; mty < dims . Height ; mty + + )
{
2012-09-17 00:16:19 +00:00
for ( int mtx = 0 ; mtx < dims . Width ; mtx + + )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
for ( int tx = 0 ; tx < count8x8 ; tx + + )
{
for ( int ty = 0 ; ty < count8x8 ; ty + + )
{
2012-09-21 08:10:14 +00:00
int mapIndex = mty * dims . Width + mtx ;
2012-09-08 20:03:04 +00:00
var te = map [ mapIndex ] ;
int tileNum = te . tilenum + tx + ty * 16 + baseTileNum ;
int srcOfs = tileNum * 64 ;
for ( int i = 0 , y = 0 ; y < 8 ; y + + )
{
for ( int x = 0 ; x < 8 ; x + + , i + + )
{
int px = x ;
int py = y ;
2012-09-21 07:10:54 +00:00
if ( ( te . flags & TileEntryFlags . Horz ) ! = 0 ) px = 7 - x ;
if ( ( te . flags & TileEntryFlags . Vert ) ! = 0 ) py = 7 - y ;
2012-09-08 20:03:04 +00:00
int dstX = ( mtx * count8x8 + tx ) * 8 + px ;
int dstY = ( mty * count8x8 + ty ) * 8 + py ;
int dstOfs = dstY * stride + dstX ;
2012-09-21 08:10:14 +00:00
int color = tileCache [ srcOfs & tileCacheMask ] ;
srcOfs + + ;
2012-11-22 06:57:01 +00:00
if ( color = = 0 & & usingUserBackColor )
{ }
else
{
color + = te . palette * ncolors ;
color + = paletteStart ;
}
2012-09-08 20:03:04 +00:00
screen [ dstOfs ] = color ;
}
}
}
}
2012-09-06 08:35:45 +00:00
}
2012-09-08 20:03:04 +00:00
}
2012-09-06 08:35:45 +00:00
}
2012-12-02 02:51:30 +00:00
public TileEntry [ ] FetchMode7Tilemap ( )
{
TileEntry [ ] buf = new TileEntry [ 128 * 128 ] ;
for ( int ty = 0 , tidx = 0 ; ty < 128 ; ty + + )
{
for ( int tx = 0 ; tx < 128 ; tx + + , tidx + + )
{
int tileEntry = vram [ tidx * 2 ] ;
buf [ tidx ] . address = tidx * 2 ;
buf [ tidx ] . tilenum = ( ushort ) tileEntry ;
//palette and flags are ok defaulting to 0
}
}
return buf ;
}
2012-09-08 20:03:04 +00:00
/// <summary>
2012-09-21 08:10:14 +00:00
/// fetches a tilemap. this is simple; apparently only the screen size (shape) is a factor (not the tile size)
2012-09-08 20:03:04 +00:00
/// </summary>
public TileEntry [ ] FetchTilemap ( int addr , ScreenSize size )
{
var blockDims = SizeInBlocksForBGSize ( size ) ;
int blocksw = blockDims . Width ;
int blocksh = blockDims . Height ;
int width = blockDims . Width * 32 ;
2012-09-17 00:16:19 +00:00
int height = blockDims . Height * 32 ;
2012-09-08 20:03:04 +00:00
TileEntry [ ] buf = new TileEntry [ width * height ] ;
for ( int by = 0 ; by < blocksh ; by + + )
{
for ( int bx = 0 ; bx < blocksw ; bx + + )
{
for ( int y = 0 ; y < 32 ; y + + )
{
for ( int x = 0 ; x < 32 ; x + + )
{
int idx = ( by * 32 + y ) * width + bx * 32 + x ;
ushort entry = * ( ushort * ) ( vram + addr ) ;
buf [ idx ] . tilenum = ( ushort ) ( entry & 0x3FF ) ;
buf [ idx ] . palette = ( byte ) ( ( entry > > 10 ) & 7 ) ;
buf [ idx ] . flags = ( TileEntryFlags ) ( ( entry > > 13 ) & 7 ) ;
2012-11-26 06:30:30 +00:00
buf [ idx ] . address = addr ;
2012-09-08 20:03:04 +00:00
addr + = 2 ;
}
}
}
}
return buf ;
}
//TODO - paletteize and colorize could be in one step, for more speed
2012-09-06 08:35:45 +00:00
public void Paletteize ( int * buf , int offset , int startcolor , int numpixels )
{
for ( int i = 0 ; i < numpixels ; i + + )
{
2012-11-22 06:57:01 +00:00
int entry = buf [ offset + i ] ;
int color ;
if ( entry = = 0 & & usingUserBackColor )
color = userBackColor ;
else color = cgram [ startcolor + entry ] & 0x7FFF ; //unfortunate that we have to mask this here.. maybe do it in a more optimal spot when we port it to c++
buf [ offset + i ] = color ;
2012-09-06 08:35:45 +00:00
}
}
public void Colorize ( int * buf , int offset , int numpixels )
{
for ( int i = 0 ; i < numpixels ; i + + )
{
buf [ offset + i ] = colortable [ 491520 + buf [ offset + i ] ] ;
}
}
2012-09-24 06:47:34 +00:00
int [ ] [ ] _tileCache = new int [ 18 ] [ ] ;
2012-09-06 08:35:45 +00:00
2012-11-22 06:57:01 +00:00
bool usingUserBackColor = false ;
int userBackColor ;
public void SetBackColor ( int snescol )
{
usingUserBackColor = true ;
userBackColor = snescol ;
}
2012-09-08 20:03:04 +00:00
/// <summary>
2012-09-21 17:21:21 +00:00
/// Caches all tiles at the 2bpp, 4bpp, and 8bpp decoded states.
/// we COULD defer this til we need it, you know. sort of a cool idea, not too hard
2012-09-08 20:03:04 +00:00
/// </summary>
public void CacheTiles ( )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
//generate 2bpp tiles
2012-12-25 20:36:04 +00:00
int numtiles = 65536 / 8 / 2 ;
2012-09-08 20:03:04 +00:00
int [ ] tiles = new int [ 8 * 8 * numtiles ] ;
_tileCache [ 2 ] = tiles ;
for ( int i = 0 ; i < numtiles ; i + + )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
Decode8x8x2bpp ( tiles , i * 64 , 16 * i , 8 ) ;
2012-09-06 08:35:45 +00:00
}
2012-09-08 20:03:04 +00:00
//merge 2bpp tiles into 4bpp and 8bpp
CacheTiles_Merge ( 2 ) ;
CacheTiles_Merge ( 4 ) ;
2012-09-21 17:21:21 +00:00
CacheTilesMode7 ( ) ;
2012-09-24 06:47:34 +00:00
CacheTilesMode7ExtBg ( ) ;
2012-09-21 17:21:21 +00:00
}
public void CacheTilesMode7 ( )
{
int numtiles = 256 ;
int [ ] tiles = new int [ 8 * 8 * numtiles ] ;
_tileCache [ 7 ] = tiles ;
for ( int i = 0 , j = 0 ; i < numtiles ; i + + )
{
for ( int y = 0 ; y < 8 ; y + + )
for ( int x = 0 ; x < 8 ; x + + , j + + )
tiles [ j ] = vram [ j * 2 + 1 ] ;
}
2012-09-06 08:35:45 +00:00
}
2012-10-01 21:51:55 +00:00
//not being used.. do we need it?
public int [ ] GetCachedTile ( int bpp , int tilenum )
{
int [ ] ret = new int [ 8 * 8 ] ;
int idx = tilenum * 64 ;
for ( int i = 0 ; i < 64 ; i + + )
ret [ i ] = _tileCache [ bpp ] [ idx + i ] ;
return ret ;
}
2012-09-24 06:47:34 +00:00
void CacheTilesMode7ExtBg ( )
{
int numtiles = 256 ;
int [ ] tiles = new int [ 8 * 8 * numtiles ] ;
_tileCache [ 17 ] = tiles ;
int [ ] mode7tiles = _tileCache [ 7 ] ;
int numPixels = numtiles * 8 * 8 ;
for ( int i = 0 ; i < numPixels ; i + + )
tiles [ i ] = mode7tiles [ i ] & 0x7F ;
}
2012-09-06 08:35:45 +00:00
/// <summary>
2012-09-08 20:03:04 +00:00
/// merges one type of tiles with another to create the higher-order bitdepth.
/// TODO - templateize this when we change it to c++
2012-09-06 08:35:45 +00:00
/// </summary>
2012-09-08 20:03:04 +00:00
void CacheTiles_Merge ( int fromBpp )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
int toBpp = fromBpp * 2 ;
int shift = fromBpp ;
int numtiles = 8192 / toBpp ;
int [ ] tilesDst = new int [ 8 * 8 * numtiles ] ;
_tileCache [ toBpp ] = tilesDst ;
2012-09-21 08:10:14 +00:00
int [ ] tilesSrc = _tileCache [ fromBpp ] ;
2012-09-08 20:03:04 +00:00
for ( int i = 0 ; i < numtiles ; i + + )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
int srcAddr = i * 128 ;
int dstAddr = i * 64 ;
for ( int p = 0 ; p < 64 ; p + + )
{
int tileA = tilesSrc [ srcAddr + p ] ;
int tileB = tilesSrc [ srcAddr + p + 64 ] ;
tilesDst [ dstAddr + p ] = tileA | ( tileB < < shift ) ;
}
2012-09-06 08:35:45 +00:00
}
}
/// <summary>
2012-09-08 20:03:04 +00:00
/// decodes an 8x8 tile to a linear framebuffer type thing. fundamental unit of tile decoding.
2012-09-06 08:35:45 +00:00
/// </summary>
2012-09-08 20:03:04 +00:00
public void Decode8x8x2bpp ( int [ ] buf , int offset , int addr , int stride = 8 )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
for ( int y = 0 ; y < 8 ; y + + )
2012-09-06 08:35:45 +00:00
{
2012-09-08 20:03:04 +00:00
byte val = vram [ addr + 1 ] ;
for ( int x = 0 ; x < 8 ; x + + ) buf [ offset + y * stride + x ] = val > > ( 7 - x ) & 1 ;
val = vram [ addr + 0 ] ;
for ( int x = 0 ; x < 8 ; x + + ) buf [ offset + y * stride + x ] = ( buf [ offset + y * stride + x ] < < 1 ) | ( val > > ( 7 - x ) & 1 ) ;
addr + = 2 ;
2012-09-06 08:35:45 +00:00
}
2012-09-08 20:03:04 +00:00
}
2012-09-06 08:35:45 +00:00
2012-09-21 17:21:21 +00:00
/// <summary>
/// renders the mode7 tiles to a screen with the predefined size.
/// </summary>
2012-12-02 02:51:30 +00:00
public void RenderMode7TilesToScreen ( int * screen , int stride , bool ext , bool directColor , int tilesWide = 16 , int startTile = 0 , int numTiles = 256 )
2012-09-08 20:03:04 +00:00
{
2012-09-24 06:47:34 +00:00
int [ ] tilebuf = _tileCache [ ext ? 17 : 7 ] ;
2012-09-21 17:21:21 +00:00
for ( int i = 0 ; i < numTiles ; i + + )
{
2012-12-02 02:51:30 +00:00
int tnum = startTile + i ;
//TODO - mask by possible number of tiles? only in OBJ rendering mode?
2012-09-21 17:21:21 +00:00
int ty = i / tilesWide ;
int tx = i % tilesWide ;
int dstOfs = ( ty * 8 ) * stride + tx * 8 ;
2012-12-02 02:51:30 +00:00
int srcOfs = tnum * 64 ;
2012-09-21 17:21:21 +00:00
for ( int y = 0 , p = 0 ; y < 8 ; y + + )
2012-09-24 07:46:54 +00:00
{
2012-09-21 17:21:21 +00:00
for ( int x = 0 ; x < 8 ; x + + , p + + )
{
screen [ dstOfs + y * stride + x ] = tilebuf [ srcOfs + p ] ;
}
2012-09-24 07:46:54 +00:00
}
2012-09-21 17:21:21 +00:00
}
int numPixels = numTiles * 8 * 8 ;
2012-09-24 07:46:54 +00:00
if ( directColor ) DirectColorify ( screen , numPixels ) ;
else Paletteize ( screen , 0 , 0 , numPixels ) ;
2012-09-21 17:21:21 +00:00
Colorize ( screen , 0 , numPixels ) ;
2012-09-06 08:35:45 +00:00
}
2012-12-02 02:51:30 +00:00
/// <summary>
/// renders the tiles to a screen of the crudely specified size.
/// we might need 16x16 unscrambling and some other perks here eventually.
/// provide a start color to use as the basis for the palette
/// </summary>
public void RenderTilesToScreen ( int * screen , int tilesWide , int tilesTall , int stride , int bpp , int startcolor , int startTile = 0 , int numTiles = - 1 , bool descramble16 = false )
{
if ( numTiles = = - 1 )
numTiles = 8192 / bpp ;
int [ ] tilebuf = _tileCache [ bpp ] ;
for ( int i = 0 ; i < numTiles ; i + + )
{
int tnum = startTile + i ;
//TODO - mask by possible number of tiles? only in OBJ rendering mode?
int ty = i / tilesWide ;
int tx = i % tilesWide ;
int dstOfs = ( ty * 8 ) * stride + tx * 8 ;
int srcOfs = tnum * 64 ;
for ( int y = 0 , p = 0 ; y < 8 ; y + + )
for ( int x = 0 ; x < 8 ; x + + , p + + )
{
screen [ dstOfs + y * stride + x ] = tilebuf [ srcOfs + p ] ;
}
}
int numPixels = numTiles * 8 * 8 ;
Paletteize ( screen , 0 , startcolor , numPixels ) ;
Colorize ( screen , 0 , numPixels ) ;
}
2012-12-03 17:57:19 +00:00
public void RenderSpriteToScreen ( int * screen , int stride , int destx , int desty , ScreenInfo si , int spritenum , OAMInfo oam = null , int xlimit = 1024 , int ylimit = 1024 , byte [ , ] spriteMap = null )
2012-11-23 23:44:45 +00:00
{
var dims = new [ ] { SNESGraphicsDecoder . ObjSizes [ si . OBSEL_Size , 0 ] , SNESGraphicsDecoder . ObjSizes [ si . OBSEL_Size , 1 ] } ;
2012-12-03 17:57:19 +00:00
if ( oam = = null )
oam = new OAMInfo ( this , si , spritenum ) ;
2012-11-23 23:44:45 +00:00
var dim = dims [ oam . Size ] ;
int [ ] tilebuf = _tileCache [ 4 ] ;
int baseaddr ;
if ( oam . Table = = 0 )
baseaddr = si . OBJTable0Addr ;
else
baseaddr = si . OBJTable1Addr ;
2012-12-03 07:50:23 +00:00
//TODO - flips of 'undocumented' rectangular oam settings are wrong. probably easy to do right, but we need a test
2012-11-23 23:44:45 +00:00
int bcol = oam . Tile & 0xF ;
int brow = ( oam . Tile > > 4 ) & 0xF ;
2012-12-03 07:50:23 +00:00
for ( int oy = 0 ; oy < dim . Height ; oy + + )
for ( int ox = 0 ; ox < dim . Width ; ox + + )
2012-11-23 23:44:45 +00:00
{
2012-12-03 07:50:23 +00:00
int x = ox ;
int y = oy ;
int dy , dx ;
if ( oam . HFlip )
dx = dim . Width - 1 - x ;
else dx = x ;
if ( oam . VFlip )
dy = dim . Height - 1 - y ;
else dy = y ;
dx + = destx ;
dy + = desty ;
2012-12-03 17:57:19 +00:00
if ( dx > = xlimit | | dy > = ylimit | | dx < 0 | | dy < 0 )
continue ;
2012-11-23 23:44:45 +00:00
int col = ( bcol + ( x > > 3 ) ) & 0xF ;
int row = ( brow + ( y > > 3 ) ) & 0xF ;
int sx = x & 0x7 ;
int sy = y & 0x7 ;
int addr = baseaddr * 2 + ( row * 16 + col ) * 64 ;
addr + = sy * 8 + sx ;
int dofs = stride * dy + dx ;
2012-12-03 17:57:19 +00:00
int color = tilebuf [ addr ] ;
if ( spriteMap ! = null & & color = = 0 )
{
//skip transparent pixels
}
else
{
screen [ dofs ] = color ;
Paletteize ( screen , dofs , oam . Palette * 16 + 128 , 1 ) ;
Colorize ( screen , dofs , 1 ) ;
if ( spriteMap ! = null ) spriteMap [ dx , dy ] = ( byte ) spritenum ;
}
2012-11-23 23:44:45 +00:00
}
}
2012-09-21 05:56:47 +00:00
public int Colorize ( int rgb555 )
{
2012-12-03 07:50:23 +00:00
//skip to max luminance in the palette table
2012-09-21 05:56:47 +00:00
return colortable [ 491520 + rgb555 ] ;
}
/// <summary>
/// returns the current palette, transformed into an int array, for more convenience
/// </summary>
public int [ ] GetPalette ( )
{
var ret = new int [ 256 ] ;
for ( int i = 0 ; i < 256 ; i + + )
ret [ i ] = cgram [ i ] & 0x7FFF ;
return ret ;
}
2012-09-24 07:46:54 +00:00
public void DirectColorify ( int * screen , int numPixels )
{
for ( int i = 0 ; i < numPixels ; i + + )
screen [ i ] = directColorTable [ screen [ i ] ] ;
}
2012-09-08 20:03:04 +00:00
} //class SNESGraphicsDecoder
} //namespace