pokeplatinum/src/bg_window.c
2024-11-23 22:15:35 +01:00

2692 lines
92 KiB
C

#include "bg_window.h"
#include <nitro.h>
#include <string.h>
#include "gx_layers.h"
#include "heap.h"
#include "math.h"
static u8 ConvertToGxBgScreenSize(u8 bgScreenSize, u8 bgType);
static void GetBgScreenTileDimensions(u8 bgScreenSize, u8 *outXTiles, u8 *outYTiles);
static void UpdateBgOffsetWithVal(Background *bg, enum BgOffsetUpdateOp op, int val);
static void ResetBgAffineTransforms(BgConfig *bgConfig, u8 bgLayer);
static void CopyOrDecompressData(const void *src, void *dest, u32 size);
static void LoadBgVRAMScr(u8 bgLayer, void *src, u32 offset, u32 size);
static void LoadBgVRAMChar(u8 bgLayer, void *src, u32 offset, u32 size);
static u16 CalcTilemapIndexFromCoords(u8 x, u8 y, u8 bgScreenSize);
static void CopyToBgTilemapRectText(Background *bg, u8 destX, u8 destY, u8 destWidth, u8 destHeight, u16 *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 mode);
static void CopyToBgTilemapRectAffine(Background *bg, u8 destX, u8 destY, u8 destWidth, u8 destHeight, u8 *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 mode);
static void FillBgTilemapRectText(Background *bg, u16 fillVal, u8 x, u8 y, u8 width, u8 height, u8 palette);
static void FillBgTilemapRectAffine(Background *bg, u8 fillVal, u8 x, u8 y, u8 width, u8 height);
static void Copy4bppTo8bpp(const u8 *src, u32 size, u8 *dest, u8 palette);
static void *Convert4bppTo8bpp(const u8 *src, u32 size, u8 palette, u32 heapID);
static void CopyWindowToVramText(Window *window);
static void ScheduleCopyWindowToVramText(Window *window);
static void CopyWindowToVramAffine(Window *window);
static void ScheduleCopyWindowToVramAffine(Window *window);
static void ClearAndCopyWindowToVramText(Window *window);
static void ClearAndScheduleCopyWindowToVramText(Window *window);
static void ClearAndCopyWindowToVramAffine(Window *window);
static void ClearAndScheduleCopyWindowToVramAffine(Window *window);
static void ScrollWindow4bpp(Window *window, u8 direction, u8 distance, u8 fillVal);
static void ScrollWindow8bpp(Window *window, u8 direction, u8 distance, u8 fillVal);
static void ApplyFlipFlagsToTile(BgConfig *bgConfig, u8 flags, u8 *tile);
static void PutWindowToTilemapText(Window *window);
static void PutWindowToTilemapAffine(Window *window);
static void ClearWindowTilemapText(Window *window);
static void ClearWindowTilemapAffine(Window *window);
static void UpdateBgRotationWithVal(Background *bg, enum BgAffineUpdateOp op, u16 val);
static void UpdateBgAffineScaleWithVal(Background *bg, enum BgAffineUpdateOp op, fx32 val);
static void UpdateBgAffineRotationCenterWithVal(Background *bg, enum BgAffineUpdateOp op, int val);
static void RunScheduledScrolls(BgConfig *bgConfig);
static void RunScheduledTilemapTransfers(BgConfig *bgConfig);
BgConfig *BgConfig_New(u32 heapID)
{
BgConfig *bgConfig = Heap_AllocFromHeap(heapID, sizeof(BgConfig));
memset(bgConfig, 0, sizeof(BgConfig));
bgConfig->heapID = heapID;
bgConfig->scrollScheduled = 0;
bgConfig->bufferTransferScheduled = 0;
return bgConfig;
}
u32 BgConfig_GetHeapID(BgConfig *bgConfig)
{
return bgConfig->heapID;
}
void SetAllGraphicsModes(const GraphicsModes *graphicsModes)
{
GX_SetGraphicsMode(graphicsModes->displayMode, graphicsModes->mainBgMode, graphicsModes->bg0As2DOr3D);
GXS_SetGraphicsMode(graphicsModes->subBgMode);
GX_SetBGScrOffset(GX_BGSCROFFSET_0x00000);
GX_SetBGCharOffset(GX_BGCHAROFFSET_0x00000);
GXLayers_DisableEngineALayers();
GXLayers_DisableEngineBLayers();
}
void SetScreenGraphicsModes(const GraphicsModes *graphicsModes, u8 screen)
{
if (screen == DS_SCREEN_MAIN) {
GX_SetGraphicsMode(graphicsModes->displayMode, graphicsModes->mainBgMode, graphicsModes->bg0As2DOr3D);
GXLayers_DisableEngineALayers();
} else {
GXS_SetGraphicsMode(graphicsModes->subBgMode);
GXLayers_DisableEngineBLayers();
}
}
void Bg_InitFromTemplate(BgConfig *bgConfig, u8 bgLayer, const BgTemplate *bgTemplate, u8 bgType)
{
u8 screenSize = ConvertToGxBgScreenSize(bgTemplate->screenSize, bgType);
switch (bgLayer) {
case BG_LAYER_MAIN_0:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG0, TRUE);
G2_SetBG0Control(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase, bgTemplate->bgExtPltt);
G2_SetBG0Priority(bgTemplate->priority);
G2_BG0Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_MAIN_1:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG1, TRUE);
G2_SetBG1Control(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase, bgTemplate->bgExtPltt);
G2_SetBG1Priority(bgTemplate->priority);
G2_BG1Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_MAIN_2:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG2, TRUE);
switch (bgType) {
default:
case BG_TYPE_STATIC:
G2_SetBG2ControlText(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_AFFINE:
G2_SetBG2ControlAffine(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_STATIC_WITH_AFFINE:
G2_SetBG2Control256x16Pltt(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
}
G2_SetBG2Priority(bgTemplate->priority);
G2_BG2Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_MAIN_3:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG3, TRUE);
switch (bgType) {
default:
case BG_TYPE_STATIC:
G2_SetBG3ControlText(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_AFFINE:
G2_SetBG3ControlAffine(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_STATIC_WITH_AFFINE:
G2_SetBG3Control256x16Pltt(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
}
G2_SetBG3Priority(bgTemplate->priority);
G2_BG3Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_SUB_0:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG0, 1);
G2S_SetBG0Control(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase, bgTemplate->bgExtPltt);
G2S_SetBG0Priority(bgTemplate->priority);
G2S_BG0Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_SUB_1:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG1, 1);
G2S_SetBG1Control(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase, bgTemplate->bgExtPltt);
G2S_SetBG1Priority(bgTemplate->priority);
G2S_BG1Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_SUB_2:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG2, 1);
switch (bgType) {
default:
case BG_TYPE_STATIC:
G2S_SetBG2ControlText(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_AFFINE:
G2S_SetBG2ControlAffine(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_STATIC_WITH_AFFINE:
G2S_SetBG2Control256x16Pltt(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
}
G2S_SetBG2Priority(bgTemplate->priority);
G2S_BG2Mosaic(bgTemplate->mosaic);
break;
case BG_LAYER_SUB_3:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG3, 1);
switch (bgType) {
default:
case BG_TYPE_STATIC:
G2S_SetBG3ControlText(screenSize, bgTemplate->colorMode, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_AFFINE:
G2S_SetBG3ControlAffine(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
case BG_TYPE_STATIC_WITH_AFFINE:
G2S_SetBG3Control256x16Pltt(screenSize, bgTemplate->areaOver, bgTemplate->screenBase, bgTemplate->charBase);
break;
}
G2S_SetBG3Priority(bgTemplate->priority);
G2S_BG3Mosaic(bgTemplate->mosaic);
break;
}
bgConfig->bgs[bgLayer].rotation = 0;
bgConfig->bgs[bgLayer].xScale = FX32_ONE;
bgConfig->bgs[bgLayer].yScale = FX32_ONE;
bgConfig->bgs[bgLayer].xCenter = 0;
bgConfig->bgs[bgLayer].yCenter = 0;
if (bgTemplate->bufferSize) {
bgConfig->bgs[bgLayer].tilemapBuffer = Heap_AllocFromHeap(bgConfig->heapID, bgTemplate->bufferSize);
MI_CpuClear16(bgConfig->bgs[bgLayer].tilemapBuffer, bgTemplate->bufferSize);
bgConfig->bgs[bgLayer].bufferSize = bgTemplate->bufferSize;
bgConfig->bgs[bgLayer].baseTile = bgTemplate->baseTile;
} else {
bgConfig->bgs[bgLayer].tilemapBuffer = NULL;
bgConfig->bgs[bgLayer].bufferSize = 0;
bgConfig->bgs[bgLayer].baseTile = 0;
}
bgConfig->bgs[bgLayer].screenSize = bgTemplate->screenSize;
bgConfig->bgs[bgLayer].type = bgType;
bgConfig->bgs[bgLayer].colorMode = bgTemplate->colorMode;
if (bgType == BG_TYPE_STATIC && bgTemplate->colorMode == GX_BG_COLORMODE_16) {
bgConfig->bgs[bgLayer].tileSize = TILE_SIZE_4BPP;
} else {
bgConfig->bgs[bgLayer].tileSize = TILE_SIZE_8BPP;
}
Bg_SetOffset(bgConfig, bgLayer, 0, bgTemplate->x);
Bg_SetOffset(bgConfig, bgLayer, 3, bgTemplate->y);
}
void Bg_SetControlParam(BgConfig *bgConfig, u8 bgLayer, enum BgControlParam bgControlParam, u8 val)
{
if (bgControlParam == BG_CONTROL_PARAM_COLOR_MODE) {
bgConfig->bgs[bgLayer].colorMode = val;
}
switch (bgLayer) {
case BG_LAYER_MAIN_0: {
GXBg01Control bgControl = G2_GetBG0Control();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG0Control(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase, bgControl.bgExtPltt);
} break;
case BG_LAYER_MAIN_1: {
GXBg01Control bgControl = G2_GetBG1Control();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG1Control(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase, bgControl.bgExtPltt);
} break;
case BG_LAYER_MAIN_2:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText bgControl = G2_GetBG2ControlText();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG2ControlText(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_AFFINE: {
GXBg23ControlAffine bgControl = G2_GetBG2ControlAffine();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG2ControlAffine(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt bgControl = G2_GetBG2Control256x16Pltt();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG2Control256x16Pltt(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
}
break;
case BG_LAYER_MAIN_3:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText bgControl = G2_GetBG3ControlText();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG3ControlText(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_AFFINE: {
GXBg23ControlAffine bgControl = G2_GetBG3ControlAffine();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG3ControlAffine(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt bgControl = G2_GetBG3Control256x16Pltt();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2_SetBG3Control256x16Pltt(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
}
break;
case BG_LAYER_SUB_0: {
GXBg01Control bgControl = G2S_GetBG0Control();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG0Control(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase, bgControl.bgExtPltt);
} break;
case BG_LAYER_SUB_1: {
GXBg01Control bgControl = G2S_GetBG1Control();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG1Control(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase, bgControl.bgExtPltt);
} break;
case BG_LAYER_SUB_2:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText bgControl = G2S_GetBG2ControlText();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG2ControlText(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_AFFINE: {
GXBg23ControlAffine bgControl = G2S_GetBG2ControlAffine();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG2ControlAffine(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt bgControl = G2S_GetBG2Control256x16Pltt();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG2Control256x16Pltt(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
}
break;
case BG_LAYER_SUB_3:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText bgControl = G2S_GetBG3ControlText();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG3ControlText(bgControl.screenSize, bgConfig->bgs[bgLayer].colorMode, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_AFFINE: {
GXBg23ControlAffine bgControl = G2S_GetBG3ControlAffine();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG3ControlAffine(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt bgControl = G2S_GetBG3Control256x16Pltt();
if (bgControlParam == BG_CONTROL_PARAM_SCREEN_BASE) {
bgControl.screenBase = val;
} else if (bgControlParam == BG_CONTROL_PARAM_CHAR_BASE) {
bgControl.charBase = val;
}
G2S_SetBG3Control256x16Pltt(bgControl.screenSize, bgControl.areaOver, bgControl.screenBase, bgControl.charBase);
} break;
}
break;
}
}
static u8 ConvertToGxBgScreenSize(u8 bgScreenSize, u8 bgType)
{
switch (bgType) {
case BG_TYPE_STATIC:
if (bgScreenSize == BG_SCREEN_SIZE_256x256) {
return GX_BG_SCRSIZE_TEXT_256x256;
}
if (bgScreenSize == BG_SCREEN_SIZE_256x512) {
return GX_BG_SCRSIZE_TEXT_256x512;
}
if (bgScreenSize == BG_SCREEN_SIZE_512x256) {
return GX_BG_SCRSIZE_TEXT_512x256;
}
if (bgScreenSize == BG_SCREEN_SIZE_512x512) {
return GX_BG_SCRSIZE_TEXT_512x512;
}
break;
case BG_TYPE_AFFINE:
if (bgScreenSize == BG_SCREEN_SIZE_128x128) {
return GX_BG_SCRSIZE_AFFINE_128x128;
}
if (bgScreenSize == BG_SCREEN_SIZE_256x256) {
return GX_BG_SCRSIZE_AFFINE_256x256;
}
if (bgScreenSize == BG_SCREEN_SIZE_512x512) {
return GX_BG_SCRSIZE_AFFINE_512x512;
}
if (bgScreenSize == BG_SCREEN_SIZE_1024x1024) {
return GX_BG_SCRSIZE_AFFINE_1024x1024;
}
break;
case BG_TYPE_STATIC_WITH_AFFINE:
if (bgScreenSize == BG_SCREEN_SIZE_128x128) {
return GX_BG_SCRSIZE_256x16PLTT_128x128;
}
if (bgScreenSize == BG_SCREEN_SIZE_256x256) {
return GX_BG_SCRSIZE_256x16PLTT_256x256;
}
if (bgScreenSize == BG_SCREEN_SIZE_512x512) {
return GX_BG_SCRSIZE_256x16PLTT_512x512;
}
if (bgScreenSize == BG_SCREEN_SIZE_1024x1024) {
return GX_BG_SCRSIZE_256x16PLTT_1024x1024;
}
break;
}
return BG_SCREEN_SIZE_128x128;
}
static void GetBgScreenTileDimensions(u8 bgScreenSize, u8 *outXTiles, u8 *outYTiles)
{
switch (bgScreenSize) {
case BG_SCREEN_SIZE_128x128:
*outXTiles = 16;
*outYTiles = 16;
return;
case BG_SCREEN_SIZE_256x256:
*outXTiles = 32;
*outYTiles = 32;
return;
case BG_SCREEN_SIZE_256x512:
*outXTiles = 32;
*outYTiles = 64;
return;
case BG_SCREEN_SIZE_512x256:
*outXTiles = 64;
*outYTiles = 32;
return;
case BG_SCREEN_SIZE_512x512:
*outXTiles = 64;
*outYTiles = 64;
return;
case BG_SCREEN_SIZE_1024x1024:
*outXTiles = 128;
*outYTiles = 128;
return;
}
}
void Bg_FreeTilemapBuffer(BgConfig *bgConfig, u8 bgLayer)
{
if (bgConfig->bgs[bgLayer].tilemapBuffer == NULL) {
return;
}
Heap_FreeToHeap(bgConfig->bgs[bgLayer].tilemapBuffer);
bgConfig->bgs[bgLayer].tilemapBuffer = NULL;
}
void Bg_SetPriority(u8 bgLayer, u8 priority)
{
switch (bgLayer) {
case BG_LAYER_MAIN_0:
G2_SetBG0Priority(priority);
break;
case BG_LAYER_MAIN_1:
G2_SetBG1Priority(priority);
break;
case BG_LAYER_MAIN_2:
G2_SetBG2Priority(priority);
break;
case BG_LAYER_MAIN_3:
G2_SetBG3Priority(priority);
break;
case BG_LAYER_SUB_0:
G2S_SetBG0Priority(priority);
break;
case BG_LAYER_SUB_1:
G2S_SetBG1Priority(priority);
break;
case BG_LAYER_SUB_2:
G2S_SetBG2Priority(priority);
break;
case BG_LAYER_SUB_3:
G2S_SetBG3Priority(priority);
break;
}
}
void Bg_ToggleLayer(u8 bgLayer, u8 enable)
{
switch (bgLayer) {
case BG_LAYER_MAIN_0:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG0, enable);
break;
case BG_LAYER_MAIN_1:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG1, enable);
break;
case BG_LAYER_MAIN_2:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG2, enable);
break;
case BG_LAYER_MAIN_3:
GXLayers_EngineAToggleLayers(GX_PLANEMASK_BG3, enable);
break;
case BG_LAYER_SUB_0:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG0, enable);
break;
case BG_LAYER_SUB_1:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG1, enable);
break;
case BG_LAYER_SUB_2:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG2, enable);
break;
case BG_LAYER_SUB_3:
GXLayers_EngineBToggleLayers(GX_PLANEMASK_BG3, enable);
break;
}
}
void Bg_SetOffset(BgConfig *bgConfig, u8 bgLayer, u8 bgUpdateOffsetOp, int val)
{
UpdateBgOffsetWithVal(&bgConfig->bgs[bgLayer], bgUpdateOffsetOp, val);
int hOffset = bgConfig->bgs[bgLayer].xOffset;
int vOffset = bgConfig->bgs[bgLayer].yOffset;
switch (bgLayer) {
case BG_LAYER_MAIN_0:
G2_SetBG0Offset(hOffset, vOffset);
return;
case BG_LAYER_MAIN_1:
G2_SetBG1Offset(hOffset, vOffset);
return;
case BG_LAYER_MAIN_2:
if (bgConfig->bgs[BG_LAYER_MAIN_2].type == BG_TYPE_STATIC) {
G2_SetBG2Offset(hOffset, vOffset);
} else {
ResetBgAffineTransforms(bgConfig, BG_LAYER_MAIN_2);
}
return;
case BG_LAYER_MAIN_3:
if (bgConfig->bgs[BG_LAYER_MAIN_3].type == BG_TYPE_STATIC) {
G2_SetBG3Offset(hOffset, vOffset);
} else {
ResetBgAffineTransforms(bgConfig, BG_LAYER_MAIN_3);
}
return;
case BG_LAYER_SUB_0:
G2S_SetBG0Offset(hOffset, vOffset);
return;
case BG_LAYER_SUB_1:
G2S_SetBG1Offset(hOffset, vOffset);
return;
case BG_LAYER_SUB_2:
if (bgConfig->bgs[BG_LAYER_SUB_2].type == BG_TYPE_STATIC) {
G2S_SetBG2Offset(hOffset, vOffset);
} else {
ResetBgAffineTransforms(bgConfig, BG_LAYER_SUB_2);
}
return;
case BG_LAYER_SUB_3:
if (bgConfig->bgs[BG_LAYER_SUB_3].type == BG_TYPE_STATIC) {
G2S_SetBG3Offset(hOffset, vOffset);
} else {
ResetBgAffineTransforms(bgConfig, BG_LAYER_SUB_3);
}
return;
}
}
int Bg_GetXOffset(BgConfig *bgConfig, enum BgLayer bgLayer)
{
return bgConfig->bgs[bgLayer].xOffset;
}
int Bg_GetYOffset(BgConfig *bgConfig, enum BgLayer bgLayer)
{
return bgConfig->bgs[bgLayer].yOffset;
}
static void UpdateBgOffsetWithVal(Background *bg, enum BgOffsetUpdateOp op, int val)
{
switch (op) {
case BG_OFFSET_UPDATE_SET_X:
bg->xOffset = val;
break;
case BG_OFFSET_UPDATE_ADD_X:
bg->xOffset += val;
break;
case BG_OFFSET_UPDATE_SUB_X:
bg->xOffset -= val;
break;
case BG_OFFSET_UPDATE_SET_Y:
bg->yOffset = val;
break;
case BG_OFFSET_UPDATE_ADD_Y:
bg->yOffset += val;
break;
case BG_OFFSET_UPDATE_SUB_Y:
bg->yOffset -= val;
break;
}
}
void Bg_SetAffineParams(BgConfig *bgConfig, u8 bgLayer, const MtxFx22 *mtx, int centerX, int centerY)
{
switch (bgLayer) {
case BG_LAYER_MAIN_2:
G2_SetBG2Affine(mtx, centerX, centerY, bgConfig->bgs[bgLayer].xOffset, bgConfig->bgs[bgLayer].yOffset);
return;
case BG_LAYER_MAIN_3:
G2_SetBG3Affine(mtx, centerX, centerY, bgConfig->bgs[bgLayer].xOffset, bgConfig->bgs[bgLayer].yOffset);
return;
case BG_LAYER_SUB_2:
G2S_SetBG2Affine(mtx, centerX, centerY, bgConfig->bgs[bgLayer].xOffset, bgConfig->bgs[bgLayer].yOffset);
return;
case BG_LAYER_SUB_3:
G2S_SetBG3Affine(mtx, centerX, centerY, bgConfig->bgs[bgLayer].xOffset, bgConfig->bgs[bgLayer].yOffset);
return;
}
}
static void ResetBgAffineTransforms(BgConfig *bgConfig, u8 bgLayer)
{
MtxFx22 mtx;
CreateAffineTransformationMatrix(&mtx, 0, FX32_ONE, FX32_ONE, AFFINE_MODE_NORMAL);
Bg_SetAffineParams(bgConfig, bgLayer, &mtx, 0, 0);
}
static void CopyOrDecompressData(const void *src, void *dest, u32 size)
{
if (size == 0) {
MI_UncompressLZ8(src, dest);
return;
}
if ((((u32)src % 4) == 0) && (((u32)dest % 4) == 0) && (((u16)size % 4) == 0)) {
MI_CpuCopy32(src, dest, size);
return;
}
MI_CpuCopy16(src, dest, size);
}
void Bg_CopyTilemapBufferToVRAM(BgConfig *bgConfig, u8 bgLayer)
{
Bg_CopyTilemapBufferRangeToVRAM(bgConfig,
bgLayer,
bgConfig->bgs[bgLayer].tilemapBuffer,
bgConfig->bgs[bgLayer].bufferSize,
bgConfig->bgs[bgLayer].baseTile);
}
void Bg_CopyTilemapBufferRangeToVRAM(BgConfig *bgConfig, u8 bgLayer, void *src, u32 size, u32 offset)
{
if (size == 0) {
void *buf = bgConfig->bgs[bgLayer].tilemapBuffer;
if (buf != NULL) {
CopyOrDecompressData(src, buf, size);
LoadBgVRAMScr(bgLayer,
buf,
bgConfig->bgs[bgLayer].baseTile * 2,
bgConfig->bgs[bgLayer].bufferSize);
return;
}
u32 decompressedSize = MI_GetUncompressedSize(src);
void *tmp = Heap_AllocFromHeapAtEnd(bgConfig->heapID, decompressedSize);
CopyOrDecompressData(src, tmp, size);
LoadBgVRAMScr(bgLayer, tmp, offset * 2, decompressedSize);
Heap_FreeToHeap(tmp);
return;
}
LoadBgVRAMScr(bgLayer, src, offset * 2, size);
}
static void LoadBgVRAMScr(u8 bgLayer, void *src, u32 offset, u32 size)
{
DC_FlushRange(src, size);
switch (bgLayer) {
case BG_LAYER_MAIN_0:
GX_LoadBG0Scr(src, offset, size);
return;
case BG_LAYER_MAIN_1:
GX_LoadBG1Scr(src, offset, size);
return;
case BG_LAYER_MAIN_2:
GX_LoadBG2Scr(src, offset, size);
return;
case BG_LAYER_MAIN_3:
GX_LoadBG3Scr(src, offset, size);
return;
case BG_LAYER_SUB_0:
GXS_LoadBG0Scr(src, offset, size);
return;
case BG_LAYER_SUB_1:
GXS_LoadBG1Scr(src, offset, size);
return;
case BG_LAYER_SUB_2:
GXS_LoadBG2Scr(src, offset, size);
return;
case BG_LAYER_SUB_3:
GXS_LoadBG3Scr(src, offset, size);
return;
}
}
void Bg_LoadTilemapBuffer(BgConfig *bgConfig, u8 bgLayer, void *src, u32 size)
{
CopyOrDecompressData(src, bgConfig->bgs[bgLayer].tilemapBuffer, size);
}
void Bg_LoadTiles(BgConfig *bgConfig, u8 bgLayer, void *src, u32 size, u32 tileStart)
{
if (bgConfig->bgs[bgLayer].colorMode == GX_BG_COLORMODE_16) {
Bg_LoadTilesToVRAM(bgConfig, bgLayer, src, size, tileStart * TILE_SIZE_4BPP);
} else {
Bg_LoadTilesToVRAM(bgConfig, bgLayer, src, size, tileStart * TILE_SIZE_8BPP);
}
}
void Bg_LoadTilesToVRAM(BgConfig *bgConfig, u8 bgLayer, void *src, u32 size, u32 offset)
{
if (size == 0) {
u32 decompressedSize = MI_GetUncompressedSize(src);
void *tmp = Heap_AllocFromHeapAtEnd(bgConfig->heapID, decompressedSize);
CopyOrDecompressData(src, tmp, size);
LoadBgVRAMChar(bgLayer, tmp, offset, decompressedSize);
Heap_FreeToHeap(tmp);
return;
}
LoadBgVRAMChar(bgLayer, (void *)src, offset, size);
}
static void LoadBgVRAMChar(u8 bgLayer, void *src, u32 offset, u32 size)
{
DC_FlushRange(src, size);
switch (bgLayer) {
case BG_LAYER_MAIN_0:
GX_LoadBG0Char(src, offset, size);
return;
case BG_LAYER_MAIN_1:
GX_LoadBG1Char(src, offset, size);
return;
case BG_LAYER_MAIN_2:
GX_LoadBG2Char(src, offset, size);
return;
case BG_LAYER_MAIN_3:
GX_LoadBG3Char(src, offset, size);
return;
case BG_LAYER_SUB_0:
GXS_LoadBG0Char(src, offset, size);
return;
case BG_LAYER_SUB_1:
GXS_LoadBG1Char(src, offset, size);
return;
case BG_LAYER_SUB_2:
GXS_LoadBG2Char(src, offset, size);
return;
case BG_LAYER_SUB_3:
GXS_LoadBG3Char(src, offset, size);
return;
}
}
void Bg_ClearTilesRange(u8 bgLayer, u32 size, u32 offset, u32 heapID)
{
u32 *buf = (u32 *)Heap_AllocFromHeapAtEnd(heapID, size);
memset(buf, 0, size);
LoadBgVRAMChar(bgLayer, buf, offset, size);
Heap_FreeToHeapExplicit(heapID, buf);
}
void Bg_FillTilesRange(BgConfig *bgConfig, u32 bgLayer, u32 fillVal, u32 numTiles, u32 offset)
{
u32 *buf;
u32 size = numTiles * bgConfig->bgs[bgLayer].tileSize;
buf = Heap_AllocFromHeapAtEnd(bgConfig->heapID, size);
if (bgConfig->bgs[bgLayer].tileSize == TILE_SIZE_4BPP) {
fillVal = (fillVal << 12) | (fillVal << 8) | (fillVal << 4) | fillVal;
fillVal |= (fillVal << 16);
} else {
fillVal = (fillVal << 24) | (fillVal << 16) | (fillVal << 8) | fillVal;
}
MI_CpuFillFast(buf, fillVal, size);
LoadBgVRAMChar(bgLayer, buf, offset * bgConfig->bgs[bgLayer].tileSize, size);
Heap_FreeToHeap(buf);
}
void Bg_LoadPalette(u8 bgLayer, void *src, u16 size, u16 offset)
{
DC_FlushRange(src, size);
if (bgLayer < BG_LAYER_SUB_0) {
GX_LoadBGPltt(src, offset, size);
} else {
GXS_LoadBGPltt(src, offset, size);
}
}
void Bg_MaskPalette(u8 bgLayer, u16 mask)
{
Bg_LoadPalette(bgLayer, &mask, sizeof(u16), 0);
}
static u16 CalcTilemapIndexFromCoords(u8 x, u8 y, u8 bgScreenSize)
{
u16 ret;
switch (bgScreenSize) {
case BG_SCREEN_SIZE_128x128:
ret = y * 16 + x;
break;
case BG_SCREEN_SIZE_256x256:
case BG_SCREEN_SIZE_256x512:
ret = y * 32 + x;
break;
case BG_SCREEN_SIZE_512x256:
ret = ((x >> 5) * 32 + y) * 32 + (x & 0x1F);
break;
case BG_SCREEN_SIZE_512x512:
ret = (x >> 5) + (y >> 5) * 2;
ret *= 1024;
ret += (y & 0x1F) * 32 + (x & 0x1F);
break;
case BG_SCREEN_SIZE_1024x1024:
ret = 0;
}
return ret;
}
static u16 CalcTilemapIndexFromCoordsWidthHeight(u8 x, u8 y, u8 width, u8 height)
{
u8 coordType = 0;
u16 tilemapIndex = 0;
s16 adjustedWidth = width - 32;
s16 adjustedHeight = height - 32;
if (x / 32) {
coordType += 1;
}
if (y / 32) {
coordType += 2;
}
switch (coordType) {
case 0:
if (adjustedWidth >= 0) {
tilemapIndex += y * 32 + x;
} else {
tilemapIndex += y * width + x;
}
break;
case 1:
if (adjustedHeight >= 0) {
tilemapIndex += 1024;
} else {
tilemapIndex += 32 * height;
}
tilemapIndex += y * adjustedWidth + (x & 0x1F);
break;
case 2:
tilemapIndex += width * 32;
if (adjustedWidth >= 0) {
tilemapIndex += (y & 0x1F) * 32 + x;
} else {
tilemapIndex += (y & 0x1F) * width + x;
}
break;
case 3:
tilemapIndex += width * 32 + adjustedHeight * 32;
tilemapIndex += (y & 0x1F) * adjustedWidth + (x & 0x1F);
break;
}
return tilemapIndex;
}
void Bg_LoadToTilemapRect(BgConfig *bgConfig, u8 bgLayer, const void *src, u8 destX, u8 destY, u8 destWidth, u8 destHeight)
{
Bg_CopyToTilemapRect(bgConfig, bgLayer, destX, destY, destWidth, destHeight, src, 0, 0, destWidth, destHeight);
}
void Bg_CopyToTilemapRect(BgConfig *bgConfig, u8 bgLayer, u8 destX, u8 destY, u8 destWidth, u8 destHeight, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight)
{
if (bgConfig->bgs[bgLayer].type != BG_TYPE_AFFINE) {
CopyToBgTilemapRectText(&bgConfig->bgs[bgLayer], destX, destY, destWidth, destHeight, (u16 *)src, srcX, srcY, srcWidth, srcHeight, TILEMAP_COPY_SRC_FLAT);
} else {
CopyToBgTilemapRectAffine(&bgConfig->bgs[bgLayer], destX, destY, destWidth, destHeight, (u8 *)src, srcX, srcY, srcWidth, srcHeight, TILEMAP_COPY_SRC_FLAT);
}
}
void Bg_CopyRectToTilemapRect(BgConfig *bgConfig, u8 bgLayer, u8 destX, u8 destY, u8 destWidth, u8 destHeight, const void *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight)
{
if (bgConfig->bgs[bgLayer].type != BG_TYPE_AFFINE) {
CopyToBgTilemapRectText(&bgConfig->bgs[bgLayer], destX, destY, destWidth, destHeight, (u16 *)src, srcX, srcY, srcWidth, srcHeight, TILEMAP_COPY_SRC_RECT);
} else {
CopyToBgTilemapRectAffine(&bgConfig->bgs[bgLayer], destX, destY, destWidth, destHeight, (u8 *)src, srcX, srcY, srcWidth, srcHeight, TILEMAP_COPY_SRC_RECT);
}
}
static void CopyToBgTilemapRectText(Background *bg, u8 destX, u8 destY, u8 destWidth, u8 destHeight, u16 *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 mode)
{
u16 *dest = bg->tilemapBuffer;
if (dest == NULL) {
return;
}
u8 widthTiles, heightTiles;
GetBgScreenTileDimensions(bg->screenSize, &widthTiles, &heightTiles);
u8 i, j;
if (mode == TILEMAP_COPY_SRC_FLAT) {
for (i = 0; i < destHeight; i++) {
if (destY + i >= heightTiles || srcY + i >= srcHeight) {
break;
}
for (j = 0; j < destWidth; j++) {
if (destX + j >= widthTiles || srcX + j >= srcWidth) {
break;
}
dest[CalcTilemapIndexFromCoords(destX + j, destY + i, bg->screenSize)] = src[(srcY + i) * srcWidth + srcX + j];
}
}
} else {
for (i = 0; i < destHeight; i++) {
if (destY + i >= heightTiles || srcY + i >= srcHeight) {
break;
}
for (j = 0; j < destWidth; j++) {
if (destX + j >= widthTiles || srcX + j >= srcWidth) {
break;
}
dest[CalcTilemapIndexFromCoords(destX + j, destY + i, bg->screenSize)] = src[CalcTilemapIndexFromCoordsWidthHeight(srcX + j, srcY + i, srcWidth, srcHeight)];
}
}
}
}
static void CopyToBgTilemapRectAffine(Background *bg, u8 destX, u8 destY, u8 destWidth, u8 destHeight, u8 *src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 mode)
{
u8 *dest = bg->tilemapBuffer;
if (dest == NULL) {
return;
}
u8 widthTiles, heightTiles;
GetBgScreenTileDimensions(bg->screenSize, &widthTiles, &heightTiles);
u8 i, j;
if (mode == TILEMAP_COPY_SRC_FLAT) {
for (i = 0; i < destHeight; i++) {
if (destY + i >= heightTiles || srcY + i >= srcHeight) {
break;
}
for (j = 0; j < destWidth; j++) {
if (destX + j >= widthTiles || srcX + j >= srcWidth) {
break;
}
dest[CalcTilemapIndexFromCoords(destX + j, destY + i, bg->screenSize)] = src[(srcY + i) * srcWidth + srcX + j];
}
}
} else {
for (i = 0; i < destHeight; i++) {
if (destY + i >= heightTiles || srcY + i >= srcHeight) {
break;
}
for (j = 0; j < destWidth; j++) {
if (destX + j >= widthTiles || srcX + j >= srcWidth) {
break;
}
dest[CalcTilemapIndexFromCoords(destX + j, destY + i, bg->screenSize)] = src[CalcTilemapIndexFromCoordsWidthHeight(srcX + j, srcY + i, srcWidth, srcHeight)];
}
}
}
}
void Bg_FillTilemapRect(BgConfig *bgConfig, u8 bgLayer, u16 fillVal, u8 x, u8 y, u8 width, u8 height, u8 palette)
{
if (bgConfig->bgs[bgLayer].type != BG_TYPE_AFFINE) {
FillBgTilemapRectText(&bgConfig->bgs[bgLayer], fillVal, x, y, width, height, palette);
} else {
FillBgTilemapRectAffine(&bgConfig->bgs[bgLayer], fillVal, x, y, width, height);
}
}
static void FillBgTilemapRectText(Background *bg, u16 fillVal, u8 x, u8 y, u8 width, u8 height, u8 palette)
{
u16 *dest = bg->tilemapBuffer;
if (dest == NULL) {
return;
}
u8 widthTiles, heightTiles;
GetBgScreenTileDimensions(bg->screenSize, &widthTiles, &heightTiles);
u8 i, j;
for (i = y; i < y + height; i++) {
if (i >= heightTiles) {
break;
}
for (j = x; j < x + width; j++) {
if (j >= widthTiles) {
break;
}
u16 idx = CalcTilemapIndexFromCoords(j, i, bg->screenSize);
if (palette == TILEMAP_FILL_VAL_INCLUDES_PALETTE) {
dest[idx] = fillVal;
} else if (palette == TILEMAP_FILL_VAL_KEEP_PALETTE) {
dest[idx] = (dest[idx] & 0xF000) + fillVal;
} else {
dest[idx] = (palette << 12) + fillVal;
}
}
}
}
static void FillBgTilemapRectAffine(Background *bg, u8 fillVal, u8 x, u8 y, u8 width, u8 height)
{
u8 *dest = bg->tilemapBuffer;
if (dest == NULL) {
return;
}
u8 widthTiles, heightTiles;
GetBgScreenTileDimensions(bg->screenSize, &widthTiles, &heightTiles);
u8 i, j;
for (i = y; i < y + height; i++) {
if (i >= heightTiles) {
break;
}
for (j = x; j < x + width; j++) {
if (j >= widthTiles) {
break;
}
dest[CalcTilemapIndexFromCoords(j, i, bg->screenSize)] = fillVal;
}
}
}
void Bg_ChangeTilemapRectPalette(BgConfig *bgConfig, u8 bgLayer, u8 x, u8 y, u8 width, u8 height, u8 palette)
{
u16 *dest = bgConfig->bgs[bgLayer].tilemapBuffer;
if (dest == NULL) {
return;
}
u8 widthTiles, heightTiles;
GetBgScreenTileDimensions(bgConfig->bgs[bgLayer].screenSize, &widthTiles, &heightTiles);
u8 i, j;
for (i = y; i < y + height; i++) {
if (i >= heightTiles) {
break;
}
for (j = x; j < x + width; j++) {
if (j >= widthTiles) {
break;
}
u16 idx = CalcTilemapIndexFromCoords(j, i, bgConfig->bgs[bgLayer].screenSize);
dest[idx] = (dest[idx] & 0xFFF) | (palette << 12);
}
}
}
void Bg_ClearTilemap(BgConfig *bgConfig, u8 bgLayer)
{
if (bgConfig->bgs[bgLayer].tilemapBuffer == NULL) {
return;
}
MI_CpuClear16(bgConfig->bgs[bgLayer].tilemapBuffer, bgConfig->bgs[bgLayer].bufferSize);
Bg_CopyTilemapBufferToVRAM(bgConfig, bgLayer);
}
void Bg_FillTilemap(BgConfig *bgConfig, u8 bgLayer, u16 fillVal)
{
if (bgConfig->bgs[bgLayer].tilemapBuffer == NULL) {
return;
}
MI_CpuFill16(bgConfig->bgs[bgLayer].tilemapBuffer, fillVal, bgConfig->bgs[bgLayer].bufferSize);
Bg_CopyTilemapBufferToVRAM(bgConfig, bgLayer);
}
void Bg_ScheduleFillTilemap(BgConfig *bgConfig, u8 bgLayer, u16 fillVal)
{
if (bgConfig->bgs[bgLayer].tilemapBuffer == NULL) {
return;
}
MI_CpuFill16(bgConfig->bgs[bgLayer].tilemapBuffer, fillVal, bgConfig->bgs[bgLayer].bufferSize);
Bg_ScheduleTilemapTransfer(bgConfig, bgLayer);
}
void *Bg_GetCharPtr(u8 bgLayer)
{
switch (bgLayer) {
case BG_LAYER_MAIN_0:
return G2_GetBG0CharPtr();
case BG_LAYER_MAIN_1:
return G2_GetBG1CharPtr();
case BG_LAYER_MAIN_2:
return G2_GetBG2CharPtr();
case BG_LAYER_MAIN_3:
return G2_GetBG3CharPtr();
case BG_LAYER_SUB_0:
return G2S_GetBG0CharPtr();
case BG_LAYER_SUB_1:
return G2S_GetBG1CharPtr();
case BG_LAYER_SUB_2:
return G2S_GetBG2CharPtr();
case BG_LAYER_SUB_3:
return G2S_GetBG3CharPtr();
}
return NULL;
}
static void Copy4bppTo8bpp(const u8 *src, u32 size, u8 *dest, u8 palette)
{
palette <<= 4;
for (u32 i = 0; i < size; i++) {
dest[i * 2] = src[i] & 0xf;
if (dest[i * 2] != 0) {
dest[i * 2] += palette;
}
dest[i * 2 + 1] = (src[i] >> 4) & 0xf;
if (dest[i * 2 + 1] != 0) {
dest[i * 2 + 1] += palette;
}
}
}
static void *Convert4bppTo8bpp(const u8 *src, u32 size, u8 palette, u32 heapID)
{
void *dest = Heap_AllocFromHeap(heapID, size * 2);
Copy4bppTo8bpp(src, size, dest, palette);
return dest;
}
void *Bg_GetTilemapBuffer(BgConfig *bgConfig, u8 bgLayer)
{
return bgConfig->bgs[bgLayer].tilemapBuffer;
}
int Bg_GetXOffset2(BgConfig *bgConfig, u8 bgLayer)
{
return bgConfig->bgs[bgLayer].xOffset;
}
u16 Bg_GetRotation(BgConfig *bgConfig, u8 bgLayer)
{
return bgConfig->bgs[bgLayer].rotation;
}
u8 Bg_GetPriority(BgConfig *bgConfig, u8 bgLayer)
{
switch (bgLayer) {
case BG_LAYER_MAIN_0: {
GXBg01Control control = G2_GetBG0Control();
return control.priority;
}
case BG_LAYER_MAIN_1: {
GXBg01Control control = G2_GetBG1Control();
return control.priority;
}
case BG_LAYER_MAIN_2:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText control = G2_GetBG2ControlText();
return control.priority;
}
case BG_TYPE_AFFINE: {
GXBg23ControlAffine control = G2_GetBG2ControlAffine();
return control.priority;
}
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt control = G2_GetBG2Control256x16Pltt();
return control.priority;
}
}
case BG_LAYER_MAIN_3:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText control = G2_GetBG3ControlText();
return control.priority;
}
case BG_TYPE_AFFINE: {
GXBg23ControlAffine control = G2_GetBG3ControlAffine();
return control.priority;
}
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt control = G2_GetBG3Control256x16Pltt();
return control.priority;
}
}
case BG_LAYER_SUB_0: {
GXBg01Control control = G2S_GetBG0Control();
return control.priority;
}
case BG_LAYER_SUB_1: {
GXBg01Control control = G2S_GetBG1Control();
return control.priority;
}
case BG_LAYER_SUB_2:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText control = G2S_GetBG2ControlText();
return control.priority;
}
case BG_TYPE_AFFINE: {
GXBg23ControlAffine control = G2S_GetBG2ControlAffine();
return control.priority;
}
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt control = G2S_GetBG2Control256x16Pltt();
return control.priority;
}
}
case BG_LAYER_SUB_3:
switch (bgConfig->bgs[bgLayer].type) {
default:
case BG_TYPE_STATIC: {
GXBg23ControlText control = G2S_GetBG3ControlText();
return control.priority;
}
case BG_TYPE_AFFINE: {
GXBg23ControlAffine control = G2S_GetBG3ControlAffine();
return control.priority;
}
case BG_TYPE_STATIC_WITH_AFFINE: {
GXBg23Control256x16Pltt control = G2S_GetBG3Control256x16Pltt();
return control.priority;
}
}
}
return 0;
}
#define CalcPixelAddressFromBlit4bpp(ptr, x, y, width) ((u8 *)((ptr) + (((x) >> 1) & 3) + (((x) << 2) & 0x3FE0) + ((((y) << 2) & 0x3FE0) * (width)) + (((u32)(((y) << 2) & 0x1C)))))
#define CalcPixelAddressFromBlit8bpp(ptr, x, y, width) ((u8 *)((ptr) + ((x) & 7) + (((x) << 3) & 0x7FC0) + ((((y) << 3) & 0x7FC0) * (width)) + (((u32)(((y) << 3) & 0x38)))))
#define ConvertPixelsToTiles(x) (((x) + ((x) & 7)) >> 3)
void Bitmap_BlitRect4bpp(const Bitmap *src, const Bitmap *dest, u16 srcX, u16 srcY, u16 destX, u16 destY, u16 width, u16 height, u16 transparent)
{
int loopSrcX, loopDestX, loopSrcY, loopDestY, toOrr, shift, xEnd, yEnd, multiplierSrcY, multiplierDestY;
u8 *srcPixels, *destPixels;
if (dest->width - destX < width) {
xEnd = dest->width - destX + srcX;
} else {
xEnd = width + srcX;
}
if (dest->height - destY < height) {
yEnd = dest->height - destY + srcY;
} else {
yEnd = height + srcY;
}
multiplierSrcY = ConvertPixelsToTiles(src->width);
multiplierDestY = ConvertPixelsToTiles(dest->width);
if (transparent == 0xFFFF) {
for (loopSrcY = srcY, loopDestY = destY; loopSrcY < yEnd; loopSrcY++, loopDestY++) {
for (loopSrcX = srcX, loopDestX = destX; loopSrcX < xEnd; loopSrcX++, loopDestX++) {
srcPixels = CalcPixelAddressFromBlit4bpp(src->pixels, loopSrcX, loopSrcY, multiplierSrcY);
destPixels = CalcPixelAddressFromBlit4bpp(dest->pixels, loopDestX, loopDestY, multiplierDestY);
toOrr = (*srcPixels >> ((loopSrcX & 1) * 4)) & 0xF;
shift = (loopDestX & 1) * 4;
*destPixels = ((toOrr << shift) | (*destPixels & (0xF0 >> shift)));
}
}
} else {
for (loopSrcY = srcY, loopDestY = destY; loopSrcY < yEnd; loopSrcY++, loopDestY++) {
for (loopSrcX = srcX, loopDestX = destX; loopSrcX < xEnd; loopSrcX++, loopDestX++) {
srcPixels = CalcPixelAddressFromBlit4bpp(src->pixels, loopSrcX, loopSrcY, multiplierSrcY);
destPixels = CalcPixelAddressFromBlit4bpp(dest->pixels, loopDestX, loopDestY, multiplierDestY);
toOrr = (*srcPixels >> ((loopSrcX & 1) * 4)) & 0xF;
if (toOrr != transparent) {
shift = (loopDestX & 1) * 4;
*destPixels = ((toOrr << shift) | (*destPixels & (0xF0 >> shift)));
}
}
}
}
}
void Bitmap_BlitRect8bpp(const Bitmap *src, const Bitmap *dest, u16 srcX, u16 srcY, u16 destX, u16 destY, u16 width, u16 height, u16 transparent)
{
int loopSrcX, loopDestX, loopSrcY, loopDestY, xEnd, yEnd, multiplierSrcY, multiplierDestY;
u8 *srcPixels, *destPixels;
if (dest->width - destX < width) {
xEnd = dest->width - destX + srcX;
} else {
xEnd = width + srcX;
}
if (dest->height - destY < height) {
yEnd = dest->height - destY + srcY;
} else {
yEnd = height + srcY;
}
multiplierSrcY = ConvertPixelsToTiles(src->width);
multiplierDestY = ConvertPixelsToTiles(dest->width);
if (transparent == 0xffff) {
for (loopSrcY = srcY, loopDestY = destY; loopSrcY < yEnd; loopSrcY++, loopDestY++) {
for (loopSrcX = srcX, loopDestX = destX; loopSrcX < xEnd; loopSrcX++, loopDestX++) {
srcPixels = CalcPixelAddressFromBlit8bpp(src->pixels, loopSrcX, loopSrcY, multiplierSrcY);
destPixels = CalcPixelAddressFromBlit8bpp(dest->pixels, loopDestX, loopDestY, multiplierDestY);
*destPixels = *srcPixels;
}
}
} else {
for (loopSrcY = srcY, loopDestY = destY; loopSrcY < yEnd; loopSrcY++, loopDestY++) {
for (loopSrcX = srcX, loopDestX = destX; loopSrcX < xEnd; loopSrcX++, loopDestX++) {
srcPixels = CalcPixelAddressFromBlit8bpp(src->pixels, loopSrcX, loopSrcY, multiplierSrcY);
destPixels = CalcPixelAddressFromBlit8bpp(dest->pixels, loopDestX, loopDestY, multiplierDestY);
if (*srcPixels != transparent) {
*destPixels = *srcPixels;
}
}
}
}
}
void Bitmap_FillRect4bpp(const Bitmap *bitmap, u16 x, u16 y, u16 width, u16 height, u8 fillVal)
{
int xEnd = x + width;
if (xEnd > bitmap->width) {
xEnd = bitmap->width;
}
int yEnd = y + height;
if (yEnd > bitmap->height) {
yEnd = bitmap->height;
}
int blitWidth = ConvertPixelsToTiles(bitmap->width);
for (int i = y; i < yEnd; i++) {
for (int j = x; j < xEnd; j++) {
u8 *pixels = CalcPixelAddressFromBlit4bpp(bitmap->pixels, j, i, blitWidth);
if (j & 1) {
*pixels &= 0xF;
*pixels |= (fillVal << 4);
} else {
*pixels &= 0xF0;
*pixels |= fillVal;
}
}
}
}
void Bitmap_FillRect8bpp(const Bitmap *bitmap, u16 x, u16 y, u16 width, u16 height, u8 fillVal)
{
int xEnd = x + width;
if (xEnd > bitmap->width) {
xEnd = bitmap->width;
}
int yEnd = y + height;
if (yEnd > bitmap->height) {
yEnd = bitmap->height;
}
int blitWidth = ConvertPixelsToTiles(bitmap->width);
for (int i = y; i < yEnd; i++) {
for (int j = x; j < xEnd; j++) {
u8 *pixels = CalcPixelAddressFromBlit8bpp(bitmap->pixels, j, i, blitWidth);
*pixels = fillVal;
}
}
}
Window *Window_New(u32 heapID, u8 winCount)
{
Window *windows = (Window *)Heap_AllocFromHeap(heapID, sizeof(Window) * winCount);
for (u16 i = 0; i < winCount; i++) {
Window_Init(&windows[i]);
}
return windows;
}
void Window_Init(Window *window)
{
window->bgConfig = NULL;
window->bgLayer = 0xFF;
window->tilemapLeft = 0;
window->tilemapTop = 0;
window->width = 0;
window->height = 0;
window->palette = 0;
window->baseTile = 0;
window->pixels = NULL;
window->colorMode = BG_COLOR_MODE_4BPP;
}
u8 Window_IsInUse(Window *window)
{
if (window->bgConfig == NULL || window->bgLayer == 0xFF || window->pixels == NULL) {
return FALSE;
}
return TRUE;
}
void Window_Add(BgConfig *bgConfig, Window *window, u8 bgLayer, u8 tilemapLeft, u8 tilemapTop, u8 width, u8 height, u8 palette, u16 baseTile)
{
if (bgConfig->bgs[bgLayer].tilemapBuffer == NULL) {
return;
}
u32 size = width * height * bgConfig->bgs[bgLayer].tileSize;
void *pixels = Heap_AllocFromHeap(bgConfig->heapID, size);
if (pixels == NULL) {
return;
}
window->bgConfig = bgConfig;
window->bgLayer = bgLayer;
window->tilemapLeft = tilemapLeft;
window->tilemapTop = tilemapTop;
window->width = width;
window->height = height;
window->palette = palette;
window->baseTile = baseTile;
window->pixels = pixels;
window->colorMode = (bgConfig->bgs[bgLayer].colorMode == GX_BG_COLORMODE_16) ? BG_COLOR_MODE_4BPP : BG_COLOR_MODE_8BPP;
}
void Window_AddToTopLeftCorner(BgConfig *bgConfig, Window *window, u8 width, u8 height, u16 baseTile, u8 fillVal)
{
u32 size = width * height * TILE_SIZE_4BPP;
void *pixels = Heap_AllocFromHeap(bgConfig->heapID, size);
fillVal |= fillVal << 4;
memset(pixels, fillVal, size);
if (pixels == NULL) {
return;
}
window->bgConfig = bgConfig;
window->width = width;
window->height = height;
window->baseTile = baseTile;
window->pixels = pixels;
window->colorMode = BG_COLOR_MODE_4BPP;
}
void Window_AddFromTemplate(BgConfig *bgConfig, Window *window, const WindowTemplate *template)
{
Window_Add(bgConfig,
window,
template->bgLayer,
template->tilemapLeft,
template->tilemapTop,
template->width,
template->height,
template->palette,
template->baseTile);
}
void Window_Remove(Window *window)
{
Heap_FreeToHeap(window->pixels);
window->bgConfig = NULL;
window->bgLayer = 0xFF;
window->tilemapLeft = 0;
window->tilemapTop = 0;
window->width = 0;
window->height = 0;
window->palette = 0;
window->baseTile = 0;
window->pixels = NULL;
}
void Windows_Delete(Window *window, u8 winCount)
{
for (u16 i = 0; i < winCount; i++) {
if (window[i].pixels != NULL) {
Heap_FreeToHeap(window[i].pixels);
}
}
Heap_FreeToHeap(window);
}
typedef void (*WindowFunc)(Window *);
static const WindowFunc sCopyWindowToVramFuncs[] = {
[BG_TYPE_STATIC] = CopyWindowToVramText,
[BG_TYPE_AFFINE] = CopyWindowToVramAffine,
[BG_TYPE_STATIC_WITH_AFFINE] = CopyWindowToVramText,
};
static const WindowFunc sScheduleCopyWindowToVramFuncs[] = {
[BG_TYPE_STATIC] = ScheduleCopyWindowToVramText,
[BG_TYPE_AFFINE] = ScheduleCopyWindowToVramAffine,
[BG_TYPE_STATIC_WITH_AFFINE] = ScheduleCopyWindowToVramText,
};
static const WindowFunc sClearAndCopyWindowToVramFuncs[] = {
[BG_TYPE_STATIC] = ClearAndCopyWindowToVramText,
[BG_TYPE_AFFINE] = ClearAndCopyWindowToVramAffine,
[BG_TYPE_STATIC_WITH_AFFINE] = ClearAndCopyWindowToVramText,
};
static const WindowFunc sClearAndScheduleCopyWindowToVramFuncs[] = {
[BG_TYPE_STATIC] = ClearAndScheduleCopyWindowToVramText,
[BG_TYPE_AFFINE] = ClearAndScheduleCopyWindowToVramAffine,
[BG_TYPE_STATIC_WITH_AFFINE] = ClearAndScheduleCopyWindowToVramText,
};
static const WindowFunc sPutWindowToTilemapFuncs[] = {
[BG_TYPE_STATIC] = PutWindowToTilemapText,
[BG_TYPE_AFFINE] = PutWindowToTilemapAffine,
[BG_TYPE_STATIC_WITH_AFFINE] = PutWindowToTilemapText,
};
static const WindowFunc sClearWindowTilemapFuncs[] = {
[BG_TYPE_STATIC] = ClearWindowTilemapText,
[BG_TYPE_AFFINE] = ClearWindowTilemapAffine,
[BG_TYPE_STATIC_WITH_AFFINE] = ClearWindowTilemapText,
};
static const u8 sScreenSizeToTilemapWidth[] = {
[BG_SCREEN_SIZE_128x128] = 0x10,
[BG_SCREEN_SIZE_256x256] = 0x20,
[BG_SCREEN_SIZE_256x512] = 0x20,
[BG_SCREEN_SIZE_512x256] = 0x20,
[BG_SCREEN_SIZE_512x512] = 0x20,
[BG_SCREEN_SIZE_1024x1024] = 0x20,
};
void Window_CopyToVRAM(Window *window)
{
GF_ASSERT(window != NULL);
GF_ASSERT(window->bgConfig != NULL);
GF_ASSERT(window->bgLayer < BG_LAYER_MAX);
GF_ASSERT(window->bgConfig->bgs[window->bgLayer].type < NELEMS(sCopyWindowToVramFuncs));
sCopyWindowToVramFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
}
void Window_ScheduleCopyToVRAM(Window *window)
{
GF_ASSERT(window != NULL);
GF_ASSERT(window->bgConfig != NULL);
GF_ASSERT(window->bgLayer < BG_LAYER_MAX);
GF_ASSERT(window->bgConfig->bgs[window->bgLayer].type < NELEMS(sScheduleCopyWindowToVramFuncs));
sScheduleCopyWindowToVramFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
}
void Window_PutToTilemap(Window *window)
{
sPutWindowToTilemapFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
}
void Window_PutRectToTilemap(Window *window, u32 width, u32 height)
{
u32 oldWidth = window->width;
u32 oldHeight = window->height;
window->width = width;
window->height = height;
sPutWindowToTilemapFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
window->width = oldWidth;
window->height = oldHeight;
}
void Window_ClearTilemap(Window *window)
{
sClearWindowTilemapFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
}
static void PutWindowToTilemapText(Window *window)
{
u32 x, y, tilemapRight, tilemapBottom, tile, idx, tilemapWidth;
u16 *tilemap = window->bgConfig->bgs[window->bgLayer].tilemapBuffer;
if (tilemap == NULL) {
return;
}
tilemapWidth = 32; // required to match
tile = window->baseTile;
tilemapRight = window->tilemapLeft + window->width;
tilemapBottom = window->tilemapTop + window->height;
for (y = window->tilemapTop; y < tilemapBottom; y++) {
for (x = window->tilemapLeft; x < tilemapRight; x++) {
idx = ((y & 0x20) * 32) + ((x & 0x20) * 32) + ((y & 0x1F) * tilemapWidth) + (x & 0x1F);
tilemap[idx] = tile | (window->palette << 12);
tile++;
}
}
}
static void PutWindowToTilemapAffine(Window *window)
{
if (window->bgConfig->bgs[window->bgLayer].tilemapBuffer == NULL) {
return;
}
int x, y, tile, tilemapWidth;
u8 *tilemap;
tilemapWidth = sScreenSizeToTilemapWidth[window->bgConfig->bgs[window->bgLayer].screenSize];
tilemap = (u8 *)(window->bgConfig->bgs[window->bgLayer].tilemapBuffer) + window->tilemapTop * tilemapWidth + window->tilemapLeft;
tile = window->baseTile;
for (y = 0; y < window->height; y++) {
for (x = 0; x < window->width; x++) {
tilemap[x] = tile++;
}
tilemap += tilemapWidth;
}
}
static void ClearWindowTilemapText(Window *window)
{
if (window->bgConfig->bgs[window->bgLayer].tilemapBuffer == NULL) {
return;
}
u32 x, y, tilemapBottom, idx;
u32 tilemapWidth = sScreenSizeToTilemapWidth[window->bgConfig->bgs[window->bgLayer].screenSize];
u16 *tilemap = window->bgConfig->bgs[window->bgLayer].tilemapBuffer;
u32 tilemapRight = window->tilemapLeft + window->width;
tilemapBottom = window->tilemapTop + window->height; // required this way to match
for (y = window->tilemapTop; y < tilemapBottom; y++) {
for (x = window->tilemapLeft; x < tilemapRight; x++) {
idx = ((y & 0x20) * 32) + ((x & 0x20) * 32) + ((y & 0x1F) * tilemapWidth) + (x & 0x1F);
tilemap[idx] = 0;
}
}
}
static void ClearWindowTilemapAffine(Window *window)
{
if (window->bgConfig->bgs[window->bgLayer].tilemapBuffer == NULL) {
return;
}
int x, y, tilemapWidth = sScreenSizeToTilemapWidth[window->bgConfig->bgs[window->bgLayer].screenSize];
u8 *tilemap = (u8 *)(window->bgConfig->bgs[window->bgLayer].tilemapBuffer) + window->tilemapTop * tilemapWidth + window->tilemapLeft;
for (y = 0; y < window->height; y++) {
for (x = 0; x < window->width; x++) {
tilemap[x] = 0;
}
tilemap += tilemapWidth;
}
}
static void CopyWindowToVramText(Window *window)
{
PutWindowToTilemapText(window);
Window_LoadTiles(window);
Bg_CopyTilemapBufferRangeToVRAM(window->bgConfig,
window->bgLayer,
window->bgConfig->bgs[window->bgLayer].tilemapBuffer,
window->bgConfig->bgs[window->bgLayer].bufferSize,
window->bgConfig->bgs[window->bgLayer].baseTile);
}
static void ScheduleCopyWindowToVramText(Window *window)
{
PutWindowToTilemapText(window);
Bg_ScheduleTilemapTransfer(window->bgConfig, window->bgLayer);
Window_LoadTiles(window);
}
static void CopyWindowToVramAffine(Window *window)
{
PutWindowToTilemapAffine(window);
Bg_CopyTilemapBufferRangeToVRAM(window->bgConfig,
window->bgLayer,
window->bgConfig->bgs[window->bgLayer].tilemapBuffer,
window->bgConfig->bgs[window->bgLayer].bufferSize,
window->bgConfig->bgs[window->bgLayer].baseTile);
Bg_LoadTiles(window->bgConfig,
window->bgLayer,
window->pixels,
window->width * window->height * 0x40,
window->baseTile);
}
static void ScheduleCopyWindowToVramAffine(Window *window)
{
PutWindowToTilemapAffine(window);
Bg_ScheduleTilemapTransfer(window->bgConfig, window->bgLayer);
Bg_LoadTiles(window->bgConfig,
window->bgLayer,
window->pixels,
window->width * window->height * 0x40,
window->baseTile);
}
void Window_LoadTiles(Window *window)
{
u32 size = window->width * window->height * window->bgConfig->bgs[window->bgLayer].tileSize;
Bg_LoadTiles(window->bgConfig, window->bgLayer, window->pixels, size, window->baseTile);
}
void Window_ClearAndCopyToVRAM(Window *window)
{
sClearAndCopyWindowToVramFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
}
void Window_ClearAndScheduleCopyToVRAM(Window *window)
{
sClearAndScheduleCopyWindowToVramFuncs[window->bgConfig->bgs[window->bgLayer].type](window);
}
static void ClearAndCopyWindowToVramText(Window *window)
{
ClearWindowTilemapText(window);
Bg_CopyTilemapBufferRangeToVRAM(window->bgConfig,
window->bgLayer,
window->bgConfig->bgs[window->bgLayer].tilemapBuffer,
window->bgConfig->bgs[window->bgLayer].bufferSize,
window->bgConfig->bgs[window->bgLayer].baseTile);
}
static void ClearAndScheduleCopyWindowToVramText(Window *window)
{
ClearWindowTilemapText(window);
Bg_ScheduleTilemapTransfer(window->bgConfig, window->bgLayer);
}
static void ClearAndCopyWindowToVramAffine(Window *window)
{
ClearWindowTilemapAffine(window);
Bg_CopyTilemapBufferRangeToVRAM(window->bgConfig,
window->bgLayer,
window->bgConfig->bgs[window->bgLayer].tilemapBuffer,
window->bgConfig->bgs[window->bgLayer].bufferSize,
window->bgConfig->bgs[window->bgLayer].baseTile);
}
static void ClearAndScheduleCopyWindowToVramAffine(Window *window)
{
ClearWindowTilemapAffine(window);
Bg_ScheduleTilemapTransfer(window->bgConfig, window->bgLayer);
}
void Window_FillTilemap(Window *window, u8 val)
{
if (window->bgConfig->bgs[window->bgLayer].tileSize == TILE_SIZE_4BPP) {
val = (val << 4) | val;
}
u32 fill = (val << 24) | (val << 16) | (val << 8) | val;
u32 size = window->bgConfig->bgs[window->bgLayer].tileSize * window->width * window->height;
MI_CpuFillFast(window->pixels, fill, size);
}
void Window_BlitBitmapRect(Window *window, void *src, u16 srcX, u16 srcY, u16 srcWidth, u16 srcHeight, u16 destX, u16 destY, u16 destWidth, u16 destHeight)
{
Window_BlitBitmapRectWithTransparency(window, src, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, 0);
}
void Window_BlitBitmapRectWithTransparency(Window *window, void *src, u16 srcX, u16 srcY, u16 srcWidth, u16 srcHeight, u16 destX, u16 destY, u16 destWidth, u16 destHeight, u16 transparent)
{
Bitmap bmpSrc, bmpDest;
bmpSrc.pixels = src;
bmpSrc.width = srcWidth;
bmpSrc.height = srcHeight;
bmpDest.pixels = window->pixels;
bmpDest.width = window->width * 8;
bmpDest.height = window->height * 8;
if (window->bgConfig->bgs[window->bgLayer].colorMode == GX_BG_COLORMODE_16) {
Bitmap_BlitRect4bpp(&bmpSrc, &bmpDest, srcX, srcY, destX, destY, destWidth, destHeight, transparent);
} else {
Bitmap_BlitRect8bpp(&bmpSrc, &bmpDest, srcX, srcY, destX, destY, destWidth, destHeight, transparent);
}
}
void Window_FillRectWithColor(Window *window, u8 color, u16 x, u16 y, u16 width, u16 height)
{
Bitmap bmp;
bmp.pixels = window->pixels;
bmp.width = window->width * 8;
bmp.height = window->height * 8;
if (window->bgConfig->bgs[window->bgLayer].colorMode == GX_BG_COLORMODE_16) {
Bitmap_FillRect4bpp((const Bitmap *)&bmp, x, y, width, height, color);
} else {
Bitmap_FillRect8bpp((const Bitmap *)&bmp, x, y, width, height, color);
}
}
#define CopyGlyph4bpp(glyphPixels, srcX, srcY, srcWidth, srcHeight, windowPixels, destX, destY, destWidth, table) \
{ \
int srcJ, destJ, srcI, destI, bits; \
u8 toOrr; \
u8 tableFlag; \
u8 tableBit; \
u8 *dest; \
const u8 *src; \
u32 pixelData; \
\
src = glyphPixels + (srcY / 8 * 64) + (srcX / 8 * 32); \
if (srcY == 0) { \
destI = destY + srcY; \
tableBit = table & 0xFF; \
} else { \
destI = destY + srcY; \
for (srcI = 0; srcI < 8; srcI++) { \
if (((table >> srcI) & 1) != 0) { \
destI++; \
} \
} \
tableBit = table >> 8; \
} \
for (srcI = 0; srcI < srcHeight; srcI++) { \
pixelData = *(u32 *)src; \
tableFlag = (tableBit >> srcI) & 1; \
for (srcJ = 0, destJ = destX + srcX; srcJ < srcWidth; srcJ++, destJ++) { \
dest = CalcPixelAddressFromBlit4bpp(windowPixels, destJ, destI, destWidth); \
toOrr = (pixelData >> (srcJ * 4)) & 0xF; \
if (toOrr != 0) { \
bits = (destJ & 1) * 4; \
toOrr = (toOrr << bits) | (*dest & (0xF0 >> bits)); \
*dest = toOrr; \
if (tableFlag) { \
dest = CalcPixelAddressFromBlit4bpp(windowPixels, destJ, destI + 1, destWidth); \
*dest = toOrr; \
} \
} \
} \
if (tableFlag) { \
destI += 2; \
} else { \
destI += 1; \
} \
src += 4; \
} \
}
#define CopyGlyph8bpp(glyphPixels, srcX, srcY, srcWidth, srcHeight, windowPixels, destX, destY, destWidth, table) \
{ \
int srcJ, destJ, srcI, destI; \
u8 toOrr; \
u8 tableFlag; \
u8 tableBit; \
u8 *dest; \
const u8 *src; \
u8 *pixelData; \
\
src = glyphPixels + (srcY / 8 * 128) + (srcX / 8 * 64); \
if (srcY == 0) { \
destI = destY + srcY; \
tableBit = table & 0xFF; \
} else { \
destI = destY + srcY; \
for (srcI = 0; srcI < 8; srcI++) { \
if (((table >> srcI) & 1) != 0) { \
destI++; \
} \
} \
tableBit = table >> 8; \
} \
for (srcI = 0; srcI < srcHeight; srcI++) { \
pixelData = (u8 *)src; \
tableFlag = (tableBit >> srcI) & 1; \
for (srcJ = 0, destJ = destX + srcX; srcJ < srcWidth; srcJ++, destJ++) { \
dest = CalcPixelAddressFromBlit8bpp(windowPixels, destJ, destI, destWidth); \
toOrr = pixelData[srcJ]; \
if (toOrr != 0) { \
*dest = toOrr; \
if (tableFlag) { \
dest = CalcPixelAddressFromBlit8bpp(windowPixels, destJ, destI + 1, destWidth); \
*dest = toOrr; \
} \
} \
} \
if (tableFlag) { \
destI += 2; \
} else { \
destI += 1; \
} \
src += 8; \
} \
}
void Window_CopyGlyph(Window *window, const u8 *glyphPixels, u16 srcWidth, u16 srcHeight, u16 destX, u16 destY, u16 table)
{
u8 *windowPixels = window->pixels;
u16 destWidth = window->width * 8, destHeight = window->height * 8;
int srcRight, srcBottom;
u8 glyphSizeParam;
if (destWidth - destX < srcWidth) {
srcRight = destWidth - destX;
} else {
srcRight = srcWidth;
}
if (destHeight - destY < srcHeight) {
srcBottom = destHeight - destY;
} else {
srcBottom = srcHeight;
}
glyphSizeParam = 0;
if (srcRight > 8) {
glyphSizeParam |= 1;
}
if (srcBottom > 8) {
glyphSizeParam |= 2;
}
if (window->colorMode == BG_COLOR_MODE_4BPP) {
switch (glyphSizeParam) {
case 0: // 1x1
CopyGlyph4bpp(glyphPixels, 0, 0, srcRight, srcBottom, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
return;
case 1: // 2x1
CopyGlyph4bpp(glyphPixels, 0, 0, 8, srcBottom, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph4bpp(glyphPixels, 8, 0, srcRight - 8, srcBottom, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
return;
case 2: // 1x2
CopyGlyph4bpp(glyphPixels, 0, 0, srcRight, 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph4bpp(glyphPixels, 0, 8, srcRight, srcBottom - 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
return;
case 3: // 2x2
CopyGlyph4bpp(glyphPixels, 0, 0, 8, 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph4bpp(glyphPixels, 8, 0, srcRight - 8, 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph4bpp(glyphPixels, 0, 8, 8, srcBottom - 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph4bpp(glyphPixels, 8, 8, srcRight - 8, srcBottom - 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
return;
}
} else {
u8 *convertedSrc = Convert4bppTo8bpp(glyphPixels, srcWidth * 4 * srcHeight * 8, window->palette, window->bgConfig->heapID);
switch (glyphSizeParam) {
case 0: // 1x1
CopyGlyph8bpp(convertedSrc, 0, 0, srcRight, srcBottom, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
break;
case 1: // 2x1
CopyGlyph8bpp(convertedSrc, 0, 0, 8, srcBottom, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph8bpp(convertedSrc, 8, 0, srcRight - 8, srcBottom, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
break;
case 2: // 1x2
CopyGlyph8bpp(convertedSrc, 0, 0, srcRight, 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph8bpp(convertedSrc, 0, 8, srcRight, srcBottom - 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
break;
case 3: // 2x2
CopyGlyph8bpp(convertedSrc, 0, 0, 8, 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph8bpp(convertedSrc, 8, 0, srcRight - 8, 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph8bpp(convertedSrc, 0, 8, 8, srcBottom - 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
CopyGlyph8bpp(convertedSrc, 8, 8, srcRight - 8, srcBottom - 8, windowPixels, destX, destY, ConvertPixelsToTiles(destWidth), table);
break;
}
Heap_FreeToHeap(convertedSrc);
}
}
void Window_Scroll(Window *window, u8 direction, u8 distance, u8 fillVal)
{
if (window->bgConfig->bgs[window->bgLayer].colorMode == GX_BG_COLORMODE_16) {
ScrollWindow4bpp(window, direction, distance, fillVal);
} else {
ScrollWindow8bpp(window, direction, distance, fillVal);
}
}
static void ScrollWindow4bpp(Window *window, u8 direction, u8 distance, u8 fillVal)
{
u8 *pixels = window->pixels;
int y0, y1, y2;
int fill = (fillVal << 24) | (fillVal << 16) | (fillVal << 8) | (fillVal << 0);
int size = window->height * window->width * TILE_SIZE_4BPP;
u32 width = window->width;
int i, j;
switch (direction) {
case SCROLL_DIRECTION_UP:
for (i = 0; i < size; i += TILE_SIZE_4BPP) {
y0 = distance;
for (j = 0; j < 8; j++) {
y1 = i + (j << 2);
y2 = i + (((width * (y0 & ~7)) | (y0 & 7)) << 2);
if (y2 < size) {
*(u32 *)(pixels + y1) = *(u32 *)(pixels + y2);
} else {
*(u32 *)(pixels + y1) = fill;
}
y0++;
}
}
break;
case SCROLL_DIRECTION_DOWN:
pixels += size - 4;
for (i = 0; i < size; i += TILE_SIZE_4BPP) {
y0 = distance;
for (j = 0; j < 8; j++) {
y1 = i + (j << 2);
y2 = i + (((width * (y0 & ~7)) | (y0 & 7)) << 2);
if (y2 < size) {
*(u32 *)(pixels - y1) = *(u32 *)(pixels - y2);
} else {
*(u32 *)(pixels - y1) = fill;
}
y0++;
}
}
break;
case SCROLL_DIRECTION_LEFT:
break;
case SCROLL_DIRECTION_RIGHT:
break;
}
}
static void ScrollWindow8bpp(Window *window, u8 direction, u8 distance, u8 fillVal)
{
u8 *pixels = window->pixels;
int y0, y1, y2;
int fill = (fillVal << 24) | (fillVal << 16) | (fillVal << 8) | fillVal;
int size = window->height * window->width * TILE_SIZE_8BPP;
u32 width = window->width;
int i, j;
switch (direction) {
case SCROLL_DIRECTION_UP:
for (i = 0; i < size; i += TILE_SIZE_8BPP) {
y0 = distance;
for (j = 0; j < 8; j++) {
y1 = i + (j << 3);
y2 = i + (((width * (y0 & ~7)) | (y0 & 7)) << 3);
if (y2 < size) {
*(u32 *)(pixels + y1) = *(u32 *)(pixels + y2);
} else {
*(u32 *)(pixels + y1) = fill;
}
y1 += 4;
y2 += 4;
if (y2 < size + 4) {
*(u32 *)(pixels + y1) = *(u32 *)(pixels + y2);
} else {
*(u32 *)(pixels + y1) = fill;
}
y0++;
}
}
break;
case SCROLL_DIRECTION_DOWN:
pixels += (size - 8);
for (i = 0; i < size; i += TILE_SIZE_8BPP) {
y0 = distance;
for (j = 0; j < 8; j++) {
y1 = i + (j << 3);
y2 = i + (((width * (y0 & ~7)) | (y0 & 7)) << 3);
if (y2 < size) {
*(u32 *)(pixels - y1) = *(u32 *)(pixels - y2);
} else {
*(u32 *)(pixels - y1) = fill;
}
y1 -= 4;
y2 -= 4;
if (y2 < size - 4) {
*(u32 *)(pixels - y1) = *(u32 *)(pixels - y2);
} else {
*(u32 *)(pixels - y1) = fill;
}
y0++;
}
}
break;
case SCROLL_DIRECTION_LEFT:
break;
case SCROLL_DIRECTION_RIGHT:
break;
}
}
BgConfig *Window_GetBgConfig(Window *window)
{
return window->bgConfig;
}
u8 Window_GetBgLayer(Window *window)
{
return window->bgLayer;
}
u8 Window_GetWidth(Window *window)
{
return window->width;
}
u8 Window_GetHeight(Window *window)
{
return window->height;
}
u8 Window_GetXPos(Window *window)
{
return window->tilemapLeft;
}
u8 Window_GetYPos(Window *window)
{
return window->tilemapTop;
}
u16 Window_GetBaseTile(Window *window)
{
return window->baseTile;
}
void Window_SetXPos(Window *window, u8 x)
{
window->tilemapLeft = x;
}
void Window_SetYPos(Window *window, u8 y)
{
window->tilemapTop = y;
}
void Window_SetPalette(Window *window, u8 palette)
{
window->palette = palette;
}
void Bg_RunScheduledUpdates(BgConfig *bgConfig)
{
RunScheduledScrolls(bgConfig);
RunScheduledTilemapTransfers(bgConfig);
bgConfig->scrollScheduled = FALSE;
bgConfig->bufferTransferScheduled = FALSE;
}
static void RunScheduledTilemapTransfers(BgConfig *bgConfig)
{
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_MAIN_0)) != 0) {
LoadBgVRAMScr(BG_LAYER_MAIN_0, bgConfig->bgs[BG_LAYER_MAIN_0].tilemapBuffer, bgConfig->bgs[BG_LAYER_MAIN_0].baseTile * 0x2, bgConfig->bgs[BG_LAYER_MAIN_0].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_MAIN_1)) != 0) {
LoadBgVRAMScr(BG_LAYER_MAIN_1, bgConfig->bgs[BG_LAYER_MAIN_1].tilemapBuffer, bgConfig->bgs[BG_LAYER_MAIN_1].baseTile * 0x2, bgConfig->bgs[BG_LAYER_MAIN_1].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_MAIN_2)) != 0) {
LoadBgVRAMScr(BG_LAYER_MAIN_2, bgConfig->bgs[BG_LAYER_MAIN_2].tilemapBuffer, bgConfig->bgs[BG_LAYER_MAIN_2].baseTile * 0x2, bgConfig->bgs[BG_LAYER_MAIN_2].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_MAIN_3)) != 0) {
LoadBgVRAMScr(BG_LAYER_MAIN_3, bgConfig->bgs[BG_LAYER_MAIN_3].tilemapBuffer, bgConfig->bgs[BG_LAYER_MAIN_3].baseTile * 0x2, bgConfig->bgs[BG_LAYER_MAIN_3].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_SUB_0)) != 0) {
LoadBgVRAMScr(BG_LAYER_SUB_0, bgConfig->bgs[BG_LAYER_SUB_0].tilemapBuffer, bgConfig->bgs[BG_LAYER_SUB_0].baseTile * 0x2, bgConfig->bgs[BG_LAYER_SUB_0].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_SUB_1)) != 0) {
LoadBgVRAMScr(BG_LAYER_SUB_1, bgConfig->bgs[BG_LAYER_SUB_1].tilemapBuffer, bgConfig->bgs[BG_LAYER_SUB_1].baseTile * 0x2, bgConfig->bgs[BG_LAYER_SUB_1].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_SUB_2)) != 0) {
LoadBgVRAMScr(BG_LAYER_SUB_2, bgConfig->bgs[BG_LAYER_SUB_2].tilemapBuffer, bgConfig->bgs[BG_LAYER_SUB_2].baseTile * 0x2, bgConfig->bgs[BG_LAYER_SUB_2].bufferSize);
}
if ((bgConfig->bufferTransferScheduled & (1 << BG_LAYER_SUB_3)) != 0) {
LoadBgVRAMScr(BG_LAYER_SUB_3, bgConfig->bgs[BG_LAYER_SUB_3].tilemapBuffer, bgConfig->bgs[BG_LAYER_SUB_3].baseTile * 0x2, bgConfig->bgs[BG_LAYER_SUB_3].bufferSize);
}
}
void Bg_ScheduleTilemapTransfer(BgConfig *bgConfig, u8 bgLayer)
{
bgConfig->bufferTransferScheduled |= (1 << bgLayer);
}
static void RunScheduledScrolls(BgConfig *bgConfig)
{
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_MAIN_0)) != 0) {
G2_SetBG0Offset(bgConfig->bgs[BG_LAYER_MAIN_0].xOffset, bgConfig->bgs[BG_LAYER_MAIN_0].yOffset);
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_MAIN_1)) != 0) {
G2_SetBG1Offset(bgConfig->bgs[BG_LAYER_MAIN_1].xOffset, bgConfig->bgs[BG_LAYER_MAIN_1].yOffset);
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_MAIN_2)) != 0) {
if (bgConfig->bgs[BG_LAYER_MAIN_2].type == BG_TYPE_STATIC) {
G2_SetBG2Offset(bgConfig->bgs[BG_LAYER_MAIN_2].xOffset, bgConfig->bgs[BG_LAYER_MAIN_2].yOffset);
} else {
MtxFx22 mtx;
CreateAffineTransformationMatrix(&mtx, bgConfig->bgs[BG_LAYER_MAIN_2].rotation, bgConfig->bgs[BG_LAYER_MAIN_2].xScale, bgConfig->bgs[BG_LAYER_MAIN_2].yScale, AFFINE_MODE_MAX_360);
G2_SetBG2Affine(&mtx, bgConfig->bgs[BG_LAYER_MAIN_2].xCenter, bgConfig->bgs[BG_LAYER_MAIN_2].yCenter, bgConfig->bgs[BG_LAYER_MAIN_2].xOffset, bgConfig->bgs[BG_LAYER_MAIN_2].yOffset);
}
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_MAIN_3)) != 0) {
if (bgConfig->bgs[BG_LAYER_MAIN_3].type == BG_TYPE_STATIC) {
G2_SetBG3Offset(bgConfig->bgs[BG_LAYER_MAIN_3].xOffset, bgConfig->bgs[BG_LAYER_MAIN_3].yOffset);
} else {
MtxFx22 mtx;
CreateAffineTransformationMatrix(&mtx, bgConfig->bgs[BG_LAYER_MAIN_3].rotation, bgConfig->bgs[BG_LAYER_MAIN_3].xScale, bgConfig->bgs[BG_LAYER_MAIN_3].yScale, AFFINE_MODE_MAX_360);
G2_SetBG3Affine(&mtx, bgConfig->bgs[BG_LAYER_MAIN_3].xCenter, bgConfig->bgs[BG_LAYER_MAIN_3].yCenter, bgConfig->bgs[BG_LAYER_MAIN_3].xOffset, bgConfig->bgs[BG_LAYER_MAIN_3].yOffset);
}
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_SUB_0)) != 0) {
G2S_SetBG0Offset(bgConfig->bgs[BG_LAYER_SUB_0].xOffset, bgConfig->bgs[BG_LAYER_SUB_0].yOffset);
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_SUB_1)) != 0) {
G2S_SetBG1Offset(bgConfig->bgs[BG_LAYER_SUB_1].xOffset, bgConfig->bgs[BG_LAYER_SUB_1].yOffset);
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_SUB_2)) != 0) {
if (bgConfig->bgs[BG_LAYER_SUB_2].type == BG_TYPE_STATIC) {
G2S_SetBG2Offset(bgConfig->bgs[BG_LAYER_SUB_2].xOffset, bgConfig->bgs[BG_LAYER_SUB_2].yOffset);
} else {
MtxFx22 mtx;
CreateAffineTransformationMatrix(&mtx, bgConfig->bgs[BG_LAYER_SUB_2].rotation, bgConfig->bgs[BG_LAYER_SUB_2].xScale, bgConfig->bgs[BG_LAYER_SUB_2].yScale, AFFINE_MODE_MAX_360);
G2S_SetBG2Affine(&mtx, bgConfig->bgs[BG_LAYER_SUB_2].xCenter, bgConfig->bgs[BG_LAYER_SUB_2].yCenter, bgConfig->bgs[BG_LAYER_SUB_2].xOffset, bgConfig->bgs[BG_LAYER_SUB_2].yOffset);
}
}
if ((bgConfig->scrollScheduled & (1 << BG_LAYER_SUB_3)) != 0) {
if (bgConfig->bgs[BG_LAYER_SUB_3].type == BG_TYPE_STATIC) {
G2S_SetBG3Offset(bgConfig->bgs[BG_LAYER_SUB_3].xOffset, bgConfig->bgs[BG_LAYER_SUB_3].yOffset);
} else {
MtxFx22 mtx;
CreateAffineTransformationMatrix(&mtx, bgConfig->bgs[BG_LAYER_SUB_3].rotation, bgConfig->bgs[BG_LAYER_SUB_3].xScale, bgConfig->bgs[BG_LAYER_SUB_3].yScale, AFFINE_MODE_MAX_360);
G2S_SetBG3Affine(&mtx, bgConfig->bgs[BG_LAYER_SUB_3].xCenter, bgConfig->bgs[BG_LAYER_SUB_3].yCenter, bgConfig->bgs[BG_LAYER_SUB_3].xOffset, bgConfig->bgs[BG_LAYER_SUB_3].yOffset);
}
}
}
void Bg_ScheduleScroll(BgConfig *bgConfig, u8 bgLayer, u8 bgUpdateOffsetOp, int val)
{
UpdateBgOffsetWithVal(&bgConfig->bgs[bgLayer], bgUpdateOffsetOp, val);
bgConfig->scrollScheduled |= (1 << bgLayer);
}
void Bg_ScheduleAffineRotation(BgConfig *bgConfig, u8 bgLayer, u8 bgAffineUpdateOp, u16 val)
{
UpdateBgRotationWithVal(&bgConfig->bgs[bgLayer], bgAffineUpdateOp, val);
bgConfig->scrollScheduled |= (1 << bgLayer);
}
static void UpdateBgRotationWithVal(Background *bg, enum BgAffineUpdateOp op, u16 val)
{
switch (op) {
case BG_AFFINE_UPDATE_SET_ROTATION:
bg->rotation = val;
break;
case BG_AFFINE_UPDATE_ADD_ROTATION:
bg->rotation += val;
break;
case BG_AFFINE_UPDATE_SUB_ROTATION:
bg->rotation -= val;
break;
default:
break;
}
}
void Bg_ScheduleAffineScale(BgConfig *bgConfig, u8 bgLayer, u8 bgAffineUpdateOp, fx32 val)
{
UpdateBgAffineScaleWithVal(&bgConfig->bgs[bgLayer], bgAffineUpdateOp, val);
bgConfig->scrollScheduled |= (1 << bgLayer);
}
static void UpdateBgAffineScaleWithVal(Background *bg, enum BgAffineUpdateOp op, fx32 val)
{
switch (op) {
case BG_AFFINE_UPDATE_SET_X_SCALE:
bg->xScale = val;
break;
case BG_AFFINE_UPDATE_ADD_X_SCALE:
bg->xScale += val;
break;
case BG_AFFINE_UPDATE_SUB_X_SCALE:
bg->xScale -= val;
break;
case BG_AFFINE_UPDATE_SET_Y_SCALE:
bg->yScale = val;
break;
case BG_AFFINE_UPDATE_ADD_Y_SCALE:
bg->yScale += val;
break;
case BG_AFFINE_UPDATE_SUB_Y_SCALE:
bg->yScale -= val;
break;
default:
break;
}
}
void Bg_ScheduleAffineRotationCenter(BgConfig *bgConfig, u8 bgLayer, u8 bgAffineUpdateOp, int val)
{
UpdateBgAffineRotationCenterWithVal(&bgConfig->bgs[bgLayer], bgAffineUpdateOp, val);
bgConfig->scrollScheduled |= (1 << bgLayer);
}
static void UpdateBgAffineRotationCenterWithVal(Background *bg, enum BgAffineUpdateOp op, int val)
{
switch (op) {
case BG_AFFINE_UPDATE_SET_X_CENTER:
bg->xCenter = val;
break;
case BG_AFFINE_UPDATE_ADD_X_CENTER:
bg->xCenter += val;
break;
case BG_AFFINE_UPDATE_SUB_X_CENTER:
bg->xCenter -= val;
break;
case BG_AFFINE_UPDATE_SET_Y_CENTER:
bg->yCenter = val;
break;
case BG_AFFINE_UPDATE_ADD_Y_CENTER:
bg->yCenter += val;
break;
case BG_AFFINE_UPDATE_SUB_Y_CENTER:
bg->yCenter -= val;
break;
default:
break;
}
}
u8 Bg_DoesPixelAtXYMatchVal(BgConfig *bgConfig, u8 bgLayer, u16 x, u16 y, u16 *src)
{
u8 *charPtr;
u16 tilemapIndex;
u8 xPixelOffset, yPixelOffset;
u8 pixelVal;
u8 i;
if (bgConfig->bgs[bgLayer].tilemapBuffer == NULL) {
return FALSE;
}
tilemapIndex = CalcTilemapIndexFromCoords(x / 8, y / 8, bgConfig->bgs[bgLayer].screenSize);
charPtr = Bg_GetCharPtr(bgLayer);
xPixelOffset = x & 7;
yPixelOffset = y & 7;
if (bgConfig->bgs[bgLayer].colorMode == GX_BG_COLORMODE_16) {
u16 *tilemapBuffer = bgConfig->bgs[bgLayer].tilemapBuffer;
u8 *tile = Heap_AllocFromHeapAtEnd(bgConfig->heapID, 64);
charPtr += (tilemapBuffer[tilemapIndex] & 0x3FF) * TILE_SIZE_4BPP;
for (i = 0; i < TILE_SIZE_4BPP; i++) {
tile[i * 2] = charPtr[i] & 0xF;
tile[i * 2 + 1] = charPtr[i] >> 4;
}
ApplyFlipFlagsToTile(bgConfig, (tilemapBuffer[tilemapIndex] >> 10) & 3, tile);
pixelVal = tile[xPixelOffset + yPixelOffset * 8];
Heap_FreeToHeap(tile);
if ((*src & (1 << pixelVal)) != 0) {
return TRUE;
}
} else {
if (bgConfig->bgs[bgLayer].type != BG_TYPE_AFFINE) {
u16 *tilemapBuffer = bgConfig->bgs[bgLayer].tilemapBuffer;
u8 *tile = Heap_AllocFromHeapAtEnd(bgConfig->heapID, 64);
memcpy(tile, &charPtr[(tilemapBuffer[tilemapIndex] & 0x3FF) * TILE_SIZE_8BPP], 64);
ApplyFlipFlagsToTile(bgConfig, (tilemapBuffer[tilemapIndex] >> 10) & 3, tile);
pixelVal = tile[xPixelOffset + yPixelOffset * 8];
Heap_FreeToHeap(tile);
} else {
u8 *tilemapBuffer = bgConfig->bgs[bgLayer].tilemapBuffer;
pixelVal = charPtr[tilemapBuffer[tilemapIndex] * TILE_SIZE_8BPP + xPixelOffset + yPixelOffset * 8];
}
// BUG: Infinite loop
while (TRUE) {
if (*src == 0xFFFF) {
break;
}
if (pixelVal == (u8)*src) {
return TRUE;
}
}
}
return FALSE;
}
static void ApplyFlipFlagsToTile(BgConfig *bgConfig, u8 flags, u8 *tile)
{
if (flags == 0) {
return;
}
u8 *buf = Heap_AllocFromHeapAtEnd(bgConfig->heapID, 64);
if (flags & 1) {
for (u8 i = 0; i < 8; i++) {
for (u8 j = 0; j < 8; j++) {
buf[i * 8 + j] = tile[i * 8 + (7 - j)];
}
}
memcpy(tile, buf, 64);
}
if (flags & 2) {
for (u8 i = 0; i < 8; i++) {
memcpy(&buf[i * 8], &tile[(7 - i) * 8], 8);
}
memcpy(tile, buf, 64);
}
Heap_FreeToHeap(buf);
}