BizHawk/attic/GarboDev/Renderer.cs

953 lines
34 KiB
C#

namespace GarboDev
{
using System;
using System.Collections.Generic;
using System.Text;
public partial class Renderer : IRenderer
{
private Memory memory;
private uint[] scanline = new uint[240];
private byte[] blend = new byte[240];
private byte[] windowCover = new byte[240];
private uint[] back = new uint[240 * 160];
//private uint[] front = new uint[240 * 160];
private const uint pitch = 240;
// Convenience variable as I use it everywhere, set once in RenderLine
private ushort dispCnt;
// Window helper variables
private byte win0x1, win0x2, win0y1, win0y2;
private byte win1x1, win1x2, win1y1, win1y2;
private byte win0Enabled, win1Enabled, winObjEnabled, winOutEnabled;
private bool winEnabled;
private byte blendSource, blendTarget;
private byte blendA, blendB, blendY;
private int blendType;
private int curLine = 0;
private static uint[] colorLUT;
static Renderer()
{
colorLUT = new uint[0x10000];
// Pre-calculate the color LUT
for (uint i = 0; i <= 0xFFFF; i++)
{
uint r = (i & 0x1FU);
uint g = (i & 0x3E0U) >> 5;
uint b = (i & 0x7C00U) >> 10;
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
colorLUT[i] = (r << 16) | (g << 8) | b;
}
}
public Memory Memory
{
set { this.memory = value; }
}
public void Initialize(object data)
{
}
public void Reset()
{
}
public uint[] ShowFrame()
{
//Array.Copy(this.back, this.front, this.front.Length);
//return this.front;
return this.back;
}
public void RenderLine(int line)
{
this.curLine = line;
// Render the line
this.dispCnt = Memory.ReadU16(this.memory.IORam, Memory.DISPCNT);
if ((this.dispCnt & (1 << 7)) != 0)
{
uint bgColor = Renderer.GbaTo32((ushort)0x7FFF);
for (int i = 0; i < 240; i++) this.scanline[i] = bgColor;
}
else
{
this.winEnabled = false;
if ((this.dispCnt & (1 << 13)) != 0)
{
// Calculate window 0 information
ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN0V);
this.win0y1 = (byte)(winy >> 8);
this.win0y2 = (byte)(winy & 0xff);
ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN0H);
this.win0x1 = (byte)(winx >> 8);
this.win0x2 = (byte)(winx & 0xff);
if (this.win0x2 > 240 || this.win0x1 > this.win0x2)
{
this.win0x2 = 240;
}
if (this.win0y2 > 160 || this.win0y1 > this.win0y2)
{
this.win0y2 = 160;
}
this.win0Enabled = this.memory.IORam[Memory.WININ];
this.winEnabled = true;
}
if ((this.dispCnt & (1 << 14)) != 0)
{
// Calculate window 1 information
ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN1V);
this.win1y1 = (byte)(winy >> 8);
this.win1y2 = (byte)(winy & 0xff);
ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN1H);
this.win1x1 = (byte)(winx >> 8);
this.win1x2 = (byte)(winx & 0xff);
if (this.win1x2 > 240 || this.win1x1 > this.win1x2)
{
this.win1x2 = 240;
}
if (this.win1y2 > 160 || this.win1y1 > this.win1y2)
{
this.win1y2 = 160;
}
this.win1Enabled = this.memory.IORam[Memory.WININ + 1];
this.winEnabled = true;
}
if ((this.dispCnt & (1 << 15)) != 0 && (this.dispCnt & (1 << 12)) != 0)
{
// Object windows are enabled
this.winObjEnabled = this.memory.IORam[Memory.WINOUT + 1];
this.winEnabled = true;
}
if (this.winEnabled)
{
this.winOutEnabled = this.memory.IORam[Memory.WINOUT];
}
// Calculate blending information
ushort bldcnt = Memory.ReadU16(this.memory.IORam, Memory.BLDCNT);
this.blendType = (bldcnt >> 6) & 0x3;
this.blendSource = (byte)(bldcnt & 0x3F);
this.blendTarget = (byte)((bldcnt >> 8) & 0x3F);
ushort bldalpha = Memory.ReadU16(this.memory.IORam, Memory.BLDALPHA);
this.blendA = (byte)(bldalpha & 0x1F);
if (this.blendA > 0x10) this.blendA = 0x10;
this.blendB = (byte)((bldalpha >> 8) & 0x1F);
if (this.blendB > 0x10) this.blendB = 0x10;
this.blendY = (byte)(this.memory.IORam[Memory.BLDY] & 0x1F);
if (this.blendY > 0x10) this.blendY = 0x10;
switch (this.dispCnt & 0x7)
{
case 0: this.RenderMode0Line(); break;
case 1: this.RenderMode1Line(); break;
case 2: this.RenderMode2Line(); break;
case 3: this.RenderMode3Line(); break;
case 4: this.RenderMode4Line(); break;
case 5: this.RenderMode5Line(); break;
}
}
Array.Copy(this.scanline, 0, this.back, this.curLine * Renderer.pitch, Renderer.pitch);
}
private void DrawBackdrop()
{
byte[] palette = this.memory.PaletteRam;
// Initialize window coverage buffer if neccesary
if (this.winEnabled)
{
for (int i = 0; i < 240; i++)
{
this.windowCover[i] = this.winOutEnabled;
}
if ((this.dispCnt & (1 << 15)) != 0)
{
// Sprite window
this.DrawSpriteWindows();
}
if ((this.dispCnt & (1 << 14)) != 0)
{
// Window 1
if (this.curLine >= this.win1y1 && this.curLine < this.win1y2)
{
for (int i = this.win1x1; i < this.win1x2; i++)
{
this.windowCover[i] = this.win1Enabled;
}
}
}
if ((this.dispCnt & (1 << 13)) != 0)
{
// Window 0
if (this.curLine >= this.win0y1 && this.curLine < this.win0y2)
{
for (int i = this.win0x1; i < this.win0x2; i++)
{
this.windowCover[i] = this.win0Enabled;
}
}
}
}
// Draw backdrop first
uint bgColor = Renderer.GbaTo32((ushort)(palette[0] | (palette[1] << 8)));
uint modColor = bgColor;
if (this.blendType == 2 && (this.blendSource & (1 << 5)) != 0)
{
// Brightness increase
uint r = bgColor & 0xFF;
uint g = (bgColor >> 8) & 0xFF;
uint b = (bgColor >> 16) & 0xFF;
r = r + (((0xFF - r) * this.blendY) >> 4);
g = g + (((0xFF - g) * this.blendY) >> 4);
b = b + (((0xFF - b) * this.blendY) >> 4);
modColor = r | (g << 8) | (b << 16);
}
else if (this.blendType == 3 && (this.blendSource & (1 << 5)) != 0)
{
// Brightness decrease
uint r = bgColor & 0xFF;
uint g = (bgColor >> 8) & 0xFF;
uint b = (bgColor >> 16) & 0xFF;
r = r - ((r * this.blendY) >> 4);
g = g - ((g * this.blendY) >> 4);
b = b - ((b * this.blendY) >> 4);
modColor = r | (g << 8) | (b << 16);
}
if (this.winEnabled)
{
for (int i = 0; i < 240; i++)
{
if ((this.windowCover[i] & (1 << 5)) != 0)
{
this.scanline[i] = modColor;
}
else
{
this.scanline[i] = bgColor;
}
this.blend[i] = 1 << 5;
}
}
else
{
for (int i = 0; i < 240; i++)
{
this.scanline[i] = modColor;
this.blend[i] = 1 << 5;
}
}
}
private void RenderTextBg(int bg)
{
if (this.winEnabled)
{
switch (this.blendType)
{
case 0:
this.RenderTextBgWindow(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgWindowBlend(bg);
else
this.RenderTextBgWindow(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgWindowBrightInc(bg);
else
this.RenderTextBgWindow(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgWindowBrightDec(bg);
else
this.RenderTextBgWindow(bg);
break;
}
}
else
{
switch (this.blendType)
{
case 0:
this.RenderTextBgNormal(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgBlend(bg);
else
this.RenderTextBgNormal(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgBrightInc(bg);
else
this.RenderTextBgNormal(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgBrightDec(bg);
else
this.RenderTextBgNormal(bg);
break;
}
}
}
private void RenderRotScaleBg(int bg)
{
if (this.winEnabled)
{
switch (this.blendType)
{
case 0:
this.RenderRotScaleBgWindow(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgWindowBlend(bg);
else
this.RenderRotScaleBgWindow(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgWindowBrightInc(bg);
else
this.RenderRotScaleBgWindow(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgWindowBrightDec(bg);
else
this.RenderRotScaleBgWindow(bg);
break;
}
}
else
{
switch (this.blendType)
{
case 0:
this.RenderRotScaleBgNormal(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgBlend(bg);
else
this.RenderRotScaleBgNormal(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgBrightInc(bg);
else
this.RenderRotScaleBgNormal(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgBrightDec(bg);
else
this.RenderRotScaleBgNormal(bg);
break;
}
}
}
private void DrawSprites(int pri)
{
if (this.winEnabled)
{
switch (this.blendType)
{
case 0:
this.DrawSpritesWindow(pri);
break;
case 1:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesWindowBlend(pri);
else
this.DrawSpritesWindow(pri);
break;
case 2:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesWindowBrightInc(pri);
else
this.DrawSpritesWindow(pri);
break;
case 3:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesWindowBrightDec(pri);
else
this.DrawSpritesWindow(pri);
break;
}
}
else
{
switch (this.blendType)
{
case 0:
this.DrawSpritesNormal(pri);
break;
case 1:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesBlend(pri);
else
this.DrawSpritesNormal(pri);
break;
case 2:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesBrightInc(pri);
else
this.DrawSpritesNormal(pri);
break;
case 3:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesBrightDec(pri);
else
this.DrawSpritesNormal(pri);
break;
}
}
}
private void RenderMode0Line()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
for (int pri = 3; pri >= 0; pri--)
{
for (int i = 3; i >= 0; i--)
{
if ((this.dispCnt & (1 << (8 + i))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
if ((bgcnt & 0x3) == pri)
{
this.RenderTextBg(i);
}
}
}
this.DrawSprites(pri);
}
}
private void RenderMode1Line()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
for (int pri = 3; pri >= 0; pri--)
{
if ((this.dispCnt & (1 << (8 + 2))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
if ((bgcnt & 0x3) == pri)
{
this.RenderRotScaleBg(2);
}
}
for (int i = 1; i >= 0; i--)
{
if ((this.dispCnt & (1 << (8 + i))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
if ((bgcnt & 0x3) == pri)
{
this.RenderTextBg(i);
}
}
}
this.DrawSprites(pri);
}
}
private void RenderMode2Line()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
for (int pri = 3; pri >= 0; pri--)
{
for (int i = 3; i >= 2; i--)
{
if ((this.dispCnt & (1 << (8 + i))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
if ((bgcnt & 0x3) == pri)
{
this.RenderRotScaleBg(i);
}
}
}
this.DrawSprites(pri);
}
}
private void RenderMode3Line()
{
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
byte blendMaskType = (byte)(1 << 2);
int bgPri = bg2Cnt & 0x3;
for (int pri = 3; pri > bgPri; pri--)
{
this.DrawSprites(pri);
}
if ((this.dispCnt & (1 << 10)) != 0)
{
// Background enabled, render it
int x = this.memory.Bgx[0];
int y = this.memory.Bgy[0];
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
for (int i = 0; i < 240; i++)
{
int ax = ((int)x) >> 8;
int ay = ((int)y) >> 8;
if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160)
{
int curIdx = ((ay * 240) + ax) * 2;
this.scanline[i] = Renderer.GbaTo32((ushort)(vram[curIdx] | (vram[curIdx + 1] << 8)));
this.blend[i] = blendMaskType;
}
x += dx;
y += dy;
}
}
for (int pri = bgPri; pri >= 0; pri--)
{
this.DrawSprites(pri);
}
}
private void RenderMode4Line()
{
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
byte blendMaskType = (byte)(1 << 2);
int bgPri = bg2Cnt & 0x3;
for (int pri = 3; pri > bgPri; pri--)
{
this.DrawSprites(pri);
}
if ((this.dispCnt & (1 << 10)) != 0)
{
// Background enabled, render it
int baseIdx = 0;
if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx = 0xA000;
int x = this.memory.Bgx[0];
int y = this.memory.Bgy[0];
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
for (int i = 0; i < 240; i++)
{
int ax = ((int)x) >> 8;
int ay = ((int)y) >> 8;
if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160)
{
int lookup = vram[baseIdx + (ay * 240) + ax];
if (lookup != 0)
{
this.scanline[i] = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
this.blend[i] = blendMaskType;
}
}
x += dx;
y += dy;
}
}
for (int pri = bgPri; pri >= 0; pri--)
{
this.DrawSprites(pri);
}
}
private void RenderMode5Line()
{
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
byte blendMaskType = (byte)(1 << 2);
int bgPri = bg2Cnt & 0x3;
for (int pri = 3; pri > bgPri; pri--)
{
this.DrawSprites(pri);
}
if ((this.dispCnt & (1 << 10)) != 0)
{
// Background enabled, render it
int baseIdx = 0;
if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx += 160 * 128 * 2;
int x = this.memory.Bgx[0];
int y = this.memory.Bgy[0];
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
for (int i = 0; i < 240; i++)
{
int ax = ((int)x) >> 8;
int ay = ((int)y) >> 8;
if (ax >= 0 && ax < 160 && ay >= 0 && ay < 128)
{
int curIdx = (int)(ay * 160 + ax) * 2;
this.scanline[i] = Renderer.GbaTo32((ushort)(vram[baseIdx + curIdx] | (vram[baseIdx + curIdx + 1] << 8)));
this.blend[i] = blendMaskType;
}
x += dx;
y += dy;
}
}
for (int pri = bgPri; pri >= 0; pri--)
{
this.DrawSprites(pri);
}
}
private void DrawSpriteWindows()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
// OBJ must be enabled in this.dispCnt
if ((this.dispCnt & (1 << 12)) == 0) return;
for (int oamNum = 127; oamNum >= 0; oamNum--)
{
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
// Not an object window, so continue
if (((attr0 >> 10) & 3) != 2) continue;
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
int x = attr1 & 0x1FF;
int y = attr0 & 0xFF;
int width = -1, height = -1;
switch ((attr0 >> 14) & 3)
{
case 0:
// Square
switch ((attr1 >> 14) & 3)
{
case 0: width = 8; height = 8; break;
case 1: width = 16; height = 16; break;
case 2: width = 32; height = 32; break;
case 3: width = 64; height = 64; break;
}
break;
case 1:
// Horizontal Rectangle
switch ((attr1 >> 14) & 3)
{
case 0: width = 16; height = 8; break;
case 1: width = 32; height = 8; break;
case 2: width = 32; height = 16; break;
case 3: width = 64; height = 32; break;
}
break;
case 2:
// Vertical Rectangle
switch ((attr1 >> 14) & 3)
{
case 0: width = 8; height = 16; break;
case 1: width = 8; height = 32; break;
case 2: width = 16; height = 32; break;
case 3: width = 32; height = 64; break;
}
break;
}
// Check double size flag here
int rwidth = width, rheight = height;
if ((attr0 & (1 << 8)) != 0)
{
// Rot-scale on
if ((attr0 & (1 << 9)) != 0)
{
rwidth *= 2;
rheight *= 2;
}
}
else
{
// Invalid sprite
if ((attr0 & (1 << 9)) != 0)
width = -1;
}
if (width == -1)
{
// Invalid sprite
continue;
}
// Y clipping
if (y > ((y + rheight) & 0xff))
{
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
}
else
{
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
}
int scale = 1;
if ((attr0 & (1 << 13)) != 0) scale = 2;
int spritey = this.curLine - y;
if (spritey < 0) spritey += 256;
if ((attr0 & (1 << 8)) == 0)
{
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
int baseSprite;
if ((this.dispCnt & (1 << 6)) != 0)
{
// 1 dimensional
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
}
else
{
// 2 dimensional
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
}
int baseInc = scale;
if ((attr1 & (1 << 12)) != 0)
{
baseSprite += ((width / 8) * scale) - scale;
baseInc = -baseInc;
}
if ((attr0 & (1 << 13)) != 0)
{
// 256 colors
for (int i = x; i < x + width; i++)
{
if ((i & 0x1ff) < 240)
{
int tx = (i - x) & 7;
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
int lookup = vram[0x10000 + curIdx];
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
if (((i - x) & 7) == 7) baseSprite += baseInc;
}
}
else
{
// 16 colors
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
for (int i = x; i < x + width; i++)
{
if ((i & 0x1ff) < 240)
{
int tx = (i - x) & 7;
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
int lookup = vram[0x10000 + curIdx];
if ((tx & 1) == 0)
{
lookup &= 0xf;
}
else
{
lookup >>= 4;
}
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
if (((i - x) & 7) == 7) baseSprite += baseInc;
}
}
}
else
{
int rotScaleParam = (attr1 >> 9) & 0x1F;
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
int cx = rwidth / 2;
int cy = rheight / 2;
int baseSprite = attr2 & 0x3FF;
int pitch;
if ((this.dispCnt & (1 << 6)) != 0)
{
// 1 dimensional
pitch = (width / 8) * scale;
}
else
{
// 2 dimensional
pitch = 0x20;
}
short rx = (short)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
short ry = (short)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
// Draw a rot/scale sprite
if ((attr0 & (1 << 13)) != 0)
{
// 256 colors
for (int i = x; i < x + rwidth; i++)
{
int tx = rx >> 8;
int ty = ry >> 8;
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height)
{
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
int lookup = vram[0x10000 + curIdx];
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
rx += dx;
ry += dy;
}
}
else
{
// 16 colors
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
for (int i = x; i < x + rwidth; i++)
{
int tx = rx >> 8;
int ty = ry >> 8;
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height)
{
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
int lookup = vram[0x10000 + curIdx];
if ((tx & 1) == 0)
{
lookup &= 0xf;
}
else
{
lookup >>= 4;
}
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
rx += dx;
ry += dy;
}
}
}
}
}
public static uint GbaTo32(ushort color)
{
// more accurate, but slower :(
// return colorLUT[color];
return ((color & 0x1FU) << 19) | ((color & 0x3E0U) << 6) | ((color & 0x7C00U) >> 7);
}
}
}