diff --git a/melonDS.cbp b/melonDS.cbp
index be1f30b7..1f51dc93 100644
--- a/melonDS.cbp
+++ b/melonDS.cbp
@@ -53,6 +53,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -161,6 +179,12 @@
+
+
+
+
+
+
diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp
index 288ee581..b510767c 100644
--- a/src/GPU2D.cpp
+++ b/src/GPU2D.cpp
@@ -74,7 +74,9 @@
// mosaic:
// * mosaic grid starts at 0,0 regardless of the BG/sprite position
// * when changing it midframe: new X setting is applied immediately, new Y setting is applied only
-// after the end of the current mosaic row
+// after the end of the current mosaic row (when Y counter needs reloaded)
+// * for rotscaled sprites: coordinates that are inside the sprite are clamped to the sprite region
+// after being transformed for mosaic
// TODO: find which parts of DISPCNT are latched. for example, not possible to change video mode midframe.
@@ -82,6 +84,16 @@
GPU2D::GPU2D(u32 num)
{
Num = num;
+
+ // initialize mosaic table
+ for (int m = 0; m < 16; m++)
+ {
+ for (int x = 0; x < 256; x++)
+ {
+ int offset = x % (m+1);
+ MosaicTable[m][x] = offset;
+ }
+ }
}
GPU2D::~GPU2D()
@@ -118,6 +130,8 @@ void GPU2D::Reset()
BGMosaicYMax = 0;
OBJMosaicY = 0;
OBJMosaicYMax = 0;
+ CurBGXMosaicTable = MosaicTable[0];
+ CurOBJXMosaicTable = MosaicTable[0];
BlendCnt = 0;
EVA = 16;
@@ -207,6 +221,9 @@ void GPU2D::DoSavestate(Savestate* file)
BGExtPalStatus[2] = 0;
BGExtPalStatus[3] = 0;
OBJExtPalStatus = 0;
+
+ CurBGXMosaicTable = MosaicTable[BGMosaicSize[0]];
+ CurOBJXMosaicTable = MosaicTable[OBJMosaicSize[0]];
}
}
@@ -365,10 +382,12 @@ void GPU2D::Write8(u32 addr, u8 val)
case 0x04C:
BGMosaicSize[0] = val & 0xF;
BGMosaicSize[1] = val >> 4;
+ CurBGXMosaicTable = MosaicTable[BGMosaicSize[0]];
return;
case 0x04D:
OBJMosaicSize[0] = val & 0xF;
OBJMosaicSize[1] = val >> 4;
+ CurOBJXMosaicTable = MosaicTable[OBJMosaicSize[0]];
return;
case 0x050: BlendCnt = (BlendCnt & 0x3F00) | val; return;
@@ -497,8 +516,10 @@ void GPU2D::Write16(u32 addr, u16 val)
case 0x04C:
BGMosaicSize[0] = val & 0xF;
BGMosaicSize[1] = (val >> 4) & 0xF;
+ CurBGXMosaicTable = MosaicTable[BGMosaicSize[0]];
OBJMosaicSize[0] = (val >> 8) & 0xF;
OBJMosaicSize[1] = val >> 12;
+ CurOBJXMosaicTable = MosaicTable[OBJMosaicSize[0]];
return;
case 0x050: BlendCnt = val & 0x3FFF; return;
@@ -710,6 +731,24 @@ u32 GPU2D::ColorComposite(int i, u32 val1, u32 val2)
}
+void GPU2D::UpdateMosaicCounters(u32 line)
+{
+ // Y mosaic uses incrementing 4-bit counters
+ // the transformed Y position is updated every time the counter matches the MOSAIC register
+
+ if (OBJMosaicYCount == OBJMosaicSize[1])
+ {
+ OBJMosaicYCount = 0;
+ OBJMosaicY = line + 1;
+ }
+ else
+ {
+ OBJMosaicYCount++;
+ OBJMosaicYCount &= 0xF;
+ }
+}
+
+
void GPU2D::DrawScanline(u32 line)
{
int stride = Accelerated ? (256*3 + 1) : 256;
@@ -756,6 +795,7 @@ void GPU2D::DrawScanline(u32 line)
// always render regular graphics
DrawScanline_BGOBJ(line);
+ UpdateMosaicCounters(line);
switch (dispmode)
{
@@ -901,8 +941,10 @@ void GPU2D::VBlankEnd()
BGMosaicY = 0;
BGMosaicYMax = BGMosaicSize[1];
+ //OBJMosaicY = 0;
+ //OBJMosaicYMax = OBJMosaicSize[1];
OBJMosaicY = 0;
- OBJMosaicYMax = OBJMosaicSize[1];
+ OBJMosaicYCount = 0;
if (Accelerated)
{
@@ -1193,33 +1235,33 @@ u16* GPU2D::GetBGExtPal(u32 slot, u32 pal)
return dst;
}
-u16* GPU2D::GetOBJExtPal(u32 pal)
+u16* GPU2D::GetOBJExtPal()
{
- u16* dst = &OBJExtPalCache[pal << 8];
+ u16* dst = OBJExtPalCache;
- if (!(OBJExtPalStatus & (1< 0)) DrawBG_##type(line, num); else DrawBG_##type(line, num); }
+
+#define DoDrawBG_Large(line) \
+ { if ((BGCnt[2] & 0x0040) && (BGMosaicSize[0] > 0)) DrawBG_Large(line); else DrawBG_Large(line); }
+
template
void GPU2D::DrawScanlineBGMode(u32 line)
{
@@ -1292,11 +1340,11 @@ void GPU2D::DrawScanlineBGMode(u32 line)
if (DispCnt & 0x0800)
{
if (bgmode >= 3)
- DrawBG_Extended(line, 3);
+ DoDrawBG(Extended, line, 3)
else if (bgmode >= 1)
- DrawBG_Affine(line, 3);
+ DoDrawBG(Affine, line, 3)
else
- DrawBG_Text(line, 3);
+ DoDrawBG(Text, line, 3)
}
}
if ((BGCnt[2] & 0x3) == i)
@@ -1304,18 +1352,18 @@ void GPU2D::DrawScanlineBGMode(u32 line)
if (DispCnt & 0x0400)
{
if (bgmode == 5)
- DrawBG_Extended(line, 2);
+ DoDrawBG(Extended, line, 2)
else if (bgmode == 4 || bgmode == 2)
- DrawBG_Affine(line, 2);
+ DoDrawBG(Affine, line, 2)
else
- DrawBG_Text(line, 2);
+ DoDrawBG(Text, line, 2)
}
}
if ((BGCnt[1] & 0x3) == i)
{
if (DispCnt & 0x0200)
{
- DrawBG_Text(line, 1);
+ DoDrawBG(Text, line, 1)
}
}
if ((BGCnt[0] & 0x3) == i)
@@ -1325,11 +1373,11 @@ void GPU2D::DrawScanlineBGMode(u32 line)
if ((!Num) && (DispCnt & 0x8))
DrawBG_3D();
else
- DrawBG_Text(line, 0);
+ DoDrawBG(Text, line, 0)
}
}
if ((DispCnt & 0x1000) && NumSprites)
- InterleaveSprites(0x8000 | (i<<16));
+ InterleaveSprites(0x40000 | (i<<16));
}
}
@@ -1341,7 +1389,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line)
{
if (DispCnt & 0x0400)
{
- DrawBG_Large(line);
+ DoDrawBG_Large(line)
}
}
if ((BGCnt[0] & 0x3) == i)
@@ -1353,7 +1401,7 @@ void GPU2D::DrawScanlineBGMode6(u32 line)
}
}
if ((DispCnt & 0x1000) && NumSprites)
- InterleaveSprites(0x8000 | (i<<16));
+ InterleaveSprites(0x40000 | (i<<16));
}
}
@@ -1367,7 +1415,7 @@ void GPU2D::DrawScanlineBGMode7(u32 line)
{
if (DispCnt & 0x0200)
{
- DrawBG_Text(line, 1);
+ DoDrawBG(Text, line, 1)
}
}
if ((BGCnt[0] & 0x3) == i)
@@ -1377,11 +1425,11 @@ void GPU2D::DrawScanlineBGMode7(u32 line)
if ((!Num) && (DispCnt & 0x8))
DrawBG_3D();
else
- DrawBG_Text(line, 0);
+ DoDrawBG(Text, line, 0)
}
}
if ((DispCnt & 0x1000) && NumSprites)
- InterleaveSprites(0x8000 | (i<<16));
+ InterleaveSprites(0x40000 | (i<<16));
}
}
@@ -1417,6 +1465,8 @@ void GPU2D::DrawScanline_BGOBJ(u32 line)
else
memset(WindowMask, 0xFF, 256);
+ ApplySpriteMosaicX();
+
switch (DispCnt & 0x7)
{
case 0: DrawScanlineBGMode<0>(line); break;
@@ -1542,13 +1592,13 @@ void GPU2D::DrawScanline_BGOBJ(u32 line)
else
BGMosaicY++;
- if (OBJMosaicY >= OBJMosaicYMax)
+ /*if (OBJMosaicY >= OBJMosaicYMax)
{
OBJMosaicY = 0;
OBJMosaicYMax = OBJMosaicSize[1];
}
else
- OBJMosaicY++;
+ OBJMosaicY++;*/
}
@@ -1557,6 +1607,7 @@ void GPU2D::DrawPixel_Normal(u32* dst, u16 color, u32 flag)
u8 r = (color & 0x001F) << 1;
u8 g = (color & 0x03E0) >> 4;
u8 b = (color & 0x7C00) >> 9;
+ //g |= ((color & 0x8000) >> 15);
*(dst+256) = *dst;
*dst = r | (g << 8) | (b << 16) | flag;
@@ -1618,10 +1669,10 @@ void GPU2D::DrawBG_3D()
}
}
+template
void GPU2D::DrawBG_Text(u32 line, u32 bgnum)
{
u16 bgcnt = BGCnt[bgnum];
- u32 xmos = 0, xmossize = 0;
u32 tilesetaddr, tilemapaddr;
u16* pal;
@@ -1632,9 +1683,8 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)
if (bgcnt & 0x0040)
{
- // mosaic
+ // vertical mosaic
yoff -= BGMosaicY;
- xmossize = BGMosaicSize[0];
}
u32 widexmask = (bgcnt & 0x4000) ? 0x100 : 0;
@@ -1671,15 +1721,15 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)
u16* curpal;
u32 pixelsaddr;
u8 color;
+ u32 lastxpos;
if (bgcnt & 0x0080)
{
// 256-color
// preload shit as needed
- if (xoff & 0x7)
+ if ((xoff & 0x7) || mosaic)
{
- // load a new tile
curtile = GPU::ReadVRAM_BG(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3));
if (extpal) curpal = GetBGExtPal(extpalslot, curtile>>12);
@@ -1689,31 +1739,34 @@ void GPU2D::DrawBG_Text(u32 line, u32 bgnum)
+ (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 3);
}
+ if (mosaic) lastxpos = xoff;
+
for (int i = 0; i < 256; i++)
{
- if (!(xoff & 0x7))
+ u32 xpos;
+ if (mosaic) xpos = xoff - CurBGXMosaicTable[i];
+ else xpos = xoff;
+
+ if ((!mosaic && (!(xpos & 0x7))) ||
+ (mosaic && ((xpos >> 3) != (lastxpos >> 3))))
{
// load a new tile
- curtile = GPU::ReadVRAM_BG(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3));
+ curtile = GPU::ReadVRAM_BG(tilemapaddr + ((xpos & 0xF8) >> 2) + ((xpos & widexmask) << 3));
if (extpal) curpal = GetBGExtPal(extpalslot, curtile>>12);
else curpal = pal;
pixelsaddr = tilesetaddr + ((curtile & 0x03FF) << 6)
+ (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 3);
+
+ if (mosaic) lastxpos = xpos;
}
// draw pixel
if (WindowMask[i] & (1<(pixelsaddr + tilexoff);
- xmos = xmossize;
- }
- else
- xmos--;
+ u32 tilexoff = (curtile & 0x0400) ? (7-(xpos&0x7)) : (xpos&0x7);
+ color = GPU::ReadVRAM_BG(pixelsaddr + tilexoff);
if (color)
DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3));
curpal = pal + ((curtile & 0xF000) >> 8);
pixelsaddr = tilesetaddr + ((curtile & 0x03FF) << 5)
+ (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 2);
}
+ if (mosaic) lastxpos = xoff;
+
for (int i = 0; i < 256; i++)
{
- if (!(xoff & 0x7))
+ u32 xpos;
+ if (mosaic) xpos = xoff - CurBGXMosaicTable[i];
+ else xpos = xoff;
+
+ if ((!mosaic && (!(xpos & 0x7))) ||
+ (mosaic && ((xpos >> 3) != (lastxpos >> 3))))
{
// load a new tile
- curtile = GPU::ReadVRAM_BG(tilemapaddr + ((xoff & 0xF8) >> 2) + ((xoff & widexmask) << 3));
+ curtile = GPU::ReadVRAM_BG(tilemapaddr + ((xpos & 0xF8) >> 2) + ((xpos & widexmask) << 3));
curpal = pal + ((curtile & 0xF000) >> 8);
pixelsaddr = tilesetaddr + ((curtile & 0x03FF) << 5)
+ (((curtile & 0x0800) ? (7-(yoff&0x7)) : (yoff&0x7)) << 2);
+
+ if (mosaic) lastxpos = xpos;
}
// draw pixel
- // TODO: optimize VRAM access
if (WindowMask[i] & (1<(pixelsaddr + (tilexoff >> 1)) >> 4;
- }
- else
- {
- color = GPU::ReadVRAM_BG(pixelsaddr + (tilexoff >> 1)) & 0x0F;
- }
- xmos = xmossize;
+ color = GPU::ReadVRAM_BG(pixelsaddr + (tilexoff >> 1)) >> 4;
}
else
- xmos--;
+ {
+ color = GPU::ReadVRAM_BG(pixelsaddr + (tilexoff >> 1)) & 0x0F;
+ }
if (color)
DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<
void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)
{
u16 bgcnt = BGCnt[bgnum];
- u32 xmos = 0, xmossize = 0;
u32 tilesetaddr, tilemapaddr;
u16* pal;
@@ -1808,10 +1862,9 @@ void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)
if (bgcnt & 0x0040)
{
- // mosaic
+ // vertical mosaic
rotX -= (BGMosaicY * rotB);
rotY -= (BGMosaicY * rotD);
- xmossize = BGMosaicSize[0];
}
if (Num)
@@ -1838,28 +1891,31 @@ void GPU2D::DrawBG_Affine(u32 line, u32 bgnum)
{
if (WindowMask[i] & (1< 0)
+ s32 finalX, finalY;
+ if (mosaic)
{
- if (color)
- DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<(tilemapaddr + ((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11)));
+ finalX = rotX;
+ finalY = rotY;
+ }
+
+ if ((!((finalX|finalY) & overflowmask)))
+ {
+ curtile = GPU::ReadVRAM_BG(tilemapaddr + ((((finalY & coordmask) >> 11) << yshift) + ((finalX & coordmask) >> 11)));
// draw pixel
- u32 tilexoff = (rotX >> 8) & 0x7;
- u32 tileyoff = (rotY >> 8) & 0x7;
+ u32 tilexoff = (finalX >> 8) & 0x7;
+ u32 tileyoff = (finalY >> 8) & 0x7;
color = GPU::ReadVRAM_BG(tilesetaddr + (curtile << 6) + (tileyoff << 3) + tilexoff);
if (color)
DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<
void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)
{
u16 bgcnt = BGCnt[bgnum];
- u32 xmos = 0, xmossize = 0;
u32 tilesetaddr, tilemapaddr;
u16* pal;
@@ -1892,10 +1948,9 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)
if (bgcnt & 0x0040)
{
- // mosaic
+ // vertical mosaic
rotX -= (BGMosaicY * rotB);
rotY -= (BGMosaicY * rotD);
- xmossize = BGMosaicSize[0];
}
if (bgcnt & 0x0080)
@@ -1937,22 +1992,25 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)
{
if (WindowMask[i] & (1< 0)
+ s32 finalX, finalY;
+ if (mosaic)
{
- if (color & 0x8000)
- DrawPixel(&BGOBJLine[i], color, 0x01000000<(tilemapaddr + (((((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8)) << 1));
+ finalX = rotX;
+ finalY = rotY;
+ }
+
+ if (!(finalX & ofxmask) && !(finalY & ofymask))
+ {
+ color = GPU::ReadVRAM_BG(tilemapaddr + (((((finalY & ymask) >> 8) << yshift) + ((finalX & xmask) >> 8)) << 1));
if (color & 0x8000)
DrawPixel(&BGOBJLine[i], color, 0x01000000< 0)
+ s32 finalX, finalY;
+ if (mosaic)
{
- if (color)
- DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8));
+ finalX = rotX;
+ finalY = rotY;
+ }
+
+ if (!(finalX & ofxmask) && !(finalY & ofymask))
+ {
+ color = GPU::ReadVRAM_BG(tilemapaddr + (((finalY & ymask) >> 8) << yshift) + ((finalX & xmask) >> 8));
if (color)
DrawPixel(&BGOBJLine[i], pal[color], 0x01000000< 0)
+ s32 finalX, finalY;
+ if (mosaic)
{
- if (color)
- DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<(tilemapaddr + (((((rotY & coordmask) >> 11) << yshift) + ((rotX & coordmask) >> 11)) << 1));
+ finalX = rotX;
+ finalY = rotY;
+ }
+
+ if ((!((finalX|finalY) & overflowmask)))
+ {
+ curtile = GPU::ReadVRAM_BG(tilemapaddr + (((((finalY & coordmask) >> 11) << yshift) + ((finalX & coordmask) >> 11)) << 1));
if (extpal) curpal = GetBGExtPal(bgnum, curtile>>12);
else curpal = pal;
// draw pixel
- u32 tilexoff = (rotX >> 8) & 0x7;
- u32 tileyoff = (rotY >> 8) & 0x7;
+ u32 tilexoff = (finalX >> 8) & 0x7;
+ u32 tileyoff = (finalY >> 8) & 0x7;
if (curtile & 0x0400) tilexoff = 7-tilexoff;
if (curtile & 0x0800) tileyoff = 7-tileyoff;
@@ -2066,8 +2132,6 @@ void GPU2D::DrawBG_Extended(u32 line, u32 bgnum)
if (color)
DrawPixel(&BGOBJLine[i], curpal[color], 0x01000000<
void GPU2D::DrawBG_Large(u32 line) // BG is always BG2
{
u16 bgcnt = BGCnt[2];
- u32 xmos = 0, xmossize = 0;
u32 tilesetaddr, tilemapaddr;
u16* pal;
@@ -2125,10 +2189,9 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2
if (bgcnt & 0x0040)
{
- // mosaic
+ // vertical mosaic
rotX -= (BGMosaicY * rotB);
rotY -= (BGMosaicY * rotD);
- xmossize = BGMosaicSize[0];
}
if (Num) tilemapaddr = 0x06200000;
@@ -2145,17 +2208,22 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2
{
if (WindowMask[i] & (1<<2))
{
- if (xmos > 0)
+ s32 finalX, finalY;
+ if (mosaic)
{
- if (color)
- DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<2);
-
- xmos--;
+ int im = CurBGXMosaicTable[i];
+ finalX = rotX - (im * rotA);
+ finalY = rotY - (im * rotC);
}
else
- if (!(rotX & ofxmask) && !(rotY & ofymask))
{
- color = GPU::ReadVRAM_BG(tilemapaddr + (((rotY & ymask) >> 8) << yshift) + ((rotX & xmask) >> 8));
+ finalX = rotX;
+ finalY = rotY;
+ }
+
+ if (!(finalX & ofxmask) && !(finalY & ofymask))
+ {
+ color = GPU::ReadVRAM_BG(tilemapaddr + (((finalY & ymask) >> 8) << yshift) + ((finalX & xmask) >> 8));
if (color)
DrawPixel(&BGOBJLine[i], pal[color], 0x01000000<<2);
@@ -2170,17 +2238,88 @@ void GPU2D::DrawBG_Large(u32 line) // BG is always BG2
BGYRefInternal[0] += rotD;
}
+void GPU2D::ApplySpriteMosaicX()
+{
+ // apply X mosaic if needed
+ // X mosaic for sprites is applied after all sprites are rendered
+
+ if (OBJMosaicSize[0] == 0) return;
+
+ u32 lastcolor = OBJLine[0];
+
+ for (u32 i = 1; i < 256; i++)
+ {
+ if (!(OBJLine[i] & 0x100000))
+ {
+ // not a mosaic'd sprite pixel
+ continue;
+ }
+
+ if ((!(OBJLine[i-1] & 0x100000)) || (CurOBJXMosaicTable[i] == 0))
+ lastcolor = OBJLine[i];
+ else
+ OBJLine[i] = lastcolor;
+ }
+}
+
void GPU2D::InterleaveSprites(u32 prio)
{
- for (u32 i = 0; i < 256; i++)
+ u16* pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200];
+
+ if (DispCnt & 0x80000000)
{
- if (((OBJLine[i] & 0xF8000) == prio) && (WindowMask[i] & 0x10))
+ u16* extpal = GetOBJExtPal();
+
+ for (u32 i = 0; i < 256; i++)
{
- DrawPixel(&BGOBJLine[i], OBJLine[i] & 0x7FFF, OBJLine[i] & 0xFF000000);
+ if ((OBJLine[i] & 0x70000) != prio) continue;
+ if (!(WindowMask[i] & 0x10)) continue;
+
+ u16 color;
+ u32 pixel = OBJLine[i];
+
+ if (pixel & 0x8000)
+ color = pixel & 0x7FFF;
+ else if (pixel & 0x1000)
+ color = pal[pixel & 0xFF];
+ else
+ color = extpal[pixel & 0xFFF];
+
+ DrawPixel(&BGOBJLine[i], color, pixel & 0xFF000000);
+ }
+ }
+ else
+ {
+ // optimized no-extpal version
+
+ for (u32 i = 0; i < 256; i++)
+ {
+ if ((OBJLine[i] & 0x70000) != prio) continue;
+ if (!(WindowMask[i] & 0x10)) continue;
+
+ u16 color;
+ u32 pixel = OBJLine[i];
+
+ if (pixel & 0x8000)
+ color = pixel & 0x7FFF;
+ else
+ color = pal[pixel & 0xFF];
+
+ DrawPixel(&BGOBJLine[i], color, pixel & 0xFF000000);
}
}
}
+#define DoDrawSprite(type, ...) \
+ if (iswin) \
+ { \
+ DrawSprite_##type(__VA_ARGS__); \
+ } \
+ else \
+ { \
+ DrawSprite_##type(__VA_ARGS__); \
+ }
+
void GPU2D::DrawSprites(u32 line)
{
NumSprites = 0;
@@ -2241,10 +2380,7 @@ void GPU2D::DrawSprites(u32 line)
u32 rotparamgroup = (attrib[1] >> 9) & 0x1F;
- if (iswin)
- DrawSprite_Rotscale(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
- else
- DrawSprite_Rotscale(attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
+ DoDrawSprite(Rotscale, attrib, &oam[(rotparamgroup*16) + 3], boundwidth, boundheight, width, height, xpos, ypos);
NumSprites++;
}
@@ -2270,10 +2406,7 @@ void GPU2D::DrawSprites(u32 line)
if (attrib[1] & 0x2000)
ypos = height-1 - ypos;
- if (iswin)
- DrawSprite_Normal(attrib, width, xpos, ypos);
- else
- DrawSprite_Normal(attrib, width, xpos, ypos);
+ DoDrawSprite(Normal, attrib, width, xpos, ypos);
NumSprites++;
}
@@ -2284,25 +2417,22 @@ void GPU2D::DrawSprites(u32 line)
template
void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos)
{
- u32 prio = ((attrib[2] & 0x0C00) << 6) | 0x8000;
+ u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000;
u32 tilenum = attrib[2] & 0x03FF;
u32 spritemode = window ? 0 : ((attrib[0] >> 10) & 0x3);
- u32 xmos = 0, xmossize = 0;
u32 ytilefactor;
s32 centerX = boundwidth >> 1;
s32 centerY = boundheight >> 1;
- if (attrib[0] & 0x1000)
+ if ((attrib[0] & 0x1000) && !window)
{
- // mosaic
- ypos -= OBJMosaicY;
+ // apply Y mosaic
+ ypos = OBJMosaicY - (attrib[0] & 0xFF);
if (ypos < 0) ypos = 0;
- xmossize = OBJMosaicSize[0];
- if (xpos > 0)
- xmos = (xmossize+1) - (xpos % (xmossize+1));
+ pixelattr |= 0x100000;
}
u32 xoff;
@@ -2337,7 +2467,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (!alpha) return;
alpha++;
- prio |= (0xC0000000 | (alpha << 24));
+ pixelattr |= (0xC0000000 | (alpha << 24));
if (DispCnt & 0x40)
{
@@ -2369,31 +2499,23 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
}
u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;
- if (xmos && !(attrib[0]&0x0200))
- color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1));
for (; xoff < boundwidth;)
{
if ((u32)rotX < width && (u32)rotY < height)
{
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1));
- xmos = xmossize;
- }
- else
- xmos--;
+ color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY >> 8) * ytilefactor) + ((rotX >> 8) << 1));
if (color & 0x8000)
{
if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = color | prio;
+ else OBJLine[xpos] = color | pixelattr;
+ }
+ else if (!window)
+ {
+ if (OBJLine[xpos] == 0)
+ OBJLine[xpos] = pixelattr & 0x180000;
}
- }
- else
- {
- if (xmos == 0) xmos = xmossize;
- else xmos--;
}
rotX += rotA;
@@ -2414,8 +2536,8 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
ytilefactor = 0x20;
}
- if (spritemode == 1) prio |= 0x80000000;
- else prio |= 0x10000000;
+ if (spritemode == 1) pixelattr |= 0x80000000;
+ else pixelattr |= 0x10000000;
if (attrib[0] & 0x2000)
{
@@ -2424,40 +2546,30 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
ytilefactor <<= 5;
u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;
- u32 extpal = (DispCnt & 0x80000000);
-
- u16* pal;
if (!window)
{
- if (extpal) pal = GetOBJExtPal(attrib[2] >> 12);
- else pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200];
+ if (!(DispCnt & 0x80000000))
+ pixelattr |= 0x1000;
+ else
+ pixelattr |= ((attrib[2] & 0xF000) >> 4);
}
- if (xmos && !(attrib[0]&0x0200))
- color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8));
-
for (; xoff < boundwidth;)
{
if ((u32)rotX < width && (u32)rotY < height)
{
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8));
- xmos = xmossize;
- }
- else
- xmos--;
+ color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>5) + ((rotX>>11)*64) + ((rotX&0x700)>>8));
if (color)
{
if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = pal[color] | prio;
+ else OBJLine[xpos] = color | pixelattr;
+ }
+ else if (!window)
+ {
+ if (OBJLine[xpos] == 0)
+ OBJLine[xpos] = pixelattr & 0x180000;
}
- }
- else
- {
- if (xmos == 0) xmos = xmossize;
- else xmos--;
}
rotX += rotA;
@@ -2473,49 +2585,32 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
ytilefactor <<= 5;
u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;
- u16* pal;
if (!window)
{
- pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200];
- pal += (attrib[2] & 0xF000) >> 8;
- }
-
- if (xmos && !(attrib[0]&0x0200))
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9));
- if (rotX & 0x100)
- color >>= 4;
- else
- color &= 0x0F;
+ pixelattr |= 0x1000;
+ pixelattr |= ((attrib[2] & 0xF000) >> 8);
}
for (; xoff < boundwidth;)
{
if ((u32)rotX < width && (u32)rotY < height)
{
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9));
- if (rotX & 0x100)
- color >>= 4;
- else
- color &= 0x0F;
-
- xmos = xmossize;
- }
+ color = GPU::ReadVRAM_OBJ(pixelsaddr + ((rotY>>11)*ytilefactor) + ((rotY&0x700)>>6) + ((rotX>>11)*32) + ((rotX&0x700)>>9));
+ if (rotX & 0x100)
+ color >>= 4;
else
- xmos--;
+ color &= 0x0F;
if (color)
{
if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = pal[color] | prio;
+ else OBJLine[xpos] = color | pixelattr;
+ }
+ else if (!window)
+ {
+ if (OBJLine[xpos] == 0)
+ OBJLine[xpos] = pixelattr & 0x180000;
}
- }
- else
- {
- if (xmos == 0) xmos = xmossize;
- else xmos--;
}
rotX += rotA;
@@ -2530,22 +2625,19 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
template
void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
{
- u32 prio = ((attrib[2] & 0x0C00) << 6) | 0x8000;
+ u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000;
u32 tilenum = attrib[2] & 0x03FF;
u32 spritemode = window ? 0 : ((attrib[0] >> 10) & 0x3);
- u32 xmos = 0, xmossize = 0;
u32 wmask = width - 8; // really ((width - 1) & ~0x7)
- if (attrib[0] & 0x1000)
+ if ((attrib[0] & 0x1000) && !window)
{
- // mosaic
- ypos -= OBJMosaicY;
+ // apply Y mosaic
+ ypos = OBJMosaicY - (attrib[0] & 0xFF);
if (ypos < 0) ypos = 0;
- xmossize = OBJMosaicSize[0];
- if (xpos > 0)
- xmos = (xmossize+1) - (xpos % (xmossize+1));
+ pixelattr |= 0x100000;
}
u32 xoff;
@@ -2572,7 +2664,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
if (!alpha) return;
alpha++;
- prio |= (0xC0000000 | (alpha << 24));
+ pixelattr |= (0xC0000000 | (alpha << 24));
if (DispCnt & 0x40)
{
@@ -2604,60 +2696,39 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
}
u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;
+ s32 pixelstride;
- if (attrib[1] & 0x1000)
+ if (attrib[1] & 0x1000) // xflip
{
- pixelsaddr += ((width-1 - xoff) << 1);
- if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr);
-
- for (; xoff < xend;)
- {
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr);
- xmos = xmossize;
- }
- else
- xmos--;
-
- pixelsaddr -= 2;
-
- if (color & 0x8000)
- {
- if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = color | prio;
- }
-
- xoff++;
- xpos++;
- }
+ pixelsaddr += (width-1 << 1);
+ pixelsaddr -= (xoff << 1);
+ pixelstride = -2;
}
else
{
pixelsaddr += (xoff << 1);
- if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr);
+ pixelstride = 2;
+ }
- for (; xoff < xend;)
+ for (; xoff < xend;)
+ {
+ color = GPU::ReadVRAM_OBJ(pixelsaddr);
+
+ pixelsaddr += pixelstride;
+
+ if (color & 0x8000)
{
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr);
- xmos = xmossize;
- }
- else
- xmos--;
-
- pixelsaddr += 2;
-
- if (color & 0x8000)
- {
- if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = color | prio;
- }
-
- xoff++;
- xpos++;
+ if (window) OBJWindow[xpos] = 1;
+ else OBJLine[xpos] = color | pixelattr;
}
+ else if (!window)
+ {
+ if (OBJLine[xpos] == 0)
+ OBJLine[xpos] = pixelattr & 0x180000;
+ }
+
+ xoff++;
+ xpos++;
}
}
else
@@ -2672,8 +2743,8 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
tilenum += ((ypos >> 3) * 0x20);
}
- if (spritemode == 1) prio |= 0x80000000;
- else prio |= 0x10000000;
+ if (spritemode == 1) pixelattr |= 0x80000000;
+ else pixelattr |= 0x10000000;
if (attrib[0] & 0x2000)
{
@@ -2681,73 +2752,51 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
tilenum <<= 5;
u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;
pixelsaddr += ((ypos & 0x7) << 3);
+ s32 pixelstride;
- u32 extpal = (DispCnt & 0x80000000);
-
- u16* pal;
if (!window)
{
- if (extpal) pal = GetOBJExtPal(attrib[2] >> 12);
- else pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200];
+ if (!(DispCnt & 0x80000000))
+ pixelattr |= 0x1000;
+ else
+ pixelattr |= ((attrib[2] & 0xF000) >> 4);
}
- if (attrib[1] & 0x1000) // xflip. TODO: do better? oh well for now this works
+ if (attrib[1] & 0x1000) // xflip
{
- pixelsaddr += (((width-1 - xoff) & wmask) << 3);
- pixelsaddr += ((width-1 - xoff) & 0x7);
- if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr);
-
- for (; xoff < xend;)
- {
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr);
- xmos = xmossize;
- }
- else
- xmos--;
-
- pixelsaddr--;
-
- if (color)
- {
- if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = pal[color] | prio;
- }
-
- xoff++;
- xpos++;
- if (!(xoff & 0x7)) pixelsaddr -= 56;
- }
+ pixelsaddr += (((width-1) & wmask) << 3);
+ pixelsaddr += ((width-1) & 0x7);
+ pixelsaddr -= ((xoff & wmask) << 3);
+ pixelsaddr -= (xoff & 0x7);
+ pixelstride = -1;
}
else
{
pixelsaddr += ((xoff & wmask) << 3);
pixelsaddr += (xoff & 0x7);
- if (xmos) color = GPU::ReadVRAM_OBJ(pixelsaddr);
+ pixelstride = 1;
+ }
- for (; xoff < xend;)
+ for (; xoff < xend;)
+ {
+ color = GPU::ReadVRAM_OBJ(pixelsaddr);
+
+ pixelsaddr += pixelstride;
+
+ if (color)
{
- if (xmos == 0)
- {
- color = GPU::ReadVRAM_OBJ(pixelsaddr);
- xmos = xmossize;
- }
- else
- xmos--;
-
- pixelsaddr++;
-
- if (color)
- {
- if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = pal[color] | prio;
- }
-
- xoff++;
- xpos++;
- if (!(xoff & 0x7)) pixelsaddr += 56;
+ if (window) OBJWindow[xpos] = 1;
+ else OBJLine[xpos] = color | pixelattr;
}
+ else if (!window)
+ {
+ if (OBJLine[xpos] == 0)
+ OBJLine[xpos] = pixelattr & 0x180000;
+ }
+
+ xoff++;
+ xpos++;
+ if (!(xoff & 0x7)) pixelsaddr += (56 * pixelstride);
}
}
else
@@ -2756,81 +2805,59 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos)
tilenum <<= 5;
u32 pixelsaddr = (Num ? 0x06600000 : 0x06400000) + tilenum;
pixelsaddr += ((ypos & 0x7) << 2);
+ s32 pixelstride;
- u16* pal;
if (!window)
{
- pal = (u16*)&GPU::Palette[Num ? 0x600 : 0x200];
- pal += (attrib[2] & 0xF000) >> 8;
+ pixelattr |= 0x1000;
+ pixelattr |= ((attrib[2] & 0xF000) >> 8);
}
- if (attrib[1] & 0x1000) // xflip. TODO: do better? oh well for now this works
+ // TODO: optimize VRAM access!!
+ // TODO: do xflip better? the 'two pixels per byte' thing makes it a bit shitty
+
+ if (attrib[1] & 0x1000) // xflip
{
- pixelsaddr += (((width-1 - xoff) & wmask) << 2);
- pixelsaddr += (((width-1 - xoff) & 0x7) >> 1);
- if (xmos)
- {
- if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F;
- else color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4;
- }
-
- for (; xoff < xend;)
- {
- if (xmos == 0)
- {
- if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F;
- else color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4;
- xmos = xmossize;
- }
- else
- xmos--;
-
- if (xoff & 0x1) pixelsaddr--;
-
- if (color)
- {
- if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = pal[color] | prio;
- }
-
- xoff++;
- xpos++;
- if (!(xoff & 0x7)) pixelsaddr -= 28;
- }
+ pixelsaddr += (((width-1) & wmask) << 2);
+ pixelsaddr += (((width-1) & 0x7) >> 1);
+ pixelsaddr -= ((xoff & wmask) << 2);
+ pixelsaddr -= ((xoff & 0x7) >> 1);
+ pixelstride = -1;
}
else
{
pixelsaddr += ((xoff & wmask) << 2);
pixelsaddr += ((xoff & 0x7) >> 1);
- if (xmos)
+ pixelstride = 1;
+ }
+
+ for (; xoff < xend;)
+ {
+ if (attrib[1] & 0x1000)
{
- if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4;
- else color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F;
+ if (xoff & 0x1) { color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F; pixelsaddr--; }
+ else color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4;
+ }
+ else
+ {
+ if (xoff & 0x1) { color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4; pixelsaddr++; }
+ else color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F;
}
- for (; xoff < xend;)
+ if (color)
{
- if (xmos == 0)
- {
- if (xoff & 0x1) color = GPU::ReadVRAM_OBJ(pixelsaddr) >> 4;
- else color = GPU::ReadVRAM_OBJ(pixelsaddr) & 0x0F;
- xmos = xmossize;
- }
- else
- xmos--;
-
- if (xoff & 0x1) pixelsaddr++;
-
- if (color)
- {
- if (window) OBJWindow[xpos] = 1;
- else OBJLine[xpos] = pal[color] | prio;
- }
-
- xoff++;
- xpos++;
- if (!(xoff & 0x7)) pixelsaddr += 28;
+ if (window) OBJWindow[xpos] = 1;
+ else OBJLine[xpos] = color | pixelattr;
}
+ else if (!window)
+ {
+ if (OBJLine[xpos] == 0)
+ OBJLine[xpos] = pixelattr & 0x180000;
+ }
+
+ xoff++;
+ xpos++;
+ if (!(xoff & 0x7)) pixelsaddr += ((attrib[1] & 0x1000) ? -28 : 28);
}
}
}
diff --git a/src/GPU2D.h b/src/GPU2D.h
index 6ad97830..8c6ec276 100644
--- a/src/GPU2D.h
+++ b/src/GPU2D.h
@@ -63,7 +63,7 @@ public:
void OBJExtPalDirty();
u16* GetBGExtPal(u32 slot, u32 pal);
- u16* GetOBJExtPal(u32 pal);
+ u16* GetOBJExtPal();
private:
u32 Num;
@@ -111,7 +111,11 @@ private:
u8 BGMosaicSize[2];
u8 OBJMosaicSize[2];
u8 BGMosaicY, BGMosaicYMax;
- u8 OBJMosaicY, OBJMosaicYMax;
+ u8 OBJMosaicYCount, OBJMosaicY, OBJMosaicYMax;
+
+ u8 MosaicTable[16][256];
+ u8* CurBGXMosaicTable;
+ u8* CurOBJXMosaicTable;
u16 BlendCnt;
u16 BlendAlpha;
@@ -133,6 +137,8 @@ private:
u32 ColorBrightnessDown(u32 val, u32 factor);
u32 ColorComposite(int i, u32 val1, u32 val2);
+ void UpdateMosaicCounters(u32 line);
+
template void DrawScanlineBGMode(u32 line);
void DrawScanlineBGMode6(u32 line);
void DrawScanlineBGMode7(u32 line);
@@ -143,11 +149,12 @@ private:
void (*DrawPixel)(u32* dst, u16 color, u32 flag);
void DrawBG_3D();
- void DrawBG_Text(u32 line, u32 bgnum);
- void DrawBG_Affine(u32 line, u32 bgnum);
- void DrawBG_Extended(u32 line, u32 bgnum);
- void DrawBG_Large(u32 line);
+ template void DrawBG_Text(u32 line, u32 bgnum);
+ template void DrawBG_Affine(u32 line, u32 bgnum);
+ template void DrawBG_Extended(u32 line, u32 bgnum);
+ template void DrawBG_Large(u32 line);
+ void ApplySpriteMosaicX();
void InterleaveSprites(u32 prio);
template void DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos);
template void DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos);
diff --git a/src/libui_sdl/MelonCap.cpp b/src/libui_sdl/MelonCap.cpp
new file mode 100644
index 00000000..fde440ae
--- /dev/null
+++ b/src/libui_sdl/MelonCap.cpp
@@ -0,0 +1,347 @@
+/*
+ Copyright 2016-2019 Arisotura
+
+ This file is part of melonDS.
+
+ melonDS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#include
+#include
+#include "MelonCap.h"
+#include "libui/ui.h"
+#include "../NDS.h"
+#include "../GPU.h"
+
+#include
+#include
+#include
+#include
+
+
+namespace MelonCap
+{
+
+uiWindow* Window;
+uiArea* Area;
+uiAreaHandler AreaHandler;
+uiDrawBitmap* WinBitmap;
+bool WinBitmapInited;
+
+u32* WinBitmapData;
+
+// this crap was built from the reverse-engineering of ds_capture.exe
+// mixed in with their Linux capture sample code
+
+GUID InterfaceClass = {0xA0B880F6, 0xD6A5, 0x4700, {0xA8, 0xEA, 0x22, 0x28, 0x2A, 0xCA, 0x55, 0x87}};
+HANDLE CapHandle;
+WINUSB_INTERFACE_HANDLE CapUSBHandle;
+
+
+void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params)
+{
+ if (!WinBitmapInited)
+ {
+ if (WinBitmap) uiDrawFreeBitmap(WinBitmap);
+
+ WinBitmapInited = true;
+ WinBitmap = uiDrawNewBitmap(params->Context, 768, 384, 0);
+ }
+
+ if (!WinBitmap) return;
+ if (!WinBitmapData) return;
+
+ uiRect rc = {0, 0, 768, 384};
+
+ uiDrawBitmapUpdate(WinBitmap, WinBitmapData);
+ uiDrawBitmapDraw(params->Context, WinBitmap, &rc, &rc, 0);
+}
+
+void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt)
+{
+}
+
+void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left)
+{
+}
+
+void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area)
+{
+}
+
+int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
+{
+ return 1;
+}
+
+void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
+{
+}
+
+
+void Init()
+{
+ printf("MelonCap init\n");
+
+ HDEVINFO devinfo = SetupDiGetClassDevsW(&InterfaceClass, NULL, NULL, DIGCF_DEVICEINTERFACE|DIGCF_PRESENT);
+ if (devinfo == INVALID_HANDLE_VALUE) return;
+
+ int member = 0;
+ bool good = false;
+ for (;;)
+ {
+ SP_DEVICE_INTERFACE_DATA interfacedata;
+ memset(&interfacedata, 0, sizeof(interfacedata));
+ interfacedata.cbSize = sizeof(interfacedata);
+
+ BOOL ret = SetupDiEnumDeviceInterfaces(devinfo, NULL, &InterfaceClass, member, &interfacedata);
+ if (!ret)
+ {
+ printf("found %d interfaces\n", member);
+ break;
+ }
+
+ DWORD requiredsize = 0;
+ SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, NULL, NULL, &requiredsize, NULL);
+ printf("%d: required size %d\n", member, requiredsize);
+
+ PSP_DEVICE_INTERFACE_DETAIL_DATA_W interfacedetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new u8[requiredsize];
+ interfacedetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_W);
+ ret = SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, interfacedetail, requiredsize, NULL, NULL);
+ if (ret)
+ {
+ printf("got interface detail: path=%S\n", interfacedetail->DevicePath);
+ HANDLE file = CreateFileW(interfacedetail->DevicePath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+ if (file != INVALID_HANDLE_VALUE)
+ {
+ WINUSB_INTERFACE_HANDLE usbhandle;
+ ret = WinUsb_Initialize(file, &usbhandle);
+ if (ret)
+ {
+ int val;
+ val = 0x1E;
+ WinUsb_SetPipePolicy(usbhandle, 0x00, PIPE_TRANSFER_TIMEOUT, 4, &val);
+ val = 0x32;
+ WinUsb_SetPipePolicy(usbhandle, 0x82, PIPE_TRANSFER_TIMEOUT, 4, &val);
+ val = 0x01;
+ WinUsb_SetPipePolicy(usbhandle, 0x82, RAW_IO, 1, &val);
+
+ printf("looking good\n");
+ good = true;
+
+ CapHandle = file;
+ CapUSBHandle = usbhandle;
+ }
+ else
+ CloseHandle(file);
+ }
+ }
+
+ delete[] (u8*)interfacedetail;
+
+ if (good) break;
+
+ member++;
+ }
+
+ SetupDiDestroyDeviceInfoList(devinfo);
+
+
+ AreaHandler.Draw = OnAreaDraw;
+ AreaHandler.MouseEvent = OnAreaMouseEvent;
+ AreaHandler.MouseCrossed = OnAreaMouseCrossed;
+ AreaHandler.DragBroken = OnAreaDragBroken;
+ AreaHandler.KeyEvent = OnAreaKeyEvent;
+ AreaHandler.Resize = OnAreaResize;
+
+ WinBitmapInited = false;
+ WinBitmapData = new u32[768*384];
+
+ Window = uiNewWindow("melonDS - topnotch pixel checker", 768, 384, 0, 0, 0);
+ Area = uiNewArea(&AreaHandler);
+ uiWindowSetChild(Window, uiControl(Area));
+
+ uiControlShow(uiControl(Window));
+}
+
+void DeInit()
+{
+ uiControlDestroy(uiControl(Window));
+ uiDrawFreeBitmap(WinBitmap);
+ WinBitmapInited = false;
+ delete[] WinBitmapData;
+
+ WinUsb_Free(CapUSBHandle);
+ CloseHandle(CapHandle);
+}
+
+
+int VendorIn(u8 req, u16 len, u8* buf)
+{
+ WINUSB_SETUP_PACKET pkt;
+ pkt.RequestType = 0xC0; // device to host
+ pkt.Request = req;
+ pkt.Value = 0; // ?????
+ pkt.Index = 0;
+ pkt.Length = len;
+
+ ULONG ret = 0;
+ BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL);
+ if (!res) return -1;
+ return ret;
+}
+
+int VendorOut(u8 req, u16 val, u16 len, u8* buf)
+{
+ WINUSB_SETUP_PACKET pkt;
+ pkt.RequestType = 0x40; // host to device
+ pkt.Request = req;
+ pkt.Value = val;
+ pkt.Index = 0;
+ pkt.Length = len;
+
+ ULONG ret = 0;
+ BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL);
+ if (!res) return -1;
+ return ret;
+}
+
+int BulkIn(u8* buf, u32 len)
+{
+ ULONG ret = 0;
+ BOOL res = WinUsb_ReadPipe(CapUSBHandle, 0x82, buf, len, &ret, NULL);
+ if (!res) return -1;
+ return ret;
+}
+
+
+u32 ConvertColor(u16 col)
+{
+ u32 b = col & 0x001F;
+ u32 g = (col & 0x07E0) >> 5;
+ u32 r = (col & 0xF800) >> 11;
+
+ u32 ret = 0xFF000000;
+ ret |= ((r << 3) | (r >> 2)) << 16;
+ ret |= ((g << 2) | (g >> 4)) << 8;
+ ret |= (b << 3) | (b >> 2);
+ return ret;
+}
+
+void CaptureFrame()
+{
+ u32 ret;
+ u8 derp;
+ u32 framelen = 256*384*2;
+ u16 frame[framelen/2];
+ u32 framepos = 0;
+ u8 frameinfo[64];
+
+ ret = VendorOut(0x30, 0, 0, &derp);
+ if (ret < 0) return;
+
+ int tries = 0;
+ while (framepos < framelen)
+ {
+ ret = BulkIn((u8*)&frame[framepos/2], framelen-framepos);
+ if (ret < 0) break;
+ if (ret == 0)
+ {
+ tries++;
+ if (tries >= 100) break;
+ continue;
+ }
+ framepos += ret;
+ }
+
+ ret = VendorIn(0x30, 64, frameinfo);
+ if (ret < 0) return;
+ if ((frameinfo[0] & 0x03) != 0x03) return;
+ if (!frameinfo[52]) return;
+
+ u16* in = &frame[0];
+ u32* out = &WinBitmapData[256];
+
+ for (int y = 0; y < 384; y++)
+ {
+ u32* out = &WinBitmapData[((y/2)*768) + ((y&1)*128) + 256];
+
+ if (!(frameinfo[y>>3] & (1<<(y&7))))
+ {
+ continue;
+ }
+
+ for (int x = 0; x < 256/2; x++)
+ {
+ out[0] = ConvertColor(in[1]);
+ out[768*192] = ConvertColor(in[0]);
+ out++;
+ in += 2;
+ }
+ }
+}
+
+void Update()
+{
+ // melonDS output
+
+ int frontbuf = GPU::FrontBuffer;
+
+ u32* topbuf = GPU::Framebuffer[frontbuf][0];
+ if (topbuf)
+ {
+ for (int y = 0; y < 192; y++)
+ {
+ memcpy(&WinBitmapData[y*768], &topbuf[y*256], 256*4);
+ }
+ }
+
+ u32* botbuf = GPU::Framebuffer[frontbuf][1];
+ if (botbuf)
+ {
+ for (int y = 0; y < 192; y++)
+ {
+ memcpy(&WinBitmapData[(y+192)*768], &botbuf[y*256], 256*4);
+ }
+ }
+
+ // DS capture
+
+ CaptureFrame();
+
+ // compare
+
+ for (int y = 0; y < 384; y++)
+ {
+ for (int x = 0; x < 256; x++)
+ {
+ u32 colA = WinBitmapData[(y*768) + x + 0];
+ u32 colB = WinBitmapData[(y*768) + x + 256];
+
+ // best we get from the capture card is RGB565
+ // so we'll ignore the lower bits
+ const u32 mask = 0x00F8FCF8;
+ colA &= mask;
+ colB &= mask;
+
+ if (colA == colB) WinBitmapData[(y*768) + x + 512] = 0xFF00FF00;
+ else WinBitmapData[(y*768) + x + 512] = 0xFFFF0000;
+ }
+ }
+
+ uiAreaQueueRedrawAll(Area);
+}
+
+}
diff --git a/src/libui_sdl/MelonCap.h b/src/libui_sdl/MelonCap.h
new file mode 100644
index 00000000..1185dea0
--- /dev/null
+++ b/src/libui_sdl/MelonCap.h
@@ -0,0 +1,34 @@
+/*
+ Copyright 2016-2019 Arisotura
+
+ This file is part of melonDS.
+
+ melonDS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef MELONCAP_H
+#define MELONCAP_H
+
+#include "types.h"
+
+namespace MelonCap
+{
+
+void Init();
+void DeInit();
+
+void Update();
+
+}
+
+#endif // MELONCAP_H
diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp
index d3f9024c..08d38b2b 100644
--- a/src/libui_sdl/main.cpp
+++ b/src/libui_sdl/main.cpp
@@ -48,6 +48,10 @@
#include "OSD.h"
+#ifdef MELONCAP
+#include "MelonCap.h"
+#endif // MELONCAP
+
// savestate slot mapping
// 1-8: regular slots (quick access)
@@ -999,6 +1003,10 @@ int EmuThreadFunc(void* burp)
// emulate
u32 nlines = NDS::RunFrame();
+#ifdef MELONCAP
+ MelonCap::Update();
+#endif // MELONCAP
+
if (EmuRunning == 0) break;
if (Screen_UseGL)
@@ -2737,6 +2745,10 @@ int main(int argc, char** argv)
uiMenuItemSetChecked(MenuItem_AudioSync, Config::AudioSync==1);
uiMenuItemSetChecked(MenuItem_ShowOSD, Config::ShowOSD==1);
+#ifdef MELONCAP
+ MelonCap::Init();
+#endif // MELONCAP
+
AudioSync = SDL_CreateCond();
AudioSyncLock = SDL_CreateMutex();
@@ -2820,6 +2832,10 @@ int main(int argc, char** argv)
if (MicWavBuffer) delete[] MicWavBuffer;
+#ifdef MELONCAP
+ MelonCap::DeInit();
+#endif // MELONCAP
+
if (ScreenBitmap[0]) uiDrawFreeBitmap(ScreenBitmap[0]);
if (ScreenBitmap[1]) uiDrawFreeBitmap(ScreenBitmap[1]);