akmenu-next/arm9/source/gdi.cpp
ApacheThunder 7f66ae6613 Fix DSi build
* Fix DSI build. Now works correctly on DSi.
* Fixed soft reset into boot.nds. Now works on both DSi and flashcarts.
Also fixed for theme reset.
* Fixed soft reset into GBA mode. Was using libnds's broken swi call.
Replaced with modified version that isn't broken.
* Fixed warnings generated during compile.
* Removed old LD/Specs from arm7 folder. This is needed to allow proper
arm7 entry point for TWL_FIRM compatiblity on 3DS. Didn't seem to break
DS mode copy so a DSi specific build of arm7 make file is not needed at
the moment.
* custom banner added via banner.bin and -t ndstool command.
* Flashcart build now uses -h 0x200 command for better compatiblity with
older homebrew launchers/flashcarts.
2024-12-22 16:28:38 -06:00

588 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
gdi.cpp
Copyright (C) 2007 Acekard, www.acekard.com
Copyright (C) 2007-2009 somebody
Copyright (C) 2009 yellow wood goblin
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "gdi.h"
#include <cstdio>
#include <cstring>
#include "../../share/memtool.h"
#include "dbgtool.h"
#include "fontfactory.h"
#include "globalsettings.h"
#include "sprite.h"
#include "userinput.h"
static inline void dmaCopyWordsGdi(uint8 channel, const void* src, void* dest, uint32 size) {
DC_FlushRange(src, size);
dmaCopyWords(channel, src, dest, size);
DC_InvalidateRange(dest, size);
}
#ifdef DEBUG
PrintConsole custom_console;
static void MyInitConsole(u16* aBufferSub1, u16* aBufferSub2) {
custom_console = *consoleGetDefault();
custom_console.loadGraphics = false;
consoleInit(&custom_console, custom_console.bgLayer, BgType_Text4bpp, BgSize_T_256x256,
custom_console.mapBase, custom_console.gfxBase, false, false);
custom_console.fontBgMap = aBufferSub1;
custom_console.fontBgGfx = aBufferSub2;
dmaCopy(custom_console.font.gfx, custom_console.fontBgGfx,
custom_console.font.numChars * 64 / 2);
custom_console.fontCurPal = 15 << 12;
u16* palette = BG_PALETTE_SUB;
palette[1 * 16 - 15] = RGB15(0, 0, 0); // 30 normal black
palette[2 * 16 - 15] = RGB15(15, 0, 0); // 31 normal red
palette[3 * 16 - 15] = RGB15(0, 15, 0); // 32 normal green
palette[4 * 16 - 15] = RGB15(15, 15, 0); // 33 normal yellow
palette[5 * 16 - 15] = RGB15(0, 0, 15); // 34 normal blue
palette[6 * 16 - 15] = RGB15(15, 0, 15); // 35 normal magenta
palette[7 * 16 - 15] = RGB15(0, 15, 15); // 36 normal cyan
palette[8 * 16 - 15] = RGB15(24, 24, 24); // 37 normal white
palette[9 * 16 - 15] = RGB15(15, 15, 15); // 40 bright black
palette[10 * 16 - 15] = RGB15(31, 0, 0); // 41 bright red
palette[11 * 16 - 15] = RGB15(0, 31, 0); // 42 bright green
palette[12 * 16 - 15] = RGB15(31, 31, 0); // 43 bright yellow
palette[13 * 16 - 15] = RGB15(0, 0, 31); // 44 bright blue
palette[14 * 16 - 15] = RGB15(31, 0, 31); // 45 bright magenta
palette[15 * 16 - 15] = RGB15(0, 31, 31); // 46 bright cyan
palette[16 * 16 - 15] = RGB15(31, 31, 31); // 47 & 39 bright white
}
#endif
cGdi::cGdi() {
_transColor = 0;
_mainEngineLayer = MEL_UP;
_subEngineMode = SEM_TEXT;
_bufferMain2 = NULL;
_bufferSub2 = NULL;
#ifdef DEBUG
_bufferSub3 = NULL;
#endif
_sprites = NULL;
}
cGdi::~cGdi() {
if (NULL != _bufferMain2) delete[] _bufferMain2;
if (NULL != _bufferSub2) delete[] _bufferSub2;
#ifdef DEBUG
if (NULL != _bufferSub3) delete[] _bufferSub3;
#endif
if (NULL != _sprites) delete[] _sprites;
}
void cGdi::init() {
swapLCD();
activeFbMain();
activeFbSub();
cSprite::sysinit();
}
void cGdi::initBg(const std::string& aFileName) {
_sprites = new cSprite[12];
_background = createBMP15FromFile(aFileName);
if (_background.width() < SCREEN_WIDTH && _background.height() < SCREEN_WIDTH) {
_background = createBMP15(SCREEN_WIDTH, SCREEN_HEIGHT);
zeroMemory(_background.buffer(), _background.height() * _background.pitch());
}
u32 pitch = _background.pitch() >> 1;
for (size_t ii = 0; ii < 3; ++ii) {
for (size_t jj = 0; jj < 4; ++jj) {
size_t index = ii * 4 + jj;
_sprites[index].init(2 + index);
_sprites[index].setSize(SS_SIZE_64);
_sprites[index].setPriority(3);
_sprites[index].setBufferOffset(32 + index * 64);
_sprites[index].setPosition(jj * 64, ii * 64);
for (size_t kk = 0; kk < 64; ++kk) {
for (size_t ll = 0; ll < 64; ++ll) {
((u16*)_sprites[index].buffer())[kk * 64 + ll] =
((u16*)_background.buffer())[(kk + ii * 64) * pitch + (ll + jj * 64)];
}
}
_sprites[index].show();
}
}
oamUpdate(&oamMain);
}
void cGdi::swapLCD(void) {
lcdSwap();
}
void cGdi::activeFbMain(void) {
vramSetBankB(VRAM_B_MAIN_BG_0x06000000);
vramSetBankD(VRAM_D_MAIN_BG_0x06020000);
vramSetBankA(VRAM_A_MAIN_SPRITE_0x06400000);
REG_BG2CNT = BG_BMP16_256x256 | BG_BMP_BASE(0) | BG_PRIORITY_1;
REG_BG2PA = 1 << 8; // 2 =放大倍数
REG_BG2PD = 1 << 8; // 2 =放大倍数
REG_BG2PB = 0;
REG_BG2PC = 0;
REG_BG2Y = 0;
REG_BG2X = 0;
REG_BG3CNT = BG_BMP16_256x256 | BG_BMP_BASE(8) | BG_PRIORITY_2;
REG_BG3PA = 1 << 8; // 2 =放大倍数
REG_BG3PD = 1 << 8; // 2 =放大倍数
REG_BG3PB = 0;
REG_BG3PC = 0;
REG_BG3Y = 0;
REG_BG3X = 0;
_bufferMain1 = (u16*)0x06000000;
_bufferMain2 = (u16*)new u32[256 * 192];
_bufferMain3 = (u16*)0x06020000;
setMainEngineLayer(MEL_UP);
zeroMemory(_bufferMain1, 0x20000);
fillMemory(_bufferMain3, 0x20000, 0xffffffff);
REG_BLDCNT = BLEND_ALPHA | BLEND_DST_BG2 | BLEND_DST_BG3;
REG_BLDALPHA = (4 << 8) | 7;
swiWaitForVBlank(); // remove tearing at bottop screen
videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE |
DISPLAY_SPR_1D_BMP_SIZE_128 | DISPLAY_SPR_1D_BMP);
}
void cGdi::activeFbSub(void) {
#ifdef DEBUG
_bufferSub3 = (u16*)new u32[0x1200];
MyInitConsole(_bufferSub3 + 0x2000, _bufferSub3);
#endif
// 分配显存存, 128k
vramSetBankC(VRAM_C_SUB_BG_0x06200000); // 128k
// 初始化为文字模式
_subEngineMode = SEM_GRAPHICS;
// BMP bg 的参数设置,从 VRAM地址 0x06200000 开始优先级3
REG_BG2CNT_SUB = BG_BMP16_256x256 | BG_BMP_BASE(0) | BG_PRIORITY_1;
REG_BG2PA_SUB = 1 << 8;
REG_BG2PD_SUB = 1 << 8;
REG_BG2PB_SUB = 0;
REG_BG2PC_SUB = 0;
REG_BG2Y_SUB = 0;
REG_BG2X_SUB = 0;
_bufferSub1 = (u16*)0x06200000;
_bufferSub2 = (u16*)new u32[256 * 192 / 2];
fillMemory(_bufferSub2, 0x18000, 0xffffffff);
fillMemory(_bufferSub1, 0x18000, 0xffffffff);
// text BG
// text bg 的字模占用 32(字节/字模) * 256(个ascii字) = 8192 字节显存,
// 文字显示占用 32 x 32 * 2 = 2048 字节显存
// 字模从 block 8 开始 = 0x06200000 + 8 * 0x4000 = 0x06220000
// 文字信息从 block 72 开始 = 0x06200000 + 72 * 0x800 = 0x06224000
// 优先级 2
#ifdef DEBUG
BG_PALETTE_SUB[255] = RGB15(31, 31, 31); // by default font will be rendered with color 255
REG_BG0CNT_SUB = BG_TILE_BASE(0) | BG_MAP_BASE(8) | BG_PRIORITY_2;
#endif
swiWaitForVBlank(); // remove tearing at top screen
// 模式5开两层BG一层BMP一层文字(用于调试)bmp层现在默认关闭
videoSetModeSub(MODE_5_2D | DISPLAY_BG2_ACTIVE);
}
void cGdi::drawLine(s16 x1, s16 y1, s16 x2, s16 y2, GRAPHICS_ENGINE engine) {
if ((x1 == x2) && (y1 == y2)) return;
if (x1 == x2) {
int ys, ye;
if (y1 < y2) {
ys = y1;
ye = y2 - 1;
} else {
ys = y2 + 1;
ye = y1;
}
for (int py = ys; py <= ye; py++) {
drawPixel(x1, py, engine);
}
return;
}
if (y1 == y2) {
int xs, xe;
if (x1 < x2) {
xs = x1;
xe = x2 - 1;
} else {
xs = x2 + 1;
xe = x1;
}
if (GE_MAIN == engine)
fillRect(_penColor, _penColor, xs, y1, xe - xs + 1, 1, engine);
else
fillRect(_penColorSub, _penColorSub, xs, y1, xe - xs + 1, 1, engine);
return;
}
if (abs(x2 - x1) > abs(y2 - y1)) {
int px = 0;
float py = 0;
int xe = x2 - x1;
float ye = y2 - y1;
int xv;
float yv;
if (0 < xe) {
xv = 1;
} else {
xv = -1;
}
yv = ye / abs(xe);
while (px != xe) {
drawPixel(x1 + px, y1 + (int)py, engine);
px += xv;
py += yv;
}
return;
} else {
float px = 0;
int py = 0;
float xe = x2 - x1;
int ye = y2 - y1;
float xv;
int yv;
xv = xe / abs(ye);
if (0 < ye) {
yv = 1;
} else {
yv = -1;
}
while (py != ye) {
drawPixel(x1 + (int)px, y1 + py, engine);
px += xv;
py += yv;
}
return;
}
}
void cGdi::frameRect(s16 x, s16 y, u16 w, u16 h, GRAPHICS_ENGINE engine) {
drawLine(x, y, x + w - 1, y, engine);
drawLine(x + w - 1, y, x + w - 1, y + h - 1, engine);
drawLine(x + w - 1, y + h - 1, x, y + h - 1, engine);
drawLine(x, y + h - 1, x, y, engine);
}
void cGdi::frameRect(s16 x, s16 y, u16 w, u16 h, u16 thickness, GRAPHICS_ENGINE engine) {
for (size_t ii = 0; ii < thickness; ++ii) {
frameRect(x, y, w, h, engine);
if (h <= 2 || w <= 2) break;
++x;
++y;
w -= 2;
h -= 2;
}
}
void cGdi::fillRect(u16 color1, u16 color2, s16 x, s16 y, u16 w, u16 h, GRAPHICS_ENGINE engine) {
ALIGN(4) u16 color[2] = { (u16)(BIT(15) | color1), (u16)(BIT(15) | color2) };
u16* pSrc = (u16*)color;
u16* pDest = NULL;
if (GE_MAIN == engine)
pDest = _bufferMain2 + (y << 8) + x + _layerPitch;
else
pDest = _bufferSub2 + (y << 8) + x;
bool destAligned = !(x & 1);
u16 destInc = 256 - w;
u16 halfWidth = w >> 1;
u16 remain = w & 1;
if (destAligned)
for (u32 i = 0; i < h; ++i) {
swiFastCopy(pSrc, pDest, COPY_MODE_WORD | COPY_MODE_FILL | halfWidth);
pDest += halfWidth << 1;
if (remain) *pDest++ = *pSrc;
pDest += destInc;
}
else
for (u32 i = 0; i < h; ++i) {
for (u32 j = 0; j < w; ++j) {
*pDest++ = pSrc[j & 1];
}
pDest += destInc;
}
}
void cGdi::fillRectBlend(u16 color1, u16 color2, s16 x, s16 y, u16 w, u16 h, GRAPHICS_ENGINE engine,
u16 opacity) {
if (opacity == 0) return;
if (opacity == 100) {
fillRect(color1, color2, x, y, w, h, engine);
return;
}
u16* pSrc = ((GE_MAIN == engine) ? (u16*)_background.buffer() : _bufferSub2) + (y << 8) + x;
u16* pDest = ((GE_MAIN == engine) ? (_bufferMain2 + _layerPitch) : _bufferSub2) + (y << 8) + x;
u32 alpha = (opacity * 32) / 100;
u32 destInc = 256 - w;
for (u32 ii = 0; ii < h; ++ii) {
for (u32 jj = 0; jj < w; ++jj) {
u32 original = *pSrc++ & 0x7fff;
u32 color = (jj & 1) ? color2 : color1;
u32 rb = ((color & 0x7c1f) * alpha + (original & 0x7c1f) * (32 - alpha)) & 0xf83e0;
u32 g = ((color & 0x3e0) * alpha + (original & 0x3e0) * (32 - alpha)) & 0x7c00;
*pDest++ = ((rb | g) >> 5) | BIT(15);
}
pDest += destInc;
pSrc += destInc;
}
}
void cGdi::bitBlt(const void* src, s16 srcW, s16 srcH, s16 destX, s16 destY, u16 destW, u16 destH,
GRAPHICS_ENGINE engine) {
if (destW <= 0) return;
u16* pSrc = (u16*)src;
u16* pDest = NULL;
if (GE_MAIN == engine)
pDest = _bufferMain2 + (destY)*256 + destX + _layerPitch;
else
pDest = _bufferSub2 + (destY)*256 + destX;
bool destAligned = !(destX & 1);
if (destW > srcW) destW = srcW;
if (destH > srcH) destH = srcH;
u16 srcInc = srcW - destW;
u16 destInc = 256 - destW;
u16 destHalfWidth = destW >> 1;
u16 lineSize = destW << 1;
u16 remain = destW & 1;
if (destAligned) {
for (u32 i = 0; i < destH; ++i) {
dmaCopyWordsGdi(3, pSrc, pDest, lineSize);
pDest += destHalfWidth << 1;
pSrc += destHalfWidth << 1;
if (remain) *pDest++ = *pSrc++;
pDest += destInc;
pSrc += srcInc;
}
}
}
void cGdi::bitBlt(const void* src, s16 destX, s16 destY, u16 destW, u16 destH,
GRAPHICS_ENGINE engine) {
u16* pSrc = (u16*)src;
u16* pDest = NULL;
if (GE_MAIN == engine)
pDest = _bufferMain2 + (destY)*256 + destX + _layerPitch;
else
pDest = _bufferSub2 + (destY)*256 + destX;
u16 pitchPixel = (destW + (destW & 1));
u16 destInc = 256 - pitchPixel;
u16 halfPitch = pitchPixel >> 1;
u16 remain = pitchPixel & 1;
for (u16 i = 0; i < destH; ++i) {
swiFastCopy(pSrc, pDest, COPY_MODE_WORD | COPY_MODE_COPY | halfPitch);
pDest += halfPitch << 1;
pSrc += halfPitch << 1;
if (remain) *pDest++ = *pSrc++;
pDest += destInc;
}
}
// maskBlt 要destW是偶数速度可以快一倍
// 不是偶数也可以,但要求在内存中 src 的 pitch 凑成偶数
void cGdi::maskBlt(const void* src, s16 destX, s16 destY, u16 destW, u16 destH,
GRAPHICS_ENGINE engine) {
u16* pSrc = (u16*)src;
u16* pDest = NULL;
bool destAligned = !(destX & 1);
if (GE_MAIN == engine)
pDest = _bufferMain2 + (destY)*256 + destX + _layerPitch;
else
pDest = _bufferSub2 + (destY)*256 + destX;
u16 pitch = (destW + (destW & 1));
u16 destInc = 256 - pitch;
u16 halfPitch = pitch >> 1;
if (destAligned)
for (u32 i = 0; i < destH; ++i) {
for (u32 j = 0; j < halfPitch; ++j) {
if (((*(u32*)pSrc) & 0x80008000) == 0x80008000) {
*(u32*)pDest = *(u32*)pSrc;
pSrc += 2;
pDest += 2;
} else {
if (*pSrc & 0x8000) *pDest = *pSrc;
pSrc++;
pDest++;
if (*pSrc & 0x8000) *pDest = *pSrc;
pSrc++;
pDest++;
}
}
pDest += destInc;
}
else
for (u16 i = 0; i < destH; ++i) {
for (u16 j = 0; j < pitch; ++j) {
if (*pSrc & 0x8000) *pDest = *pSrc;
pDest++;
pSrc++;
}
pDest += destInc;
}
}
void cGdi::maskBlt(const void* src, s16 srcW, s16 srcH, s16 destX, s16 destY, u16 destW, u16 destH,
GRAPHICS_ENGINE engine) {
if (destW <= 0) return;
u16* pSrc = (u16*)src;
u16* pDest = NULL;
if (GE_MAIN == engine)
pDest = _bufferMain2 + (destY)*256 + destX + _layerPitch;
else
pDest = _bufferSub2 + (destY)*256 + destX;
bool destAligned = !(destX & 1);
if (destW > srcW) destW = srcW;
if (destH > srcH) destH = srcH;
u16 srcInc = srcW - destW;
u16 destInc = 256 - destW;
u16 destHalfWidth = destW >> 1;
u16 pitch = (destW + (destW & 1));
u16 remain = destW & 1;
if (destAligned) {
for (u32 i = 0; i < destH; ++i) {
for (u32 j = 0; j < destHalfWidth; ++j) {
if (((*(u32*)pSrc) & 0x80008000) == 0x80008000) {
*(u32*)pDest = *(u32*)pSrc;
pSrc += 2;
pDest += 2;
} else {
if (*pSrc & 0x8000) *pDest = *pSrc;
pSrc++;
pDest++;
if (*pSrc & 0x8000) *pDest = *pSrc;
pSrc++;
pDest++;
}
}
if (remain) *pDest++ = *pSrc++;
pDest += destInc;
pSrc += srcInc;
}
} else
for (u16 i = 0; i < destH; ++i) {
for (u16 j = 0; j < pitch; ++j) {
if (*pSrc & 0x8000) *pDest = *pSrc;
pDest++;
pSrc++;
}
pDest += destInc;
pSrc += srcInc;
}
}
void cGdi::textOutRect(s16 x, s16 y, u16 w, u16 h, const char* text, GRAPHICS_ENGINE engine) {
const s16 originX = x, limitY = y + h - gs().fontHeight;
while (*text) {
if ('\r' == *text || '\n' == *text) {
y += gs().fontHeight; // FIXME
x = originX;
++text;
if (y > limitY) break;
} else {
u32 ww, add;
font().Info(text, &ww, &add);
if (x + (s16)ww < originX + w) {
font().Draw((GE_MAIN == engine) ? (_bufferMain2 + _layerPitch) : _bufferSub2, x, y,
(const u8*)text, (GE_MAIN == engine) ? _penColor : _penColorSub);
}
text += add;
x += ww;
}
}
}
void cGdi::present(GRAPHICS_ENGINE engine) {
if (GE_MAIN == engine) { // 翻转主引擎
dmaCopyWordsGdi(3, _bufferMain2 + _layerPitch, _bufferMain1 + (_mainEngineLayer << 16),
256 * 192 * 2);
fillMemory((void*)(_bufferMain2 + _layerPitch), 256 * 192 * 2, 0);
oamUpdate(&oamMain);
} else if (GE_SUB == engine) { // 翻转副引擎
if (SEM_GRAPHICS == _subEngineMode)
dmaCopyWordsGdi(3, (void*)_bufferSub2, (void*)_bufferSub1, 256 * 192 * 2);
fillMemory((void*)_bufferSub2, 0x18000, 0xffffffff);
}
}
// special version for window switching
void cGdi::present(void) {
swiWaitForVBlank();
dmaCopyWordsGdi(3, _bufferMain2, _bufferMain1, 256 * 192 * 2);
dmaCopyWordsGdi(3, _bufferMain2 + (256 * 192), _bufferMain1 + (1 << 16), 256 * 192 * 2);
fillMemory((void*)_bufferMain2, 256 * 192 * 4, 0);
}
#ifdef DEBUG
void cGdi::switchSubEngineMode() {
// 需要保存和恢复文本模式的现场
switch (_subEngineMode) {
case SEM_GRAPHICS: // 当前是图形模式的话就恢复刚才的text现场
videoSetModeSub(MODE_5_2D | DISPLAY_BG0_ACTIVE);
custom_console.fontBgMap = (u16*)0x6204000;
custom_console.fontBgGfx = (u16*)0x6200000;
dmaCopyWordsGdi(3, (void*)_bufferSub3, (void*)_bufferSub1, 0x4800);
break;
case SEM_TEXT: // 当前是文字模式的话,保存现场,切到图形模式
videoSetModeSub(MODE_5_2D | DISPLAY_BG2_ACTIVE);
custom_console.fontBgMap = _bufferSub3 + 0x2000;
custom_console.fontBgGfx = _bufferSub3;
dmaCopyWordsGdi(3, (void*)_bufferSub1, (void*)_bufferSub3, 0x4800);
break;
};
_subEngineMode = (SUB_ENGINE_MODE)(_subEngineMode ^ 1);
}
#endif