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]);